// 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 <C2ArcVideoAcceleratorFactory.h>
|
#include <C2VDAAdaptorProxy.h>
|
|
#include <arc/MojoProcessSupport.h>
|
#include <arc/MojoThread.h>
|
#include <base/bind.h>
|
#include <base/files/scoped_file.h>
|
#include <mojo/public/cpp/platform/platform_handle.h>
|
#include <mojo/public/cpp/system/platform_handle.h>
|
|
#include <binder/IServiceManager.h>
|
#include <utils/Log.h>
|
|
namespace mojo {
|
template <>
|
struct TypeConverter<::arc::VideoFramePlane, android::VideoFramePlane> {
|
static ::arc::VideoFramePlane Convert(const android::VideoFramePlane& plane) {
|
return ::arc::VideoFramePlane{static_cast<int32_t>(plane.mOffset),
|
static_cast<int32_t>(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<bool>::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<bool>> 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<bool>> future, uint32_t version) {
|
ALOGI("VideoDecodeAccelerator ready (version=%d)", version);
|
|
future->set(true);
|
}
|
|
void C2VDAAdaptorProxy::ProvidePictureBuffers(::arc::mojom::PictureBufferFormatPtr format) {
|
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<int>(error));
|
return VideoDecodeAcceleratorAdaptor::PLATFORM_FAILURE;
|
}
|
}
|
|
void C2VDAAdaptorProxy::NotifyError(::arc::mojom::VideoDecodeAccelerator::Result error) {
|
ALOGE("NotifyError %d", static_cast<int>(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<int>(profile),
|
static_cast<int>(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<VideoDecodeAcceleratorAdaptor::Result>(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) {
|
ALOGV("assignPictureBuffers: %d", numOutputBuffers);
|
mMojoTaskRunner->PostTask(FROM_HERE,
|
::base::Bind(&C2VDAAdaptorProxy::assignPictureBuffersOnMojoThread,
|
::base::Unretained(this), numOutputBuffers));
|
}
|
|
void C2VDAAdaptorProxy::assignPictureBuffersOnMojoThread(uint32_t numOutputBuffers) {
|
mVDAPtr->AssignPictureBuffers(numOutputBuffers);
|
}
|
|
void C2VDAAdaptorProxy::importBufferForPicture(int32_t pictureBufferId, HalPixelFormat format,
|
int handleFd,
|
const std::vector<VideoFramePlane>& planes) {
|
ALOGV("importBufferForPicture");
|
mMojoTaskRunner->PostTask(
|
FROM_HERE,
|
::base::Bind(&C2VDAAdaptorProxy::importBufferForPictureOnMojoThread,
|
::base::Unretained(this), pictureBufferId, format, handleFd, planes));
|
}
|
|
void C2VDAAdaptorProxy::importBufferForPictureOnMojoThread(
|
int32_t pictureBufferId, HalPixelFormat format, int handleFd,
|
const std::vector<VideoFramePlane>& planes) {
|
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;
|
}
|
|
mVDAPtr->ImportBufferForPicture(pictureBufferId,
|
static_cast<::arc::mojom::HalPixelFormat>(format),
|
std::move(wrappedHandle),
|
mojo::ConvertTo<std::vector<::arc::VideoFramePlane>>(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<void> 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
|