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