| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "media/gpu/mac/video_toolbox_decompression_session.h" |
| |
| #include "base/logging.h" |
| #include "media/base/media_log.h" |
| |
| namespace media { |
| |
| namespace { |
| |
| void OnOutputThunk(void* decompression_output_refcon, |
| void* source_frame_refcon, |
| OSStatus status, |
| VTDecodeInfoFlags info_flags, |
| CVImageBufferRef image_buffer, |
| CMTime presentation_time_stamp, |
| CMTime presentation_duration) { |
| VideoToolboxDecompressionSessionImpl* vtdsi = |
| static_cast<VideoToolboxDecompressionSessionImpl*>( |
| decompression_output_refcon); |
| vtdsi->OnOutputOnAnyThread(reinterpret_cast<uintptr_t>(source_frame_refcon), |
| status, info_flags, |
| base::apple::ScopedCFTypeRef<CVImageBufferRef>( |
| image_buffer, base::scoped_policy::RETAIN)); |
| } |
| |
| } // namespace |
| |
| VideoToolboxDecompressionSessionImpl::VideoToolboxDecompressionSessionImpl( |
| scoped_refptr<base::SequencedTaskRunner> task_runner, |
| std::unique_ptr<MediaLog> media_log, |
| OutputCB output_cb) |
| : task_runner_(std::move(task_runner)), |
| media_log_(std::move(media_log)), |
| output_cb_(std::move(output_cb)) { |
| DVLOG(1) << __func__; |
| weak_this_ = weak_this_factory_.GetWeakPtr(); |
| } |
| |
| VideoToolboxDecompressionSessionImpl::~VideoToolboxDecompressionSessionImpl() { |
| DVLOG(1) << __func__; |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| Invalidate(); |
| } |
| |
| bool VideoToolboxDecompressionSessionImpl::Create( |
| CMFormatDescriptionRef format, |
| CFDictionaryRef decoder_config, |
| CFDictionaryRef image_config) { |
| DVLOG(2) << __func__; |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| DCHECK(!IsValid()); |
| |
| VTDecompressionOutputCallbackRecord callback = {&OnOutputThunk, |
| static_cast<void*>(this)}; |
| |
| OSStatus status = VTDecompressionSessionCreate( |
| /*allocator=*/kCFAllocatorDefault, |
| /*videoFormatDescription=*/format, |
| /*videoDecoderSpecification=*/decoder_config, |
| /*destinationImageBufferAttributes=*/image_config, |
| /*outputCallback=*/&callback, session_.InitializeInto()); |
| if (status != noErr) { |
| OSSTATUS_MEDIA_LOG(ERROR, status, media_log_.get()) |
| << "VTDecompressionSessionCreate()"; |
| DCHECK(!session_); |
| return false; |
| } |
| |
| DCHECK(session_); |
| return true; |
| } |
| |
| void VideoToolboxDecompressionSessionImpl::Invalidate() { |
| DVLOG(2) << __func__; |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| |
| if (!session_) { |
| return; |
| } |
| |
| VTDecompressionSessionInvalidate(session_.get()); |
| session_.reset(); |
| |
| // Drop in-flight OnOutput() tasks. Reassignment of |weak_this_| is safe |
| // because OnOutputOnAnyThread() will not be called again until we create a |
| // new session. |
| weak_this_factory_.InvalidateWeakPtrs(); |
| weak_this_ = weak_this_factory_.GetWeakPtr(); |
| } |
| |
| bool VideoToolboxDecompressionSessionImpl::IsValid() { |
| DVLOG(4) << __func__; |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| return session_.get(); |
| } |
| |
| bool VideoToolboxDecompressionSessionImpl::CanAcceptFormat( |
| CMFormatDescriptionRef format) { |
| DVLOG(4) << __func__; |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| CHECK(session_); |
| return VTDecompressionSessionCanAcceptFormatDescription(session_.get(), |
| format); |
| } |
| |
| bool VideoToolboxDecompressionSessionImpl::DecodeFrame(CMSampleBufferRef sample, |
| uintptr_t context) { |
| DVLOG(3) << __func__; |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| CHECK(session_); |
| |
| VTDecodeFrameFlags decode_flags = |
| kVTDecodeFrame_EnableAsynchronousDecompression; |
| |
| OSStatus status = VTDecompressionSessionDecodeFrame( |
| session_.get(), sample, decode_flags, reinterpret_cast<void*>(context), |
| nullptr); |
| if (status != noErr) { |
| OSSTATUS_MEDIA_LOG(ERROR, status, media_log_.get()) |
| << "VTDecompressionSessionDecodeFrame()"; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void VideoToolboxDecompressionSessionImpl::OnOutputOnAnyThread( |
| uintptr_t context, |
| OSStatus status, |
| VTDecodeInfoFlags flags, |
| base::apple::ScopedCFTypeRef<CVImageBufferRef> image) { |
| DVLOG(4) << __func__; |
| task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&VideoToolboxDecompressionSessionImpl::OnOutput, |
| weak_this_, context, status, flags, std::move(image))); |
| } |
| |
| void VideoToolboxDecompressionSessionImpl::OnOutput( |
| uintptr_t context, |
| OSStatus status, |
| VTDecodeInfoFlags flags, |
| base::apple::ScopedCFTypeRef<CVImageBufferRef> image) { |
| DVLOG(3) << __func__; |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| CHECK(session_); |
| output_cb_.Run(context, status, flags, std::move(image)); |
| } |
| |
| } // namespace media |