| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 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 "content/common/gpu/media/vt_video_encode_accelerator_mac.h" | 5 #include "media/gpu/vt_video_encode_accelerator_mac.h" |
| 6 | 6 |
| 7 #include <memory> | 7 #include <memory> |
| 8 | 8 |
| 9 #include "base/thread_task_runner_handle.h" | 9 #include "base/thread_task_runner_handle.h" |
| 10 #include "media/base/mac/coremedia_glue.h" | 10 #include "media/base/mac/coremedia_glue.h" |
| 11 #include "media/base/mac/corevideo_glue.h" | 11 #include "media/base/mac/corevideo_glue.h" |
| 12 #include "media/base/mac/video_frame_mac.h" | 12 #include "media/base/mac/video_frame_mac.h" |
| 13 | 13 |
| 14 namespace content { | 14 namespace media { |
| 15 | 15 |
| 16 namespace { | 16 namespace { |
| 17 | 17 |
| 18 // TODO(emircan): Check if we can find the actual system capabilities via | 18 // TODO(emircan): Check if we can find the actual system capabilities via |
| 19 // creating VTCompressionSessions with varying requirements. | 19 // creating VTCompressionSessions with varying requirements. |
| 20 // See crbug.com/584784. | 20 // See crbug.com/584784. |
| 21 const size_t kBitsPerByte = 8; | 21 const size_t kBitsPerByte = 8; |
| 22 const size_t kDefaultResolutionWidth = 640; | 22 const size_t kDefaultResolutionWidth = 640; |
| 23 const size_t kDefaultResolutionHeight = 480; | 23 const size_t kDefaultResolutionHeight = 480; |
| 24 const size_t kMaxFrameRateNumerator = 30; | 24 const size_t kMaxFrameRateNumerator = 30; |
| 25 const size_t kMaxFrameRateDenominator = 1; | 25 const size_t kMaxFrameRateDenominator = 1; |
| 26 const size_t kMaxResolutionWidth = 4096; | 26 const size_t kMaxResolutionWidth = 4096; |
| 27 const size_t kMaxResolutionHeight = 2160; | 27 const size_t kMaxResolutionHeight = 2160; |
| 28 const size_t kNumInputBuffers = 3; | 28 const size_t kNumInputBuffers = 3; |
| 29 | 29 |
| 30 } // namespace | 30 } // namespace |
| 31 | 31 |
| 32 struct VTVideoEncodeAccelerator::InProgressFrameEncode { | 32 struct VTVideoEncodeAccelerator::InProgressFrameEncode { |
| 33 InProgressFrameEncode(base::TimeDelta rtp_timestamp, | 33 InProgressFrameEncode(base::TimeDelta rtp_timestamp, base::TimeTicks ref_time) |
| 34 base::TimeTicks ref_time) | |
| 35 : timestamp(rtp_timestamp), reference_time(ref_time) {} | 34 : timestamp(rtp_timestamp), reference_time(ref_time) {} |
| 36 const base::TimeDelta timestamp; | 35 const base::TimeDelta timestamp; |
| 37 const base::TimeTicks reference_time; | 36 const base::TimeTicks reference_time; |
| 38 | 37 |
| 39 private: | 38 private: |
| 40 DISALLOW_IMPLICIT_CONSTRUCTORS(InProgressFrameEncode); | 39 DISALLOW_IMPLICIT_CONSTRUCTORS(InProgressFrameEncode); |
| 41 }; | 40 }; |
| 42 | 41 |
| 43 struct VTVideoEncodeAccelerator::EncodeOutput { | 42 struct VTVideoEncodeAccelerator::EncodeOutput { |
| 44 EncodeOutput(VTEncodeInfoFlags info_flags, CMSampleBufferRef sbuf) | 43 EncodeOutput(VTEncodeInfoFlags info_flags, CMSampleBufferRef sbuf) |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 123 << ", initial_bitrate=" << initial_bitrate; | 122 << ", initial_bitrate=" << initial_bitrate; |
| 124 DCHECK(thread_checker_.CalledOnValidThread()); | 123 DCHECK(thread_checker_.CalledOnValidThread()); |
| 125 DCHECK(client); | 124 DCHECK(client); |
| 126 | 125 |
| 127 if (media::PIXEL_FORMAT_I420 != format) { | 126 if (media::PIXEL_FORMAT_I420 != format) { |
| 128 DLOG(ERROR) << "Input format not supported= " | 127 DLOG(ERROR) << "Input format not supported= " |
| 129 << media::VideoPixelFormatToString(format); | 128 << media::VideoPixelFormatToString(format); |
| 130 return false; | 129 return false; |
| 131 } | 130 } |
| 132 if (media::H264PROFILE_BASELINE != output_profile) { | 131 if (media::H264PROFILE_BASELINE != output_profile) { |
| 133 DLOG(ERROR) << "Output profile not supported= " | 132 DLOG(ERROR) << "Output profile not supported= " << output_profile; |
| 134 << output_profile; | |
| 135 return false; | 133 return false; |
| 136 } | 134 } |
| 137 | 135 |
| 138 videotoolbox_glue_ = VideoToolboxGlue::Get(); | 136 videotoolbox_glue_ = VideoToolboxGlue::Get(); |
| 139 if (!videotoolbox_glue_) { | 137 if (!videotoolbox_glue_) { |
| 140 DLOG(ERROR) << "Failed creating VideoToolbox glue."; | 138 DLOG(ERROR) << "Failed creating VideoToolbox glue."; |
| 141 return false; | 139 return false; |
| 142 } | 140 } |
| 143 | 141 |
| 144 client_ptr_factory_.reset(new base::WeakPtrFactory<Client>(client)); | 142 client_ptr_factory_.reset(new base::WeakPtrFactory<Client>(client)); |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 221 | 219 |
| 222 void VTVideoEncodeAccelerator::Destroy() { | 220 void VTVideoEncodeAccelerator::Destroy() { |
| 223 DVLOG(3) << __FUNCTION__; | 221 DVLOG(3) << __FUNCTION__; |
| 224 DCHECK(thread_checker_.CalledOnValidThread()); | 222 DCHECK(thread_checker_.CalledOnValidThread()); |
| 225 | 223 |
| 226 // Cancel all callbacks. | 224 // Cancel all callbacks. |
| 227 client_ptr_factory_.reset(); | 225 client_ptr_factory_.reset(); |
| 228 | 226 |
| 229 if (encoder_thread_.IsRunning()) { | 227 if (encoder_thread_.IsRunning()) { |
| 230 encoder_thread_task_runner_->PostTask( | 228 encoder_thread_task_runner_->PostTask( |
| 231 FROM_HERE, | 229 FROM_HERE, base::Bind(&VTVideoEncodeAccelerator::DestroyTask, |
| 232 base::Bind(&VTVideoEncodeAccelerator::DestroyTask, | 230 base::Unretained(this))); |
| 233 base::Unretained(this))); | |
| 234 encoder_thread_.Stop(); | 231 encoder_thread_.Stop(); |
| 235 } else { | 232 } else { |
| 236 DestroyTask(); | 233 DestroyTask(); |
| 237 } | 234 } |
| 238 } | 235 } |
| 239 | 236 |
| 240 void VTVideoEncodeAccelerator::EncodeTask( | 237 void VTVideoEncodeAccelerator::EncodeTask( |
| 241 const scoped_refptr<media::VideoFrame>& frame, | 238 const scoped_refptr<media::VideoFrame>& frame, |
| 242 bool force_keyframe) { | 239 bool force_keyframe) { |
| 243 DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); | 240 DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); |
| (...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 409 client_task_runner_->PostTask( | 406 client_task_runner_->PostTask( |
| 410 FROM_HERE, base::Bind(&Client::BitstreamBufferReady, client_, | 407 FROM_HERE, base::Bind(&Client::BitstreamBufferReady, client_, |
| 411 buffer_ref->id, 0, false)); | 408 buffer_ref->id, 0, false)); |
| 412 return; | 409 return; |
| 413 } | 410 } |
| 414 | 411 |
| 415 auto sample_attachments = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex( | 412 auto sample_attachments = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex( |
| 416 CoreMediaGlue::CMSampleBufferGetSampleAttachmentsArray( | 413 CoreMediaGlue::CMSampleBufferGetSampleAttachmentsArray( |
| 417 encode_output->sample_buffer.get(), true), | 414 encode_output->sample_buffer.get(), true), |
| 418 0)); | 415 0)); |
| 419 const bool keyframe = | 416 const bool keyframe = !CFDictionaryContainsKey( |
| 420 !CFDictionaryContainsKey(sample_attachments, | 417 sample_attachments, CoreMediaGlue::kCMSampleAttachmentKey_NotSync()); |
| 421 CoreMediaGlue::kCMSampleAttachmentKey_NotSync()); | |
| 422 | 418 |
| 423 size_t used_buffer_size = 0; | 419 size_t used_buffer_size = 0; |
| 424 const bool copy_rv = media::video_toolbox::CopySampleBufferToAnnexBBuffer( | 420 const bool copy_rv = media::video_toolbox::CopySampleBufferToAnnexBBuffer( |
| 425 encode_output->sample_buffer.get(), keyframe, buffer_ref->size, | 421 encode_output->sample_buffer.get(), keyframe, buffer_ref->size, |
| 426 reinterpret_cast<char*>(buffer_ref->shm->memory()), &used_buffer_size); | 422 reinterpret_cast<char*>(buffer_ref->shm->memory()), &used_buffer_size); |
| 427 if (!copy_rv) { | 423 if (!copy_rv) { |
| 428 DLOG(ERROR) << "Cannot copy output from SampleBuffer to AnnexBBuffer."; | 424 DLOG(ERROR) << "Cannot copy output from SampleBuffer to AnnexBBuffer."; |
| 429 used_buffer_size = 0; | 425 used_buffer_size = 0; |
| 430 } | 426 } |
| 431 | 427 |
| 432 client_task_runner_->PostTask( | 428 client_task_runner_->PostTask( |
| 433 FROM_HERE, base::Bind(&Client::BitstreamBufferReady, client_, | 429 FROM_HERE, base::Bind(&Client::BitstreamBufferReady, client_, |
| 434 buffer_ref->id, used_buffer_size, keyframe)); | 430 buffer_ref->id, used_buffer_size, keyframe)); |
| 435 } | 431 } |
| 436 | 432 |
| 437 bool VTVideoEncodeAccelerator::ResetCompressionSession() { | 433 bool VTVideoEncodeAccelerator::ResetCompressionSession() { |
| 438 DCHECK(thread_checker_.CalledOnValidThread()); | 434 DCHECK(thread_checker_.CalledOnValidThread()); |
| 439 | 435 |
| 440 DestroyCompressionSession(); | 436 DestroyCompressionSession(); |
| 441 | 437 |
| 442 CFTypeRef attributes_keys[] = { | 438 CFTypeRef attributes_keys[] = {kCVPixelBufferOpenGLCompatibilityKey, |
| 443 kCVPixelBufferOpenGLCompatibilityKey, | 439 kCVPixelBufferIOSurfacePropertiesKey, |
| 444 kCVPixelBufferIOSurfacePropertiesKey, | 440 kCVPixelBufferPixelFormatTypeKey}; |
| 445 kCVPixelBufferPixelFormatTypeKey | |
| 446 }; | |
| 447 const int format[] = { | 441 const int format[] = { |
| 448 CoreVideoGlue::kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange}; | 442 CoreVideoGlue::kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange}; |
| 449 CFTypeRef attributes_values[] = { | 443 CFTypeRef attributes_values[] = { |
| 450 kCFBooleanTrue, | 444 kCFBooleanTrue, |
| 451 media::video_toolbox::DictionaryWithKeysAndValues(nullptr, nullptr, 0) | 445 media::video_toolbox::DictionaryWithKeysAndValues(nullptr, nullptr, 0) |
| 452 .release(), | 446 .release(), |
| 453 media::video_toolbox::ArrayWithIntegers(format, arraysize(format)) | 447 media::video_toolbox::ArrayWithIntegers(format, arraysize(format)) |
| 454 .release()}; | 448 .release()}; |
| 455 const base::ScopedCFTypeRef<CFDictionaryRef> attributes = | 449 const base::ScopedCFTypeRef<CFDictionaryRef> attributes = |
| 456 media::video_toolbox::DictionaryWithKeysAndValues( | 450 media::video_toolbox::DictionaryWithKeysAndValues( |
| (...skipping 16 matching lines...) Expand all Loading... |
| 473 | 467 |
| 474 bool VTVideoEncodeAccelerator::CreateCompressionSession( | 468 bool VTVideoEncodeAccelerator::CreateCompressionSession( |
| 475 base::ScopedCFTypeRef<CFDictionaryRef> attributes, | 469 base::ScopedCFTypeRef<CFDictionaryRef> attributes, |
| 476 const gfx::Size& input_size, | 470 const gfx::Size& input_size, |
| 477 bool require_hw_encoding) { | 471 bool require_hw_encoding) { |
| 478 DCHECK(thread_checker_.CalledOnValidThread()); | 472 DCHECK(thread_checker_.CalledOnValidThread()); |
| 479 | 473 |
| 480 std::vector<CFTypeRef> encoder_keys; | 474 std::vector<CFTypeRef> encoder_keys; |
| 481 std::vector<CFTypeRef> encoder_values; | 475 std::vector<CFTypeRef> encoder_values; |
| 482 if (require_hw_encoding) { | 476 if (require_hw_encoding) { |
| 483 encoder_keys.push_back(videotoolbox_glue_ | 477 encoder_keys.push_back( |
| 484 ->kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder()); | 478 videotoolbox_glue_ |
| 479 ->kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncode
r()); |
| 485 encoder_values.push_back(kCFBooleanTrue); | 480 encoder_values.push_back(kCFBooleanTrue); |
| 486 } else { | 481 } else { |
| 487 encoder_keys.push_back(videotoolbox_glue_ | 482 encoder_keys.push_back( |
| 488 ->kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder()); | 483 videotoolbox_glue_ |
| 484 ->kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder
()); |
| 489 encoder_values.push_back(kCFBooleanTrue); | 485 encoder_values.push_back(kCFBooleanTrue); |
| 490 } | 486 } |
| 491 base::ScopedCFTypeRef<CFDictionaryRef> encoder_spec = | 487 base::ScopedCFTypeRef<CFDictionaryRef> encoder_spec = |
| 492 media::video_toolbox::DictionaryWithKeysAndValues( | 488 media::video_toolbox::DictionaryWithKeysAndValues( |
| 493 encoder_keys.data(), encoder_values.data(), encoder_keys.size()); | 489 encoder_keys.data(), encoder_values.data(), encoder_keys.size()); |
| 494 | 490 |
| 495 // Create the compression session. | 491 // Create the compression session. |
| 496 // Note that the encoder object is given to the compression session as the | 492 // Note that the encoder object is given to the compression session as the |
| 497 // callback context using a raw pointer. The C API does not allow us to use a | 493 // callback context using a raw pointer. The C API does not allow us to use a |
| 498 // smart pointer, nor is this encoder ref counted. However, this is still | 494 // smart pointer, nor is this encoder ref counted. However, this is still |
| 499 // safe, because we 1) we own the compression session and 2) we tear it down | 495 // safe, because we 1) we own the compression session and 2) we tear it down |
| 500 // safely. When destructing the encoder, the compression session is flushed | 496 // safely. When destructing the encoder, the compression session is flushed |
| 501 // and invalidated. Internally, VideoToolbox will join all of its threads | 497 // and invalidated. Internally, VideoToolbox will join all of its threads |
| 502 // before returning to the client. Therefore, when control returns to us, we | 498 // before returning to the client. Therefore, when control returns to us, we |
| 503 // are guaranteed that the output callback will not execute again. | 499 // are guaranteed that the output callback will not execute again. |
| 504 OSStatus status = videotoolbox_glue_->VTCompressionSessionCreate( | 500 OSStatus status = videotoolbox_glue_->VTCompressionSessionCreate( |
| 505 kCFAllocatorDefault, | 501 kCFAllocatorDefault, input_size.width(), input_size.height(), |
| 506 input_size.width(), | 502 CoreMediaGlue::kCMVideoCodecType_H264, encoder_spec, attributes, |
| 507 input_size.height(), | |
| 508 CoreMediaGlue::kCMVideoCodecType_H264, | |
| 509 encoder_spec, | |
| 510 attributes, | |
| 511 nullptr /* compressedDataAllocator */, | 503 nullptr /* compressedDataAllocator */, |
| 512 &VTVideoEncodeAccelerator::CompressionCallback, | 504 &VTVideoEncodeAccelerator::CompressionCallback, |
| 513 reinterpret_cast<void*>(this), | 505 reinterpret_cast<void*>(this), compression_session_.InitializeInto()); |
| 514 compression_session_.InitializeInto()); | |
| 515 if (status != noErr) { | 506 if (status != noErr) { |
| 516 DLOG(ERROR) << " VTCompressionSessionCreate failed: " << status; | 507 DLOG(ERROR) << " VTCompressionSessionCreate failed: " << status; |
| 517 return false; | 508 return false; |
| 518 } | 509 } |
| 519 DVLOG(3) << " VTCompressionSession created with HW encode: " | 510 DVLOG(3) << " VTCompressionSession created with HW encode: " |
| 520 << require_hw_encoding << ", input size=" << input_size.ToString(); | 511 << require_hw_encoding << ", input size=" << input_size.ToString(); |
| 521 return true; | 512 return true; |
| 522 } | 513 } |
| 523 | 514 |
| 524 bool VTVideoEncodeAccelerator::ConfigureCompressionSession() { | 515 bool VTVideoEncodeAccelerator::ConfigureCompressionSession() { |
| (...skipping 19 matching lines...) Expand all Loading... |
| 544 DCHECK(thread_checker_.CalledOnValidThread() || | 535 DCHECK(thread_checker_.CalledOnValidThread() || |
| 545 (encoder_thread_.IsRunning() && | 536 (encoder_thread_.IsRunning() && |
| 546 encoder_thread_task_runner_->BelongsToCurrentThread())); | 537 encoder_thread_task_runner_->BelongsToCurrentThread())); |
| 547 | 538 |
| 548 if (compression_session_) { | 539 if (compression_session_) { |
| 549 videotoolbox_glue_->VTCompressionSessionInvalidate(compression_session_); | 540 videotoolbox_glue_->VTCompressionSessionInvalidate(compression_session_); |
| 550 compression_session_.reset(); | 541 compression_session_.reset(); |
| 551 } | 542 } |
| 552 } | 543 } |
| 553 | 544 |
| 554 } // namespace content | 545 } // namespace media |
| OLD | NEW |