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, ¶m_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, ¶m_set, ¶m_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(¶m_set_ptrs[0], ¶m_set_sizes[0])) { 180 LOG(LS_ERROR) << "Failed to read SPS"; 181 return false; 182 } 183 if (!reader.ReadNalu(¶m_set_ptrs[1], ¶m_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