• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "sdk/objc/components/video_codec/nalu_rewriter.h"
13 
14 #include <CoreFoundation/CoreFoundation.h>
15 #include <memory>
16 #include <vector>
17 
18 #include "rtc_base/checks.h"
19 #include "rtc_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 
H264CMSampleBufferToAnnexBBuffer(CMSampleBufferRef avcc_sample_buffer,bool is_keyframe,rtc::Buffer * annexb_buffer,std::unique_ptr<RTPFragmentationHeader> * out_header)32 bool H264CMSampleBufferToAnnexBBuffer(
33     CMSampleBufferRef avcc_sample_buffer,
34     bool is_keyframe,
35     rtc::Buffer* annexb_buffer,
36     std::unique_ptr<RTPFragmentationHeader>* out_header) {
37   RTC_DCHECK(avcc_sample_buffer);
38   RTC_DCHECK(out_header);
39   out_header->reset(nullptr);
40 
41   // Get format description from the sample buffer.
42   CMVideoFormatDescriptionRef description =
43       CMSampleBufferGetFormatDescription(avcc_sample_buffer);
44   if (description == nullptr) {
45     RTC_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     RTC_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         RTC_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     RTC_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       RTC_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     RTC_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<RTPFragmentationHeader> header(new RTPFragmentationHeader());
147   header->VerifyAndAllocateFragmentationHeader(frag_offsets.size());
148   RTC_DCHECK_EQ(frag_lengths.size(), frag_offsets.size());
149   for (size_t i = 0; i < frag_offsets.size(); ++i) {
150     header->fragmentationOffset[i] = frag_offsets[i];
151     header->fragmentationLength[i] = frag_lengths[i];
152   }
153   *out_header = std::move(header);
154   CFRelease(contiguous_buffer);
155   return true;
156 }
157 
H264AnnexBBufferToCMSampleBuffer(const uint8_t * annexb_buffer,size_t annexb_buffer_size,CMVideoFormatDescriptionRef video_format,CMSampleBufferRef * out_sample_buffer,CMMemoryPoolRef memory_pool)158 bool H264AnnexBBufferToCMSampleBuffer(const uint8_t* annexb_buffer,
159                                       size_t annexb_buffer_size,
160                                       CMVideoFormatDescriptionRef video_format,
161                                       CMSampleBufferRef* out_sample_buffer,
162                                       CMMemoryPoolRef memory_pool) {
163   RTC_DCHECK(annexb_buffer);
164   RTC_DCHECK(out_sample_buffer);
165   RTC_DCHECK(video_format);
166   *out_sample_buffer = nullptr;
167 
168   AnnexBBufferReader reader(annexb_buffer, annexb_buffer_size);
169   if (reader.SeekToNextNaluOfType(kSps)) {
170     // Buffer contains an SPS NALU - skip it and the following PPS
171     const uint8_t* data;
172     size_t data_len;
173     if (!reader.ReadNalu(&data, &data_len)) {
174       RTC_LOG(LS_ERROR) << "Failed to read SPS";
175       return false;
176     }
177     if (!reader.ReadNalu(&data, &data_len)) {
178       RTC_LOG(LS_ERROR) << "Failed to read PPS";
179       return false;
180     }
181   } else {
182     // No SPS NALU - start reading from the first NALU in the buffer
183     reader.SeekToStart();
184   }
185 
186   // Allocate memory as a block buffer.
187   CMBlockBufferRef block_buffer = nullptr;
188   CFAllocatorRef block_allocator = CMMemoryPoolGetAllocator(memory_pool);
189   OSStatus status = CMBlockBufferCreateWithMemoryBlock(
190       kCFAllocatorDefault, nullptr, reader.BytesRemaining(), block_allocator,
191       nullptr, 0, reader.BytesRemaining(), kCMBlockBufferAssureMemoryNowFlag,
192       &block_buffer);
193   if (status != kCMBlockBufferNoErr) {
194     RTC_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(kCFAllocatorDefault, block_buffer,
202                                            block_allocator, nullptr, 0, 0, 0,
203                                            &contiguous_buffer);
204     if (status != noErr) {
205       RTC_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     RTC_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(kCFAllocatorDefault, contiguous_buffer, true,
240                                 nullptr, nullptr, video_format, 1, 0, nullptr,
241                                 0, nullptr, out_sample_buffer);
242   if (status != noErr) {
243     RTC_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 
CreateVideoFormatDescription(const uint8_t * annexb_buffer,size_t annexb_buffer_size)251 CMVideoFormatDescriptionRef CreateVideoFormatDescription(
252     const uint8_t* annexb_buffer,
253     size_t annexb_buffer_size) {
254   const uint8_t* param_set_ptrs[2] = {};
255   size_t param_set_sizes[2] = {};
256   AnnexBBufferReader reader(annexb_buffer, annexb_buffer_size);
257   // Skip everyting before the SPS, then read the SPS and PPS
258   if (!reader.SeekToNextNaluOfType(kSps)) {
259     return nullptr;
260   }
261   if (!reader.ReadNalu(&param_set_ptrs[0], &param_set_sizes[0])) {
262     RTC_LOG(LS_ERROR) << "Failed to read SPS";
263     return nullptr;
264   }
265   if (!reader.ReadNalu(&param_set_ptrs[1], &param_set_sizes[1])) {
266     RTC_LOG(LS_ERROR) << "Failed to read PPS";
267     return nullptr;
268   }
269 
270   // Parse the SPS and PPS into a CMVideoFormatDescription.
271   CMVideoFormatDescriptionRef description = nullptr;
272   OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets(
273       kCFAllocatorDefault, 2, param_set_ptrs, param_set_sizes, 4, &description);
274   if (status != noErr) {
275     RTC_LOG(LS_ERROR) << "Failed to create video format description.";
276     return nullptr;
277   }
278   return description;
279 }
280 
AnnexBBufferReader(const uint8_t * annexb_buffer,size_t length)281 AnnexBBufferReader::AnnexBBufferReader(const uint8_t* annexb_buffer,
282                                        size_t length)
283     : start_(annexb_buffer), length_(length) {
284   RTC_DCHECK(annexb_buffer);
285   offsets_ = H264::FindNaluIndices(annexb_buffer, length);
286   offset_ = offsets_.begin();
287 }
288 
289 AnnexBBufferReader::~AnnexBBufferReader() = default;
290 
ReadNalu(const uint8_t ** out_nalu,size_t * out_length)291 bool AnnexBBufferReader::ReadNalu(const uint8_t** out_nalu,
292                                   size_t* out_length) {
293   RTC_DCHECK(out_nalu);
294   RTC_DCHECK(out_length);
295   *out_nalu = nullptr;
296   *out_length = 0;
297 
298   if (offset_ == offsets_.end()) {
299     return false;
300   }
301   *out_nalu = start_ + offset_->payload_start_offset;
302   *out_length = offset_->payload_size;
303   ++offset_;
304   return true;
305 }
306 
BytesRemaining() const307 size_t AnnexBBufferReader::BytesRemaining() const {
308   if (offset_ == offsets_.end()) {
309     return 0;
310   }
311   return length_ - offset_->start_offset;
312 }
313 
SeekToStart()314 void AnnexBBufferReader::SeekToStart() {
315   offset_ = offsets_.begin();
316 }
317 
SeekToNextNaluOfType(NaluType type)318 bool AnnexBBufferReader::SeekToNextNaluOfType(NaluType type) {
319   for (; offset_ != offsets_.end(); ++offset_) {
320     if (offset_->payload_size < 1)
321       continue;
322     if (ParseNaluType(*(start_ + offset_->payload_start_offset)) == type)
323       return true;
324   }
325   return false;
326 }
AvccBufferWriter(uint8_t * const avcc_buffer,size_t length)327 AvccBufferWriter::AvccBufferWriter(uint8_t* const avcc_buffer, size_t length)
328     : start_(avcc_buffer), offset_(0), length_(length) {
329   RTC_DCHECK(avcc_buffer);
330 }
331 
WriteNalu(const uint8_t * data,size_t data_size)332 bool AvccBufferWriter::WriteNalu(const uint8_t* data, size_t data_size) {
333   // Check if we can write this length of data.
334   if (data_size + kAvccHeaderByteSize > BytesRemaining()) {
335     return false;
336   }
337   // Write length header, which needs to be big endian.
338   uint32_t big_endian_length = CFSwapInt32HostToBig(data_size);
339   memcpy(start_ + offset_, &big_endian_length, sizeof(big_endian_length));
340   offset_ += sizeof(big_endian_length);
341   // Write data.
342   memcpy(start_ + offset_, data, data_size);
343   offset_ += data_size;
344   return true;
345 }
346 
BytesRemaining() const347 size_t AvccBufferWriter::BytesRemaining() const {
348   return length_ - offset_;
349 }
350 
351 }  // namespace webrtc
352