blob: e3db6ff54951fac5ccaa27cd40ecd32634f89129 [file] [log] [blame]
// Copyright 2020 The Fuchsia 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 <fuchsia/mediacodec/cpp/fidl.h>
#include <fuchsia/sysmem/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async/cpp/task.h>
#include <lib/sys/cpp/component_context.h>
#include <lib/zx/time.h>
#include <stdint.h>
#include <stdio.h>
#include <fstream>
#include <iostream>
#include <thread>
#include "src/lib/fxl/command_line.h"
#include "src/lib/fxl/strings/string_number_conversions.h"
#include "src/media/codec/examples/encode_file/encoder_client.h"
namespace {
constexpr char kHelpOption[] = "help";
constexpr char kInputOption[] = "input";
constexpr char kInputWidthOption[] = "input-width";
constexpr char kInputHeightOption[] = "input-height";
constexpr char kInputFramesOption[] = "input-frames";
constexpr char kOutputOption[] = "output";
constexpr char kEncoderBitrateOption[] = "bitrate";
constexpr char kEncoderFramerateOption[] = "framerate";
constexpr char kEncoderCodecOption[] = "codec";
constexpr char kEncoderGopSizeOption[] = "gop";
constexpr char kDefaultInputFrames[] = "0";
constexpr char kDefaultOutputFile[] = "/tmp/out.h264";
constexpr char kDefaultEncoderBitrate[] = "1000000";
constexpr char kDefaultEncoderFramerate[] = "24";
constexpr char kDefaultEncoderCodec[] = "h264";
constexpr char kDefaultEncoderGop[] = "30";
constexpr char kH264[] = "h264";
constexpr char kH265[] = "h265";
} // namespace
static void Usage(const fxl::CommandLine& command_line) {
printf("\nUsage: %s [options]\n", command_line.argv0().c_str());
printf("Open an input file, encode it, and write output to a file\n");
printf("\nValid options:\n");
printf(
" --%s=<filename>\tRequired. The input file to read from. Should contain raw NV12 video "
"frames.\n",
kInputOption);
printf(" --%s=<width>\tRequired. The input width in pixels.\n", kInputWidthOption);
printf(" --%s=<height>\tRequired. The input height in pixels.\n", kInputHeightOption);
printf("\n By default will encode all frames in input file\n");
printf(" --%s=<frames>\tThe number of frames to encode from input file\n", kInputFramesOption);
printf("\n By default will write to %s\n", kDefaultOutputFile);
printf(" --%s=<filename>\tThe output file to write encoded video to\n", kOutputOption);
printf("\n By default will select encoded bitrate of %s\n", kDefaultEncoderBitrate);
printf(" --%s=<bitrate>\tTarget encoded bitrate\n", kEncoderBitrateOption);
printf("\n By default will select encoded framerate of %s\n", kDefaultEncoderFramerate);
printf(" --%s=<framerate>\tTarget encoded framerate\n", kEncoderFramerateOption);
printf("\n By default will select %s\n", kDefaultEncoderCodec);
printf(" --%s=<codec>\tWhich codec to encode with. Can be h264 or h265.\n", kEncoderCodecOption);
printf("\n By default will select encoded GOP size of %s\n", kDefaultEncoderGop);
printf(" --%s=<gop>\tThe number of frames between key frames\n", kEncoderGopSizeOption);
}
int main(int argc, char* argv[]) {
std::ofstream out_file;
std::ifstream in_file;
fxl::CommandLine command_line = fxl::CommandLineFromArgcArgv(argc, argv);
if (command_line.HasOption(kHelpOption)) {
Usage(command_line);
return 0;
}
std::string in_filename, in_width_str, in_height_str;
if (!command_line.GetOptionValue(kInputOption, &in_filename)) {
std::cerr << "Input filename required" << std::endl;
Usage(command_line);
return EXIT_FAILURE;
}
if (!command_line.GetOptionValue(kInputWidthOption, &in_width_str)) {
std::cerr << "Input width required" << std::endl;
Usage(command_line);
return EXIT_FAILURE;
}
if (!command_line.GetOptionValue(kInputHeightOption, &in_height_str)) {
std::cerr << "Input height required" << std::endl;
Usage(command_line);
return EXIT_FAILURE;
}
auto input_frames =
command_line.GetOptionValueWithDefault(kInputFramesOption, kDefaultInputFrames);
auto out_filename = command_line.GetOptionValueWithDefault(kOutputOption, kDefaultOutputFile);
auto bitrate_str =
command_line.GetOptionValueWithDefault(kEncoderBitrateOption, kDefaultEncoderBitrate);
auto framerate_str =
command_line.GetOptionValueWithDefault(kEncoderFramerateOption, kDefaultEncoderFramerate);
auto codec_str =
command_line.GetOptionValueWithDefault(kEncoderCodecOption, kDefaultEncoderCodec);
auto gop_str = command_line.GetOptionValueWithDefault(kEncoderGopSizeOption, kDefaultEncoderGop);
uint32_t bitrate = fxl::StringToNumber<uint32_t>(bitrate_str);
uint32_t framerate = fxl::StringToNumber<uint32_t>(framerate_str);
uint32_t gop_size = fxl::StringToNumber<uint32_t>(gop_str);
uint32_t input_width = fxl::StringToNumber<uint32_t>(in_width_str);
uint32_t input_height = fxl::StringToNumber<uint32_t>(in_height_str);
uint32_t input_frame_limit = fxl::StringToNumber<uint32_t>(input_frames);
if (codec_str != kH264 && codec_str != kH265) {
std::cerr << "Invalid codec" << std::endl;
Usage(command_line);
return EXIT_FAILURE;
}
std::string mime_type = "video/";
mime_type.append(codec_str);
// NV12 frame size
size_t frame_size = input_width * input_height * 3 / 2;
in_file.open(in_filename, std::ios::in | std::ios::binary);
if (!in_file.is_open()) {
std::cerr << "Failed to open input file" << std::endl;
return EXIT_FAILURE;
}
out_file.open(out_filename, std::ios::out | std::ios::binary | std::ios::trunc);
if (!out_file.is_open()) {
std::cerr << "Failed to open output file" << std::endl;
return EXIT_FAILURE;
}
std::cout << "Encoding " << in_filename << " to " << out_filename << std::endl;
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
auto context = sys::ComponentContext::CreateAndServeOutgoingDirectory();
fuchsia::sysmem::AllocatorHandle allocator;
fuchsia::mediacodec::CodecFactoryHandle codec_factory;
auto status = context->svc()->Connect(allocator.NewRequest());
if (status != ZX_OK) {
std::cerr << "Failed to request allocator service" << std::endl;
return EXIT_FAILURE;
}
status = context->svc()->Connect(codec_factory.NewRequest());
if (status != ZX_OK) {
std::cerr << "Failed to request codec factory service" << std::endl;
return EXIT_FAILURE;
}
status = context->svc()->Connect(allocator.NewRequest());
if (status != ZX_OK) {
std::cerr << "Failed to request allocator service" << std::endl;
return EXIT_FAILURE;
}
auto encoder_result = EncoderClient::Create(std::move(codec_factory), std::move(allocator),
bitrate, gop_size, mime_type);
if (encoder_result.is_error()) {
std::cerr << "Failed to create encoder client" << std::endl;
return EXIT_FAILURE;
}
auto encoder = encoder_result.take_value();
size_t bytes_written = 0;
size_t frames_written = 0;
// TODO(afoxley) add support for non-equal display and coded dimensions
fuchsia::sysmem::ImageFormat_2 image_format = {
.pixel_format.type = fuchsia::sysmem::PixelFormatType::NV12,
.coded_width = input_width,
.coded_height = input_height,
.bytes_per_row = input_width,
.display_width = input_width,
.display_height = input_height,
.color_space = fuchsia::sysmem::ColorSpace{.type = fuchsia::sysmem::ColorSpaceType::REC709},
};
encoder->Start(std::move(image_format), framerate);
EncoderClient::OutputPacketHandler output_packet_handler = [&out_file, &bytes_written](
uint8_t* buffer, size_t len) {
bytes_written += len;
out_file.write(reinterpret_cast<char*>(buffer), len);
};
encoder->SetOutputPacketHandler(std::move(output_packet_handler));
EncoderClient::InputBufferReadyHandler input_buffer_ready_handler =
[&in_file, &frames_written, frame_size, input_frame_limit](uint8_t* buffer, size_t size) {
if (input_frame_limit > 0 && frames_written >= input_frame_limit) {
return 0UL;
}
if (size < frame_size) {
std::cerr << "Buffer too small";
return 0UL;
}
in_file.read(reinterpret_cast<char*>(buffer), frame_size);
if (!in_file) {
// eof or error
return 0UL;
}
frames_written++;
return frame_size;
};
encoder->SetInputBufferReadyHandler(std::move(input_buffer_ready_handler));
EncoderClient::OutputEndOfStreamHandler output_end_of_stream_handler = [&frames_written,
&bytes_written, &loop]() {
std::cout << "Encoded " << frames_written << " frames in " << bytes_written << " bytes"
<< std::endl;
loop.Quit();
};
encoder->SetOutputEndOfStreamHandler(std::move(output_end_of_stream_handler));
loop.Run();
return 0;
}