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