| // Copyright 2019 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "arc/vm/libvda/decode/gpu/gpu_vda_impl.h" |
| |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/bind.h> |
| #include <base/bind_helpers.h> |
| #include <base/callback.h> |
| #include <base/files/file_util.h> |
| #include <base/memory/ref_counted.h> |
| #include <base/single_thread_task_runner.h> |
| #include <base/stl_util.h> |
| #include <base/synchronization/waitable_event.h> |
| #include <chromeos/dbus/service_constants.h> |
| #include <dbus/bus.h> |
| #include <dbus/message.h> |
| #include <dbus/object_path.h> |
| #include <dbus/object_proxy.h> |
| #include <mojo/public/cpp/bindings/binding.h> |
| #include <mojo/public/cpp/system/platform_handle.h> |
| #include <sys/eventfd.h> |
| |
| #include "arc/vm/libvda/gbm_util.h" |
| #include "arc/vm/libvda/gpu/format_util.h" |
| #include "arc/vm/libvda/gpu/mojom/video_decode_accelerator.mojom.h" |
| |
| namespace arc { |
| namespace { |
| |
| // TODO(alexlau): Query for this instead of hard coding. |
| constexpr vda_input_format_t kInputFormats[] = { |
| {VP8PROFILE_MIN /* profile */, 2 /* min_width */, 2 /* min_height */, |
| 1920 /* max_width */, 1080 /* max_height */}, |
| {VP9PROFILE_PROFILE0 /* profile */, 2 /* min_width */, 2 /* min_height */, |
| 1920 /* max_width */, 1080 /* max_height */}, |
| {H264PROFILE_MAIN /* profile */, 2 /* min_width */, 2 /* min_height */, |
| 1920 /* max_width */, 1080 /* max_height */}}; |
| |
| inline vda_result_t ConvertResult( |
| arc::mojom::VideoDecodeAccelerator::Result error) { |
| switch (error) { |
| case arc::mojom::VideoDecodeAccelerator::Result::SUCCESS: |
| return SUCCESS; |
| case arc::mojom::VideoDecodeAccelerator::Result::ILLEGAL_STATE: |
| return ILLEGAL_STATE; |
| case arc::mojom::VideoDecodeAccelerator::Result::INVALID_ARGUMENT: |
| return INVALID_ARGUMENT; |
| case arc::mojom::VideoDecodeAccelerator::Result::UNREADABLE_INPUT: |
| return UNREADABLE_INPUT; |
| case arc::mojom::VideoDecodeAccelerator::Result::PLATFORM_FAILURE: |
| return PLATFORM_FAILURE; |
| case arc::mojom::VideoDecodeAccelerator::Result::INSUFFICIENT_RESOURCES: |
| return INSUFFICIENT_RESOURCES; |
| case arc::mojom::VideoDecodeAccelerator::Result::CANCELLED: |
| return CANCELLED; |
| default: |
| DLOG(ERROR) << "Unknown error code: " << error; |
| return PLATFORM_FAILURE; |
| } |
| } |
| |
| inline arc::mojom::HalPixelFormat ConvertPixelFormatToHalPixelFormat( |
| vda_pixel_format_t format) { |
| switch (format) { |
| case YV12: |
| return arc::mojom::HalPixelFormat::HAL_PIXEL_FORMAT_YV12; |
| case NV12: |
| return arc::mojom::HalPixelFormat::HAL_PIXEL_FORMAT_NV12; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| bool CheckValidOutputFormat(vda_pixel_format_t format, size_t num_planes) { |
| switch (format) { |
| case NV12: |
| if (num_planes != 2) { |
| LOG(ERROR) << "Invalid number of planes for NV12 format, expected 2 " |
| "but received " |
| << num_planes; |
| return false; |
| } |
| break; |
| case YV12: |
| if (num_planes != 3) { |
| LOG(ERROR) << "Invalid number of planes for YV12 format, expected 3 " |
| "but received " |
| << num_planes; |
| return false; |
| } |
| break; |
| default: |
| LOG(WARNING) << "Unexpected format: " << format; |
| return false; |
| } |
| return true; |
| } |
| |
| // GpuVdaContext is the GPU decode session context created by GpuVdaImpl which |
| // handles all mojo VideoDecodeClient invocations and callbacks. |
| class GpuVdaContext : public VdaContext, arc::mojom::VideoDecodeClient { |
| public: |
| // Create a new GpuVdaContext. Must be called on |ipc_task_runner|. |
| GpuVdaContext( |
| const scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner, |
| arc::mojom::VideoDecodeAcceleratorPtr vda_ptr); |
| GpuVdaContext(const GpuVdaContext&) = delete; |
| GpuVdaContext& operator=(const GpuVdaContext&) = delete; |
| |
| ~GpuVdaContext(); |
| |
| using InitializeCallback = base::OnceCallback<void(vda_result_t)>; |
| |
| // Initializes the VDA context object. When complete, callback is called with |
| // the result. Must be called on |ipc_task_runner_|. |
| void Initialize(vda_profile_t profile, InitializeCallback callback); |
| |
| // VdaContext overrides. |
| vda_result_t Decode(int32_t bitstream_id, |
| base::ScopedFD fd, |
| uint32_t offset, |
| uint32_t bytes_used) override; |
| vda_result_t SetOutputBufferCount(size_t num_output_buffers) override; |
| vda_result_t UseOutputBuffer(int32_t picture_buffer_id, |
| vda_pixel_format_t format, |
| base::ScopedFD fd, |
| size_t num_planes, |
| video_frame_plane_t* planes) override; |
| vda_result_t ReuseOutputBuffer(int32_t picture_buffer_id) override; |
| vda_result_t Reset() override; |
| vda_result_t Flush() override; |
| |
| // arc::mojom::VideoDecodeClient overrides. |
| void ProvidePictureBuffers(arc::mojom::PictureBufferFormatPtr format_ptr, |
| arc::mojom::RectPtr visible_rect_ptr) override; |
| void ProvidePictureBuffersDeprecated( |
| arc::mojom::PictureBufferFormatPtr format_ptr) override; |
| void PictureReady(arc::mojom::PicturePtr) override; |
| void NotifyError(arc::mojom::VideoDecodeAccelerator::Result error) override; |
| void NotifyEndOfBitstreamBuffer(int32_t bitstream_id) override; |
| |
| private: |
| // Callback invoked when VideoDecodeAcceleratorPtr::Initialize completes. |
| void OnInitialized(InitializeCallback callback, |
| arc::mojom::VideoDecodeAccelerator::Result result); |
| |
| // Callback for VideoDecodeAcceleratorPtr connection errors. |
| void OnVdaError(uint32_t custom_reason, const std::string& description); |
| |
| // Callback for VideoDecodeClientPtr connection errors. |
| void OnVdaClientError(uint32_t custom_reason, const std::string& description); |
| |
| // Callback invoked when VideoDecodeAcceleratorPtr::Reset completes. |
| void OnResetDone(arc::mojom::VideoDecodeAccelerator::Result result); |
| |
| // Callback invoked when VideoDecodeAcceleratorPtr::Flush completes. |
| void OnFlushDone(arc::mojom::VideoDecodeAccelerator::Result result); |
| |
| // Executes a decode. Called on |ipc_task_runner_|. |
| void DecodeOnIpcThread(int32_t bitstream_id, |
| base::ScopedFD fd, |
| uint32_t offset, |
| uint32_t bytes_used); |
| |
| // Handles a SetOutputBuffer request by invoking a VideoDecodeAccelerator mojo |
| // function. Called on |ipc_task_runner_|. |
| void SetOutputBufferCountOnIpcThread(size_t num_output_buffers); |
| |
| // Handles a UseOutputBuffer request by invoking a VideoDecodeAccelerator mojo |
| // function. Called on |ipc_task_runner_|. |
| void UseOutputBufferOnIpcThread(int32_t picture_buffer_id, |
| vda_pixel_format_t format, |
| base::ScopedFD fd, |
| std::vector<video_frame_plane_t> planes); |
| |
| // Handles a ReuseOutputBuffer request by invoking a VideoDecodeAccelerator |
| // mojo function. Called on |ipc_task_runner_|. |
| void ReuseOutputBufferOnIpcThread(int32_t picture_buffer_id); |
| |
| // Handles a Reset request by invoking a VideoDecodeAccelerator mojo function. |
| // Called on |ipc_task_runner_|. |
| void ResetOnIpcThread(); |
| |
| // Handles a SetOutputBuffer request by invoking a VideoDecodeAccelerator mojo |
| // function. Called on |ipc_task_runner_|. |
| void FlushOnIpcThread(); |
| |
| scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner_; |
| // TODO(alexlau): Use THREAD_CHECKER macro after libchrome uprev |
| // (crbug.com/909719). |
| base::ThreadChecker ipc_thread_checker_; |
| arc::mojom::VideoDecodeAcceleratorPtr vda_ptr_; |
| mojo::Binding<arc::mojom::VideoDecodeClient> binding_; |
| |
| std::set<int32_t> decoding_bitstream_ids_; |
| }; |
| |
| GpuVdaContext::GpuVdaContext( |
| const scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner, |
| arc::mojom::VideoDecodeAcceleratorPtr vda_ptr) |
| : ipc_task_runner_(std::move(ipc_task_runner)), |
| vda_ptr_(std::move(vda_ptr)), |
| binding_(this) { |
| // Since ipc_thread_checker_ binds to whichever thread it's created on, check |
| // that we're on the correct thread first using BelongsToCurrentThread. |
| DCHECK(ipc_task_runner_->BelongsToCurrentThread()); |
| vda_ptr_.set_connection_error_with_reason_handler( |
| base::BindRepeating(&GpuVdaContext::OnVdaError, base::Unretained(this))); |
| |
| DLOG(INFO) << "Created new GPU context"; |
| } |
| |
| void GpuVdaContext::Initialize(vda_profile_t profile, |
| InitializeCallback callback) { |
| DCHECK(ipc_thread_checker_.CalledOnValidThread()); |
| arc::mojom::VideoDecodeClientPtr client_ptr; |
| binding_.Bind(mojo::MakeRequest(&client_ptr)); |
| binding_.set_connection_error_with_reason_handler(base::BindRepeating( |
| &GpuVdaContext::OnVdaClientError, base::Unretained(this))); |
| |
| arc::mojom::VideoDecodeAcceleratorConfigPtr config = |
| arc::mojom::VideoDecodeAcceleratorConfig::New(); |
| // TODO(alexlau): Think about how to specify secure mode dynamically. |
| config->secure_mode = false; |
| config->profile = ConvertCodecProfileToMojoProfile(profile); |
| |
| vda_ptr_->Initialize( |
| std::move(config), std::move(client_ptr), |
| base::BindRepeating(&GpuVdaContext::OnInitialized, base::Unretained(this), |
| base::Passed(std::move(callback)))); |
| } |
| |
| void GpuVdaContext::OnInitialized( |
| InitializeCallback callback, |
| arc::mojom::VideoDecodeAccelerator::Result result) { |
| DCHECK(ipc_thread_checker_.CalledOnValidThread()); |
| std::move(callback).Run(ConvertResult(result)); |
| } |
| |
| GpuVdaContext::~GpuVdaContext() { |
| DCHECK(ipc_thread_checker_.CalledOnValidThread()); |
| } |
| |
| void GpuVdaContext::OnVdaError(uint32_t custom_reason, |
| const std::string& description) { |
| DCHECK(ipc_thread_checker_.CalledOnValidThread()); |
| DLOG(ERROR) << "VideoDecodeAccelerator mojo connection error. custom_reason=" |
| << custom_reason << " description=" << description; |
| } |
| |
| void GpuVdaContext::OnVdaClientError(uint32_t custom_reason, |
| const std::string& description) { |
| DCHECK(ipc_thread_checker_.CalledOnValidThread()); |
| DLOG(ERROR) << "VideoDecodeClient mojo connection error. custom_reason=" |
| << custom_reason << " description=" << description; |
| } |
| |
| vda_result_t GpuVdaContext::Decode(int32_t bitstream_id, |
| base::ScopedFD fd, |
| uint32_t offset, |
| uint32_t bytes_used) { |
| ipc_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&GpuVdaContext::DecodeOnIpcThread, base::Unretained(this), |
| bitstream_id, std::move(fd), offset, bytes_used)); |
| return SUCCESS; |
| } |
| |
| vda_result_t GpuVdaContext::SetOutputBufferCount(size_t num_output_buffers) { |
| ipc_task_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&GpuVdaContext::SetOutputBufferCountOnIpcThread, |
| base::Unretained(this), num_output_buffers)); |
| return SUCCESS; |
| } |
| |
| void GpuVdaContext::SetOutputBufferCountOnIpcThread(size_t num_output_buffers) { |
| vda_ptr_->AssignPictureBuffers(num_output_buffers); |
| } |
| |
| void GpuVdaContext::DecodeOnIpcThread(int32_t bitstream_id, |
| base::ScopedFD fd, |
| uint32_t offset, |
| uint32_t bytes_used) { |
| DCHECK(ipc_thread_checker_.CalledOnValidThread()); |
| |
| mojo::ScopedHandle handle_fd = mojo::WrapPlatformFile(std::move(fd)); |
| if (!handle_fd.is_valid()) { |
| LOG(ERROR) << "Invalid bitstream handle."; |
| return; |
| } |
| |
| decoding_bitstream_ids_.insert(bitstream_id); |
| |
| arc::mojom::BitstreamBufferPtr buf = arc::mojom::BitstreamBuffer::New(); |
| buf->bitstream_id = bitstream_id; |
| buf->handle_fd = std::move(handle_fd); |
| buf->offset = offset; |
| buf->bytes_used = bytes_used; |
| |
| vda_ptr_->Decode(std::move(buf)); |
| } |
| |
| vda_result_t GpuVdaContext::UseOutputBuffer(int32_t picture_buffer_id, |
| vda_pixel_format_t format, |
| base::ScopedFD fd, |
| size_t num_planes, |
| video_frame_plane_t* planes) { |
| if (!CheckValidOutputFormat(format, num_planes)) |
| return INVALID_ARGUMENT; |
| |
| // Move semantics don't seem to work with mojo pointers so copy the |
| // video_frame_plane_t objects and handle in the ipc thread. This allows |
| // |planes| ownership to be retained by the user. |
| std::vector<video_frame_plane_t> planes_vector(planes, planes + num_planes); |
| ipc_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&GpuVdaContext::UseOutputBufferOnIpcThread, |
| base::Unretained(this), picture_buffer_id, format, |
| std::move(fd), std::move(planes_vector))); |
| return SUCCESS; |
| } |
| |
| void GpuVdaContext::UseOutputBufferOnIpcThread( |
| int32_t picture_buffer_id, |
| vda_pixel_format_t format, |
| base::ScopedFD fd, |
| std::vector<video_frame_plane_t> planes) { |
| mojo::ScopedHandle handle_fd = mojo::WrapPlatformFile(std::move(fd)); |
| if (!handle_fd.is_valid()) { |
| LOG(ERROR) << "Invalid output buffer handle."; |
| return; |
| } |
| |
| std::vector<arc::mojom::VideoFramePlanePtr> mojo_planes; |
| for (const auto& plane : planes) { |
| arc::mojom::VideoFramePlanePtr mojo_plane = |
| arc::mojom::VideoFramePlane::New(); |
| mojo_plane->offset = plane.offset; |
| mojo_plane->stride = plane.stride; |
| mojo_planes.push_back(std::move(mojo_plane)); |
| } |
| |
| vda_ptr_->ImportBufferForPicture( |
| picture_buffer_id, ConvertPixelFormatToHalPixelFormat(format), |
| std::move(handle_fd), std::move(mojo_planes)); |
| } |
| |
| vda_result_t GpuVdaContext::ReuseOutputBuffer(int32_t picture_buffer_id) { |
| ipc_task_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&GpuVdaContext::ReuseOutputBufferOnIpcThread, |
| base::Unretained(this), picture_buffer_id)); |
| return SUCCESS; |
| } |
| |
| void GpuVdaContext::ReuseOutputBufferOnIpcThread(int32_t picture_buffer_id) { |
| DCHECK(ipc_thread_checker_.CalledOnValidThread()); |
| vda_ptr_->ReusePictureBuffer(picture_buffer_id); |
| } |
| |
| vda_result GpuVdaContext::Reset() { |
| ipc_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&GpuVdaContext::ResetOnIpcThread, base::Unretained(this))); |
| return SUCCESS; |
| } |
| |
| void GpuVdaContext::ResetOnIpcThread() { |
| DCHECK(ipc_thread_checker_.CalledOnValidThread()); |
| vda_ptr_->Reset( |
| base::BindRepeating(&GpuVdaContext::OnResetDone, base::Unretained(this))); |
| } |
| |
| void GpuVdaContext::OnResetDone( |
| arc::mojom::VideoDecodeAccelerator::Result result) { |
| DispatchResetResponse(ConvertResult(result)); |
| } |
| |
| vda_result GpuVdaContext::Flush() { |
| ipc_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&GpuVdaContext::FlushOnIpcThread, base::Unretained(this))); |
| return SUCCESS; |
| } |
| |
| void GpuVdaContext::FlushOnIpcThread() { |
| DCHECK(ipc_thread_checker_.CalledOnValidThread()); |
| vda_ptr_->Flush( |
| base::BindRepeating(&GpuVdaContext::OnFlushDone, base::Unretained(this))); |
| } |
| |
| void GpuVdaContext::OnFlushDone( |
| arc::mojom::VideoDecodeAccelerator::Result result) { |
| DispatchFlushResponse(ConvertResult(result)); |
| } |
| |
| // VideoDecodeClient implementation function. |
| void GpuVdaContext::ProvidePictureBuffers( |
| arc::mojom::PictureBufferFormatPtr format_ptr, |
| arc::mojom::RectPtr visible_rect_ptr) { |
| DCHECK(ipc_thread_checker_.CalledOnValidThread()); |
| DispatchProvidePictureBuffers( |
| format_ptr->min_num_buffers, format_ptr->coded_size->width, |
| format_ptr->coded_size->height, visible_rect_ptr->left, |
| visible_rect_ptr->top, visible_rect_ptr->right, visible_rect_ptr->bottom); |
| } |
| |
| void GpuVdaContext::ProvidePictureBuffersDeprecated( |
| arc::mojom::PictureBufferFormatPtr format_ptr) { |
| NOTIMPLEMENTED(); |
| } |
| |
| // VideoDecodeClient implementation function. |
| void GpuVdaContext::PictureReady(arc::mojom::PicturePtr picture_ptr) { |
| DCHECK(ipc_thread_checker_.CalledOnValidThread()); |
| DispatchPictureReady( |
| picture_ptr->picture_buffer_id, picture_ptr->bitstream_id, |
| picture_ptr->crop_rect->left, picture_ptr->crop_rect->top, |
| picture_ptr->crop_rect->right, picture_ptr->crop_rect->bottom); |
| } |
| |
| // VideoDecodeClient implementation function. |
| void GpuVdaContext::NotifyError( |
| arc::mojom::VideoDecodeAccelerator::Result error) { |
| DCHECK(ipc_thread_checker_.CalledOnValidThread()); |
| DispatchNotifyError(ConvertResult(error)); |
| } |
| |
| // VideoDecodeClient implementation function. |
| void GpuVdaContext::NotifyEndOfBitstreamBuffer(int32_t bitstream_id) { |
| DCHECK(ipc_thread_checker_.CalledOnValidThread()); |
| |
| DispatchNotifyEndOfBitstreamBuffer(bitstream_id); |
| |
| if (decoding_bitstream_ids_.erase(bitstream_id) == 0) { |
| LOG(ERROR) << "Could not find bitstream id: " << bitstream_id; |
| return; |
| } |
| } |
| |
| } // namespace |
| |
| // static |
| GpuVdaImpl* GpuVdaImpl::Create(VafConnection* conn) { |
| auto impl = std::make_unique<GpuVdaImpl>(conn); |
| if (!impl->Initialize()) { |
| LOG(ERROR) << "Could not initialize GpuVdaImpl."; |
| return nullptr; |
| } |
| |
| return impl.release(); |
| } |
| |
| GpuVdaImpl::GpuVdaImpl(VafConnection* conn) : connection_(conn) {} |
| |
| GpuVdaImpl::~GpuVdaImpl() = default; |
| |
| std::vector<vda_input_format_t> GpuVdaImpl::GetSupportedInputFormats() { |
| std::vector<vda_input_format_t> supported_input_formats; |
| |
| for (int i = 0; i < base::size(kInputFormats); i++) { |
| auto* context = InitDecodeSession(kInputFormats[i].profile); |
| if (context) { |
| supported_input_formats.emplace_back(kInputFormats[i]); |
| CloseDecodeSession(context); |
| } |
| } |
| |
| return supported_input_formats; |
| } |
| |
| bool GpuVdaImpl::PopulateCapabilities() { |
| input_formats_ = GetSupportedInputFormats(); |
| if (input_formats_.empty()) |
| return false; |
| |
| capabilities_.num_input_formats = input_formats_.size(); |
| capabilities_.input_formats = input_formats_.data(); |
| |
| output_formats_ = GetSupportedRawFormats(GbmUsageType::DECODE); |
| if (output_formats_.empty()) |
| return false; |
| |
| capabilities_.num_output_formats = output_formats_.size(); |
| capabilities_.output_formats = output_formats_.data(); |
| return true; |
| } |
| |
| bool GpuVdaImpl::Initialize() { |
| ipc_task_runner_ = connection_->GetIpcTaskRunner(); |
| |
| if (!PopulateCapabilities()) { |
| ipc_task_runner_.reset(); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| VdaContext* GpuVdaImpl::InitDecodeSession(vda_profile_t profile) { |
| if (!ipc_task_runner_) { |
| DLOG(FATAL) << "InitDecodeSession called before successful Initialize()."; |
| return nullptr; |
| } |
| |
| DCHECK(!ipc_task_runner_->BelongsToCurrentThread()); |
| DLOG(INFO) << "Initializing decode session with profile " << profile; |
| |
| base::WaitableEvent init_complete_event( |
| base::WaitableEvent::ResetPolicy::MANUAL, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| |
| VdaContext* context = nullptr; |
| ipc_task_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&GpuVdaImpl::InitDecodeSessionOnIpcThread, |
| base::Unretained(this), profile, |
| &init_complete_event, &context)); |
| init_complete_event.Wait(); |
| return context; |
| } |
| |
| void GpuVdaImpl::InitDecodeSessionOnIpcThread( |
| vda_profile_t profile, |
| base::WaitableEvent* init_complete_event, |
| VdaContext** out_context) { |
| DCHECK(ipc_task_runner_->BelongsToCurrentThread()); |
| |
| arc::mojom::VideoDecodeAcceleratorPtr vda_ptr; |
| connection_->CreateDecodeAccelerator(&vda_ptr); |
| |
| std::unique_ptr<GpuVdaContext> context = |
| std::make_unique<GpuVdaContext>(ipc_task_runner_, std::move(vda_ptr)); |
| GpuVdaContext* context_ptr = context.get(); |
| context_ptr->Initialize( |
| profile, |
| base::BindRepeating( |
| &GpuVdaImpl::InitDecodeSessionAfterContextInitializedOnIpcThread, |
| base::Unretained(this), init_complete_event, out_context, |
| base::Passed(std::move(context)))); |
| } |
| |
| void GpuVdaImpl::InitDecodeSessionAfterContextInitializedOnIpcThread( |
| base::WaitableEvent* init_complete_event, |
| VdaContext** out_context, |
| std::unique_ptr<VdaContext> context, |
| vda_result_t result) { |
| DCHECK(ipc_task_runner_->BelongsToCurrentThread()); |
| |
| if (result == SUCCESS) { |
| *out_context = context.release(); |
| } else { |
| DLOG(ERROR) << "Failed to initialize decode session."; |
| } |
| init_complete_event->Signal(); |
| } |
| |
| void GpuVdaImpl::CloseDecodeSession(VdaContext* context) { |
| if (!ipc_task_runner_) { |
| DLOG(FATAL) << "CloseDecodeSession called before successful Initialize()."; |
| return; |
| } |
| DLOG(INFO) << "Closing decode session"; |
| RunTaskOnThread(ipc_task_runner_, |
| base::BindOnce(&GpuVdaImpl::CloseDecodeSessionOnIpcThread, |
| base::Unretained(this), context)); |
| } |
| |
| void GpuVdaImpl::CloseDecodeSessionOnIpcThread(VdaContext* context) { |
| DCHECK(ipc_task_runner_->BelongsToCurrentThread()); |
| delete context; |
| } |
| |
| } // namespace arc |