blob: b7330e1f9c20cd20e7e3708ca7dbaae1c399330c [file] [log] [blame]
Zeke Chin71f6f442015-06-29 14:34:58 -07001/*
2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 *
10 */
11
Anders Carlsson7bca8ca2018-08-30 09:30:29 +020012#include "sdk/objc/components/video_codec/nalu_rewriter.h"
Zeke Chin71f6f442015-06-29 14:34:58 -070013
14#include <CoreFoundation/CoreFoundation.h>
kwiberg3f55dea2016-02-29 05:51:59 -080015#include <memory>
Zeke Chin71f6f442015-06-29 14:34:58 -070016#include <vector>
17
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "rtc_base/checks.h"
19#include "rtc_base/logging.h"
Zeke Chin71f6f442015-06-29 14:34:58 -070020
21namespace webrtc {
22
jianjun.zhue1b4d242016-08-15 18:56:18 -070023using H264::kAud;
24using H264::kSps;
Kári Tristan Helgasoncb18ee62016-11-02 15:07:02 +010025using H264::NaluIndex;
26using H264::NaluType;
jianjun.zhue1b4d242016-08-15 18:56:18 -070027using H264::ParseNaluType;
28
Zeke Chin71f6f442015-06-29 14:34:58 -070029const char kAnnexBHeaderBytes[4] = {0, 0, 0, 1};
30const size_t kAvccHeaderByteSize = sizeof(uint32_t);
31
Danil Chapovalov090049c2020-08-25 15:49:53 +020032bool H264CMSampleBufferToAnnexBBuffer(CMSampleBufferRef avcc_sample_buffer,
33 bool is_keyframe,
34 rtc::Buffer* annexb_buffer) {
henrikg91d6ede2015-09-17 00:24:34 -070035 RTC_DCHECK(avcc_sample_buffer);
Zeke Chin71f6f442015-06-29 14:34:58 -070036
37 // Get format description from the sample buffer.
38 CMVideoFormatDescriptionRef description =
39 CMSampleBufferGetFormatDescription(avcc_sample_buffer);
40 if (description == nullptr) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010041 RTC_LOG(LS_ERROR) << "Failed to get sample buffer's description.";
Zeke Chin71f6f442015-06-29 14:34:58 -070042 return false;
43 }
44
45 // Get parameter set information.
46 int nalu_header_size = 0;
47 size_t param_set_count = 0;
48 OSStatus status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
49 description, 0, nullptr, nullptr, &param_set_count, &nalu_header_size);
50 if (status != noErr) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010051 RTC_LOG(LS_ERROR) << "Failed to get parameter set.";
Zeke Chin71f6f442015-06-29 14:34:58 -070052 return false;
53 }
Kári Tristan Helgasoncb18ee62016-11-02 15:07:02 +010054 RTC_CHECK_EQ(nalu_header_size, kAvccHeaderByteSize);
kwibergaf476c72016-11-28 15:21:39 -080055 RTC_DCHECK_EQ(param_set_count, 2);
Zeke Chin71f6f442015-06-29 14:34:58 -070056
57 // Truncate any previous data in the buffer without changing its capacity.
58 annexb_buffer->SetSize(0);
59
Zeke Chin71f6f442015-06-29 14:34:58 -070060 // Place all parameter sets at the front of buffer.
61 if (is_keyframe) {
62 size_t param_set_size = 0;
63 const uint8_t* param_set = nullptr;
64 for (size_t i = 0; i < param_set_count; ++i) {
65 status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
66 description, i, &param_set, &param_set_size, nullptr, nullptr);
67 if (status != noErr) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010068 RTC_LOG(LS_ERROR) << "Failed to get parameter set.";
Zeke Chin71f6f442015-06-29 14:34:58 -070069 return false;
70 }
71 // Update buffer.
72 annexb_buffer->AppendData(kAnnexBHeaderBytes, sizeof(kAnnexBHeaderBytes));
73 annexb_buffer->AppendData(reinterpret_cast<const char*>(param_set),
74 param_set_size);
Zeke Chin71f6f442015-06-29 14:34:58 -070075 }
76 }
77
78 // Get block buffer from the sample buffer.
79 CMBlockBufferRef block_buffer =
80 CMSampleBufferGetDataBuffer(avcc_sample_buffer);
81 if (block_buffer == nullptr) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010082 RTC_LOG(LS_ERROR) << "Failed to get sample buffer's block buffer.";
Zeke Chin71f6f442015-06-29 14:34:58 -070083 return false;
84 }
85 CMBlockBufferRef contiguous_buffer = nullptr;
86 // Make sure block buffer is contiguous.
87 if (!CMBlockBufferIsRangeContiguous(block_buffer, 0, 0)) {
88 status = CMBlockBufferCreateContiguous(
89 nullptr, block_buffer, nullptr, nullptr, 0, 0, 0, &contiguous_buffer);
90 if (status != noErr) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010091 RTC_LOG(LS_ERROR) << "Failed to flatten non-contiguous block buffer: "
92 << status;
Zeke Chin71f6f442015-06-29 14:34:58 -070093 return false;
94 }
95 } else {
96 contiguous_buffer = block_buffer;
97 // Retain to make cleanup easier.
98 CFRetain(contiguous_buffer);
99 block_buffer = nullptr;
100 }
101
102 // Now copy the actual data.
103 char* data_ptr = nullptr;
104 size_t block_buffer_size = CMBlockBufferGetDataLength(contiguous_buffer);
105 status = CMBlockBufferGetDataPointer(contiguous_buffer, 0, nullptr, nullptr,
106 &data_ptr);
107 if (status != noErr) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100108 RTC_LOG(LS_ERROR) << "Failed to get block buffer data.";
Zeke Chin71f6f442015-06-29 14:34:58 -0700109 CFRelease(contiguous_buffer);
110 return false;
111 }
112 size_t bytes_remaining = block_buffer_size;
113 while (bytes_remaining > 0) {
Artem Titovd7ac5812021-07-27 12:23:39 +0200114 // The size type here must match `nalu_header_size`, we expect 4 bytes.
Zeke Chin71f6f442015-06-29 14:34:58 -0700115 // Read the length of the next packet of data. Must convert from big endian
116 // to host endian.
henrikg91d6ede2015-09-17 00:24:34 -0700117 RTC_DCHECK_GE(bytes_remaining, (size_t)nalu_header_size);
Peter Boström0c4e06b2015-10-07 12:23:21 +0200118 uint32_t* uint32_data_ptr = reinterpret_cast<uint32_t*>(data_ptr);
Zeke Chin71f6f442015-06-29 14:34:58 -0700119 uint32_t packet_size = CFSwapInt32BigToHost(*uint32_data_ptr);
120 // Update buffer.
121 annexb_buffer->AppendData(kAnnexBHeaderBytes, sizeof(kAnnexBHeaderBytes));
122 annexb_buffer->AppendData(data_ptr + nalu_header_size, packet_size);
Zeke Chin71f6f442015-06-29 14:34:58 -0700123
Kári Tristan Helgasoncb18ee62016-11-02 15:07:02 +0100124 size_t bytes_written = packet_size + sizeof(kAnnexBHeaderBytes);
Zeke Chin71f6f442015-06-29 14:34:58 -0700125 bytes_remaining -= bytes_written;
126 data_ptr += bytes_written;
127 }
henrikg91d6ede2015-09-17 00:24:34 -0700128 RTC_DCHECK_EQ(bytes_remaining, (size_t)0);
Zeke Chin71f6f442015-06-29 14:34:58 -0700129
Zeke Chin71f6f442015-06-29 14:34:58 -0700130 CFRelease(contiguous_buffer);
131 return true;
132}
133
philipelcce46fc2015-12-21 03:04:49 -0800134bool H264AnnexBBufferToCMSampleBuffer(const uint8_t* annexb_buffer,
135 size_t annexb_buffer_size,
136 CMVideoFormatDescriptionRef video_format,
Kári Tristan Helgason0d247722018-10-26 10:59:28 +0200137 CMSampleBufferRef* out_sample_buffer,
138 CMMemoryPoolRef memory_pool) {
henrikg91d6ede2015-09-17 00:24:34 -0700139 RTC_DCHECK(annexb_buffer);
140 RTC_DCHECK(out_sample_buffer);
tkchin5ed5ed92016-03-08 10:51:54 -0800141 RTC_DCHECK(video_format);
Zeke Chin71f6f442015-06-29 14:34:58 -0700142 *out_sample_buffer = nullptr;
143
Zeke Chin71f6f442015-06-29 14:34:58 -0700144 AnnexBBufferReader reader(annexb_buffer, annexb_buffer_size);
Guy Hershenbaum2fcb8342018-02-20 21:33:36 -0800145 if (reader.SeekToNextNaluOfType(kSps)) {
146 // Buffer contains an SPS NALU - skip it and the following PPS
147 const uint8_t* data;
148 size_t data_len;
tkchin5ed5ed92016-03-08 10:51:54 -0800149 if (!reader.ReadNalu(&data, &data_len)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100150 RTC_LOG(LS_ERROR) << "Failed to read SPS";
Zeke Chin71f6f442015-06-29 14:34:58 -0700151 return false;
152 }
tkchin5ed5ed92016-03-08 10:51:54 -0800153 if (!reader.ReadNalu(&data, &data_len)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100154 RTC_LOG(LS_ERROR) << "Failed to read PPS";
Zeke Chin71f6f442015-06-29 14:34:58 -0700155 return false;
156 }
Guy Hershenbaum2fcb8342018-02-20 21:33:36 -0800157 } else {
158 // No SPS NALU - start reading from the first NALU in the buffer
159 reader.SeekToStart();
Zeke Chin71f6f442015-06-29 14:34:58 -0700160 }
161
162 // Allocate memory as a block buffer.
Zeke Chin71f6f442015-06-29 14:34:58 -0700163 CMBlockBufferRef block_buffer = nullptr;
Kári Tristan Helgason0d247722018-10-26 10:59:28 +0200164 CFAllocatorRef block_allocator = CMMemoryPoolGetAllocator(memory_pool);
tkchin5ed5ed92016-03-08 10:51:54 -0800165 OSStatus status = CMBlockBufferCreateWithMemoryBlock(
Kári Tristan Helgason0d247722018-10-26 10:59:28 +0200166 kCFAllocatorDefault, nullptr, reader.BytesRemaining(), block_allocator,
167 nullptr, 0, reader.BytesRemaining(), kCMBlockBufferAssureMemoryNowFlag,
Zeke Chin71f6f442015-06-29 14:34:58 -0700168 &block_buffer);
169 if (status != kCMBlockBufferNoErr) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100170 RTC_LOG(LS_ERROR) << "Failed to create block buffer.";
Zeke Chin71f6f442015-06-29 14:34:58 -0700171 return false;
172 }
173
174 // Make sure block buffer is contiguous.
175 CMBlockBufferRef contiguous_buffer = nullptr;
176 if (!CMBlockBufferIsRangeContiguous(block_buffer, 0, 0)) {
Kári Tristan Helgason0d247722018-10-26 10:59:28 +0200177 status = CMBlockBufferCreateContiguous(kCFAllocatorDefault, block_buffer,
178 block_allocator, nullptr, 0, 0, 0,
179 &contiguous_buffer);
Zeke Chin71f6f442015-06-29 14:34:58 -0700180 if (status != noErr) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100181 RTC_LOG(LS_ERROR) << "Failed to flatten non-contiguous block buffer: "
182 << status;
Zeke Chin71f6f442015-06-29 14:34:58 -0700183 CFRelease(block_buffer);
184 return false;
185 }
186 } else {
187 contiguous_buffer = block_buffer;
188 block_buffer = nullptr;
189 }
190
191 // Get a raw pointer into allocated memory.
192 size_t block_buffer_size = 0;
193 char* data_ptr = nullptr;
194 status = CMBlockBufferGetDataPointer(contiguous_buffer, 0, nullptr,
195 &block_buffer_size, &data_ptr);
196 if (status != kCMBlockBufferNoErr) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100197 RTC_LOG(LS_ERROR) << "Failed to get block buffer data pointer.";
Zeke Chin71f6f442015-06-29 14:34:58 -0700198 CFRelease(contiguous_buffer);
199 return false;
200 }
henrikg91d6ede2015-09-17 00:24:34 -0700201 RTC_DCHECK(block_buffer_size == reader.BytesRemaining());
Zeke Chin71f6f442015-06-29 14:34:58 -0700202
203 // Write Avcc NALUs into block buffer memory.
204 AvccBufferWriter writer(reinterpret_cast<uint8_t*>(data_ptr),
205 block_buffer_size);
206 while (reader.BytesRemaining() > 0) {
207 const uint8_t* nalu_data_ptr = nullptr;
208 size_t nalu_data_size = 0;
209 if (reader.ReadNalu(&nalu_data_ptr, &nalu_data_size)) {
210 writer.WriteNalu(nalu_data_ptr, nalu_data_size);
211 }
212 }
213
214 // Create sample buffer.
Kári Tristan Helgason0d247722018-10-26 10:59:28 +0200215 status = CMSampleBufferCreate(kCFAllocatorDefault, contiguous_buffer, true,
216 nullptr, nullptr, video_format, 1, 0, nullptr,
217 0, nullptr, out_sample_buffer);
Zeke Chin71f6f442015-06-29 14:34:58 -0700218 if (status != noErr) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100219 RTC_LOG(LS_ERROR) << "Failed to create sample buffer.";
Zeke Chin71f6f442015-06-29 14:34:58 -0700220 CFRelease(contiguous_buffer);
221 return false;
222 }
Zeke Chin71f6f442015-06-29 14:34:58 -0700223 CFRelease(contiguous_buffer);
224 return true;
225}
226
tkchin5ed5ed92016-03-08 10:51:54 -0800227CMVideoFormatDescriptionRef CreateVideoFormatDescription(
228 const uint8_t* annexb_buffer,
229 size_t annexb_buffer_size) {
tkchin5ed5ed92016-03-08 10:51:54 -0800230 const uint8_t* param_set_ptrs[2] = {};
231 size_t param_set_sizes[2] = {};
Guy Hershenbaum2fcb8342018-02-20 21:33:36 -0800232 AnnexBBufferReader reader(annexb_buffer, annexb_buffer_size);
233 // Skip everyting before the SPS, then read the SPS and PPS
234 if (!reader.SeekToNextNaluOfType(kSps)) {
235 return nullptr;
jianjun.zhue1b4d242016-08-15 18:56:18 -0700236 }
tkchin5ed5ed92016-03-08 10:51:54 -0800237 if (!reader.ReadNalu(&param_set_ptrs[0], &param_set_sizes[0])) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100238 RTC_LOG(LS_ERROR) << "Failed to read SPS";
tkchin5ed5ed92016-03-08 10:51:54 -0800239 return nullptr;
240 }
241 if (!reader.ReadNalu(&param_set_ptrs[1], &param_set_sizes[1])) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100242 RTC_LOG(LS_ERROR) << "Failed to read PPS";
tkchin5ed5ed92016-03-08 10:51:54 -0800243 return nullptr;
244 }
Guy Hershenbaum2fcb8342018-02-20 21:33:36 -0800245
246 // Parse the SPS and PPS into a CMVideoFormatDescription.
247 CMVideoFormatDescriptionRef description = nullptr;
248 OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets(
249 kCFAllocatorDefault, 2, param_set_ptrs, param_set_sizes, 4, &description);
tkchin5ed5ed92016-03-08 10:51:54 -0800250 if (status != noErr) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100251 RTC_LOG(LS_ERROR) << "Failed to create video format description.";
tkchin5ed5ed92016-03-08 10:51:54 -0800252 return nullptr;
253 }
254 return description;
255}
256
Zeke Chin71f6f442015-06-29 14:34:58 -0700257AnnexBBufferReader::AnnexBBufferReader(const uint8_t* annexb_buffer,
258 size_t length)
Kári Tristan Helgasoncb18ee62016-11-02 15:07:02 +0100259 : start_(annexb_buffer), length_(length) {
henrikg91d6ede2015-09-17 00:24:34 -0700260 RTC_DCHECK(annexb_buffer);
Kári Tristan Helgasoncb18ee62016-11-02 15:07:02 +0100261 offsets_ = H264::FindNaluIndices(annexb_buffer, length);
262 offset_ = offsets_.begin();
Zeke Chin71f6f442015-06-29 14:34:58 -0700263}
264
Mirko Bonadei17aff352018-07-26 12:20:40 +0200265AnnexBBufferReader::~AnnexBBufferReader() = default;
266
Zeke Chin71f6f442015-06-29 14:34:58 -0700267bool AnnexBBufferReader::ReadNalu(const uint8_t** out_nalu,
268 size_t* out_length) {
henrikg91d6ede2015-09-17 00:24:34 -0700269 RTC_DCHECK(out_nalu);
270 RTC_DCHECK(out_length);
Zeke Chin71f6f442015-06-29 14:34:58 -0700271 *out_nalu = nullptr;
272 *out_length = 0;
273
Kári Tristan Helgasoncb18ee62016-11-02 15:07:02 +0100274 if (offset_ == offsets_.end()) {
Zeke Chin71f6f442015-06-29 14:34:58 -0700275 return false;
276 }
Kári Tristan Helgasoncb18ee62016-11-02 15:07:02 +0100277 *out_nalu = start_ + offset_->payload_start_offset;
278 *out_length = offset_->payload_size;
279 ++offset_;
Zeke Chin71f6f442015-06-29 14:34:58 -0700280 return true;
281}
282
283size_t AnnexBBufferReader::BytesRemaining() const {
Kári Tristan Helgasoncb18ee62016-11-02 15:07:02 +0100284 if (offset_ == offsets_.end()) {
285 return 0;
Zeke Chin71f6f442015-06-29 14:34:58 -0700286 }
Kári Tristan Helgasoncb18ee62016-11-02 15:07:02 +0100287 return length_ - offset_->start_offset;
Zeke Chin71f6f442015-06-29 14:34:58 -0700288}
289
Guy Hershenbaum2fcb8342018-02-20 21:33:36 -0800290void AnnexBBufferReader::SeekToStart() {
291 offset_ = offsets_.begin();
292}
293
294bool AnnexBBufferReader::SeekToNextNaluOfType(NaluType type) {
295 for (; offset_ != offsets_.end(); ++offset_) {
296 if (offset_->payload_size < 1)
297 continue;
298 if (ParseNaluType(*(start_ + offset_->payload_start_offset)) == type)
299 return true;
300 }
301 return false;
302}
Zeke Chin71f6f442015-06-29 14:34:58 -0700303AvccBufferWriter::AvccBufferWriter(uint8_t* const avcc_buffer, size_t length)
304 : start_(avcc_buffer), offset_(0), length_(length) {
henrikg91d6ede2015-09-17 00:24:34 -0700305 RTC_DCHECK(avcc_buffer);
Zeke Chin71f6f442015-06-29 14:34:58 -0700306}
307
308bool AvccBufferWriter::WriteNalu(const uint8_t* data, size_t data_size) {
309 // Check if we can write this length of data.
310 if (data_size + kAvccHeaderByteSize > BytesRemaining()) {
311 return false;
312 }
313 // Write length header, which needs to be big endian.
314 uint32_t big_endian_length = CFSwapInt32HostToBig(data_size);
315 memcpy(start_ + offset_, &big_endian_length, sizeof(big_endian_length));
316 offset_ += sizeof(big_endian_length);
317 // Write data.
318 memcpy(start_ + offset_, data, data_size);
319 offset_ += data_size;
320 return true;
321}
322
323size_t AvccBufferWriter::BytesRemaining() const {
324 return length_ - offset_;
325}
326
327} // namespace webrtc