Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(708)

Side by Side Diff: webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_nalu.cc

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

Powered by Google App Engine
This is Rietveld 408576698