blob: 27e0a9ef3095b283763ca9c3ec0eb44e19cd6f16 [file] [log] [blame]
// Copyright 2016 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.
#include "chrome/gpu/arc_gpu_video_decode_accelerator.h"
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/numerics/safe_math.h"
#include "base/run_loop.h"
#include "media/base/video_frame.h"
#include "media/gpu/gpu_video_decode_accelerator_factory.h"
namespace chromeos {
namespace arc {
namespace {
// An arbitrary chosen limit of the number of buffers. The number of
// buffers used is requested from the untrusted client side.
const size_t kMaxBufferCount = 128;
// Maximum number of concurrent ARC video clients.
// Currently we have no way to know the resources are not enough to create more
// VDA. Arbitrarily chosen a reasonable constant as the limit.
const int kMaxConcurrentClients = 8;
} // anonymous namespace
int ArcGpuVideoDecodeAccelerator::client_count_ = 0;
ArcGpuVideoDecodeAccelerator::InputRecord::InputRecord(
int32_t bitstream_buffer_id,
uint32_t buffer_index,
int64_t timestamp)
: bitstream_buffer_id(bitstream_buffer_id),
buffer_index(buffer_index),
timestamp(timestamp) {}
ArcGpuVideoDecodeAccelerator::InputBufferInfo::InputBufferInfo() = default;
ArcGpuVideoDecodeAccelerator::InputBufferInfo::InputBufferInfo(
InputBufferInfo&& other) = default;
ArcGpuVideoDecodeAccelerator::InputBufferInfo::~InputBufferInfo() = default;
ArcGpuVideoDecodeAccelerator::OutputBufferInfo::OutputBufferInfo() = default;
ArcGpuVideoDecodeAccelerator::OutputBufferInfo::OutputBufferInfo(
OutputBufferInfo&& other) = default;
ArcGpuVideoDecodeAccelerator::OutputBufferInfo::~OutputBufferInfo() = default;
ArcGpuVideoDecodeAccelerator::ArcGpuVideoDecodeAccelerator(
const gpu::GpuPreferences& gpu_preferences)
: arc_client_(nullptr),
next_bitstream_buffer_id_(0),
output_pixel_format_(media::PIXEL_FORMAT_UNKNOWN),
output_buffer_size_(0),
gpu_preferences_(gpu_preferences) {}
ArcGpuVideoDecodeAccelerator::~ArcGpuVideoDecodeAccelerator() {
DCHECK(thread_checker_.CalledOnValidThread());
if (vda_) {
client_count_--;
}
}
ArcVideoAccelerator::Result ArcGpuVideoDecodeAccelerator::Initialize(
const Config& config,
ArcVideoAccelerator::Client* client) {
DVLOG(5) << "Initialize(device=" << config.device_type
<< ", input_pixel_format=" << config.input_pixel_format
<< ", num_input_buffers=" << config.num_input_buffers << ")";
DCHECK(thread_checker_.CalledOnValidThread());
if (config.device_type != Config::DEVICE_DECODER)
return INVALID_ARGUMENT;
DCHECK(client);
if (arc_client_) {
DLOG(ERROR) << "Re-Initialize() is not allowed";
return ILLEGAL_STATE;
}
if (client_count_ >= kMaxConcurrentClients) {
LOG(WARNING) << "Reject to Initialize() due to too many clients: "
<< client_count_;
return INSUFFICIENT_RESOURCES;
}
arc_client_ = client;
if (config.num_input_buffers > kMaxBufferCount) {
DLOG(ERROR) << "Request too many buffers: " << config.num_input_buffers;
return INVALID_ARGUMENT;
}
input_buffer_info_.resize(config.num_input_buffers);
media::VideoDecodeAccelerator::Config vda_config;
switch (config.input_pixel_format) {
case HAL_PIXEL_FORMAT_H264:
vda_config.profile = media::H264PROFILE_MAIN;
break;
case HAL_PIXEL_FORMAT_VP8:
vda_config.profile = media::VP8PROFILE_ANY;
break;
case HAL_PIXEL_FORMAT_VP9:
vda_config.profile = media::VP9PROFILE_PROFILE0;
break;
default:
DLOG(ERROR) << "Unsupported input format: " << config.input_pixel_format;
return INVALID_ARGUMENT;
}
vda_config.output_mode =
media::VideoDecodeAccelerator::Config::OutputMode::IMPORT;
auto vda_factory = media::GpuVideoDecodeAcceleratorFactory::CreateWithNoGL();
vda_ = vda_factory->CreateVDA(
this, vda_config, gpu::GpuDriverBugWorkarounds(), gpu_preferences_);
if (!vda_) {
DLOG(ERROR) << "Failed to create VDA.";
return PLATFORM_FAILURE;
}
client_count_++;
DVLOG(5) << "Number of concurrent ArcVideoAccelerator clients: "
<< client_count_;
return SUCCESS;
}
void ArcGpuVideoDecodeAccelerator::SetNumberOfOutputBuffers(size_t number) {
DVLOG(5) << "SetNumberOfOutputBuffers(" << number << ")";
DCHECK(thread_checker_.CalledOnValidThread());
if (!vda_) {
DLOG(ERROR) << "VDA not initialized";
return;
}
if (number > kMaxBufferCount) {
DLOG(ERROR) << "Too many buffers: " << number;
arc_client_->OnError(INVALID_ARGUMENT);
return;
}
std::vector<media::PictureBuffer> buffers;
for (size_t id = 0; id < number; ++id) {
media::PictureBuffer::TextureIds texture_ids;
texture_ids.push_back(0);
// TODO(owenlin): Make sure the |coded_size| is what we want.
buffers.push_back(media::PictureBuffer(base::checked_cast<int32_t>(id),
coded_size_, texture_ids));
}
vda_->AssignPictureBuffers(buffers);
buffers_pending_import_.clear();
buffers_pending_import_.resize(number);
}
void ArcGpuVideoDecodeAccelerator::BindSharedMemory(PortType port,
uint32_t index,
base::ScopedFD ashmem_fd,
off_t offset,
size_t length) {
DVLOG(5) << "ArcGVDA::BindSharedMemory, offset: " << offset
<< ", length: " << length;
DCHECK(thread_checker_.CalledOnValidThread());
if (!vda_) {
DLOG(ERROR) << "VDA not initialized";
return;
}
if (port != PORT_INPUT) {
DLOG(ERROR) << "SharedBuffer is only supported for input";
arc_client_->OnError(INVALID_ARGUMENT);
return;
}
if (!ValidatePortAndIndex(port, index)) {
arc_client_->OnError(INVALID_ARGUMENT);
return;
}
InputBufferInfo* input_info = &input_buffer_info_[index];
input_info->handle = std::move(ashmem_fd);
input_info->offset = offset;
input_info->length = length;
}
bool ArcGpuVideoDecodeAccelerator::VerifyDmabuf(
const base::ScopedFD& dmabuf_fd,
const std::vector<DmabufPlane>& dmabuf_planes) const {
size_t num_planes = media::VideoFrame::NumPlanes(output_pixel_format_);
if (dmabuf_planes.size() != num_planes) {
DLOG(ERROR) << "Invalid number of dmabuf planes passed: "
<< dmabuf_planes.size() << ", expected: " << num_planes;
return false;
}
off_t size = lseek(dmabuf_fd.get(), 0, SEEK_END);
lseek(dmabuf_fd.get(), 0, SEEK_SET);
if (size < 0) {
DPLOG(ERROR) << "fail to find the size of dmabuf";
return false;
}
size_t i = 0;
for (const auto& plane : dmabuf_planes) {
DVLOG(4) << "Plane " << i << ", offset: " << plane.offset
<< ", stride: " << plane.stride;
size_t rows =
media::VideoFrame::Rows(i, output_pixel_format_, coded_size_.height());
base::CheckedNumeric<off_t> current_size(plane.offset);
current_size += plane.stride * rows;
if (!current_size.IsValid() || current_size.ValueOrDie() > size) {
DLOG(ERROR) << "Invalid strides/offsets";
return false;
}
++i;
}
return true;
}
void ArcGpuVideoDecodeAccelerator::BindDmabuf(
PortType port,
uint32_t index,
base::ScopedFD dmabuf_fd,
const std::vector<DmabufPlane>& dmabuf_planes) {
DCHECK(thread_checker_.CalledOnValidThread());
if (!vda_) {
DLOG(ERROR) << "VDA not initialized";
return;
}
if (port != PORT_OUTPUT) {
DLOG(ERROR) << "Dmabuf is only supported for input";
arc_client_->OnError(INVALID_ARGUMENT);
return;
}
if (!ValidatePortAndIndex(port, index)) {
arc_client_->OnError(INVALID_ARGUMENT);
return;
}
if (!VerifyDmabuf(dmabuf_fd, dmabuf_planes)) {
arc_client_->OnError(INVALID_ARGUMENT);
return;
}
OutputBufferInfo& info = buffers_pending_import_[index];
info.handle = std::move(dmabuf_fd);
info.planes = dmabuf_planes;
}
void ArcGpuVideoDecodeAccelerator::UseBuffer(PortType port,
uint32_t index,
const BufferMetadata& metadata) {
DVLOG(5) << "UseBuffer(port=" << port << ", index=" << index
<< ", metadata=(bytes_used=" << metadata.bytes_used
<< ", timestamp=" << metadata.timestamp << ")";
DCHECK(thread_checker_.CalledOnValidThread());
if (!vda_) {
DLOG(ERROR) << "VDA not initialized";
return;
}
if (!ValidatePortAndIndex(port, index)) {
arc_client_->OnError(INVALID_ARGUMENT);
return;
}
switch (port) {
case PORT_INPUT: {
InputBufferInfo* input_info = &input_buffer_info_[index];
int32_t bitstream_buffer_id = next_bitstream_buffer_id_;
// Mask against 30 bits, to avoid (undefined) wraparound on signed
// integer.
next_bitstream_buffer_id_ = (next_bitstream_buffer_id_ + 1) & 0x3FFFFFFF;
int dup_fd = HANDLE_EINTR(dup(input_info->handle.get()));
if (dup_fd < 0) {
DLOG(ERROR) << "dup() failed.";
arc_client_->OnError(PLATFORM_FAILURE);
return;
}
CreateInputRecord(bitstream_buffer_id, index, metadata.timestamp);
vda_->Decode(media::BitstreamBuffer(
bitstream_buffer_id, base::SharedMemoryHandle(dup_fd, true),
metadata.bytes_used, input_info->offset));
break;
}
case PORT_OUTPUT: {
// is_valid() is true for the first time the buffer is passed to the VDA.
// In that case, VDA needs to import the buffer first.
OutputBufferInfo& info = buffers_pending_import_[index];
if (info.handle.is_valid()) {
gfx::GpuMemoryBufferHandle handle;
#if defined(USE_OZONE)
handle.native_pixmap_handle.fds.emplace_back(
base::FileDescriptor(info.handle.release(), true));
for (const auto& plane : info.planes) {
handle.native_pixmap_handle.planes.emplace_back(
plane.stride, plane.offset, 0);
}
#endif
vda_->ImportBufferForPicture(index, handle);
} else {
vda_->ReusePictureBuffer(index);
}
break;
}
default:
NOTREACHED();
}
}
void ArcGpuVideoDecodeAccelerator::Reset() {
DCHECK(thread_checker_.CalledOnValidThread());
if (!vda_) {
DLOG(ERROR) << "VDA not initialized";
return;
}
vda_->Reset();
}
void ArcGpuVideoDecodeAccelerator::Flush() {
DCHECK(thread_checker_.CalledOnValidThread());
if (!vda_) {
DLOG(ERROR) << "VDA not initialized";
return;
}
vda_->Flush();
}
void ArcGpuVideoDecodeAccelerator::ProvidePictureBuffers(
uint32_t requested_num_of_buffers,
media::VideoPixelFormat output_pixel_format,
uint32_t textures_per_buffer,
const gfx::Size& dimensions,
uint32_t texture_target) {
DVLOG(5) << "ProvidePictureBuffers("
<< "requested_num_of_buffers=" << requested_num_of_buffers
<< ", dimensions=" << dimensions.ToString() << ")";
DCHECK(thread_checker_.CalledOnValidThread());
coded_size_ = dimensions;
if ((output_pixel_format_ != media::PIXEL_FORMAT_UNKNOWN) &&
(output_pixel_format_ != output_pixel_format)) {
arc_client_->OnError(PLATFORM_FAILURE);
return;
}
output_pixel_format_ = output_pixel_format;
VideoFormat video_format;
switch (output_pixel_format_) {
case media::PIXEL_FORMAT_I420:
case media::PIXEL_FORMAT_YV12:
case media::PIXEL_FORMAT_NV12:
case media::PIXEL_FORMAT_NV21:
// HAL_PIXEL_FORMAT_YCbCr_420_888 is the flexible pixel format in Android
// which handles all 420 formats, with both orderings of chroma (CbCr and
// CrCb) as well as planar and semi-planar layouts.
video_format.pixel_format = HAL_PIXEL_FORMAT_YCbCr_420_888;
break;
case media::PIXEL_FORMAT_ARGB:
video_format.pixel_format = HAL_PIXEL_FORMAT_BGRA_8888;
break;
default:
DLOG(ERROR) << "Format not supported: " << output_pixel_format_;
arc_client_->OnError(PLATFORM_FAILURE);
return;
}
video_format.buffer_size =
media::VideoFrame::AllocationSize(output_pixel_format_, coded_size_);
output_buffer_size_ = video_format.buffer_size;
video_format.min_num_buffers = requested_num_of_buffers;
video_format.coded_width = dimensions.width();
video_format.coded_height = dimensions.height();
// TODO(owenlin): How to get visible size?
video_format.crop_top = 0;
video_format.crop_left = 0;
video_format.crop_width = dimensions.width();
video_format.crop_height = dimensions.height();
arc_client_->OnOutputFormatChanged(video_format);
}
void ArcGpuVideoDecodeAccelerator::DismissPictureBuffer(
int32_t picture_buffer) {
// no-op
}
void ArcGpuVideoDecodeAccelerator::PictureReady(const media::Picture& picture) {
DVLOG(5) << "PictureReady(picture_buffer_id=" << picture.picture_buffer_id()
<< ", bitstream_buffer_id=" << picture.bitstream_buffer_id();
DCHECK(thread_checker_.CalledOnValidThread());
InputRecord* input_record = FindInputRecord(picture.bitstream_buffer_id());
if (input_record == nullptr) {
DLOG(ERROR) << "Cannot find for bitstream buffer id: "
<< picture.bitstream_buffer_id();
arc_client_->OnError(PLATFORM_FAILURE);
return;
}
BufferMetadata metadata;
metadata.timestamp = input_record->timestamp;
metadata.bytes_used = output_buffer_size_;
arc_client_->OnBufferDone(PORT_OUTPUT, picture.picture_buffer_id(), metadata);
}
void ArcGpuVideoDecodeAccelerator::NotifyEndOfBitstreamBuffer(
int32_t bitstream_buffer_id) {
DVLOG(5) << "NotifyEndOfBitstreamBuffer(" << bitstream_buffer_id << ")";
DCHECK(thread_checker_.CalledOnValidThread());
InputRecord* input_record = FindInputRecord(bitstream_buffer_id);
if (input_record == nullptr) {
arc_client_->OnError(PLATFORM_FAILURE);
return;
}
arc_client_->OnBufferDone(PORT_INPUT, input_record->buffer_index,
BufferMetadata());
}
void ArcGpuVideoDecodeAccelerator::NotifyFlushDone() {
DCHECK(thread_checker_.CalledOnValidThread());
arc_client_->OnFlushDone();
}
void ArcGpuVideoDecodeAccelerator::NotifyResetDone() {
DCHECK(thread_checker_.CalledOnValidThread());
arc_client_->OnResetDone();
}
static ArcVideoAccelerator::Result ConvertErrorCode(
media::VideoDecodeAccelerator::Error error) {
switch (error) {
case media::VideoDecodeAccelerator::ILLEGAL_STATE:
return ArcVideoAccelerator::ILLEGAL_STATE;
case media::VideoDecodeAccelerator::INVALID_ARGUMENT:
return ArcVideoAccelerator::INVALID_ARGUMENT;
case media::VideoDecodeAccelerator::UNREADABLE_INPUT:
return ArcVideoAccelerator::UNREADABLE_INPUT;
case media::VideoDecodeAccelerator::PLATFORM_FAILURE:
return ArcVideoAccelerator::PLATFORM_FAILURE;
default:
DLOG(ERROR) << "Unknown error: " << error;
return ArcVideoAccelerator::PLATFORM_FAILURE;
}
}
void ArcGpuVideoDecodeAccelerator::NotifyError(
media::VideoDecodeAccelerator::Error error) {
DCHECK(thread_checker_.CalledOnValidThread());
DLOG(ERROR) << "Error notified: " << error;
arc_client_->OnError(ConvertErrorCode(error));
}
void ArcGpuVideoDecodeAccelerator::CreateInputRecord(
int32_t bitstream_buffer_id,
uint32_t buffer_index,
int64_t timestamp) {
input_records_.push_front(
InputRecord(bitstream_buffer_id, buffer_index, timestamp));
// The same value copied from media::GpuVideoDecoder. The input record is
// needed when the input buffer or the corresponding output buffer are
// returned from VDA. However there is no guarantee how much buffers will be
// kept in the VDA. We kept the last |kMaxNumberOfInputRecords| in
// |input_records_| and drop the others.
const size_t kMaxNumberOfInputRecords = 128;
if (input_records_.size() > kMaxNumberOfInputRecords)
input_records_.pop_back();
}
ArcGpuVideoDecodeAccelerator::InputRecord*
ArcGpuVideoDecodeAccelerator::FindInputRecord(int32_t bitstream_buffer_id) {
for (auto& record : input_records_) {
if (record.bitstream_buffer_id == bitstream_buffer_id)
return &record;
}
return nullptr;
}
bool ArcGpuVideoDecodeAccelerator::ValidatePortAndIndex(PortType port,
uint32_t index) const {
switch (port) {
case PORT_INPUT:
if (index >= input_buffer_info_.size()) {
DLOG(ERROR) << "Invalid index: " << index;
return false;
}
return true;
case PORT_OUTPUT:
if (index >= buffers_pending_import_.size()) {
DLOG(ERROR) << "Invalid index: " << index;
return false;
}
return true;
default:
DLOG(ERROR) << "Invalid port: " << port;
return false;
}
}
} // namespace arc
} // namespace chromeos