1 /*
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
12 #include "webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_nalu.h"
13
14 #if defined(WEBRTC_VIDEO_TOOLBOX_SUPPORTED)
15
16 #include <CoreFoundation/CoreFoundation.h>
17 #include <vector>
18
19 #include "webrtc/base/checks.h"
20 #include "webrtc/base/logging.h"
21
22 namespace webrtc {
23
24 const char kAnnexBHeaderBytes[4] = {0, 0, 0, 1};
25 const size_t kAvccHeaderByteSize = sizeof(uint32_t);
26
H264CMSampleBufferToAnnexBBuffer(CMSampleBufferRef avcc_sample_buffer,bool is_keyframe,rtc::Buffer * annexb_buffer,webrtc::RTPFragmentationHeader ** out_header)27 bool H264CMSampleBufferToAnnexBBuffer(
28 CMSampleBufferRef avcc_sample_buffer,
29 bool is_keyframe,
30 rtc::Buffer* annexb_buffer,
31 webrtc::RTPFragmentationHeader** out_header) {
32 RTC_DCHECK(avcc_sample_buffer);
33 RTC_DCHECK(out_header);
34 *out_header = nullptr;
35
36 // Get format description from the sample buffer.
37 CMVideoFormatDescriptionRef description =
38 CMSampleBufferGetFormatDescription(avcc_sample_buffer);
39 if (description == nullptr) {
40 LOG(LS_ERROR) << "Failed to get sample buffer's description.";
41 return false;
42 }
43
44 // Get parameter set information.
45 int nalu_header_size = 0;
46 size_t param_set_count = 0;
47 OSStatus status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
48 description, 0, nullptr, nullptr, ¶m_set_count, &nalu_header_size);
49 if (status != noErr) {
50 LOG(LS_ERROR) << "Failed to get parameter set.";
51 return false;
52 }
53 // TODO(tkchin): handle other potential sizes.
54 RTC_DCHECK_EQ(nalu_header_size, 4);
55 RTC_DCHECK_EQ(param_set_count, 2u);
56
57 // Truncate any previous data in the buffer without changing its capacity.
58 annexb_buffer->SetSize(0);
59
60 size_t nalu_offset = 0;
61 std::vector<size_t> frag_offsets;
62 std::vector<size_t> frag_lengths;
63
64 // Place all parameter sets at the front of buffer.
65 if (is_keyframe) {
66 size_t param_set_size = 0;
67 const uint8_t* param_set = nullptr;
68 for (size_t i = 0; i < param_set_count; ++i) {
69 status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
70 description, i, ¶m_set, ¶m_set_size, nullptr, nullptr);
71 if (status != noErr) {
72 LOG(LS_ERROR) << "Failed to get parameter set.";
73 return false;
74 }
75 // Update buffer.
76 annexb_buffer->AppendData(kAnnexBHeaderBytes, sizeof(kAnnexBHeaderBytes));
77 annexb_buffer->AppendData(reinterpret_cast<const char*>(param_set),
78 param_set_size);
79 // Update fragmentation.
80 frag_offsets.push_back(nalu_offset + sizeof(kAnnexBHeaderBytes));
81 frag_lengths.push_back(param_set_size);
82 nalu_offset += sizeof(kAnnexBHeaderBytes) + param_set_size;
83 }
84 }
85
86 // Get block buffer from the sample buffer.
87 CMBlockBufferRef block_buffer =
88 CMSampleBufferGetDataBuffer(avcc_sample_buffer);
89 if (block_buffer == nullptr) {
90 LOG(LS_ERROR) << "Failed to get sample buffer's block buffer.";
91 return false;
92 }
93 CMBlockBufferRef contiguous_buffer = nullptr;
94 // Make sure block buffer is contiguous.
95 if (!CMBlockBufferIsRangeContiguous(block_buffer, 0, 0)) {
96 status = CMBlockBufferCreateContiguous(
97 nullptr, block_buffer, nullptr, nullptr, 0, 0, 0, &contiguous_buffer);
98 if (status != noErr) {
99 LOG(LS_ERROR) << "Failed to flatten non-contiguous block buffer: "
100 << status;
101 return false;
102 }
103 } else {
104 contiguous_buffer = block_buffer;
105 // Retain to make cleanup easier.
106 CFRetain(contiguous_buffer);
107 block_buffer = nullptr;
108 }
109
110 // Now copy the actual data.
111 char* data_ptr = nullptr;
112 size_t block_buffer_size = CMBlockBufferGetDataLength(contiguous_buffer);
113 status = CMBlockBufferGetDataPointer(contiguous_buffer, 0, nullptr, nullptr,
114 &data_ptr);
115 if (status != noErr) {
116 LOG(LS_ERROR) << "Failed to get block buffer data.";
117 CFRelease(contiguous_buffer);
118 return false;
119 }
120 size_t bytes_remaining = block_buffer_size;
121 while (bytes_remaining > 0) {
122 // The size type here must match |nalu_header_size|, we expect 4 bytes.
123 // Read the length of the next packet of data. Must convert from big endian
124 // to host endian.
125 RTC_DCHECK_GE(bytes_remaining, (size_t)nalu_header_size);
126 uint32_t* uint32_data_ptr = reinterpret_cast<uint32_t*>(data_ptr);
127 uint32_t packet_size = CFSwapInt32BigToHost(*uint32_data_ptr);
128 // Update buffer.
129 annexb_buffer->AppendData(kAnnexBHeaderBytes, sizeof(kAnnexBHeaderBytes));
130 annexb_buffer->AppendData(data_ptr + nalu_header_size, packet_size);
131 // Update fragmentation.
132 frag_offsets.push_back(nalu_offset + sizeof(kAnnexBHeaderBytes));
133 frag_lengths.push_back(packet_size);
134 nalu_offset += sizeof(kAnnexBHeaderBytes) + packet_size;
135
136 size_t bytes_written = packet_size + nalu_header_size;
137 bytes_remaining -= bytes_written;
138 data_ptr += bytes_written;
139 }
140 RTC_DCHECK_EQ(bytes_remaining, (size_t)0);
141
142 rtc::scoped_ptr<webrtc::RTPFragmentationHeader> header;
143 header.reset(new webrtc::RTPFragmentationHeader());
144 header->VerifyAndAllocateFragmentationHeader(frag_offsets.size());
145 RTC_DCHECK_EQ(frag_lengths.size(), frag_offsets.size());
146 for (size_t i = 0; i < frag_offsets.size(); ++i) {
147 header->fragmentationOffset[i] = frag_offsets[i];
148 header->fragmentationLength[i] = frag_lengths[i];
149 header->fragmentationPlType[i] = 0;
150 header->fragmentationTimeDiff[i] = 0;
151 }
152 *out_header = header.release();
153 CFRelease(contiguous_buffer);
154 return true;
155 }
156
H264AnnexBBufferToCMSampleBuffer(const uint8_t * annexb_buffer,size_t annexb_buffer_size,CMVideoFormatDescriptionRef video_format,CMSampleBufferRef * out_sample_buffer)157 bool H264AnnexBBufferToCMSampleBuffer(const uint8_t* annexb_buffer,
158 size_t annexb_buffer_size,
159 CMVideoFormatDescriptionRef video_format,
160 CMSampleBufferRef* out_sample_buffer) {
161 RTC_DCHECK(annexb_buffer);
162 RTC_DCHECK(out_sample_buffer);
163 *out_sample_buffer = nullptr;
164
165 // The buffer we receive via RTP has 00 00 00 01 start code artifically
166 // embedded by the RTP depacketizer. Extract NALU information.
167 // TODO(tkchin): handle potential case where sps and pps are delivered
168 // separately.
169 uint8_t first_nalu_type = annexb_buffer[4] & 0x1f;
170 bool is_first_nalu_type_sps = first_nalu_type == 0x7;
171
172 AnnexBBufferReader reader(annexb_buffer, annexb_buffer_size);
173 CMVideoFormatDescriptionRef description = nullptr;
174 OSStatus status = noErr;
175 if (is_first_nalu_type_sps) {
176 // Parse the SPS and PPS into a CMVideoFormatDescription.
177 const uint8_t* param_set_ptrs[2] = {};
178 size_t param_set_sizes[2] = {};
179 if (!reader.ReadNalu(¶m_set_ptrs[0], ¶m_set_sizes[0])) {
180 LOG(LS_ERROR) << "Failed to read SPS";
181 return false;
182 }
183 if (!reader.ReadNalu(¶m_set_ptrs[1], ¶m_set_sizes[1])) {
184 LOG(LS_ERROR) << "Failed to read PPS";
185 return false;
186 }
187 status = CMVideoFormatDescriptionCreateFromH264ParameterSets(
188 kCFAllocatorDefault, 2, param_set_ptrs, param_set_sizes, 4,
189 &description);
190 if (status != noErr) {
191 LOG(LS_ERROR) << "Failed to create video format description.";
192 return false;
193 }
194 } else {
195 RTC_DCHECK(video_format);
196 description = video_format;
197 // We don't need to retain, but it makes logic easier since we are creating
198 // in the other block.
199 CFRetain(description);
200 }
201
202 // Allocate memory as a block buffer.
203 // TODO(tkchin): figure out how to use a pool.
204 CMBlockBufferRef block_buffer = nullptr;
205 status = CMBlockBufferCreateWithMemoryBlock(
206 nullptr, nullptr, reader.BytesRemaining(), nullptr, nullptr, 0,
207 reader.BytesRemaining(), kCMBlockBufferAssureMemoryNowFlag,
208 &block_buffer);
209 if (status != kCMBlockBufferNoErr) {
210 LOG(LS_ERROR) << "Failed to create block buffer.";
211 CFRelease(description);
212 return false;
213 }
214
215 // Make sure block buffer is contiguous.
216 CMBlockBufferRef contiguous_buffer = nullptr;
217 if (!CMBlockBufferIsRangeContiguous(block_buffer, 0, 0)) {
218 status = CMBlockBufferCreateContiguous(
219 nullptr, block_buffer, nullptr, nullptr, 0, 0, 0, &contiguous_buffer);
220 if (status != noErr) {
221 LOG(LS_ERROR) << "Failed to flatten non-contiguous block buffer: "
222 << status;
223 CFRelease(description);
224 CFRelease(block_buffer);
225 return false;
226 }
227 } else {
228 contiguous_buffer = block_buffer;
229 block_buffer = nullptr;
230 }
231
232 // Get a raw pointer into allocated memory.
233 size_t block_buffer_size = 0;
234 char* data_ptr = nullptr;
235 status = CMBlockBufferGetDataPointer(contiguous_buffer, 0, nullptr,
236 &block_buffer_size, &data_ptr);
237 if (status != kCMBlockBufferNoErr) {
238 LOG(LS_ERROR) << "Failed to get block buffer data pointer.";
239 CFRelease(description);
240 CFRelease(contiguous_buffer);
241 return false;
242 }
243 RTC_DCHECK(block_buffer_size == reader.BytesRemaining());
244
245 // Write Avcc NALUs into block buffer memory.
246 AvccBufferWriter writer(reinterpret_cast<uint8_t*>(data_ptr),
247 block_buffer_size);
248 while (reader.BytesRemaining() > 0) {
249 const uint8_t* nalu_data_ptr = nullptr;
250 size_t nalu_data_size = 0;
251 if (reader.ReadNalu(&nalu_data_ptr, &nalu_data_size)) {
252 writer.WriteNalu(nalu_data_ptr, nalu_data_size);
253 }
254 }
255
256 // Create sample buffer.
257 status = CMSampleBufferCreate(nullptr, contiguous_buffer, true, nullptr,
258 nullptr, description, 1, 0, nullptr, 0, nullptr,
259 out_sample_buffer);
260 if (status != noErr) {
261 LOG(LS_ERROR) << "Failed to create sample buffer.";
262 CFRelease(description);
263 CFRelease(contiguous_buffer);
264 return false;
265 }
266 CFRelease(description);
267 CFRelease(contiguous_buffer);
268 return true;
269 }
270
AnnexBBufferReader(const uint8_t * annexb_buffer,size_t length)271 AnnexBBufferReader::AnnexBBufferReader(const uint8_t* annexb_buffer,
272 size_t length)
273 : start_(annexb_buffer), offset_(0), next_offset_(0), length_(length) {
274 RTC_DCHECK(annexb_buffer);
275 offset_ = FindNextNaluHeader(start_, length_, 0);
276 next_offset_ =
277 FindNextNaluHeader(start_, length_, offset_ + sizeof(kAnnexBHeaderBytes));
278 }
279
ReadNalu(const uint8_t ** out_nalu,size_t * out_length)280 bool AnnexBBufferReader::ReadNalu(const uint8_t** out_nalu,
281 size_t* out_length) {
282 RTC_DCHECK(out_nalu);
283 RTC_DCHECK(out_length);
284 *out_nalu = nullptr;
285 *out_length = 0;
286
287 size_t data_offset = offset_ + sizeof(kAnnexBHeaderBytes);
288 if (data_offset > length_) {
289 return false;
290 }
291 *out_nalu = start_ + data_offset;
292 *out_length = next_offset_ - data_offset;
293 offset_ = next_offset_;
294 next_offset_ =
295 FindNextNaluHeader(start_, length_, offset_ + sizeof(kAnnexBHeaderBytes));
296 return true;
297 }
298
BytesRemaining() const299 size_t AnnexBBufferReader::BytesRemaining() const {
300 return length_ - offset_;
301 }
302
FindNextNaluHeader(const uint8_t * start,size_t length,size_t offset) const303 size_t AnnexBBufferReader::FindNextNaluHeader(const uint8_t* start,
304 size_t length,
305 size_t offset) const {
306 RTC_DCHECK(start);
307 if (offset + sizeof(kAnnexBHeaderBytes) > length) {
308 return length;
309 }
310 // NALUs are separated by an 00 00 00 01 header. Scan the byte stream
311 // starting from the offset for the next such sequence.
312 const uint8_t* current = start + offset;
313 // The loop reads sizeof(kAnnexBHeaderBytes) at a time, so stop when there
314 // aren't enough bytes remaining.
315 const uint8_t* const end = start + length - sizeof(kAnnexBHeaderBytes);
316 while (current < end) {
317 if (current[3] > 1) {
318 current += 4;
319 } else if (current[3] == 1 && current[2] == 0 && current[1] == 0 &&
320 current[0] == 0) {
321 return current - start;
322 } else {
323 ++current;
324 }
325 }
326 return length;
327 }
328
AvccBufferWriter(uint8_t * const avcc_buffer,size_t length)329 AvccBufferWriter::AvccBufferWriter(uint8_t* const avcc_buffer, size_t length)
330 : start_(avcc_buffer), offset_(0), length_(length) {
331 RTC_DCHECK(avcc_buffer);
332 }
333
WriteNalu(const uint8_t * data,size_t data_size)334 bool AvccBufferWriter::WriteNalu(const uint8_t* data, size_t data_size) {
335 // Check if we can write this length of data.
336 if (data_size + kAvccHeaderByteSize > BytesRemaining()) {
337 return false;
338 }
339 // Write length header, which needs to be big endian.
340 uint32_t big_endian_length = CFSwapInt32HostToBig(data_size);
341 memcpy(start_ + offset_, &big_endian_length, sizeof(big_endian_length));
342 offset_ += sizeof(big_endian_length);
343 // Write data.
344 memcpy(start_ + offset_, data, data_size);
345 offset_ += data_size;
346 return true;
347 }
348
BytesRemaining() const349 size_t AvccBufferWriter::BytesRemaining() const {
350 return length_ - offset_;
351 }
352
353 } // namespace webrtc
354
355 #endif // defined(WEBRTC_VIDEO_TOOLBOX_SUPPORTED)
356