| 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 <array> | |
| 8 #include <vector> | |
| 9 | |
| 10 #include "base/big_endian.h" | |
| 11 #include "base/memory/scoped_ptr.h" | |
| 12 | |
| 13 namespace media { | |
| 14 | |
| 15 namespace video_toolbox { | |
| 16 | |
| 17 base::ScopedCFTypeRef<CFDictionaryRef> | |
| 18 DictionaryWithKeysAndValues(CFTypeRef* keys, CFTypeRef* values, size_t size) { | |
| 19 return base::ScopedCFTypeRef<CFDictionaryRef>(CFDictionaryCreate( | |
| 20 kCFAllocatorDefault, keys, values, size, &kCFTypeDictionaryKeyCallBacks, | |
| 21 &kCFTypeDictionaryValueCallBacks)); | |
| 22 } | |
| 23 | |
| 24 base::ScopedCFTypeRef<CFDictionaryRef> DictionaryWithKeyValue(CFTypeRef key, | |
| 25 CFTypeRef value) { | |
| 26 CFTypeRef keys[1] = {key}; | |
| 27 CFTypeRef values[1] = {value}; | |
| 28 return DictionaryWithKeysAndValues(keys, values, 1); | |
| 29 } | |
| 30 | |
| 31 base::ScopedCFTypeRef<CFArrayRef> ArrayWithIntegers(const int* v, size_t size) { | |
| 32 std::vector<CFNumberRef> numbers; | |
| 33 numbers.reserve(size); | |
| 34 for (const int* end = v + size; v < end; ++v) | |
| 35 numbers.push_back(CFNumberCreate(nullptr, kCFNumberSInt32Type, v)); | |
| 36 base::ScopedCFTypeRef<CFArrayRef> array(CFArrayCreate( | |
| 37 kCFAllocatorDefault, reinterpret_cast<const void**>(&numbers[0]), | |
| 38 numbers.size(), &kCFTypeArrayCallBacks)); | |
| 39 for (auto& number : numbers) { | |
| 40 CFRelease(number); | |
| 41 } | |
| 42 return array; | |
| 43 } | |
| 44 | |
| 45 base::ScopedCFTypeRef<CFArrayRef> ArrayWithIntegerAndFloat(int int_val, | |
| 46 float float_val) { | |
| 47 std::array<CFNumberRef, 2> numbers = { | |
| 48 {CFNumberCreate(nullptr, kCFNumberSInt32Type, &int_val), | |
| 49 CFNumberCreate(nullptr, kCFNumberFloat32Type, &float_val)}}; | |
| 50 base::ScopedCFTypeRef<CFArrayRef> array(CFArrayCreate( | |
| 51 kCFAllocatorDefault, reinterpret_cast<const void**>(numbers.data()), | |
| 52 numbers.size(), &kCFTypeArrayCallBacks)); | |
| 53 for (auto& number : numbers) | |
| 54 CFRelease(number); | |
| 55 return array; | |
| 56 } | |
| 57 | |
| 58 // Wrapper class for writing AnnexBBuffer output into. | |
| 59 class AnnexBBuffer { | |
| 60 public: | |
| 61 virtual bool Reserve(size_t size) = 0; | |
| 62 virtual void Append(const char* s, size_t n) = 0; | |
| 63 virtual size_t GetReservedSize() const = 0; | |
| 64 }; | |
| 65 | |
| 66 class RawAnnexBBuffer : public AnnexBBuffer { | |
| 67 public: | |
| 68 RawAnnexBBuffer(char* annexb_buffer, size_t annexb_buffer_size) | |
| 69 : annexb_buffer_(annexb_buffer), | |
| 70 annexb_buffer_size_(annexb_buffer_size), | |
| 71 annexb_buffer_offset_(0) {} | |
| 72 bool Reserve(size_t size) override { | |
| 73 reserved_size_ = size; | |
| 74 return size <= annexb_buffer_size_; | |
| 75 } | |
| 76 void Append(const char* s, size_t n) override { | |
| 77 memcpy(annexb_buffer_ + annexb_buffer_offset_, s, n); | |
| 78 annexb_buffer_offset_ += n; | |
| 79 DCHECK_GE(reserved_size_, annexb_buffer_offset_); | |
| 80 } | |
| 81 size_t GetReservedSize() const override { return reserved_size_; } | |
| 82 | |
| 83 private: | |
| 84 char* annexb_buffer_; | |
| 85 size_t annexb_buffer_size_; | |
| 86 size_t annexb_buffer_offset_; | |
| 87 size_t reserved_size_; | |
| 88 | |
| 89 DISALLOW_IMPLICIT_CONSTRUCTORS(RawAnnexBBuffer); | |
| 90 }; | |
| 91 | |
| 92 class StringAnnexBBuffer : public AnnexBBuffer { | |
| 93 public: | |
| 94 explicit StringAnnexBBuffer(std::string* str_annexb_buffer) | |
| 95 : str_annexb_buffer_(str_annexb_buffer) {} | |
| 96 bool Reserve(size_t size) override { | |
| 97 str_annexb_buffer_->reserve(size); | |
| 98 return true; | |
| 99 } | |
| 100 void Append(const char* s, size_t n) override { | |
| 101 str_annexb_buffer_->append(s, n); | |
| 102 } | |
| 103 size_t GetReservedSize() const override { return str_annexb_buffer_->size(); } | |
| 104 | |
| 105 private: | |
| 106 std::string* str_annexb_buffer_; | |
| 107 DISALLOW_IMPLICIT_CONSTRUCTORS(StringAnnexBBuffer); | |
| 108 }; | |
| 109 | |
| 110 template <typename NalSizeType> | |
| 111 void CopyNalsToAnnexB(char* avcc_buffer, | |
| 112 const size_t avcc_size, | |
| 113 AnnexBBuffer* annexb_buffer) { | |
| 114 static_assert(sizeof(NalSizeType) == 1 || sizeof(NalSizeType) == 2 || | |
| 115 sizeof(NalSizeType) == 4, | |
| 116 "NAL size type has unsupported size"); | |
| 117 static const char startcode_3[3] = {0, 0, 1}; | |
| 118 DCHECK(avcc_buffer); | |
| 119 DCHECK(annexb_buffer); | |
| 120 size_t bytes_left = avcc_size; | |
| 121 while (bytes_left > 0) { | |
| 122 DCHECK_GT(bytes_left, sizeof(NalSizeType)); | |
| 123 NalSizeType nal_size; | |
| 124 base::ReadBigEndian(avcc_buffer, &nal_size); | |
| 125 bytes_left -= sizeof(NalSizeType); | |
| 126 avcc_buffer += sizeof(NalSizeType); | |
| 127 | |
| 128 DCHECK_GE(bytes_left, nal_size); | |
| 129 annexb_buffer->Append(startcode_3, sizeof(startcode_3)); | |
| 130 annexb_buffer->Append(avcc_buffer, nal_size); | |
| 131 bytes_left -= nal_size; | |
| 132 avcc_buffer += nal_size; | |
| 133 } | |
| 134 } | |
| 135 | |
| 136 bool CopySampleBufferToAnnexBBuffer(CoreMediaGlue::CMSampleBufferRef sbuf, | |
| 137 AnnexBBuffer* annexb_buffer, | |
| 138 bool keyframe) { | |
| 139 // Perform two pass, one to figure out the total output size, and another to | |
| 140 // copy the data after having performed a single output allocation. Note that | |
| 141 // we'll allocate a bit more because we'll count 4 bytes instead of 3 for | |
| 142 // video NALs. | |
| 143 OSStatus status; | |
| 144 | |
| 145 // Get the sample buffer's block buffer and format description. | |
| 146 auto bb = CoreMediaGlue::CMSampleBufferGetDataBuffer(sbuf); | |
| 147 DCHECK(bb); | |
| 148 auto fdesc = CoreMediaGlue::CMSampleBufferGetFormatDescription(sbuf); | |
| 149 DCHECK(fdesc); | |
| 150 | |
| 151 size_t bb_size = CoreMediaGlue::CMBlockBufferGetDataLength(bb); | |
| 152 size_t total_bytes = bb_size; | |
| 153 | |
| 154 size_t pset_count; | |
| 155 int nal_size_field_bytes; | |
| 156 status = CoreMediaGlue::CMVideoFormatDescriptionGetH264ParameterSetAtIndex( | |
| 157 fdesc, 0, nullptr, nullptr, &pset_count, &nal_size_field_bytes); | |
| 158 if (status == | |
| 159 CoreMediaGlue::kCMFormatDescriptionBridgeError_InvalidParameter) { | |
| 160 DLOG(WARNING) << " assuming 2 parameter sets and 4 bytes NAL length header"; | |
| 161 pset_count = 2; | |
| 162 nal_size_field_bytes = 4; | |
| 163 } else if (status != noErr) { | |
| 164 DLOG(ERROR) | |
| 165 << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: " | |
| 166 << status; | |
| 167 return false; | |
| 168 } | |
| 169 | |
| 170 if (keyframe) { | |
| 171 const uint8_t* pset; | |
| 172 size_t pset_size; | |
| 173 for (size_t pset_i = 0; pset_i < pset_count; ++pset_i) { | |
| 174 status = | |
| 175 CoreMediaGlue::CMVideoFormatDescriptionGetH264ParameterSetAtIndex( | |
| 176 fdesc, pset_i, &pset, &pset_size, nullptr, nullptr); | |
| 177 if (status != noErr) { | |
| 178 DLOG(ERROR) | |
| 179 << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: " | |
| 180 << status; | |
| 181 return false; | |
| 182 } | |
| 183 total_bytes += pset_size + nal_size_field_bytes; | |
| 184 } | |
| 185 } | |
| 186 | |
| 187 if (!annexb_buffer->Reserve(total_bytes)) { | |
| 188 DLOG(ERROR) << "Cannot fit encode output into bitstream buffer. Requested:" | |
| 189 << total_bytes; | |
| 190 return false; | |
| 191 } | |
| 192 | |
| 193 // Copy all parameter sets before keyframes. | |
| 194 if (keyframe) { | |
| 195 const uint8_t* pset; | |
| 196 size_t pset_size; | |
| 197 for (size_t pset_i = 0; pset_i < pset_count; ++pset_i) { | |
| 198 status = | |
| 199 CoreMediaGlue::CMVideoFormatDescriptionGetH264ParameterSetAtIndex( | |
| 200 fdesc, pset_i, &pset, &pset_size, nullptr, nullptr); | |
| 201 if (status != noErr) { | |
| 202 DLOG(ERROR) | |
| 203 << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: " | |
| 204 << status; | |
| 205 return false; | |
| 206 } | |
| 207 static const char startcode_4[4] = {0, 0, 0, 1}; | |
| 208 annexb_buffer->Append(startcode_4, sizeof(startcode_4)); | |
| 209 annexb_buffer->Append(reinterpret_cast<const char*>(pset), pset_size); | |
| 210 } | |
| 211 } | |
| 212 | |
| 213 // Block buffers can be composed of non-contiguous chunks. For the sake of | |
| 214 // keeping this code simple, flatten non-contiguous block buffers. | |
| 215 base::ScopedCFTypeRef<CoreMediaGlue::CMBlockBufferRef> contiguous_bb( | |
| 216 bb, base::scoped_policy::RETAIN); | |
| 217 if (!CoreMediaGlue::CMBlockBufferIsRangeContiguous(bb, 0, 0)) { | |
| 218 contiguous_bb.reset(); | |
| 219 status = CoreMediaGlue::CMBlockBufferCreateContiguous( | |
| 220 kCFAllocatorDefault, bb, kCFAllocatorDefault, nullptr, 0, 0, 0, | |
| 221 contiguous_bb.InitializeInto()); | |
| 222 if (status != noErr) { | |
| 223 DLOG(ERROR) << " CMBlockBufferCreateContiguous failed: " << status; | |
| 224 return false; | |
| 225 } | |
| 226 } | |
| 227 | |
| 228 // Copy all the NAL units. In the process convert them from AVCC format | |
| 229 // (length header) to AnnexB format (start code). | |
| 230 char* bb_data; | |
| 231 status = CoreMediaGlue::CMBlockBufferGetDataPointer(contiguous_bb, 0, nullptr, | |
| 232 nullptr, &bb_data); | |
| 233 if (status != noErr) { | |
| 234 DLOG(ERROR) << " CMBlockBufferGetDataPointer failed: " << status; | |
| 235 return false; | |
| 236 } | |
| 237 | |
| 238 if (nal_size_field_bytes == 1) { | |
| 239 CopyNalsToAnnexB<uint8_t>(bb_data, bb_size, annexb_buffer); | |
| 240 } else if (nal_size_field_bytes == 2) { | |
| 241 CopyNalsToAnnexB<uint16_t>(bb_data, bb_size, annexb_buffer); | |
| 242 } else if (nal_size_field_bytes == 4) { | |
| 243 CopyNalsToAnnexB<uint32_t>(bb_data, bb_size, annexb_buffer); | |
| 244 } else { | |
| 245 NOTREACHED(); | |
| 246 } | |
| 247 return true; | |
| 248 } | |
| 249 | |
| 250 bool CopySampleBufferToAnnexBBuffer(CoreMediaGlue::CMSampleBufferRef sbuf, | |
| 251 bool keyframe, | |
| 252 std::string* annexb_buffer) { | |
| 253 StringAnnexBBuffer buffer(annexb_buffer); | |
| 254 return CopySampleBufferToAnnexBBuffer(sbuf, &buffer, keyframe); | |
| 255 } | |
| 256 | |
| 257 bool CopySampleBufferToAnnexBBuffer(CoreMediaGlue::CMSampleBufferRef sbuf, | |
| 258 bool keyframe, | |
| 259 size_t annexb_buffer_size, | |
| 260 char* annexb_buffer, | |
| 261 size_t* used_buffer_size) { | |
| 262 RawAnnexBBuffer buffer(annexb_buffer, annexb_buffer_size); | |
| 263 const bool copy_rv = CopySampleBufferToAnnexBBuffer(sbuf, &buffer, keyframe); | |
| 264 *used_buffer_size = buffer.GetReservedSize(); | |
| 265 return copy_rv; | |
| 266 } | |
| 267 | |
| 268 SessionPropertySetter::SessionPropertySetter( | |
| 269 base::ScopedCFTypeRef<VideoToolboxGlue::VTCompressionSessionRef> session, | |
| 270 const VideoToolboxGlue* const glue) | |
| 271 : session_(session), glue_(glue) {} | |
| 272 | |
| 273 SessionPropertySetter::~SessionPropertySetter() {} | |
| 274 | |
| 275 bool SessionPropertySetter::Set(CFStringRef key, int32_t value) { | |
| 276 DCHECK(session_); | |
| 277 DCHECK(glue_); | |
| 278 base::ScopedCFTypeRef<CFNumberRef> cfvalue( | |
| 279 CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)); | |
| 280 return glue_->VTSessionSetProperty(session_, key, cfvalue) == noErr; | |
| 281 } | |
| 282 | |
| 283 bool SessionPropertySetter::Set(CFStringRef key, bool value) { | |
| 284 DCHECK(session_); | |
| 285 DCHECK(glue_); | |
| 286 CFBooleanRef cfvalue = (value) ? kCFBooleanTrue : kCFBooleanFalse; | |
| 287 return glue_->VTSessionSetProperty(session_, key, cfvalue) == noErr; | |
| 288 } | |
| 289 | |
| 290 bool SessionPropertySetter::Set(CFStringRef key, CFStringRef value) { | |
| 291 DCHECK(session_); | |
| 292 DCHECK(glue_); | |
| 293 return glue_->VTSessionSetProperty(session_, key, value) == noErr; | |
| 294 } | |
| 295 | |
| 296 bool SessionPropertySetter::Set(CFStringRef key, CFArrayRef value) { | |
| 297 DCHECK(session_); | |
| 298 DCHECK(glue_); | |
| 299 return glue_->VTSessionSetProperty(session_, key, value) == noErr; | |
| 300 } | |
| 301 | |
| 302 } // namespace video_toolbox | |
| 303 | |
| 304 } // namespace media | |
| OLD | NEW |