Home | History | Annotate | Download | only in h264
      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 
     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 
    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 
    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 
    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 
    299 size_t AnnexBBufferReader::BytesRemaining() const {
    300   return length_ - offset_;
    301 }
    302 
    303 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 
    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 
    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 
    349 size_t AvccBufferWriter::BytesRemaining() const {
    350   return length_ - offset_;
    351 }
    352 
    353 }  // namespace webrtc
    354 
    355 #endif  // defined(WEBRTC_VIDEO_TOOLBOX_SUPPORTED)
    356