// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // #define LOG_NDEBUG 0 #define LOG_TAG "C2VDAAdaptorProxy" #include #include #include #include #include #include #include #include #include #include #include namespace mojo { template <> struct TypeConverter<::arc::VideoFramePlane, android::VideoFramePlane> { static ::arc::VideoFramePlane Convert(const android::VideoFramePlane& plane) { return ::arc::VideoFramePlane{static_cast(plane.mOffset), static_cast(plane.mStride)}; } }; } // namespace mojo namespace android { namespace arc { C2VDAAdaptorProxy::C2VDAAdaptorProxy() : C2VDAAdaptorProxy(::arc::MojoProcessSupport::getLeakyInstance()) {} C2VDAAdaptorProxy::C2VDAAdaptorProxy(::arc::MojoProcessSupport* mojoProcessSupport) : mClient(nullptr), mMojoTaskRunner(mojoProcessSupport->mojo_thread().getTaskRunner()), mBinding(this), mRelay(new ::arc::CancellationRelay()) {} C2VDAAdaptorProxy::~C2VDAAdaptorProxy() {} void C2VDAAdaptorProxy::onConnectionError(const std::string& pipeName) { ALOGE("onConnectionError (%s)", pipeName.c_str()); mRelay->cancel(); NotifyError(::arc::mojom::VideoDecodeAccelerator::Result::PLATFORM_FAILURE); } bool C2VDAAdaptorProxy::establishChannel() { ALOGV("establishChannel"); auto future = ::arc::Future::make_shared(mRelay); mMojoTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAAdaptorProxy::establishChannelOnMojoThread, ::base::Unretained(this), future)); return future->wait() && future->get(); } void C2VDAAdaptorProxy::establishChannelOnMojoThread(std::shared_ptr<::arc::Future> future) { auto& factory = ::android::GetC2ArcVideoAcceleratorFactory(); if (!factory.createVideoDecodeAccelerator(mojo::MakeRequest(&mVDAPtr))) { future->set(false); return; } mVDAPtr.set_connection_error_handler(::base::Bind(&C2VDAAdaptorProxy::onConnectionError, ::base::Unretained(this), std::string("mVDAPtr (vda pipe)"))); mVDAPtr.QueryVersion(::base::Bind(&C2VDAAdaptorProxy::onVersionReady, ::base::Unretained(this), std::move(future))); } void C2VDAAdaptorProxy::onVersionReady(std::shared_ptr<::arc::Future> future, uint32_t version) { ALOGI("VideoDecodeAccelerator ready (version=%d)", version); future->set(true); } void C2VDAAdaptorProxy::ProvidePictureBuffersDeprecated(::arc::mojom::PictureBufferFormatPtr format) { ALOGV("ProvidePictureBuffersDeprecated"); mClient->providePictureBuffers( format->min_num_buffers, media::Size(format->coded_size.width(), format->coded_size.height())); } void C2VDAAdaptorProxy::ProvidePictureBuffers(::arc::mojom::PictureBufferFormatPtr format, const gfx::Rect& visible_rect) { ALOGV("ProvidePictureBuffers"); mClient->providePictureBuffers( format->min_num_buffers, media::Size(format->coded_size.width(), format->coded_size.height())); } void C2VDAAdaptorProxy::PictureReady(::arc::mojom::PicturePtr picture) { ALOGV("PictureReady"); const auto& rect = picture->crop_rect; mClient->pictureReady(picture->picture_buffer_id, picture->bitstream_id, media::Rect(rect.x(), rect.y(), rect.right(), rect.bottom())); } static VideoDecodeAcceleratorAdaptor::Result convertErrorCode( ::arc::mojom::VideoDecodeAccelerator::Result error) { switch (error) { case ::arc::mojom::VideoDecodeAccelerator::Result::ILLEGAL_STATE: return VideoDecodeAcceleratorAdaptor::ILLEGAL_STATE; case ::arc::mojom::VideoDecodeAccelerator::Result::INVALID_ARGUMENT: return VideoDecodeAcceleratorAdaptor::INVALID_ARGUMENT; case ::arc::mojom::VideoDecodeAccelerator::Result::UNREADABLE_INPUT: return VideoDecodeAcceleratorAdaptor::UNREADABLE_INPUT; case ::arc::mojom::VideoDecodeAccelerator::Result::PLATFORM_FAILURE: return VideoDecodeAcceleratorAdaptor::PLATFORM_FAILURE; case ::arc::mojom::VideoDecodeAccelerator::Result::INSUFFICIENT_RESOURCES: return VideoDecodeAcceleratorAdaptor::INSUFFICIENT_RESOURCES; default: ALOGE("Unknown error code: %d", static_cast(error)); return VideoDecodeAcceleratorAdaptor::PLATFORM_FAILURE; } } void C2VDAAdaptorProxy::NotifyError(::arc::mojom::VideoDecodeAccelerator::Result error) { ALOGE("NotifyError %d", static_cast(error)); mClient->notifyError(convertErrorCode(error)); } void C2VDAAdaptorProxy::NotifyEndOfBitstreamBuffer(int32_t bitstream_id) { ALOGV("NotifyEndOfBitstreamBuffer"); mClient->notifyEndOfBitstreamBuffer(bitstream_id); } void C2VDAAdaptorProxy::NotifyResetDone(::arc::mojom::VideoDecodeAccelerator::Result result) { ALOGV("NotifyResetDone"); // Always notify reset done to component even if result is not success. On shutdown, MediaCodec // will wait on shutdown complete notification despite any error. If no notification, it will be // hanging until timeout and force release. if (result != ::arc::mojom::VideoDecodeAccelerator::Result::SUCCESS) { ALOGE("Reset is done incorrectly."); NotifyError(result); } mClient->notifyResetDone(); } void C2VDAAdaptorProxy::NotifyFlushDone(::arc::mojom::VideoDecodeAccelerator::Result result) { ALOGV("NotifyFlushDone"); if (result == ::arc::mojom::VideoDecodeAccelerator::Result::CANCELLED) { // Flush is cancelled by a succeeding Reset(). A client expects this behavior. ALOGE("Flush is canceled."); return; } if (result != ::arc::mojom::VideoDecodeAccelerator::Result::SUCCESS) { ALOGE("Flush is done incorrectly."); NotifyError(result); return; } mClient->notifyFlushDone(); } //static media::VideoDecodeAccelerator::SupportedProfiles C2VDAAdaptorProxy::GetSupportedProfiles( InputCodec inputCodec) { media::VideoDecodeAccelerator::SupportedProfiles profiles(1); profiles[0].min_resolution = media::Size(16, 16); profiles[0].max_resolution = media::Size(4096, 4096); switch (inputCodec) { case InputCodec::H264: profiles[0].profile = media::H264PROFILE_MAIN; break; case InputCodec::VP8: profiles[0].profile = media::VP8PROFILE_ANY; break; case InputCodec::VP9: profiles[0].profile = media::VP9PROFILE_PROFILE0; break; default: ALOGE("Unknown input codec: %d", inputCodec); return {}; } return profiles; } VideoDecodeAcceleratorAdaptor::Result C2VDAAdaptorProxy::initialize( media::VideoCodecProfile profile, bool secureMode, VideoDecodeAcceleratorAdaptor::Client* client) { ALOGV("initialize(profile=%d, secureMode=%d)", static_cast(profile), static_cast(secureMode)); DCHECK(client); DCHECK(!mClient); mClient = client; if (!establishChannel()) { ALOGE("establishChannel failed"); return VideoDecodeAcceleratorAdaptor::PLATFORM_FAILURE; } auto future = ::arc::Future<::arc::mojom::VideoDecodeAccelerator::Result>::make_shared(mRelay); mMojoTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAAdaptorProxy::initializeOnMojoThread, ::base::Unretained(this), profile, secureMode, ::arc::FutureCallback(future))); if (!future->wait()) { ALOGE("Connection lost"); return VideoDecodeAcceleratorAdaptor::PLATFORM_FAILURE; } return static_cast(future->get()); } void C2VDAAdaptorProxy::initializeOnMojoThread( const media::VideoCodecProfile profile, const bool secureMode, const ::arc::mojom::VideoDecodeAccelerator::InitializeCallback& cb) { // base::Unretained is safe because we own |mBinding|. mojo::InterfacePtr<::arc::mojom::VideoDecodeClient> client; mBinding.Bind(mojo::MakeRequest(&client)); mBinding.set_connection_error_handler(::base::Bind(&C2VDAAdaptorProxy::onConnectionError, ::base::Unretained(this), std::string("mBinding (client pipe)"))); ::arc::mojom::VideoDecodeAcceleratorConfigPtr arcConfig = ::arc::mojom::VideoDecodeAcceleratorConfig::New(); arcConfig->secure_mode = secureMode; arcConfig->profile = static_cast<::arc::mojom::VideoCodecProfile>(profile); mVDAPtr->Initialize(std::move(arcConfig), std::move(client), cb); } void C2VDAAdaptorProxy::decode(int32_t bitstreamId, int handleFd, off_t offset, uint32_t size) { ALOGV("decode"); mMojoTaskRunner->PostTask( FROM_HERE, ::base::Bind(&C2VDAAdaptorProxy::decodeOnMojoThread, ::base::Unretained(this), bitstreamId, handleFd, offset, size)); } void C2VDAAdaptorProxy::decodeOnMojoThread(int32_t bitstreamId, int handleFd, off_t offset, uint32_t size) { mojo::ScopedHandle wrappedHandle = mojo::WrapPlatformHandle(mojo::PlatformHandle(::base::ScopedFD(handleFd))); if (!wrappedHandle.is_valid()) { ALOGE("failed to wrap handle"); NotifyError(::arc::mojom::VideoDecodeAccelerator::Result::PLATFORM_FAILURE); return; } auto bufferPtr = ::arc::mojom::BitstreamBuffer::New(); bufferPtr->bitstream_id = bitstreamId; bufferPtr->handle_fd = std::move(wrappedHandle); bufferPtr->offset = offset; bufferPtr->bytes_used = size; mVDAPtr->Decode(std::move(bufferPtr)); } void C2VDAAdaptorProxy::assignPictureBuffers(uint32_t numOutputBuffers, const media::Size& size) { ALOGV("assignPictureBuffers: %d", numOutputBuffers); mMojoTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAAdaptorProxy::assignPictureBuffersOnMojoThread, ::base::Unretained(this), numOutputBuffers, size)); } void C2VDAAdaptorProxy::assignPictureBuffersOnMojoThread(uint32_t numOutputBuffers, const media::Size& size) { // TODO(crbug.com/982172): Pass size to Chrome. mVDAPtr->AssignPictureBuffers(numOutputBuffers); } void C2VDAAdaptorProxy::importBufferForPicture(int32_t pictureBufferId, HalPixelFormat format, std::vector<::base::ScopedFD> handleFds, const std::vector& planes) { ALOGV("importBufferForPicture"); mMojoTaskRunner->PostTask( FROM_HERE, ::base::BindOnce(&C2VDAAdaptorProxy::importBufferForPictureOnMojoThread, ::base::Unretained(this), pictureBufferId, format, std::move(handleFds), planes)); } void C2VDAAdaptorProxy::importBufferForPictureOnMojoThread( int32_t pictureBufferId, HalPixelFormat format, std::vector<::base::ScopedFD> handleFds, const std::vector& planes) { // TODO(hiroh): Pass all the fds to Chrome. mojo::ScopedHandle wrappedHandle = mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(handleFds[0]))); if (!wrappedHandle.is_valid()) { ALOGE("failed to wrap handle"); NotifyError(::arc::mojom::VideoDecodeAccelerator::Result::PLATFORM_FAILURE); return; } mVDAPtr->ImportBufferForPicture(pictureBufferId, static_cast<::arc::mojom::HalPixelFormat>(format), std::move(wrappedHandle), mojo::ConvertTo>(planes)); } void C2VDAAdaptorProxy::reusePictureBuffer(int32_t pictureBufferId) { ALOGV("reusePictureBuffer: %d", pictureBufferId); mMojoTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAAdaptorProxy::reusePictureBufferOnMojoThread, ::base::Unretained(this), pictureBufferId)); } void C2VDAAdaptorProxy::reusePictureBufferOnMojoThread(int32_t pictureBufferId) { mVDAPtr->ReusePictureBuffer(pictureBufferId); } void C2VDAAdaptorProxy::flush() { ALOGV("flush"); mMojoTaskRunner->PostTask( FROM_HERE, ::base::Bind(&C2VDAAdaptorProxy::flushOnMojoThread, ::base::Unretained(this))); } void C2VDAAdaptorProxy::flushOnMojoThread() { mVDAPtr->Flush(::base::Bind(&C2VDAAdaptorProxy::NotifyFlushDone, ::base::Unretained(this))); } void C2VDAAdaptorProxy::reset() { ALOGV("reset"); mMojoTaskRunner->PostTask( FROM_HERE, ::base::Bind(&C2VDAAdaptorProxy::resetOnMojoThread, ::base::Unretained(this))); } void C2VDAAdaptorProxy::resetOnMojoThread() { mVDAPtr->Reset(::base::Bind(&C2VDAAdaptorProxy::NotifyResetDone, ::base::Unretained(this))); } void C2VDAAdaptorProxy::destroy() { ALOGV("destroy"); ::arc::Future future; ::arc::PostTaskAndSetFutureWithResult( mMojoTaskRunner.get(), FROM_HERE, ::base::Bind(&C2VDAAdaptorProxy::closeChannelOnMojoThread, ::base::Unretained(this)), &future); future.get(); } void C2VDAAdaptorProxy::closeChannelOnMojoThread() { if (mBinding.is_bound()) mBinding.Close(); mVDAPtr.reset(); } } // namespace arc } // namespace android