blob: bc4914444fbfd8d0e6cf92b2d95f92d1ea17c2ba [file] [log] [blame]
// Copyright 2021 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 "media/gpu/v4l2/test/vp9_decoder.h"
#include <sys/ioctl.h>
#include "base/files/memory_mapped_file.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "media/filters/ivf_parser.h"
#include "media/filters/vp9_parser.h"
namespace media {
namespace v4l2_test {
Vp9Decoder::Vp9Decoder(std::unique_ptr<IvfParser> ivf_parser,
std::unique_ptr<V4L2IoctlShim> v4l2_ioctl,
std::unique_ptr<V4L2Queue> OUTPUT_queue,
std::unique_ptr<V4L2Queue> CAPTURE_queue)
: ivf_parser_(std::move(ivf_parser)),
vp9_parser_(
std::make_unique<Vp9Parser>(/*parsing_compressed_header=*/false)),
v4l2_ioctl_(std::move(v4l2_ioctl)),
OUTPUT_queue_(std::move(OUTPUT_queue)),
CAPTURE_queue_(std::move(CAPTURE_queue)) {}
Vp9Decoder::~Vp9Decoder() = default;
// static
std::unique_ptr<Vp9Decoder> Vp9Decoder::Create(
std::unique_ptr<IvfParser> ivf_parser,
const media::IvfFileHeader& file_header) {
constexpr uint32_t kDriverCodecFourcc = V4L2_PIX_FMT_VP9_FRAME;
// MM21 is an uncompressed opaque format that is produced by MediaTek
// video decoders.
const uint32_t kUncompressedFourcc = v4l2_fourcc('M', 'M', '2', '1');
auto v4l2_ioctl = std::make_unique<V4L2IoctlShim>();
if (!v4l2_ioctl->VerifyCapabilities(kDriverCodecFourcc,
kUncompressedFourcc)) {
LOG(ERROR) << "Device doesn't support the provided FourCCs.";
return nullptr;
}
LOG(INFO) << "Ivf file header: " << file_header.width << " x "
<< file_header.height;
auto OUTPUT_queue = std::make_unique<V4L2Queue>(
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, kDriverCodecFourcc,
gfx::Size(file_header.width, file_header.height), /*num_planes=*/1,
V4L2_MEMORY_MMAP);
// TODO(stevecho): enable V4L2_MEMORY_DMABUF memory for CAPTURE queue.
// |num_planes| represents separate memory buffers, not planes for Y, U, V.
// https://www.kernel.org/doc/html/v5.10/userspace-api/media/v4l/pixfmt-v4l2-mplane.html#c.V4L.v4l2_plane_pix_format
auto CAPTURE_queue = std::make_unique<V4L2Queue>(
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, kUncompressedFourcc,
gfx::Size(file_header.width, file_header.height), /*num_planes=*/2,
V4L2_MEMORY_MMAP);
return base::WrapUnique(
new Vp9Decoder(std::move(ivf_parser), std::move(v4l2_ioctl),
std::move(OUTPUT_queue), std::move(CAPTURE_queue)));
}
bool Vp9Decoder::Initialize() {
// TODO(stevecho): remove VIDIOC_ENUM_FRAMESIZES ioctl call
// after b/193237015 is resolved.
if (!v4l2_ioctl_->EnumFrameSizes(OUTPUT_queue_->fourcc()))
LOG(ERROR) << "EnumFrameSizes for OUTPUT queue failed.";
if (!v4l2_ioctl_->SetFmt(OUTPUT_queue_))
LOG(ERROR) << "SetFmt for OUTPUT queue failed.";
gfx::Size coded_size;
uint32_t num_planes;
if (!v4l2_ioctl_->GetFmt(CAPTURE_queue_->type(), &coded_size, &num_planes))
LOG(ERROR) << "GetFmt for CAPTURE queue failed.";
CAPTURE_queue_->set_coded_size(coded_size);
CAPTURE_queue_->set_num_planes(num_planes);
// VIDIOC_TRY_FMT() ioctl is equivalent to VIDIOC_S_FMT
// with one exception that it does not change driver state.
// VIDIOC_TRY_FMT may or may not be needed; it's used by the stateful
// Chromium V4L2VideoDecoder backend, see b/190733055#comment78.
// TODO(b/190733055): try and remove it after landing all the code.
if (!v4l2_ioctl_->TryFmt(CAPTURE_queue_))
LOG(ERROR) << "TryFmt for CAPTURE queue failed.";
if (!v4l2_ioctl_->SetFmt(CAPTURE_queue_))
LOG(ERROR) << "SetFmt for CAPTURE queue failed.";
if (!v4l2_ioctl_->ReqBufs(OUTPUT_queue_))
LOG(ERROR) << "ReqBufs for OUTPUT queue failed.";
if (!v4l2_ioctl_->QueryAndMmapQueueBuffers(OUTPUT_queue_))
LOG(ERROR) << "QueryAndMmapQueueBuffers for OUTPUT queue failed";
if (!v4l2_ioctl_->ReqBufs(CAPTURE_queue_))
LOG(ERROR) << "ReqBufs for CAPTURE queue failed.";
if (!v4l2_ioctl_->QueryAndMmapQueueBuffers(CAPTURE_queue_))
LOG(ERROR) << "QueryAndMmapQueueBuffers for CAPTURE queue failed.";
for (uint32_t i = 0; i < kRequestBufferCount; ++i) {
if (!v4l2_ioctl_->QBuf(CAPTURE_queue_, i))
LOG(ERROR) << "VIDIOC_QBUF failed for CAPTURE queue.";
}
if (!v4l2_ioctl_->MediaIocRequestAlloc())
LOG(ERROR) << "MEDIA_IOC_REQUEST_ALLOC failed";
if (!v4l2_ioctl_->StreamOn(OUTPUT_queue_->type()))
LOG(ERROR) << "StreamOn for OUTPUT queue failed.";
if (!v4l2_ioctl_->StreamOn(CAPTURE_queue_->type()))
LOG(ERROR) << "StreamOn for CAPTURE queue failed.";
return true;
}
Vp9Parser::Result Vp9Decoder::ReadNextFrame(Vp9FrameHeader* vp9_frame_header,
gfx::Size& size) {
DCHECK(vp9_frame_header);
// TODO(jchinlee): reexamine this loop for cleanup.
while (true) {
std::unique_ptr<DecryptConfig> null_config;
Vp9Parser::Result res =
vp9_parser_->ParseNextFrame(vp9_frame_header, &size, &null_config);
if (res == Vp9Parser::kEOStream) {
IvfFrameHeader ivf_frame_header{};
const uint8_t* ivf_frame_data;
if (!ivf_parser_->ParseNextFrame(&ivf_frame_header, &ivf_frame_data))
return Vp9Parser::kEOStream;
vp9_parser_->SetStream(ivf_frame_data, ivf_frame_header.frame_size,
/*stream_config=*/nullptr);
continue;
}
return res;
}
}
} // namespace v4l2_test
} // namespace media