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

Side by Side Diff: webrtc/sdk/objc/Framework/Classes/h264_video_toolbox_nalu.cc

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

Powered by Google App Engine
This is Rietveld 408576698