• 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)32 bool H264CMSampleBufferToAnnexBBuffer(CMSampleBufferRef avcc_sample_buffer,
33                                       bool is_keyframe,
34                                       rtc::Buffer* annexb_buffer) {
35   RTC_DCHECK(avcc_sample_buffer);
36 
37   // Get format description from the sample buffer.
38   CMVideoFormatDescriptionRef description =
39       CMSampleBufferGetFormatDescription(avcc_sample_buffer);
40   if (description == nullptr) {
41     RTC_LOG(LS_ERROR) << "Failed to get sample buffer's description.";
42     return false;
43   }
44 
45   // Get parameter set information.
46   int nalu_header_size = 0;
47   size_t param_set_count = 0;
48   OSStatus status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
49       description, 0, nullptr, nullptr, &param_set_count, &nalu_header_size);
50   if (status != noErr) {
51     RTC_LOG(LS_ERROR) << "Failed to get parameter set.";
52     return false;
53   }
54   RTC_CHECK_EQ(nalu_header_size, kAvccHeaderByteSize);
55   RTC_DCHECK_EQ(param_set_count, 2);
56 
57   // Truncate any previous data in the buffer without changing its capacity.
58   annexb_buffer->SetSize(0);
59 
60   // Place all parameter sets at the front of buffer.
61   if (is_keyframe) {
62     size_t param_set_size = 0;
63     const uint8_t* param_set = nullptr;
64     for (size_t i = 0; i < param_set_count; ++i) {
65       status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
66           description, i, &param_set, &param_set_size, nullptr, nullptr);
67       if (status != noErr) {
68         RTC_LOG(LS_ERROR) << "Failed to get parameter set.";
69         return false;
70       }
71       // Update buffer.
72       annexb_buffer->AppendData(kAnnexBHeaderBytes, sizeof(kAnnexBHeaderBytes));
73       annexb_buffer->AppendData(reinterpret_cast<const char*>(param_set),
74                                 param_set_size);
75     }
76   }
77 
78   // Get block buffer from the sample buffer.
79   CMBlockBufferRef block_buffer =
80       CMSampleBufferGetDataBuffer(avcc_sample_buffer);
81   if (block_buffer == nullptr) {
82     RTC_LOG(LS_ERROR) << "Failed to get sample buffer's block buffer.";
83     return false;
84   }
85   CMBlockBufferRef contiguous_buffer = nullptr;
86   // Make sure block buffer is contiguous.
87   if (!CMBlockBufferIsRangeContiguous(block_buffer, 0, 0)) {
88     status = CMBlockBufferCreateContiguous(
89         nullptr, block_buffer, nullptr, nullptr, 0, 0, 0, &contiguous_buffer);
90     if (status != noErr) {
91       RTC_LOG(LS_ERROR) << "Failed to flatten non-contiguous block buffer: "
92                         << status;
93       return false;
94     }
95   } else {
96     contiguous_buffer = block_buffer;
97     // Retain to make cleanup easier.
98     CFRetain(contiguous_buffer);
99     block_buffer = nullptr;
100   }
101 
102   // Now copy the actual data.
103   char* data_ptr = nullptr;
104   size_t block_buffer_size = CMBlockBufferGetDataLength(contiguous_buffer);
105   status = CMBlockBufferGetDataPointer(contiguous_buffer, 0, nullptr, nullptr,
106                                        &data_ptr);
107   if (status != noErr) {
108     RTC_LOG(LS_ERROR) << "Failed to get block buffer data.";
109     CFRelease(contiguous_buffer);
110     return false;
111   }
112   size_t bytes_remaining = block_buffer_size;
113   while (bytes_remaining > 0) {
114     // The size type here must match `nalu_header_size`, we expect 4 bytes.
115     // Read the length of the next packet of data. Must convert from big endian
116     // to host endian.
117     RTC_DCHECK_GE(bytes_remaining, (size_t)nalu_header_size);
118     uint32_t* uint32_data_ptr = reinterpret_cast<uint32_t*>(data_ptr);
119     uint32_t packet_size = CFSwapInt32BigToHost(*uint32_data_ptr);
120     // Update buffer.
121     annexb_buffer->AppendData(kAnnexBHeaderBytes, sizeof(kAnnexBHeaderBytes));
122     annexb_buffer->AppendData(data_ptr + nalu_header_size, packet_size);
123 
124     size_t bytes_written = packet_size + sizeof(kAnnexBHeaderBytes);
125     bytes_remaining -= bytes_written;
126     data_ptr += bytes_written;
127   }
128   RTC_DCHECK_EQ(bytes_remaining, (size_t)0);
129 
130   CFRelease(contiguous_buffer);
131   return true;
132 }
133 
H264AnnexBBufferToCMSampleBuffer(const uint8_t * annexb_buffer,size_t annexb_buffer_size,CMVideoFormatDescriptionRef video_format,CMSampleBufferRef * out_sample_buffer,CMMemoryPoolRef memory_pool)134 bool H264AnnexBBufferToCMSampleBuffer(const uint8_t* annexb_buffer,
135                                       size_t annexb_buffer_size,
136                                       CMVideoFormatDescriptionRef video_format,
137                                       CMSampleBufferRef* out_sample_buffer,
138                                       CMMemoryPoolRef memory_pool) {
139   RTC_DCHECK(annexb_buffer);
140   RTC_DCHECK(out_sample_buffer);
141   RTC_DCHECK(video_format);
142   *out_sample_buffer = nullptr;
143 
144   AnnexBBufferReader reader(annexb_buffer, annexb_buffer_size);
145   if (reader.SeekToNextNaluOfType(kSps)) {
146     // Buffer contains an SPS NALU - skip it and the following PPS
147     const uint8_t* data;
148     size_t data_len;
149     if (!reader.ReadNalu(&data, &data_len)) {
150       RTC_LOG(LS_ERROR) << "Failed to read SPS";
151       return false;
152     }
153     if (!reader.ReadNalu(&data, &data_len)) {
154       RTC_LOG(LS_ERROR) << "Failed to read PPS";
155       return false;
156     }
157   } else {
158     // No SPS NALU - start reading from the first NALU in the buffer
159     reader.SeekToStart();
160   }
161 
162   // Allocate memory as a block buffer.
163   CMBlockBufferRef block_buffer = nullptr;
164   CFAllocatorRef block_allocator = CMMemoryPoolGetAllocator(memory_pool);
165   OSStatus status = CMBlockBufferCreateWithMemoryBlock(
166       kCFAllocatorDefault, nullptr, reader.BytesRemaining(), block_allocator,
167       nullptr, 0, reader.BytesRemaining(), kCMBlockBufferAssureMemoryNowFlag,
168       &block_buffer);
169   if (status != kCMBlockBufferNoErr) {
170     RTC_LOG(LS_ERROR) << "Failed to create block buffer.";
171     return false;
172   }
173 
174   // Make sure block buffer is contiguous.
175   CMBlockBufferRef contiguous_buffer = nullptr;
176   if (!CMBlockBufferIsRangeContiguous(block_buffer, 0, 0)) {
177     status = CMBlockBufferCreateContiguous(kCFAllocatorDefault, block_buffer,
178                                            block_allocator, nullptr, 0, 0, 0,
179                                            &contiguous_buffer);
180     if (status != noErr) {
181       RTC_LOG(LS_ERROR) << "Failed to flatten non-contiguous block buffer: "
182                         << status;
183       CFRelease(block_buffer);
184       return false;
185     }
186   } else {
187     contiguous_buffer = block_buffer;
188     block_buffer = nullptr;
189   }
190 
191   // Get a raw pointer into allocated memory.
192   size_t block_buffer_size = 0;
193   char* data_ptr = nullptr;
194   status = CMBlockBufferGetDataPointer(contiguous_buffer, 0, nullptr,
195                                        &block_buffer_size, &data_ptr);
196   if (status != kCMBlockBufferNoErr) {
197     RTC_LOG(LS_ERROR) << "Failed to get block buffer data pointer.";
198     CFRelease(contiguous_buffer);
199     return false;
200   }
201   RTC_DCHECK(block_buffer_size == reader.BytesRemaining());
202 
203   // Write Avcc NALUs into block buffer memory.
204   AvccBufferWriter writer(reinterpret_cast<uint8_t*>(data_ptr),
205                           block_buffer_size);
206   while (reader.BytesRemaining() > 0) {
207     const uint8_t* nalu_data_ptr = nullptr;
208     size_t nalu_data_size = 0;
209     if (reader.ReadNalu(&nalu_data_ptr, &nalu_data_size)) {
210       writer.WriteNalu(nalu_data_ptr, nalu_data_size);
211     }
212   }
213 
214   // Create sample buffer.
215   status = CMSampleBufferCreate(kCFAllocatorDefault, contiguous_buffer, true,
216                                 nullptr, nullptr, video_format, 1, 0, nullptr,
217                                 0, nullptr, out_sample_buffer);
218   if (status != noErr) {
219     RTC_LOG(LS_ERROR) << "Failed to create sample buffer.";
220     CFRelease(contiguous_buffer);
221     return false;
222   }
223   CFRelease(contiguous_buffer);
224   return true;
225 }
226 
CreateVideoFormatDescription(const uint8_t * annexb_buffer,size_t annexb_buffer_size)227 CMVideoFormatDescriptionRef CreateVideoFormatDescription(
228     const uint8_t* annexb_buffer,
229     size_t annexb_buffer_size) {
230   const uint8_t* param_set_ptrs[2] = {};
231   size_t param_set_sizes[2] = {};
232   AnnexBBufferReader reader(annexb_buffer, annexb_buffer_size);
233   // Skip everyting before the SPS, then read the SPS and PPS
234   if (!reader.SeekToNextNaluOfType(kSps)) {
235     return nullptr;
236   }
237   if (!reader.ReadNalu(&param_set_ptrs[0], &param_set_sizes[0])) {
238     RTC_LOG(LS_ERROR) << "Failed to read SPS";
239     return nullptr;
240   }
241   if (!reader.ReadNalu(&param_set_ptrs[1], &param_set_sizes[1])) {
242     RTC_LOG(LS_ERROR) << "Failed to read PPS";
243     return nullptr;
244   }
245 
246   // Parse the SPS and PPS into a CMVideoFormatDescription.
247   CMVideoFormatDescriptionRef description = nullptr;
248   OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets(
249       kCFAllocatorDefault, 2, param_set_ptrs, param_set_sizes, 4, &description);
250   if (status != noErr) {
251     RTC_LOG(LS_ERROR) << "Failed to create video format description.";
252     return nullptr;
253   }
254   return description;
255 }
256 
AnnexBBufferReader(const uint8_t * annexb_buffer,size_t length)257 AnnexBBufferReader::AnnexBBufferReader(const uint8_t* annexb_buffer,
258                                        size_t length)
259     : start_(annexb_buffer), length_(length) {
260   RTC_DCHECK(annexb_buffer);
261   offsets_ = H264::FindNaluIndices(annexb_buffer, length);
262   offset_ = offsets_.begin();
263 }
264 
265 AnnexBBufferReader::~AnnexBBufferReader() = default;
266 
ReadNalu(const uint8_t ** out_nalu,size_t * out_length)267 bool AnnexBBufferReader::ReadNalu(const uint8_t** out_nalu,
268                                   size_t* out_length) {
269   RTC_DCHECK(out_nalu);
270   RTC_DCHECK(out_length);
271   *out_nalu = nullptr;
272   *out_length = 0;
273 
274   if (offset_ == offsets_.end()) {
275     return false;
276   }
277   *out_nalu = start_ + offset_->payload_start_offset;
278   *out_length = offset_->payload_size;
279   ++offset_;
280   return true;
281 }
282 
BytesRemaining() const283 size_t AnnexBBufferReader::BytesRemaining() const {
284   if (offset_ == offsets_.end()) {
285     return 0;
286   }
287   return length_ - offset_->start_offset;
288 }
289 
SeekToStart()290 void AnnexBBufferReader::SeekToStart() {
291   offset_ = offsets_.begin();
292 }
293 
SeekToNextNaluOfType(NaluType type)294 bool AnnexBBufferReader::SeekToNextNaluOfType(NaluType type) {
295   for (; offset_ != offsets_.end(); ++offset_) {
296     if (offset_->payload_size < 1)
297       continue;
298     if (ParseNaluType(*(start_ + offset_->payload_start_offset)) == type)
299       return true;
300   }
301   return false;
302 }
AvccBufferWriter(uint8_t * const avcc_buffer,size_t length)303 AvccBufferWriter::AvccBufferWriter(uint8_t* const avcc_buffer, size_t length)
304     : start_(avcc_buffer), offset_(0), length_(length) {
305   RTC_DCHECK(avcc_buffer);
306 }
307 
WriteNalu(const uint8_t * data,size_t data_size)308 bool AvccBufferWriter::WriteNalu(const uint8_t* data, size_t data_size) {
309   // Check if we can write this length of data.
310   if (data_size + kAvccHeaderByteSize > BytesRemaining()) {
311     return false;
312   }
313   // Write length header, which needs to be big endian.
314   uint32_t big_endian_length = CFSwapInt32HostToBig(data_size);
315   memcpy(start_ + offset_, &big_endian_length, sizeof(big_endian_length));
316   offset_ += sizeof(big_endian_length);
317   // Write data.
318   memcpy(start_ + offset_, data, data_size);
319   offset_ += data_size;
320   return true;
321 }
322 
BytesRemaining() const323 size_t AvccBufferWriter::BytesRemaining() const {
324   return length_ - offset_;
325 }
326 
327 }  // namespace webrtc
328