| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/cast/sender/h264_vt_encoder.h" | 5 #include "media/cast/sender/h264_vt_encoder.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <string> | 9 #include <string> |
| 10 #include <vector> | 10 #include <vector> |
| (...skipping 25 matching lines...) Expand all Loading... |
| 36 const VideoEncoder::FrameEncodedCallback frame_encoded_callback; | 36 const VideoEncoder::FrameEncodedCallback frame_encoded_callback; |
| 37 | 37 |
| 38 InProgressFrameEncode(RtpTimeTicks rtp, | 38 InProgressFrameEncode(RtpTimeTicks rtp, |
| 39 base::TimeTicks r_time, | 39 base::TimeTicks r_time, |
| 40 VideoEncoder::FrameEncodedCallback callback) | 40 VideoEncoder::FrameEncodedCallback callback) |
| 41 : rtp_timestamp(rtp), | 41 : rtp_timestamp(rtp), |
| 42 reference_time(r_time), | 42 reference_time(r_time), |
| 43 frame_encoded_callback(callback) {} | 43 frame_encoded_callback(callback) {} |
| 44 }; | 44 }; |
| 45 | 45 |
| 46 base::ScopedCFTypeRef<CFDictionaryRef> | |
| 47 DictionaryWithKeysAndValues(CFTypeRef* keys, CFTypeRef* values, size_t size) { | |
| 48 return base::ScopedCFTypeRef<CFDictionaryRef>(CFDictionaryCreate( | |
| 49 kCFAllocatorDefault, keys, values, size, &kCFTypeDictionaryKeyCallBacks, | |
| 50 &kCFTypeDictionaryValueCallBacks)); | |
| 51 } | |
| 52 | |
| 53 base::ScopedCFTypeRef<CFDictionaryRef> DictionaryWithKeyValue(CFTypeRef key, | |
| 54 CFTypeRef value) { | |
| 55 CFTypeRef keys[1] = {key}; | |
| 56 CFTypeRef values[1] = {value}; | |
| 57 return DictionaryWithKeysAndValues(keys, values, 1); | |
| 58 } | |
| 59 | |
| 60 base::ScopedCFTypeRef<CFArrayRef> ArrayWithIntegers(const int* v, size_t size) { | |
| 61 std::vector<CFNumberRef> numbers; | |
| 62 numbers.reserve(size); | |
| 63 for (const int* end = v + size; v < end; ++v) | |
| 64 numbers.push_back(CFNumberCreate(nullptr, kCFNumberSInt32Type, v)); | |
| 65 base::ScopedCFTypeRef<CFArrayRef> array(CFArrayCreate( | |
| 66 kCFAllocatorDefault, reinterpret_cast<const void**>(&numbers[0]), | |
| 67 numbers.size(), &kCFTypeArrayCallBacks)); | |
| 68 for (auto& number : numbers) { | |
| 69 CFRelease(number); | |
| 70 } | |
| 71 return array; | |
| 72 } | |
| 73 | |
| 74 template <typename NalSizeType> | |
| 75 void CopyNalsToAnnexB(char* avcc_buffer, | |
| 76 const size_t avcc_size, | |
| 77 std::string* annexb_buffer) { | |
| 78 static_assert(sizeof(NalSizeType) == 1 || sizeof(NalSizeType) == 2 || | |
| 79 sizeof(NalSizeType) == 4, | |
| 80 "NAL size type has unsupported size"); | |
| 81 static const char startcode_3[3] = {0, 0, 1}; | |
| 82 DCHECK(avcc_buffer); | |
| 83 DCHECK(annexb_buffer); | |
| 84 size_t bytes_left = avcc_size; | |
| 85 while (bytes_left > 0) { | |
| 86 DCHECK_GT(bytes_left, sizeof(NalSizeType)); | |
| 87 NalSizeType nal_size; | |
| 88 base::ReadBigEndian(avcc_buffer, &nal_size); | |
| 89 bytes_left -= sizeof(NalSizeType); | |
| 90 avcc_buffer += sizeof(NalSizeType); | |
| 91 | |
| 92 DCHECK_GE(bytes_left, nal_size); | |
| 93 annexb_buffer->append(startcode_3, sizeof(startcode_3)); | |
| 94 annexb_buffer->append(avcc_buffer, nal_size); | |
| 95 bytes_left -= nal_size; | |
| 96 avcc_buffer += nal_size; | |
| 97 } | |
| 98 } | |
| 99 | |
| 100 // Copy a H.264 frame stored in a CM sample buffer to an Annex B buffer. Copies | |
| 101 // parameter sets for keyframes before the frame data as well. | |
| 102 void CopySampleBufferToAnnexBBuffer(CoreMediaGlue::CMSampleBufferRef sbuf, | |
| 103 std::string* annexb_buffer, | |
| 104 bool keyframe) { | |
| 105 // Perform two pass, one to figure out the total output size, and another to | |
| 106 // copy the data after having performed a single output allocation. Note that | |
| 107 // we'll allocate a bit more because we'll count 4 bytes instead of 3 for | |
| 108 // video NALs. | |
| 109 | |
| 110 OSStatus status; | |
| 111 | |
| 112 // Get the sample buffer's block buffer and format description. | |
| 113 auto bb = CoreMediaGlue::CMSampleBufferGetDataBuffer(sbuf); | |
| 114 DCHECK(bb); | |
| 115 auto fdesc = CoreMediaGlue::CMSampleBufferGetFormatDescription(sbuf); | |
| 116 DCHECK(fdesc); | |
| 117 | |
| 118 size_t bb_size = CoreMediaGlue::CMBlockBufferGetDataLength(bb); | |
| 119 size_t total_bytes = bb_size; | |
| 120 | |
| 121 size_t pset_count; | |
| 122 int nal_size_field_bytes; | |
| 123 status = CoreMediaGlue::CMVideoFormatDescriptionGetH264ParameterSetAtIndex( | |
| 124 fdesc, 0, nullptr, nullptr, &pset_count, &nal_size_field_bytes); | |
| 125 if (status == | |
| 126 CoreMediaGlue::kCMFormatDescriptionBridgeError_InvalidParameter) { | |
| 127 DLOG(WARNING) << " assuming 2 parameter sets and 4 bytes NAL length header"; | |
| 128 pset_count = 2; | |
| 129 nal_size_field_bytes = 4; | |
| 130 } else if (status != noErr) { | |
| 131 DLOG(ERROR) | |
| 132 << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: " | |
| 133 << status; | |
| 134 return; | |
| 135 } | |
| 136 | |
| 137 if (keyframe) { | |
| 138 const uint8_t* pset; | |
| 139 size_t pset_size; | |
| 140 for (size_t pset_i = 0; pset_i < pset_count; ++pset_i) { | |
| 141 status = | |
| 142 CoreMediaGlue::CMVideoFormatDescriptionGetH264ParameterSetAtIndex( | |
| 143 fdesc, pset_i, &pset, &pset_size, nullptr, nullptr); | |
| 144 if (status != noErr) { | |
| 145 DLOG(ERROR) | |
| 146 << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: " | |
| 147 << status; | |
| 148 return; | |
| 149 } | |
| 150 total_bytes += pset_size + nal_size_field_bytes; | |
| 151 } | |
| 152 } | |
| 153 | |
| 154 annexb_buffer->reserve(total_bytes); | |
| 155 | |
| 156 // Copy all parameter sets before keyframes. | |
| 157 if (keyframe) { | |
| 158 const uint8_t* pset; | |
| 159 size_t pset_size; | |
| 160 for (size_t pset_i = 0; pset_i < pset_count; ++pset_i) { | |
| 161 status = | |
| 162 CoreMediaGlue::CMVideoFormatDescriptionGetH264ParameterSetAtIndex( | |
| 163 fdesc, pset_i, &pset, &pset_size, nullptr, nullptr); | |
| 164 if (status != noErr) { | |
| 165 DLOG(ERROR) | |
| 166 << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: " | |
| 167 << status; | |
| 168 return; | |
| 169 } | |
| 170 static const char startcode_4[4] = {0, 0, 0, 1}; | |
| 171 annexb_buffer->append(startcode_4, sizeof(startcode_4)); | |
| 172 annexb_buffer->append(reinterpret_cast<const char*>(pset), pset_size); | |
| 173 } | |
| 174 } | |
| 175 | |
| 176 // Block buffers can be composed of non-contiguous chunks. For the sake of | |
| 177 // keeping this code simple, flatten non-contiguous block buffers. | |
| 178 base::ScopedCFTypeRef<CoreMediaGlue::CMBlockBufferRef> contiguous_bb( | |
| 179 bb, base::scoped_policy::RETAIN); | |
| 180 if (!CoreMediaGlue::CMBlockBufferIsRangeContiguous(bb, 0, 0)) { | |
| 181 contiguous_bb.reset(); | |
| 182 status = CoreMediaGlue::CMBlockBufferCreateContiguous( | |
| 183 kCFAllocatorDefault, bb, kCFAllocatorDefault, nullptr, 0, 0, 0, | |
| 184 contiguous_bb.InitializeInto()); | |
| 185 if (status != noErr) { | |
| 186 DLOG(ERROR) << " CMBlockBufferCreateContiguous failed: " << status; | |
| 187 return; | |
| 188 } | |
| 189 } | |
| 190 | |
| 191 // Copy all the NAL units. In the process convert them from AVCC format | |
| 192 // (length header) to AnnexB format (start code). | |
| 193 char* bb_data; | |
| 194 status = CoreMediaGlue::CMBlockBufferGetDataPointer(contiguous_bb, 0, nullptr, | |
| 195 nullptr, &bb_data); | |
| 196 if (status != noErr) { | |
| 197 DLOG(ERROR) << " CMBlockBufferGetDataPointer failed: " << status; | |
| 198 return; | |
| 199 } | |
| 200 | |
| 201 if (nal_size_field_bytes == 1) { | |
| 202 CopyNalsToAnnexB<uint8_t>(bb_data, bb_size, annexb_buffer); | |
| 203 } else if (nal_size_field_bytes == 2) { | |
| 204 CopyNalsToAnnexB<uint16_t>(bb_data, bb_size, annexb_buffer); | |
| 205 } else if (nal_size_field_bytes == 4) { | |
| 206 CopyNalsToAnnexB<uint32_t>(bb_data, bb_size, annexb_buffer); | |
| 207 } else { | |
| 208 NOTREACHED(); | |
| 209 } | |
| 210 } | |
| 211 | |
| 212 } // namespace | 46 } // namespace |
| 213 | 47 |
| 214 class H264VideoToolboxEncoder::VideoFrameFactoryImpl | 48 class H264VideoToolboxEncoder::VideoFrameFactoryImpl |
| 215 : public base::RefCountedThreadSafe<VideoFrameFactoryImpl>, | 49 : public base::RefCountedThreadSafe<VideoFrameFactoryImpl>, |
| 216 public VideoFrameFactory { | 50 public VideoFrameFactory { |
| 217 public: | 51 public: |
| 218 // Type that proxies the VideoFrameFactory interface to this class. | 52 // Type that proxies the VideoFrameFactory interface to this class. |
| 219 class Proxy; | 53 class Proxy; |
| 220 | 54 |
| 221 VideoFrameFactoryImpl(const base::WeakPtr<H264VideoToolboxEncoder>& encoder, | 55 VideoFrameFactoryImpl(const base::WeakPtr<H264VideoToolboxEncoder>& encoder, |
| (...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 384 CastEnvironment::MAIN, FROM_HERE, | 218 CastEnvironment::MAIN, FROM_HERE, |
| 385 base::Bind(status_change_cb_, STATUS_CODEC_REINIT_PENDING)); | 219 base::Bind(status_change_cb_, STATUS_CODEC_REINIT_PENDING)); |
| 386 | 220 |
| 387 // Destroy the current session, if any. | 221 // Destroy the current session, if any. |
| 388 DestroyCompressionSession(); | 222 DestroyCompressionSession(); |
| 389 | 223 |
| 390 // On OS X, allow the hardware encoder. Don't require it, it does not support | 224 // On OS X, allow the hardware encoder. Don't require it, it does not support |
| 391 // all configurations (some of which are used for testing). | 225 // all configurations (some of which are used for testing). |
| 392 base::ScopedCFTypeRef<CFDictionaryRef> encoder_spec; | 226 base::ScopedCFTypeRef<CFDictionaryRef> encoder_spec; |
| 393 #if !defined(OS_IOS) | 227 #if !defined(OS_IOS) |
| 394 encoder_spec = DictionaryWithKeyValue( | 228 encoder_spec = video_toolbox::DictionaryWithKeyValue( |
| 395 videotoolbox_glue_ | 229 videotoolbox_glue_ |
| 396 ->kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder()
, | 230 ->kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder()
, |
| 397 kCFBooleanTrue); | 231 kCFBooleanTrue); |
| 398 #endif | 232 #endif |
| 399 | 233 |
| 400 // Force 420v so that clients can easily use these buffers as GPU textures. | 234 // Force 420v so that clients can easily use these buffers as GPU textures. |
| 401 const int format[] = { | 235 const int format[] = { |
| 402 CoreVideoGlue::kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange}; | 236 CoreVideoGlue::kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange}; |
| 403 | 237 |
| 404 // Keep these attachment settings in-sync with those in ConfigureSession(). | 238 // Keep these attachment settings in-sync with those in ConfigureSession(). |
| 405 CFTypeRef attachments_keys[] = {kCVImageBufferColorPrimariesKey, | 239 CFTypeRef attachments_keys[] = {kCVImageBufferColorPrimariesKey, |
| 406 kCVImageBufferTransferFunctionKey, | 240 kCVImageBufferTransferFunctionKey, |
| 407 kCVImageBufferYCbCrMatrixKey}; | 241 kCVImageBufferYCbCrMatrixKey}; |
| 408 CFTypeRef attachments_values[] = {kCVImageBufferColorPrimaries_ITU_R_709_2, | 242 CFTypeRef attachments_values[] = {kCVImageBufferColorPrimaries_ITU_R_709_2, |
| 409 kCVImageBufferTransferFunction_ITU_R_709_2, | 243 kCVImageBufferTransferFunction_ITU_R_709_2, |
| 410 kCVImageBufferYCbCrMatrix_ITU_R_709_2}; | 244 kCVImageBufferYCbCrMatrix_ITU_R_709_2}; |
| 411 CFTypeRef buffer_attributes_keys[] = {kCVPixelBufferPixelFormatTypeKey, | 245 CFTypeRef buffer_attributes_keys[] = {kCVPixelBufferPixelFormatTypeKey, |
| 412 kCVBufferPropagatedAttachmentsKey}; | 246 kCVBufferPropagatedAttachmentsKey}; |
| 413 CFTypeRef buffer_attributes_values[] = { | 247 CFTypeRef buffer_attributes_values[] = { |
| 414 ArrayWithIntegers(format, arraysize(format)).release(), | 248 video_toolbox::ArrayWithIntegers(format, arraysize(format)).release(), |
| 415 DictionaryWithKeysAndValues(attachments_keys, attachments_values, | 249 video_toolbox::DictionaryWithKeysAndValues( |
| 416 arraysize(attachments_keys)).release()}; | 250 attachments_keys, attachments_values, arraysize(attachments_keys)) |
| 251 .release()}; |
| 417 const base::ScopedCFTypeRef<CFDictionaryRef> buffer_attributes = | 252 const base::ScopedCFTypeRef<CFDictionaryRef> buffer_attributes = |
| 418 DictionaryWithKeysAndValues(buffer_attributes_keys, | 253 video_toolbox::DictionaryWithKeysAndValues( |
| 419 buffer_attributes_values, | 254 buffer_attributes_keys, buffer_attributes_values, |
| 420 arraysize(buffer_attributes_keys)); | 255 arraysize(buffer_attributes_keys)); |
| 421 for (auto& v : buffer_attributes_values) | 256 for (auto& v : buffer_attributes_values) |
| 422 CFRelease(v); | 257 CFRelease(v); |
| 423 | 258 |
| 424 // Create the compression session. | 259 // Create the compression session. |
| 425 | 260 |
| 426 // Note that the encoder object is given to the compression session as the | 261 // Note that the encoder object is given to the compression session as the |
| 427 // callback context using a raw pointer. The C API does not allow us to use a | 262 // callback context using a raw pointer. The C API does not allow us to use a |
| 428 // smart pointer, nor is this encoder ref counted. However, this is still | 263 // smart pointer, nor is this encoder ref counted. However, this is still |
| 429 // safe, because we 1) we own the compression session and 2) we tear it down | 264 // safe, because we 1) we own the compression session and 2) we tear it down |
| 430 // safely. When destructing the encoder, the compression session is flushed | 265 // safely. When destructing the encoder, the compression session is flushed |
| (...skipping 26 matching lines...) Expand all Loading... |
| 457 base::scoped_policy::RETAIN); | 292 base::scoped_policy::RETAIN); |
| 458 video_frame_factory_->Update(pool, frame_size_); | 293 video_frame_factory_->Update(pool, frame_size_); |
| 459 | 294 |
| 460 // Notify that reinitialization is done. | 295 // Notify that reinitialization is done. |
| 461 cast_environment_->PostTask( | 296 cast_environment_->PostTask( |
| 462 CastEnvironment::MAIN, FROM_HERE, | 297 CastEnvironment::MAIN, FROM_HERE, |
| 463 base::Bind(status_change_cb_, STATUS_INITIALIZED)); | 298 base::Bind(status_change_cb_, STATUS_INITIALIZED)); |
| 464 } | 299 } |
| 465 | 300 |
| 466 void H264VideoToolboxEncoder::ConfigureCompressionSession() { | 301 void H264VideoToolboxEncoder::ConfigureCompressionSession() { |
| 467 SetSessionProperty( | 302 video_toolbox::SessionPropertySetter session_property_setter( |
| 303 compression_session_, videotoolbox_glue_); |
| 304 session_property_setter.Set( |
| 468 videotoolbox_glue_->kVTCompressionPropertyKey_ProfileLevel(), | 305 videotoolbox_glue_->kVTCompressionPropertyKey_ProfileLevel(), |
| 469 videotoolbox_glue_->kVTProfileLevel_H264_Main_AutoLevel()); | 306 videotoolbox_glue_->kVTProfileLevel_H264_Main_AutoLevel()); |
| 470 SetSessionProperty(videotoolbox_glue_->kVTCompressionPropertyKey_RealTime(), | 307 session_property_setter.Set( |
| 471 true); | 308 videotoolbox_glue_->kVTCompressionPropertyKey_RealTime(), true); |
| 472 SetSessionProperty( | 309 session_property_setter.Set( |
| 473 videotoolbox_glue_->kVTCompressionPropertyKey_AllowFrameReordering(), | 310 videotoolbox_glue_->kVTCompressionPropertyKey_AllowFrameReordering(), |
| 474 false); | 311 false); |
| 475 SetSessionProperty( | 312 session_property_setter.Set( |
| 476 videotoolbox_glue_->kVTCompressionPropertyKey_MaxKeyFrameInterval(), 240); | 313 videotoolbox_glue_->kVTCompressionPropertyKey_MaxKeyFrameInterval(), 240); |
| 477 SetSessionProperty( | 314 session_property_setter.Set( |
| 478 videotoolbox_glue_ | 315 videotoolbox_glue_ |
| 479 ->kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration(), | 316 ->kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration(), |
| 480 240); | 317 240); |
| 481 // TODO(jfroy): implement better bitrate control | 318 // TODO(jfroy): implement better bitrate control |
| 482 // https://crbug.com/425352 | 319 // https://crbug.com/425352 |
| 483 SetSessionProperty( | 320 session_property_setter.Set( |
| 484 videotoolbox_glue_->kVTCompressionPropertyKey_AverageBitRate(), | 321 videotoolbox_glue_->kVTCompressionPropertyKey_AverageBitRate(), |
| 485 (video_config_.min_bitrate + video_config_.max_bitrate) / 2); | 322 (video_config_.min_bitrate + video_config_.max_bitrate) / 2); |
| 486 SetSessionProperty( | 323 session_property_setter.Set( |
| 487 videotoolbox_glue_->kVTCompressionPropertyKey_ExpectedFrameRate(), | 324 videotoolbox_glue_->kVTCompressionPropertyKey_ExpectedFrameRate(), |
| 488 video_config_.max_frame_rate); | 325 video_config_.max_frame_rate); |
| 489 // Keep these attachment settings in-sync with those in Initialize(). | 326 // Keep these attachment settings in-sync with those in Initialize(). |
| 490 SetSessionProperty( | 327 session_property_setter.Set( |
| 491 videotoolbox_glue_->kVTCompressionPropertyKey_ColorPrimaries(), | 328 videotoolbox_glue_->kVTCompressionPropertyKey_ColorPrimaries(), |
| 492 kCVImageBufferColorPrimaries_ITU_R_709_2); | 329 kCVImageBufferColorPrimaries_ITU_R_709_2); |
| 493 SetSessionProperty( | 330 session_property_setter.Set( |
| 494 videotoolbox_glue_->kVTCompressionPropertyKey_TransferFunction(), | 331 videotoolbox_glue_->kVTCompressionPropertyKey_TransferFunction(), |
| 495 kCVImageBufferTransferFunction_ITU_R_709_2); | 332 kCVImageBufferTransferFunction_ITU_R_709_2); |
| 496 SetSessionProperty( | 333 session_property_setter.Set( |
| 497 videotoolbox_glue_->kVTCompressionPropertyKey_YCbCrMatrix(), | 334 videotoolbox_glue_->kVTCompressionPropertyKey_YCbCrMatrix(), |
| 498 kCVImageBufferYCbCrMatrix_ITU_R_709_2); | 335 kCVImageBufferYCbCrMatrix_ITU_R_709_2); |
| 499 if (video_config_.max_number_of_video_buffers_used > 0) { | 336 if (video_config_.max_number_of_video_buffers_used > 0) { |
| 500 SetSessionProperty( | 337 session_property_setter.Set( |
| 501 videotoolbox_glue_->kVTCompressionPropertyKey_MaxFrameDelayCount(), | 338 videotoolbox_glue_->kVTCompressionPropertyKey_MaxFrameDelayCount(), |
| 502 video_config_.max_number_of_video_buffers_used); | 339 video_config_.max_number_of_video_buffers_used); |
| 503 } | 340 } |
| 504 } | 341 } |
| 505 | 342 |
| 506 void H264VideoToolboxEncoder::DestroyCompressionSession() { | 343 void H264VideoToolboxEncoder::DestroyCompressionSession() { |
| 507 DCHECK(thread_checker_.CalledOnValidThread()); | 344 DCHECK(thread_checker_.CalledOnValidThread()); |
| 508 | 345 |
| 509 // If the compression session exists, invalidate it. This blocks until all | 346 // If the compression session exists, invalidate it. This blocks until all |
| 510 // pending output callbacks have returned and any internal threads have | 347 // pending output callbacks have returned and any internal threads have |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 559 // frame into memory visible by the hardware encoder. The VideoFrame's | 396 // frame into memory visible by the hardware encoder. The VideoFrame's |
| 560 // lifetime is extended for the lifetime of the returned CVPixelBuffer. | 397 // lifetime is extended for the lifetime of the returned CVPixelBuffer. |
| 561 auto pixel_buffer = media::WrapVideoFrameInCVPixelBuffer(*video_frame); | 398 auto pixel_buffer = media::WrapVideoFrameInCVPixelBuffer(*video_frame); |
| 562 if (!pixel_buffer) { | 399 if (!pixel_buffer) { |
| 563 DLOG(ERROR) << "WrapVideoFrameInCVPixelBuffer failed."; | 400 DLOG(ERROR) << "WrapVideoFrameInCVPixelBuffer failed."; |
| 564 return false; | 401 return false; |
| 565 } | 402 } |
| 566 | 403 |
| 567 // Convert the frame timestamp to CMTime. | 404 // Convert the frame timestamp to CMTime. |
| 568 auto timestamp_cm = CoreMediaGlue::CMTimeMake( | 405 auto timestamp_cm = CoreMediaGlue::CMTimeMake( |
| 569 (reference_time - base::TimeTicks()).InMicroseconds(), USEC_PER_SEC); | 406 video_frame->timestamp().InMicroseconds(), USEC_PER_SEC); |
| 570 | 407 |
| 571 // Wrap information we'll need after the frame is encoded in a heap object. | 408 // Wrap information we'll need after the frame is encoded in a heap object. |
| 572 // We'll get the pointer back from the VideoToolbox completion callback. | 409 // We'll get the pointer back from the VideoToolbox completion callback. |
| 573 scoped_ptr<InProgressFrameEncode> request(new InProgressFrameEncode( | 410 scoped_ptr<InProgressFrameEncode> request(new InProgressFrameEncode( |
| 574 RtpTimeTicks::FromTimeDelta(video_frame->timestamp(), kVideoFrequency), | 411 RtpTimeTicks::FromTimeDelta(video_frame->timestamp(), kVideoFrequency), |
| 575 reference_time, frame_encoded_callback)); | 412 reference_time, frame_encoded_callback)); |
| 576 | 413 |
| 577 // Build a suitable frame properties dictionary for keyframes. | 414 // Build a suitable frame properties dictionary for keyframes. |
| 578 base::ScopedCFTypeRef<CFDictionaryRef> frame_props; | 415 base::ScopedCFTypeRef<CFDictionaryRef> frame_props; |
| 579 if (encode_next_frame_as_keyframe_) { | 416 if (encode_next_frame_as_keyframe_) { |
| 580 frame_props = DictionaryWithKeyValue( | 417 frame_props = video_toolbox::DictionaryWithKeyValue( |
| 581 videotoolbox_glue_->kVTEncodeFrameOptionKey_ForceKeyFrame(), | 418 videotoolbox_glue_->kVTEncodeFrameOptionKey_ForceKeyFrame(), |
| 582 kCFBooleanTrue); | 419 kCFBooleanTrue); |
| 583 encode_next_frame_as_keyframe_ = false; | 420 encode_next_frame_as_keyframe_ = false; |
| 584 } | 421 } |
| 585 | 422 |
| 586 // Submit the frame to the compression session. The function returns as soon | 423 // Submit the frame to the compression session. The function returns as soon |
| 587 // as the frame has been enqueued. | 424 // as the frame has been enqueued. |
| 588 OSStatus status = videotoolbox_glue_->VTCompressionSessionEncodeFrame( | 425 OSStatus status = videotoolbox_glue_->VTCompressionSessionEncodeFrame( |
| 589 compression_session_, pixel_buffer, timestamp_cm, | 426 compression_session_, pixel_buffer, timestamp_cm, |
| 590 CoreMediaGlue::CMTime{0, 0, 0, 0}, frame_props, | 427 CoreMediaGlue::CMTime{0, 0, 0, 0}, frame_props, |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 666 // Reset the compression session only if the frame size is not zero (which | 503 // Reset the compression session only if the frame size is not zero (which |
| 667 // will obviously fail). It is possible for the frame size to be zero if no | 504 // will obviously fail). It is possible for the frame size to be zero if no |
| 668 // frame was submitted for encoding or requested from the video frame factory | 505 // frame was submitted for encoding or requested from the video frame factory |
| 669 // before suspension. | 506 // before suspension. |
| 670 if (!frame_size_.IsEmpty()) { | 507 if (!frame_size_.IsEmpty()) { |
| 671 VLOG(1) << "OnResume: Resetting compression session."; | 508 VLOG(1) << "OnResume: Resetting compression session."; |
| 672 ResetCompressionSession(); | 509 ResetCompressionSession(); |
| 673 } | 510 } |
| 674 } | 511 } |
| 675 | 512 |
| 676 bool H264VideoToolboxEncoder::SetSessionProperty(CFStringRef key, | |
| 677 int32_t value) { | |
| 678 base::ScopedCFTypeRef<CFNumberRef> cfvalue( | |
| 679 CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)); | |
| 680 return videotoolbox_glue_->VTSessionSetProperty(compression_session_, key, | |
| 681 cfvalue) == noErr; | |
| 682 } | |
| 683 | |
| 684 bool H264VideoToolboxEncoder::SetSessionProperty(CFStringRef key, bool value) { | |
| 685 CFBooleanRef cfvalue = (value) ? kCFBooleanTrue : kCFBooleanFalse; | |
| 686 return videotoolbox_glue_->VTSessionSetProperty(compression_session_, key, | |
| 687 cfvalue) == noErr; | |
| 688 } | |
| 689 | |
| 690 bool H264VideoToolboxEncoder::SetSessionProperty(CFStringRef key, | |
| 691 CFStringRef value) { | |
| 692 return videotoolbox_glue_->VTSessionSetProperty(compression_session_, key, | |
| 693 value) == noErr; | |
| 694 } | |
| 695 | |
| 696 void H264VideoToolboxEncoder::CompressionCallback(void* encoder_opaque, | 513 void H264VideoToolboxEncoder::CompressionCallback(void* encoder_opaque, |
| 697 void* request_opaque, | 514 void* request_opaque, |
| 698 OSStatus status, | 515 OSStatus status, |
| 699 VTEncodeInfoFlags info, | 516 VTEncodeInfoFlags info, |
| 700 CMSampleBufferRef sbuf) { | 517 CMSampleBufferRef sbuf) { |
| 701 auto encoder = reinterpret_cast<H264VideoToolboxEncoder*>(encoder_opaque); | 518 auto encoder = reinterpret_cast<H264VideoToolboxEncoder*>(encoder_opaque); |
| 702 const scoped_ptr<InProgressFrameEncode> request( | 519 const scoped_ptr<InProgressFrameEncode> request( |
| 703 reinterpret_cast<InProgressFrameEncode*>(request_opaque)); | 520 reinterpret_cast<InProgressFrameEncode*>(request_opaque)); |
| 704 bool keyframe = false; | 521 bool keyframe = false; |
| 705 bool has_frame_data = false; | 522 bool has_frame_data = false; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 744 // doesn't support the concept of forward-referencing frame dependencies or | 561 // doesn't support the concept of forward-referencing frame dependencies or |
| 745 // multiple frame dependencies; so pretend that all frames are only | 562 // multiple frame dependencies; so pretend that all frames are only |
| 746 // decodable after their immediately preceding frame is decoded. This will | 563 // decodable after their immediately preceding frame is decoded. This will |
| 747 // ensure a Cast receiver only attempts to decode the frames sequentially | 564 // ensure a Cast receiver only attempts to decode the frames sequentially |
| 748 // and in order. Furthermore, the encoder is configured to never use forward | 565 // and in order. Furthermore, the encoder is configured to never use forward |
| 749 // references (see |kVTCompressionPropertyKey_AllowFrameReordering|). There | 566 // references (see |kVTCompressionPropertyKey_AllowFrameReordering|). There |
| 750 // is no way to prevent multiple reference frames. | 567 // is no way to prevent multiple reference frames. |
| 751 encoded_frame->referenced_frame_id = frame_id - 1; | 568 encoded_frame->referenced_frame_id = frame_id - 1; |
| 752 } | 569 } |
| 753 | 570 |
| 754 if (has_frame_data) | 571 if (has_frame_data) { |
| 755 CopySampleBufferToAnnexBBuffer(sbuf, &encoded_frame->data, keyframe); | 572 video_toolbox::CopySampleBufferToAnnexBBuffer(sbuf, keyframe, |
| 573 &encoded_frame->data); |
| 574 } |
| 756 | 575 |
| 757 // TODO(miu): Compute and populate the |deadline_utilization| and | 576 // TODO(miu): Compute and populate the |deadline_utilization| and |
| 758 // |lossy_utilization| performance metrics in |encoded_frame|. | 577 // |lossy_utilization| performance metrics in |encoded_frame|. |
| 759 | 578 |
| 760 encoded_frame->encode_completion_time = | 579 encoded_frame->encode_completion_time = |
| 761 encoder->cast_environment_->Clock()->NowTicks(); | 580 encoder->cast_environment_->Clock()->NowTicks(); |
| 762 encoder->cast_environment_->PostTask( | 581 encoder->cast_environment_->PostTask( |
| 763 CastEnvironment::MAIN, FROM_HERE, | 582 CastEnvironment::MAIN, FROM_HERE, |
| 764 base::Bind(request->frame_encoded_callback, | 583 base::Bind(request->frame_encoded_callback, |
| 765 base::Passed(&encoded_frame))); | 584 base::Passed(&encoded_frame))); |
| 766 } | 585 } |
| 767 | 586 |
| 768 } // namespace cast | 587 } // namespace cast |
| 769 } // namespace media | 588 } // namespace media |
| OLD | NEW |