blob: 4aca7eb8f6aa398b11b432a7942083c74bfca4a6 [file] [log] [blame]
// 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/wrapper.h"
#include <fcntl.h>
#include <utility>
#include <base/files/file_util.h>
#include <base/logging.h>
#include "arc/vm/libvda/fake/fake_vda_impl.h"
#include "arc/vm/libvda/gpu/gpu_vda_impl.h"
namespace arc {
VdaImpl::VdaImpl() {
capabilities_.num_input_formats = 0;
capabilities_.input_formats = nullptr;
capabilities_.num_output_formats = 0;
capabilities_.output_formats = nullptr;
}
VdaImpl::~VdaImpl() = default;
const vda_capabilities_t* const VdaImpl::GetCapabilities() {
return &capabilities_;
}
VdaContext::VdaContext() : event_write_thread_("EventWriteThread") {
int pipe_fds[2];
CHECK_EQ(pipe2(pipe_fds, O_CLOEXEC), 0);
event_read_fd_.reset(pipe_fds[0]);
event_write_fd_.reset(pipe_fds[1]);
// Start the dedicated event write thread for this decode session context.
CHECK(event_write_thread_.StartWithOptions(
base::Thread::Options(base::MessageLoop::TYPE_DEFAULT, 0)));
}
VdaContext::~VdaContext() = default;
int VdaContext::GetEventFd() {
return event_read_fd_.get();
}
// This method should only be called on the |event_write_thread| in order to
// write |event|. This is done so that all events are sent in sequence
// (important for PICTURE_READY events), and to ensure that a full write of
// |event| is always done as a single operation. Using the IPC thread was
// considered, but then in cases where the pipe buffer is close to being full,
// write() could block, which would not be acceptable to the IPC thread.
// Setting the pipe to non-blocking mode was also considered, but then
// we would have to re-post a new task to complete the write which could
// cause ordering issues.
void VdaContext::WriteOnEventWriteThread(vda_event_t event) {
DCHECK(event_write_thread_.task_runner()->BelongsToCurrentThread());
CHECK(base::WriteFileDescriptor(event_write_fd_.get(),
reinterpret_cast<const char*>(&event),
sizeof(vda_event_t)));
}
void VdaContext::DispatchProvidePictureBuffers(uint32_t min_num_buffers,
int32_t width,
int32_t height) {
vda_event_t event;
event.event_type = PROVIDE_PICTURE_BUFFERS;
event.event_data.provide_picture_buffers.min_num_buffers = min_num_buffers;
event.event_data.provide_picture_buffers.width = width;
event.event_data.provide_picture_buffers.height = height;
event_write_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&VdaContext::WriteOnEventWriteThread,
base::Unretained(this), std::move(event)));
}
void VdaContext::DispatchPictureReady(int32_t picture_buffer_id,
int32_t bitstream_id,
int crop_left,
int crop_top,
int crop_right,
int crop_bottom) {
vda_event_t event;
event.event_type = PICTURE_READY;
event.event_data.picture_ready.picture_buffer_id = picture_buffer_id;
event.event_data.picture_ready.bitstream_id = bitstream_id;
event.event_data.picture_ready.crop_left = crop_left;
event.event_data.picture_ready.crop_top = crop_top;
event.event_data.picture_ready.crop_right = crop_right;
event.event_data.picture_ready.crop_bottom = crop_bottom;
event_write_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&VdaContext::WriteOnEventWriteThread,
base::Unretained(this), std::move(event)));
}
void VdaContext::DispatchNotifyEndOfBitstreamBuffer(int32_t bitstream_id) {
vda_event_t event;
event.event_type = NOTIFY_END_OF_BITSTREAM_BUFFER;
event.event_data.bitstream_id = bitstream_id;
event_write_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&VdaContext::WriteOnEventWriteThread,
base::Unretained(this), std::move(event)));
}
void VdaContext::DispatchNotifyError(vda_result_t result) {
vda_event_t event;
event.event_type = NOTIFY_ERROR;
event.event_data.result = result;
event_write_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&VdaContext::WriteOnEventWriteThread,
base::Unretained(this), std::move(event)));
}
void VdaContext::DispatchResetResponse(vda_result_t result) {
vda_event_t event;
event.event_type = RESET_RESPONSE;
event.event_data.result = result;
event_write_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&VdaContext::WriteOnEventWriteThread,
base::Unretained(this), std::move(event)));
}
void VdaContext::DispatchFlushResponse(vda_result_t result) {
vda_event_t event;
event.event_type = FLUSH_RESPONSE;
event.event_data.result = result;
event_write_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&VdaContext::WriteOnEventWriteThread,
base::Unretained(this), std::move(event)));
}
} // namespace arc
void* initialize(vda_impl_type_t impl_type) {
switch (impl_type) {
case FAKE:
return arc::FakeVdaImpl::Create();
case GAVDA:
return arc::GpuVdaImpl::Create();
default:
LOG(ERROR) << "Unknown impl type " << impl_type;
return nullptr;
}
}
void deinitialize(void* impl) {
arc::VdaImpl* cast_impl = static_cast<arc::VdaImpl*>(impl);
delete cast_impl;
}
const vda_capabilities_t* get_vda_capabilities(void* impl) {
return static_cast<arc::VdaImpl*>(impl)->GetCapabilities();
}
vda_session_info_t* init_decode_session(void* impl, vda_profile profile) {
arc::VdaContext* context =
static_cast<arc::VdaImpl*>(impl)->InitDecodeSession(profile);
if (!context)
return nullptr;
vda_session_info_t* session_info = new vda_session_info_t();
session_info->ctx = context;
session_info->event_pipe_fd = context->GetEventFd();
return session_info;
}
void close_decode_session(void* impl, vda_session_info_t* session_info) {
static_cast<arc::VdaImpl*>(impl)->CloseDecodeSession(
static_cast<arc::VdaContext*>(session_info->ctx));
delete session_info;
}
vda_result_t vda_decode(void* ctx,
int32_t bitstream_id,
int fd,
uint32_t offset,
uint32_t bytes_used) {
return static_cast<arc::VdaContext*>(ctx)->Decode(bitstream_id, fd, offset,
bytes_used);
}
vda_result_t vda_set_output_buffer_count(void* ctx, size_t num_output_buffers) {
return static_cast<arc::VdaContext*>(ctx)->SetOutputBufferCount(
num_output_buffers);
}
vda_result_t vda_use_output_buffer(void* ctx,
int32_t picture_buffer_id,
vda_pixel_format_t format,
int fd,
size_t num_planes,
video_frame_plane_t* planes) {
return static_cast<arc::VdaContext*>(ctx)->UseOutputBuffer(
picture_buffer_id, format, fd, num_planes, planes);
}
vda_result vda_reset(void* ctx) {
return static_cast<arc::VdaContext*>(ctx)->Reset();
}
vda_result vda_flush(void* ctx) {
return static_cast<arc::VdaContext*>(ctx)->Flush();
}