/*****************************************************************************
* This file is part of Kvazaar HEVC encoder.
*
* Copyright (C) 2013-2015 Tampere University of Technology and others (see
* COPYING file).
*
* Kvazaar is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* Kvazaar is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with Kvazaar. If not, see .
****************************************************************************/
/*
* \file
*
*/
#include "cli.h"
#include
#include
#include
#include
#include
static const char short_options[] = "i:o:d:w:h:n:q:p:r:";
static const struct option long_options[] = {
{ "input", required_argument, NULL, 'i' },
{ "output", required_argument, NULL, 'o' },
{ "debug", required_argument, NULL, 'd' },
{ "width", required_argument, NULL, 'w' }, // deprecated
{ "height", required_argument, NULL, 'h' }, // deprecated
{ "frames", required_argument, NULL, 'n' },
{ "qp", required_argument, NULL, 'q' },
{ "period", required_argument, NULL, 'p' },
{ "ref", required_argument, NULL, 'r' },
{ "vps-period", required_argument, NULL, 0 },
{ "input-res", required_argument, NULL, 0 },
{ "input-fps", required_argument, NULL, 0 },
{ "deblock", required_argument, NULL, 0 },
{ "no-deblock", no_argument, NULL, 0 },
{ "sao", optional_argument, NULL, 0 },
{ "no-sao", no_argument, NULL, 0 },
{ "rdoq", no_argument, NULL, 0 },
{ "no-rdoq", no_argument, NULL, 0 },
{ "signhide", no_argument, NULL, 0 },
{ "no-signhide", no_argument, NULL, 0 },
{ "smp", no_argument, NULL, 0 },
{ "no-smp", no_argument, NULL, 0 },
{ "amp", no_argument, NULL, 0 },
{ "no-amp", no_argument, NULL, 0 },
{ "rd", required_argument, NULL, 0 },
{ "full-intra-search", no_argument, NULL, 0 },
{ "no-full-intra-search", no_argument, NULL, 0 },
{ "transform-skip", no_argument, NULL, 0 },
{ "no-transform-skip", no_argument, NULL, 0 },
{ "tr-depth-intra", required_argument, NULL, 0 },
{ "me", required_argument, NULL, 0 },
{ "subme", required_argument, NULL, 0 },
{ "source-scan-type", required_argument, NULL, 0 },
{ "sar", required_argument, NULL, 0 },
{ "overscan", required_argument, NULL, 0 },
{ "videoformat", required_argument, NULL, 0 },
{ "range", required_argument, NULL, 0 },
{ "colorprim", required_argument, NULL, 0 },
{ "transfer", required_argument, NULL, 0 },
{ "colormatrix", required_argument, NULL, 0 },
{ "chromaloc", required_argument, NULL, 0 },
{ "aud", no_argument, NULL, 0 },
{ "no-aud", no_argument, NULL, 0 },
{ "cqmfile", required_argument, NULL, 0 },
{ "seek", required_argument, NULL, 0 },
{ "tiles", required_argument, NULL, 0 },
{ "tiles-width-split", required_argument, NULL, 0 },
{ "tiles-height-split", required_argument, NULL, 0 },
{ "wpp", no_argument, NULL, 0 },
{ "no-wpp", no_argument, NULL, 0 },
{ "owf", required_argument, NULL, 0 },
{ "slices", required_argument, NULL, 0 },
{ "threads", required_argument, NULL, 0 },
{ "cpuid", optional_argument, NULL, 0 },
{ "no-cpuid", no_argument, NULL, 0 },
{ "pu-depth-inter", required_argument, NULL, 0 },
{ "pu-depth-intra", required_argument, NULL, 0 },
{ "info", no_argument, NULL, 0 },
{ "no-info", no_argument, NULL, 0 },
{ "gop", required_argument, NULL, 0 },
{ "bipred", no_argument, NULL, 0 },
{ "no-bipred", no_argument, NULL, 0 },
{ "bitrate", required_argument, NULL, 0 },
{ "preset", required_argument, NULL, 0 },
{ "mv-rdo", no_argument, NULL, 0 },
{ "no-mv-rdo", no_argument, NULL, 0 },
{ "psnr", no_argument, NULL, 0 },
{ "no-psnr", no_argument, NULL, 0 },
{ "version", no_argument, NULL, 0 },
{ "help", no_argument, NULL, 0 },
{ "loop-input", no_argument, NULL, 0 },
{ "mv-constraint", required_argument, NULL, 0 },
{ "hash", required_argument, NULL, 0 },
{"cu-split-termination",required_argument, NULL, 0 },
{ "crypto", required_argument, NULL, 0 },
{ "key", required_argument, NULL, 0 },
{ "me-early-termination",required_argument, NULL, 0 },
{ "intra-rdo-et", no_argument, NULL, 0 },
{ "no-intra-rdo-et", no_argument, NULL, 0 },
{ "lossless", no_argument, NULL, 0 },
{ "no-lossless", no_argument, NULL, 0 },
{ "tmvp", no_argument, NULL, 0 },
{ "no-tmvp", no_argument, NULL, 0 },
{ "rdoq-skip", no_argument, NULL, 0 },
{ "no-rdoq-skip", no_argument, NULL, 0 },
{ "input-bitdepth", required_argument, NULL, 0 },
{ "input-format", required_argument, NULL, 0 },
{ "implicit-rdpcm", no_argument, NULL, 0 },
{ "no-implicit-rdpcm", no_argument, NULL, 0 },
{ "roi", required_argument, NULL, 0 },
{ "erp-aqp", no_argument, NULL, 0 },
{ "no-erp-aqp", no_argument, NULL, 0 },
{ "level", required_argument, NULL, 0 },
{ "force-level", required_argument, NULL, 0 },
{ "high-tier", no_argument, NULL, 0 },
{ "me-steps", required_argument, NULL, 0 },
{ "fast-residual-cost", required_argument, NULL, 0 },
{ "set-qp-in-cu", no_argument, NULL, 0 },
{ "open-gop", no_argument, NULL, 0 },
{ "no-open-gop", no_argument, NULL, 0 },
{ "scaling-list", required_argument, NULL, 0 },
{ "max-merge", required_argument, NULL, 0 },
{ "early-skip", no_argument, NULL, 0 },
{ "no-early-skip", no_argument, NULL, 0 },
{ "rc-algorithm", required_argument, NULL, 0 },
{ "intra-bits", no_argument, NULL, 0 },
{ "no-intra-bits", no_argument, NULL, 0 },
{0, 0, 0, 0}
};
/**
* \brief Try to detect resolution from file name automatically
*
* \param file_name file name to get dimensions from
* \param out_width detected width
* \param out_height detected height
* \return 1 if the resolution is set, 0 on fail
*/
static int select_input_res_auto(const char *file_name, int32_t *out_width, int32_t *out_height)
{
if (!file_name) return 0;
// Find the last delimiter char ( / or \ ). Hope the other kind is not used in the name.
// If delim is not found, set pointer to the beginning.
unsigned char* sub_str = (unsigned char*)MAX(strrchr(file_name, '/'), strrchr(file_name, '\\'));
if (!sub_str) sub_str = (unsigned char*)file_name;
int success = 0;
// Try if the substring starts with "x" without either of them being 0
do {
success = (sscanf((char*)sub_str, "%dx%d%*s", out_width, out_height) == 2);
success &= (*out_width > 0 && *out_height > 0);
// Move to the next char until a digit is found or the string ends
do{
++sub_str;
} while (*sub_str != 0 && !isdigit(*sub_str));
// Continue until "x" is found or the string ends
} while (*sub_str != 0 && !success);
return success;
}
/**
* \brief Parse command line arguments.
* \param argc Number of arguments
* \param argv Argument list
* \return Pointer to the parsed options, or NULL on failure.
*/
cmdline_opts_t* cmdline_opts_parse(const kvz_api *const api, int argc, char *argv[])
{
int ok = 1;
cmdline_opts_t *opts = calloc(1, sizeof(cmdline_opts_t));
if (!opts) {
ok = 0;
goto done;
}
opts->config = api->config_alloc();
if (!opts->config || !api->config_init(opts->config)) {
ok = 0;
goto done;
}
// Parse command line options
for (optind = 0;;) {
int long_options_index = -1;
int c = getopt_long(argc, argv, short_options, long_options, &long_options_index);
if (c == -1)
break;
if (long_options_index < 0) {
int i;
for (i = 0; long_options[i].name; i++)
if (long_options[i].val == c) {
long_options_index = i;
break;
}
if (long_options_index < 0) {
// getopt_long already printed an error message
ok = 0;
goto done;
}
}
const char* name = long_options[long_options_index].name;
if (!strcmp(name, "input")) {
if (opts->input) {
fprintf(stderr, "Input error: More than one input file given.\n");
ok = 0;
goto done;
}
opts->input = strdup(optarg);
} else if (!strcmp(name, "output")) {
if (opts->output) {
fprintf(stderr, "Input error: More than one output file given.\n");
ok = 0;
goto done;
}
opts->output = strdup(optarg);
} else if (!strcmp(name, "debug")) {
if (opts->debug) {
fprintf(stderr, "Input error: More than one debug output file given.\n");
ok = 0;
goto done;
}
opts->debug = strdup(optarg);
} else if (!strcmp(name, "seek")) {
opts->seek = atoi(optarg);
} else if (!strcmp(name, "frames")) {
opts->frames = atoi(optarg);
} else if (!strcmp(name, "version")) {
opts->version = true;
goto done;
} else if (!strcmp(name, "help")) {
opts->help = true;
goto done;
} else if (!strcmp(name, "loop-input")) {
opts->loop_input = true;
} else if (!api->config_parse(opts->config, name, optarg)) {
fprintf(stderr, "invalid argument: %s=%s\n", name, optarg);
ok = 0;
goto done;
}
}
// Check for extra arguments.
if (argc - optind > 0) {
fprintf(stderr, "Input error: Extra argument found: \"%s\"\n", argv[optind]);
ok = 0;
goto done;
}
// Check that the required files were defined
if (opts->input == NULL || opts->output == NULL) {
ok = 0;
goto done;
}
if (opts->config->vps_period < 0) {
// Disabling parameter sets is only possible when using Kvazaar as
// a library.
fprintf(stderr, "Input error: vps_period must be non-negative\n");
ok = 0;
goto done;
}
// Set resolution automatically if necessary
if (opts->config->width == 0 && opts->config->height == 0) {
ok = select_input_res_auto(opts->input, &opts->config->width, &opts->config->height);
goto done;
}
done:
if (!ok) {
cmdline_opts_free(api, opts);
opts = NULL;
}
return opts;
}
/**
* \brief Deallocate a cmdline_opts_t structure.
*/
void cmdline_opts_free(const kvz_api *const api, cmdline_opts_t *opts)
{
if (opts) {
FREE_POINTER(opts->input);
FREE_POINTER(opts->output);
FREE_POINTER(opts->debug);
api->config_destroy(opts->config);
opts->config = NULL;
}
FREE_POINTER(opts);
}
void print_usage(void)
{
fprintf(stdout,
"Kvazaar usage: -i and --input-res to set input, -o to set output\n"
" --help for more information\n");
}
void print_version(void)
{
fprintf(stdout,
"Kvazaar " VERSION_STRING "\n"
"Kvazaar license: LGPL version 2\n");
}
void print_help(void)
{
fprintf(stdout,
"Usage:\n"
"kvazaar -i --input-res x -o