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, &param_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, &param_set, &param_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(&param_set_ptrs[0], &param_set_sizes[0])) {
180       LOG(LS_ERROR) << "Failed to read SPS";
181       return false;
182     }
183     if (!reader.ReadNalu(&param_set_ptrs[1], &param_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