blob: f50619a523b36c7d56cc84ff226a2bbba27b25c6 [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 <linux/videodev2.h>
#include <iostream>
#include <sstream>
#include <string>
#include "base/command_line.h"
#include "base/files/memory_mapped_file.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "media/base/video_types.h"
#include "media/filters/ivf_parser.h"
#include "media/gpu/v4l2/test/vp9_decoder.h"
using media::v4l2_test::Vp9Decoder;
namespace {
constexpr char kUsageMsg[] =
"usage: v4l2_stateless_decoder\n"
" --video=<video path>\n"
" [--frames=<number of frames to decode>]\n"
" [--v=<log verbosity>]\n"
" [--help]\n";
constexpr char kHelpMsg[] =
"This binary decodes the IVF video in <video> path with specified \n"
"video <profile> via thinly wrapped v4l2 calls.\n"
"Supported codecs: VP9 (profile 0)\n"
"\nThe following arguments are supported:\n"
" --video=<path>\n"
" Required. Path to IVF-formatted video to decode.\n"
" --frames=<int>\n"
" Optional. Number of frames to decode, defaults to all.\n"
" Override with a positive integer to decode at most that many.\n"
" --help\n"
" Display this help message and exit.\n";
} // namespace
// For stateless API, fourcc |VP9F| is needed instead of |VP90| for VP9 codec.
// https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/pixfmt-compressed.html
// Converts fourcc |VP90| from file header to fourcc |VP9F|, which is
// a format supported on driver.
uint32_t FileFourccToDriverFourcc(uint32_t header_fourcc) {
if (header_fourcc == V4L2_PIX_FMT_VP9) {
LOG(INFO) << "OUTPUT format mapped from VP90 to VP9F.";
return V4L2_PIX_FMT_VP9_FRAME;
}
return header_fourcc;
}
// Creates the appropriate decoder for |stream|, which points to IVF data.
// Returns nullptr on failure.
std::unique_ptr<Vp9Decoder> CreateDecoder(
const base::MemoryMappedFile& stream) {
CHECK(stream.IsValid());
// Set up video parser.
auto ivf_parser = std::make_unique<media::IvfParser>();
media::IvfFileHeader file_header{};
if (!ivf_parser->Initialize(stream.data(), stream.length(), &file_header)) {
LOG(ERROR) << "Couldn't initialize IVF parser";
return nullptr;
}
// Create appropriate decoder for codec.
VLOG(1) << "Creating decoder with codec "
<< media::FourccToString(file_header.fourcc);
LOG_ASSERT(file_header.fourcc == v4l2_fourcc('V', 'P', '9', '0'))
<< "Codec " << media::FourccToString(file_header.fourcc)
<< " not supported.\n"
<< kUsageMsg;
const auto driver_codec_fourcc = FileFourccToDriverFourcc(file_header.fourcc);
CHECK_EQ(driver_codec_fourcc, V4L2_PIX_FMT_VP9_FRAME)
<< "Only VP9 is supported, got: "
<< media::FourccToString(driver_codec_fourcc);
return Vp9Decoder::Create(std::move(ivf_parser), file_header);
}
int main(int argc, char** argv) {
base::CommandLine::Init(argc, argv);
logging::LoggingSettings settings;
settings.logging_dest =
logging::LOG_TO_SYSTEM_DEBUG_LOG | logging::LOG_TO_STDERR;
logging::InitLogging(settings);
const base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
if (cmd->HasSwitch("help")) {
std::cout << kUsageMsg << "\n" << kHelpMsg;
return EXIT_SUCCESS;
}
const base::FilePath video_path = cmd->GetSwitchValuePath("video");
if (video_path.empty())
LOG(FATAL) << "No input video path provided to decode.\n" << kUsageMsg;
const std::string frames = cmd->GetSwitchValueASCII("frames");
int n_frames;
if (frames.empty()) {
n_frames = 0;
} else if (!base::StringToInt(frames, &n_frames) || n_frames <= 0) {
LOG(FATAL) << "Number of frames to decode must be positive integer, got "
<< frames;
}
// Set up video stream.
base::MemoryMappedFile stream;
if (!stream.Initialize(video_path))
LOG(FATAL) << "Couldn't open file: " << video_path;
const std::unique_ptr<Vp9Decoder> dec = CreateDecoder(stream);
if (!dec)
LOG(FATAL) << "Failed to create decoder for file: " << video_path;
if (!dec->Initialize())
LOG(FATAL) << "Initialization for decoding failed.";
return EXIT_SUCCESS;
}