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

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

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

Powered by Google App Engine
This is Rietveld 408576698