OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "media/base/mac/videotoolbox_helpers.h" |
| 6 |
| 7 #include <vector> |
| 8 |
| 9 #include "base/big_endian.h" |
| 10 |
| 11 namespace media { |
| 12 |
| 13 // Copy a H.264 frame stored in a CM sample buffer to an Annex B buffer. Copies |
| 14 // parameter sets for keyframes before the frame data as well. |
| 15 void CopySampleBufferToAnnexBBuffer(CoreMediaGlue::CMSampleBufferRef sbuf, |
| 16 std::string* annexb_buffer, |
| 17 bool keyframe) { |
| 18 // Perform two pass, one to figure out the total output size, and another to |
| 19 // copy the data after having performed a single output allocation. Note that |
| 20 // we'll allocate a bit more because we'll count 4 bytes instead of 3 for |
| 21 // video NALs. |
| 22 |
| 23 OSStatus status; |
| 24 |
| 25 // Get the sample buffer's block buffer and format description. |
| 26 auto bb = CoreMediaGlue::CMSampleBufferGetDataBuffer(sbuf); |
| 27 DCHECK(bb); |
| 28 auto fdesc = CoreMediaGlue::CMSampleBufferGetFormatDescription(sbuf); |
| 29 DCHECK(fdesc); |
| 30 |
| 31 size_t bb_size = CoreMediaGlue::CMBlockBufferGetDataLength(bb); |
| 32 size_t total_bytes = bb_size; |
| 33 |
| 34 size_t pset_count; |
| 35 int nal_size_field_bytes; |
| 36 status = CoreMediaGlue::CMVideoFormatDescriptionGetH264ParameterSetAtIndex( |
| 37 fdesc, 0, nullptr, nullptr, &pset_count, &nal_size_field_bytes); |
| 38 if (status == |
| 39 CoreMediaGlue::kCMFormatDescriptionBridgeError_InvalidParameter) { |
| 40 DLOG(WARNING) << " assuming 2 parameter sets and 4 bytes NAL length header"; |
| 41 pset_count = 2; |
| 42 nal_size_field_bytes = 4; |
| 43 } else if (status != noErr) { |
| 44 DLOG(ERROR) |
| 45 << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: " |
| 46 << status; |
| 47 return; |
| 48 } |
| 49 |
| 50 if (keyframe) { |
| 51 const uint8_t* pset; |
| 52 size_t pset_size; |
| 53 for (size_t pset_i = 0; pset_i < pset_count; ++pset_i) { |
| 54 status = |
| 55 CoreMediaGlue::CMVideoFormatDescriptionGetH264ParameterSetAtIndex( |
| 56 fdesc, pset_i, &pset, &pset_size, nullptr, nullptr); |
| 57 if (status != noErr) { |
| 58 DLOG(ERROR) |
| 59 << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: " |
| 60 << status; |
| 61 return; |
| 62 } |
| 63 total_bytes += pset_size + nal_size_field_bytes; |
| 64 } |
| 65 } |
| 66 |
| 67 annexb_buffer->reserve(total_bytes); |
| 68 |
| 69 // Copy all parameter sets before keyframes. |
| 70 if (keyframe) { |
| 71 const uint8_t* pset; |
| 72 size_t pset_size; |
| 73 for (size_t pset_i = 0; pset_i < pset_count; ++pset_i) { |
| 74 status = |
| 75 CoreMediaGlue::CMVideoFormatDescriptionGetH264ParameterSetAtIndex( |
| 76 fdesc, pset_i, &pset, &pset_size, nullptr, nullptr); |
| 77 if (status != noErr) { |
| 78 DLOG(ERROR) |
| 79 << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: " |
| 80 << status; |
| 81 return; |
| 82 } |
| 83 static const char startcode_4[4] = {0, 0, 0, 1}; |
| 84 annexb_buffer->append(startcode_4, sizeof(startcode_4)); |
| 85 annexb_buffer->append(reinterpret_cast<const char*>(pset), pset_size); |
| 86 } |
| 87 } |
| 88 |
| 89 // Block buffers can be composed of non-contiguous chunks. For the sake of |
| 90 // keeping this code simple, flatten non-contiguous block buffers. |
| 91 base::ScopedCFTypeRef<CoreMediaGlue::CMBlockBufferRef> contiguous_bb( |
| 92 bb, base::scoped_policy::RETAIN); |
| 93 if (!CoreMediaGlue::CMBlockBufferIsRangeContiguous(bb, 0, 0)) { |
| 94 contiguous_bb.reset(); |
| 95 status = CoreMediaGlue::CMBlockBufferCreateContiguous( |
| 96 kCFAllocatorDefault, bb, kCFAllocatorDefault, nullptr, 0, 0, 0, |
| 97 contiguous_bb.InitializeInto()); |
| 98 if (status != noErr) { |
| 99 DLOG(ERROR) << " CMBlockBufferCreateContiguous failed: " << status; |
| 100 return; |
| 101 } |
| 102 } |
| 103 |
| 104 // Copy all the NAL units. In the process convert them from AVCC format |
| 105 // (length header) to AnnexB format (start code). |
| 106 char* bb_data; |
| 107 status = CoreMediaGlue::CMBlockBufferGetDataPointer(contiguous_bb, 0, nullptr, |
| 108 nullptr, &bb_data); |
| 109 if (status != noErr) { |
| 110 DLOG(ERROR) << " CMBlockBufferGetDataPointer failed: " << status; |
| 111 return; |
| 112 } |
| 113 |
| 114 if (nal_size_field_bytes == 1) { |
| 115 CopyNalsToAnnexB<uint8_t>(bb_data, bb_size, annexb_buffer); |
| 116 } else if (nal_size_field_bytes == 2) { |
| 117 CopyNalsToAnnexB<uint16_t>(bb_data, bb_size, annexb_buffer); |
| 118 } else if (nal_size_field_bytes == 4) { |
| 119 CopyNalsToAnnexB<uint32_t>(bb_data, bb_size, annexb_buffer); |
| 120 } else { |
| 121 NOTREACHED(); |
| 122 } |
| 123 } |
| 124 |
| 125 template <typename NalSizeType> |
| 126 void CopyNalsToAnnexB(char* avcc_buffer, |
| 127 const size_t avcc_size, |
| 128 std::string* annexb_buffer) { |
| 129 static_assert(sizeof(NalSizeType) == 1 || sizeof(NalSizeType) == 2 || |
| 130 sizeof(NalSizeType) == 4, |
| 131 "NAL size type has unsupported size"); |
| 132 static const char startcode_3[3] = {0, 0, 1}; |
| 133 DCHECK(avcc_buffer); |
| 134 DCHECK(annexb_buffer); |
| 135 size_t bytes_left = avcc_size; |
| 136 while (bytes_left > 0) { |
| 137 DCHECK_GT(bytes_left, sizeof(NalSizeType)); |
| 138 NalSizeType nal_size; |
| 139 base::ReadBigEndian(avcc_buffer, &nal_size); |
| 140 bytes_left -= sizeof(NalSizeType); |
| 141 avcc_buffer += sizeof(NalSizeType); |
| 142 |
| 143 DCHECK_GE(bytes_left, nal_size); |
| 144 annexb_buffer->append(startcode_3, sizeof(startcode_3)); |
| 145 annexb_buffer->append(avcc_buffer, nal_size); |
| 146 bytes_left -= nal_size; |
| 147 avcc_buffer += nal_size; |
| 148 } |
| 149 } |
| 150 |
| 151 base::ScopedCFTypeRef<CFDictionaryRef> |
| 152 DictionaryWithKeysAndValues(CFTypeRef* keys, CFTypeRef* values, size_t size) { |
| 153 return base::ScopedCFTypeRef<CFDictionaryRef>(CFDictionaryCreate( |
| 154 kCFAllocatorDefault, keys, values, size, &kCFTypeDictionaryKeyCallBacks, |
| 155 &kCFTypeDictionaryValueCallBacks)); |
| 156 } |
| 157 |
| 158 base::ScopedCFTypeRef<CFDictionaryRef> DictionaryWithKeyValue(CFTypeRef key, |
| 159 CFTypeRef value) { |
| 160 CFTypeRef keys[1] = {key}; |
| 161 CFTypeRef values[1] = {value}; |
| 162 return DictionaryWithKeysAndValues(keys, values, 1); |
| 163 } |
| 164 |
| 165 base::ScopedCFTypeRef<CFArrayRef> ArrayWithIntegers(const int* v, size_t size) { |
| 166 std::vector<CFNumberRef> numbers; |
| 167 numbers.reserve(size); |
| 168 for (const int* end = v + size; v < end; ++v) |
| 169 numbers.push_back(CFNumberCreate(nullptr, kCFNumberSInt32Type, v)); |
| 170 base::ScopedCFTypeRef<CFArrayRef> array(CFArrayCreate( |
| 171 kCFAllocatorDefault, reinterpret_cast<const void**>(&numbers[0]), |
| 172 numbers.size(), &kCFTypeArrayCallBacks)); |
| 173 for (auto& number : numbers) { |
| 174 CFRelease(number); |
| 175 } |
| 176 return array; |
| 177 } |
| 178 |
| 179 bool SetSessionProperty(base::ScopedCFTypeRef<VTCompressionSessionRef> session, |
| 180 const VideoToolboxGlue* const glue, |
| 181 CFStringRef key, |
| 182 int32_t value) { |
| 183 base::ScopedCFTypeRef<CFNumberRef> cfvalue( |
| 184 CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)); |
| 185 return glue->VTSessionSetProperty(session, key, cfvalue) == noErr; |
| 186 } |
| 187 |
| 188 bool SetSessionProperty(base::ScopedCFTypeRef<VTCompressionSessionRef> session, |
| 189 const VideoToolboxGlue* const glue, |
| 190 CFStringRef key, |
| 191 bool value) { |
| 192 CFBooleanRef cfvalue = (value) ? kCFBooleanTrue : kCFBooleanFalse; |
| 193 return glue->VTSessionSetProperty(session, key, cfvalue) == noErr; |
| 194 } |
| 195 |
| 196 bool SetSessionProperty(base::ScopedCFTypeRef<VTCompressionSessionRef> session, |
| 197 const VideoToolboxGlue* const glue, |
| 198 CFStringRef key, |
| 199 CFStringRef value) { |
| 200 return glue->VTSessionSetProperty(session, key, value) == noErr; |
| 201 } |
| 202 |
| 203 } // namespace media |
OLD | NEW |