blob: ab288d1973283ee4b7680ade2411c4ba809fd97c [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/decode/test/decode_event_thread.h"
#include <fcntl.h>
#include <string>
#include <utility>
#include <vector>
#include <base/bind.h>
#include <base/bind_helpers.h>
#include <base/files/file_util.h>
#include <base/posix/eintr_wrapper.h>
#include <gtest/gtest.h>
namespace arc {
namespace test {
namespace {
constexpr int kGbmBoUsage = GBM_BO_USE_TEXTURING | GBM_BO_USE_HW_VIDEO_DECODER;
const char* ConvertVdaFormatToString(vda_pixel_format_t format) {
switch (format) {
case YV12:
return "YV12";
case NV12:
return "NV12";
default:
NOTREACHED();
}
}
} // namespace
DecodeEventThread::DecodeEventThread(const vda_capabilities_t* const caps,
const vda_session_info_t* const session)
: thread_("DecodeEventThread"), caps_(caps), session_(session) {}
DecodeEventThread::~DecodeEventThread() {
Stop();
}
void DecodeEventThread::Start() {
// TODO(alexlau): don't hardcode this path, see comment in gpu_impl.cc.
gbm_device_fd_.reset(HANDLE_EINTR(open("/dev/dri/renderD128", O_RDWR)));
ASSERT_TRUE(gbm_device_fd_.is_valid());
gbm_device_.reset(gbm_create_device(gbm_device_fd_.get()));
ASSERT_NE(gbm_device_.get(), nullptr);
vda_format_ = caps_->output_formats[0];
gbm_format_ = arc::ConvertPixelFormatToGbmFormat(vda_format_);
ASSERT_NE(gbm_format_, 0);
end_of_bitstream_buffer_event_count_ = 0;
LOG(INFO) << "Using pixel format " << ConvertVdaFormatToString(vda_format_)
<< ", gbm format " << gbm_format_;
ASSERT_TRUE(thread_.StartWithOptions(
base::Thread::Options(base::MessagePumpType::IO, 0)));
thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&DecodeEventThread::StartWatching,
base::Unretained(this)));
}
void DecodeEventThread::Stop() {
DCHECK(thread_.IsRunning());
thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&DecodeEventThread::StopWatching, base::Unretained(this)));
thread_.Stop();
}
void DecodeEventThread::StartWatching() {
// Since thread_checker_ binds to whichever thread it's created on, check
// that we're on the correct thread first using BelongsToCurrentThread.
DCHECK(thread_.task_runner()->BelongsToCurrentThread());
// TODO(alexlau): Use DETACH_FROM_THREAD macro after libchrome uprev
// (crbug.com/909719).
thread_checker_.DetachFromThread();
// TODO(alexlau): Use DCHECK_CALLED_ON_VALID_THREAD macro after libchrome
// uprev (crbug.com/909719).
DCHECK(thread_checker_.CalledOnValidThread());
event_pipe_fd_controller_ = base::FileDescriptorWatcher::WatchReadable(
session_->event_pipe_fd,
base::BindRepeating(&DecodeEventThread::OnEventPipeReadable,
weak_factory_.GetWeakPtr()));
}
void DecodeEventThread::StopWatching() {
DCHECK(thread_checker_.CalledOnValidThread());
event_pipe_fd_controller_.reset();
picture_buffer_id_to_bo_map_.clear();
}
void DecodeEventThread::OnEventPipeReadable() {
DCHECK(thread_checker_.CalledOnValidThread());
vda_event_t event;
if (!base::ReadFromFD(session_->event_pipe_fd,
reinterpret_cast<char*>(&event), sizeof(vda_event_t))) {
LOG(ERROR) << "Failed to read from event pipe.";
return;
}
switch (event.event_type) {
case PICTURE_READY:
OnPictureReady(event.event_data.picture_ready);
break;
case PROVIDE_PICTURE_BUFFERS:
OnProvidePictureBuffers(event.event_data.provide_picture_buffers);
break;
case NOTIFY_END_OF_BITSTREAM_BUFFER:
OnNotifyEndOfBitstreamBuffer(event.event_data.bitstream_id);
break;
case NOTIFY_ERROR:
LOG(ERROR) << "Received NOTIFY_ERROR event with result "
<< event.event_data.result;
NOTREACHED();
break;
default:
LOG(ERROR) << "Received unknown event type " << event.event_type;
NOTREACHED();
break;
}
}
void DecodeEventThread::OnNotifyEndOfBitstreamBuffer(int32_t bitstream_id) {
DCHECK(thread_checker_.CalledOnValidThread());
VLOG(3) << "NOTIFY_END_OF_BITSTREAM_BUFFER event: bitstream_id "
<< bitstream_id;
base::AutoLock lock(end_of_bitstream_buffer_event_count_lock_);
end_of_bitstream_buffer_event_count_++;
}
uint32_t DecodeEventThread::GetAndClearEndOfBitstreamBufferEventCount() {
base::AutoLock lock(end_of_bitstream_buffer_event_count_lock_);
uint32_t ret = end_of_bitstream_buffer_event_count_;
end_of_bitstream_buffer_event_count_ = 0;
return ret;
}
void DecodeEventThread::OnPictureReady(const picture_ready_event_data_t& data) {
DCHECK(thread_checker_.CalledOnValidThread());
VLOG(3) << "PICTURE_READY event: picture_buffer_id " << data.picture_buffer_id
<< " bitstream_id " << data.bitstream_id << " crop_left "
<< data.crop_left << " crop_top " << data.crop_top << " crop_right "
<< data.crop_right << " crop_bottom " << data.crop_bottom;
// Give back the frame buffer.
auto iter = picture_buffer_id_to_bo_map_.find(data.picture_buffer_id);
if (iter == picture_buffer_id_to_bo_map_.end()) {
DLOG(ERROR) << "Could not find picture buffer id: "
<< data.picture_buffer_id;
return;
}
vda_reuse_output_buffer(session_->ctx, data.picture_buffer_id);
}
void DecodeEventThread::CallUseOutputBuffer(int32_t picture_buffer_id,
gbm_bo* bo) {
DCHECK(thread_checker_.CalledOnValidThread());
int plane_count = gbm_bo_get_plane_count(bo);
int plane_fd = gbm_bo_get_fd(bo);
std::vector<video_frame_plane_t> vda_planes;
// video_frame_plane_t* vda_planes = new video_frame_plane_t[plane_count];
for (int plane_index = 0; plane_index < plane_count; plane_index++) {
video_frame_plane_t plane;
plane.offset = gbm_bo_get_offset(bo, plane_index);
plane.stride = gbm_bo_get_stride_for_plane(bo, plane_index);
// Check that offsets are increasing which would indicate that all planes
// are in a single buffer.
if (vda_planes.size())
CHECK_GT(plane.offset, vda_planes.back().offset);
vda_planes.push_back(std::move(plane));
}
vda_use_output_buffer(session_->ctx, picture_buffer_id, vda_format_, plane_fd,
plane_count, vda_planes.data());
}
void DecodeEventThread::OnProvidePictureBuffers(
const provide_picture_buffers_event_data_t& data) {
DCHECK(thread_checker_.CalledOnValidThread());
VLOG(3) << "PROVIDE_PICTURE_BUFFERS event: min_num_buffers "
<< data.min_num_buffers << " width " << data.width << " height "
<< data.height << " visible_rect " << data.visible_rect_left << " "
<< data.visible_rect_top << " " << data.visible_rect_right << " "
<< data.visible_rect_bottom;
picture_buffer_id_to_bo_map_.clear();
vda_set_output_buffer_count(session_->ctx, data.min_num_buffers);
for (int i = 0; i < data.min_num_buffers; i++) {
int32_t picture_buffer_id = i;
arc::ScopedGbmBoPtr bo(gbm_bo_create(
gbm_device_.get(), data.width, data.height, gbm_format_, kGbmBoUsage));
CHECK_NE(bo.get(), nullptr);
CallUseOutputBuffer(picture_buffer_id, bo.get());
picture_buffer_id_to_bo_map_.insert(
std::make_pair(picture_buffer_id, std::move(bo)));
}
}
} // namespace test
} // namespace arc