blob: 8b18b958b273b9763131b42ba5a1e34888805151 [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/camera3/cpp/fidl.h>
#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_camera/camera_client.h"
#include "src/media/codec/examples/encode_camera/encoder_client.h"
namespace {
constexpr char kHelpOption[] = "help";
constexpr char kOutputOption[] = "output";
constexpr char kDurationOption[] = "duration";
constexpr char kCameraConfigOption[] = "camera-config";
constexpr char kCameraStreamOption[] = "camera-stream";
constexpr char kCameraListOption[] = "camera-list";
constexpr char kEncoderBitrateOption[] = "encoder-bitrate";
constexpr char kEncoderCodecOption[] = "encoder-codec";
constexpr char kEncoderGopSizeOption[] = "encoder-gop";
constexpr char kDefaultOutputFile[] = "/tmp/out.h264";
constexpr char kDefaultDuration[] = "30";
constexpr char kDefaultCameraConfiguration[] = "1";
constexpr char kDefaultCameraStream[] = "1";
constexpr char kDefaultEncoderBitrate[] = "700000";
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 a camera stream, encode it, and write to a file for a specified duration\n");
printf("\nValid options:\n");
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 capture for %s seconds\n", kDefaultDuration);
printf(" --%s=<duration>\tDuration in seconds to capture\n", kDurationOption);
printf("\n By default will select configuration %s\n", kDefaultCameraConfiguration);
printf(" --%s=<configuration index>\tIndex of camera configuration to use\n",
kCameraConfigOption);
printf("\n By default will select stream %s\n", kDefaultCameraStream);
printf(" --%s=<stream index>\tIndex of stream in current configuration to open\n",
kCameraStreamOption);
printf(" --%s\t Print camera streams and exit\n", kCameraListOption);
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 GOP size of %s\n", kDefaultEncoderGop);
printf(" --%s=<codec>\tWhich codec to encode with. Can be h264 or h265.\n", kEncoderCodecOption);
printf("\n By default will select %s\n", kDefaultEncoderCodec);
printf(" --%s=<gop>\tThe number of frames between key frames\n", kEncoderGopSizeOption);
}
int main(int argc, char* argv[]) {
std::ofstream out_file;
fxl::CommandLine command_line = fxl::CommandLineFromArgcArgv(argc, argv);
if (command_line.HasOption(kHelpOption)) {
Usage(command_line);
return 0;
}
auto out_filename = command_line.GetOptionValueWithDefault(kOutputOption, kDefaultOutputFile);
auto duration_str = command_line.GetOptionValueWithDefault(kDurationOption, kDefaultDuration);
auto config_str =
command_line.GetOptionValueWithDefault(kCameraConfigOption, kDefaultCameraConfiguration);
auto stream_str =
command_line.GetOptionValueWithDefault(kCameraStreamOption, kDefaultCameraStream);
auto bitrate_str =
command_line.GetOptionValueWithDefault(kEncoderBitrateOption, kDefaultEncoderBitrate);
auto codec_str =
command_line.GetOptionValueWithDefault(kEncoderCodecOption, kDefaultEncoderCodec);
auto gop_str = command_line.GetOptionValueWithDefault(kEncoderGopSizeOption, kDefaultEncoderGop);
bool list_camera = command_line.HasOption(kCameraListOption);
const zx::duration duration = zx::sec(fxl::StringToNumber<uint32_t>(duration_str));
uint32_t config = fxl::StringToNumber<uint32_t>(config_str);
uint32_t stream = fxl::StringToNumber<uint32_t>(stream_str);
uint32_t bitrate = fxl::StringToNumber<uint32_t>(bitrate_str);
uint32_t gop_size = fxl::StringToNumber<uint32_t>(gop_str);
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);
if (!duration.get()) {
std::cerr << "Invalid duration" << std::endl;
Usage(command_line);
return EXIT_FAILURE;
}
if (!list_camera) {
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 << "Writing to " << out_filename << " for " << duration_str << " seconds "
<< std::endl;
}
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
auto context = sys::ComponentContext::CreateAndServeOutgoingDirectory();
fuchsia::sysmem::AllocatorHandle allocator;
fuchsia::camera3::DeviceWatcherHandle watcher;
fuchsia::mediacodec::CodecFactoryHandle codec_factory;
auto status = context->svc()->Connect(watcher.NewRequest());
if (status != ZX_OK) {
std::cerr << "Failed to request device watcher 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 camera_result =
CameraClient::Create(std::move(watcher), std::move(allocator), list_camera, config, stream);
if (camera_result.is_error()) {
std::cerr << "Failed to create camera client" << std::endl;
return EXIT_FAILURE;
}
auto camera = camera_result.take_value();
allocator = nullptr;
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;
CameraClient::AddCollectionHandler add_collection_handler =
[&encoder](fuchsia::sysmem::BufferCollectionTokenHandle token,
fuchsia::sysmem::ImageFormat_2 image_format,
fuchsia::camera3::FrameRate frame_rate) -> uint32_t {
encoder->Start(std::move(token), std::move(image_format), frame_rate.numerator);
return 0;
};
CameraClient::RemoveCollectionHandler remove_collection_handler = [](uint32_t id) {};
CameraClient::ShowBufferHandler show_buffer_handler =
[&encoder, &frames_written](uint32_t collection_id, uint32_t buffer_index,
zx::eventpair release_fence) {
frames_written++;
encoder->QueueInputPacket(buffer_index, std::move(release_fence));
};
CameraClient::MuteStateHandler mute_handler = [](bool muted) {};
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);
};
camera->SetHandlers(std::move(add_collection_handler), std::move(remove_collection_handler),
std::move(show_buffer_handler), std::move(mute_handler));
encoder->SetOutputPacketHandler(std::move(output_packet_handler));
async::PostDelayedTask(
loop.dispatcher(),
[&loop, &bytes_written, &frames_written]() {
std::cout << "Encoded " << frames_written << " frames in " << bytes_written << " bytes"
<< std::endl;
loop.Quit();
},
duration);
loop.Run();
return 0;
}