Commit 74c051f2 authored by Henrik Gramner's avatar Henrik Gramner Committed by Anton Mitrofanov

cli: Bash autocomplete support

Allows for automatic command line completion for both options and values.

Options such as --input-csp and --input-fmt will dynamically retrieve
supported values from libavformat when compiled with lavf support.

Execute 'source tools/bash-autocomplete.sh' in bash to enable.
parent 92d36908
......@@ -27,8 +27,8 @@ SRCS_X = common/mc.c common/predict.c common/pixel.c common/macroblock.c \
SRCS_8 =
SRCCLI = x264.c input/input.c input/timecode.c input/raw.c input/y4m.c \
output/raw.c output/matroska.c output/matroska_ebml.c \
SRCCLI = x264.c autocomplete.c input/input.c input/timecode.c input/raw.c \
input/y4m.c output/raw.c output/matroska.c output/matroska_ebml.c \
output/flv.c output/flv_bytestream.c filters/filters.c \
filters/video/video.c filters/video/source.c filters/video/internal.c \
filters/video/resize.c filters/video/fix_vfr_pts.c \
......
/*****************************************************************************
* autocomplete: x264cli shell autocomplete
*****************************************************************************
* Copyright (C) 2018 x264 project
*
* Authors: Henrik Gramner <henrik@gramner.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
*
* This program is also available under a commercial proprietary license.
* For more information, contact us at licensing@x264.com.
*****************************************************************************/
#include "x264cli.h"
#include "input/input.h"
#if HAVE_LAVF
#include <libavformat/avformat.h>
#include <libavutil/pixdesc.h>
#endif
static const char * const level_names[] =
{
"1", "1.1", "1.2", "1.3", "1b",
"2", "2.1", "2.2",
"3", "3.1", "3.2",
"4", "4.1", "4.2",
"5", "5.1", "5.2",
"6", "6.1", "6.2",
NULL
};
/* Options requiring a value for which we provide suggestions. */
static const char * const opts_suggest[] =
{
"--alternative-transfer",
"--aq-mode",
"--asm",
"--avcintra-class",
"--avcintra-flavor",
"--b-adapt",
"--b-pyramid",
"--colormatrix",
"--colorprim",
"--cqm",
"--demuxer",
"--direct",
"--frame-packing",
"--input-csp",
"--input-fmt",
"--input-range",
"--level",
"--log-level",
"--me",
"--muxer",
"--nal-hrd",
"--output-csp",
"--overscan",
"--pass", "-p",
"--preset",
"--profile",
"--pulldown",
"--range",
"--subme", "-m",
"--transfer",
"--trellis", "-t",
"--tune",
"--videoformat",
"--weightp",
NULL
};
/* Options requiring a value for which we don't provide suggestions. */
static const char * const opts_nosuggest[] =
{
"--b-bias",
"--bframes", "-b",
"--deblock", "-f",
"--bitrate", "-B",
"--chroma-qp-offset",
"--chromaloc",
"--cplxblur",
"--cqm4",
"--cqm4i",
"--cqm4ic",
"--cqm4iy",
"--cqm4p",
"--cqm4pc",
"--cqm4py",
"--cqm8",
"--cqm8i",
"--cqm8p",
"--crf",
"--crf-max",
"--crop-rect",
"--deadzone-inter",
"--deadzone-intra",
"--fps",
"--frames",
"--input-depth",
"--input-res",
"--ipratio",
"--keyint", "-I",
"--lookahead-threads",
"--merange",
"--min-keyint", "-i",
"--mvrange",
"--mvrange-thread",
"--nr",
"--opencl-device",
"--output-depth",
"--partitions", "-A",
"--pbratio",
"--psy-rd",
"--qblur",
"--qcomp",
"--qp", "-q",
"--qpmax",
"--qpmin",
"--qpstep",
"--ratetol",
"--ref", "-r",
"--rc-lookahead",
"--sar",
"--scenecut",
"--seek",
"--slices",
"--slices-max",
"--slice-max-size",
"--slice-max-mbs",
"--slice-min-mbs",
"--sps-id",
"--sync-lookahead",
"--threads",
"--timebase",
"--vbv-bufsize",
"--vbv-init",
"--vbv-maxrate",
"--video-filter", "--vf",
"--zones",
NULL
};
/* Options requiring a filename. */
static const char * const opts_filename[] =
{
"--cqmfile",
"--dump-yuv",
"--index",
"--opencl-clbin",
"--output", "-o",
"--qpfile",
"--stats",
"--tcfile-in",
"--tcfile-out",
NULL
};
/* Options without an associated value. */
static const char * const opts_standalone[] =
{
"--8x8dct",
"--aud",
"--bff",
"--bluray-compat",
"--cabac",
"--constrained-intra",
"--cpu-independent",
"--dts-compress",
"--fake-interlaced",
"--fast-pskip",
"--filler",
"--force-cfr",
"--mbtree",
"--mixed-refs",
"--no-8x8dct",
"--no-asm",
"--no-cabac",
"--no-chroma-me",
"--no-dct-decimate",
"--no-deblock",
"--no-fast-pskip",
"--no-mbtree",
"--no-mixed-refs",
"--no-progress",
"--no-psy",
"--no-scenecut",
"--no-weightb",
"--non-deterministic",
"--open-gop",
"--opencl",
"--pic-struct",
"--psnr",
"--quiet",
"--sliced-threads",
"--slow-firstpass",
"--ssim",
"--stitchable",
"--tff",
"--thread-input",
"--verbose", "-v",
"--weightb",
NULL
};
/* Options which shouldn't be suggested in combination with other options. */
static const char * const opts_special[] =
{
"--fullhelp",
"--help", "-h",
"--longhelp",
"--version",
NULL
};
static int list_contains( const char * const *list, const char *s )
{
if( *s )
for( ; *list; list++ )
if( !strcmp( *list, s ) )
return 1;
return 0;
}
static void suggest( const char *s, const char *cur, int cur_len )
{
if( s && *s && !strncmp( s, cur, cur_len ) )
printf( "%s\n", s );
}
static void suggest_lower( const char *s, const char *cur, int cur_len )
{
if( s && *s && !strncasecmp( s, cur, cur_len ) )
{
for( ; *s; s++ )
putchar( *s < 'A' || *s > 'Z' ? *s : *s | 0x20 );
putchar( '\n' );
}
}
static void suggest_num_range( int start, int end, const char *cur, int cur_len )
{
char buf[16];
for( int i = start; i <= end; i++ )
{
snprintf( buf, sizeof( buf ), "%d", i );
suggest( buf, cur, cur_len );
}
}
#if HAVE_LAVF
/* Suggest each token in a string separated by delimiters. */
static void suggest_token( const char *s, int delim, const char *cur, int cur_len )
{
if( s && *s )
{
for( const char *tok_end; (tok_end = strchr( s, delim )); s = tok_end + 1 )
{
int tok_len = tok_end - s;
if( tok_len && tok_len >= cur_len && !strncmp( s, cur, cur_len ) )
printf( "%.*s\n", tok_len, s );
}
suggest( s, cur, cur_len );
}
}
#endif
#define OPT( opt ) else if( !strcmp( prev, opt ) )
#define OPT2( opt1, opt2 ) else if( !strcmp( prev, opt1 ) || !strcmp( prev, opt2 ) )
#define OPT_TYPE( type ) list_contains( opts_##type, prev )
#define suggest( s ) suggest( s, cur, cur_len )
#define suggest_lower( s ) suggest_lower( s, cur, cur_len )
#define suggest_list( list ) for( const char * const *s = list; *s; s++ ) suggest( *s )
#define suggest_num_range( start, end ) suggest_num_range( start, end, cur, cur_len )
#define suggest_token( s, delim ) suggest_token( s, delim, cur, cur_len )
int x264_cli_autocomplete( const char *prev, const char *cur )
{
int cur_len = strlen( cur );
if( 0 );
OPT( "--alternative-transfer" )
suggest_list( x264_transfer_names );
OPT( "--aq-mode" )
suggest_num_range( 0, 3 );
OPT( "--asm" )
for( const x264_cpu_name_t *cpu = x264_cpu_names; cpu->flags; cpu++ )
suggest_lower( cpu->name );
OPT( "--avcintra-class" )
suggest_list( x264_avcintra_class_names );
OPT( "--avcintra-flavor" )
suggest_list( x264_avcintra_flavor_names );
OPT( "--b-adapt" )
suggest_num_range( 0, 2 );
OPT( "--b-pyramid" )
suggest_list( x264_b_pyramid_names );
OPT( "--colormatrix" )
suggest_list( x264_colmatrix_names );
OPT( "--colorprim" )
suggest_list( x264_colorprim_names );
OPT( "--cqm" )
suggest_list( x264_cqm_names );
OPT( "--demuxer" )
suggest_list( x264_demuxer_names );
OPT( "--direct" )
suggest_list( x264_direct_pred_names );
OPT( "--frame-packing" )
suggest_num_range( 0, 7 );
OPT( "--input-csp" )
{
for( int i = X264_CSP_NONE+1; i < X264_CSP_CLI_MAX; i++ )
suggest( x264_cli_csps[i].name );
#if HAVE_LAVF
for( const AVPixFmtDescriptor *d = NULL; (d = av_pix_fmt_desc_next( d )); )
suggest( d->name );
#endif
}
OPT( "--input-fmt" )
{
#if HAVE_LAVF
av_register_all();
for( const AVInputFormat *f = NULL; (f = av_iformat_next( f )); )
suggest_token( f->name, ',' );
#endif
}
OPT( "--input-range" )
suggest_list( x264_range_names );
OPT( "--level" )
suggest_list( level_names );
OPT( "--log-level" )
suggest_list( x264_log_level_names );
OPT( "--me" )
suggest_list( x264_motion_est_names );
OPT( "--muxer" )
suggest_list( x264_muxer_names );
OPT( "--nal-hrd" )
suggest_list( x264_nal_hrd_names );
OPT( "--output-csp" )
suggest_list( x264_output_csp_names );
OPT( "--output-depth" )
{
#if HAVE_BITDEPTH8
suggest( "8" );
#endif
#if HAVE_BITDEPTH10
suggest( "10" );
#endif
}
OPT( "--overscan" )
suggest_list( x264_overscan_names );
OPT2( "--partitions", "-A" )
suggest_list( x264_partition_names );
OPT2( "--pass", "-p" )
suggest_num_range( 1, 3 );
OPT( "--preset" )
suggest_list( x264_preset_names );
OPT( "--profile" )
suggest_list( x264_valid_profile_names );
OPT( "--pulldown" )
suggest_list( x264_pulldown_names );
OPT( "--range" )
suggest_list( x264_range_names );
OPT2( "--subme", "-m" )
suggest_num_range( 0, 11 );
OPT( "--transfer" )
suggest_list( x264_transfer_names );
OPT2( "--trellis", "-t" )
suggest_num_range( 0, 2 );
OPT( "--tune" )
suggest_list( x264_tune_names );
OPT( "--videoformat" )
suggest_list( x264_vidformat_names );
OPT( "--weightp" )
suggest_num_range( 0, 2 );
else if( !OPT_TYPE( nosuggest ) && !OPT_TYPE( special ) )
{
if( OPT_TYPE( filename ) || strncmp( cur, "--", 2 ) )
return 1; /* Fall back to default shell filename autocomplete. */
/* Suggest options. */
suggest_list( opts_suggest );
suggest_list( opts_nosuggest );
suggest_list( opts_filename );
suggest_list( opts_standalone );
/* Only suggest special options if no other options have been specified. */
if( !*prev )
suggest_list( opts_special );
}
return 0;
}
_x264()
{
local path args cur prev
path="${COMP_LINE%%[[:blank:]]*}"
args="${COMP_LINE:${#path}:$((COMP_POINT-${#path}))}"
cur="${args##*[[:blank:]=]}"
prev="$(sed 's/[[:blank:]=]*$//; s/^.*[[:blank:]]//' <<< "${args%%"$cur"}")"
# Expand ~
printf -v path '%q' "$path" && eval path="${path/#'\~'/'~'}"
COMPREPLY=($("$path" --autocomplete "$prev" "$cur")) && compopt +o default
} 2>/dev/null
complete -o default -F _x264 x264
......@@ -166,11 +166,52 @@ static cli_output_t cli_output;
/* video filter operation struct */
static cli_vid_filter_t filter;
static const char * const demuxer_names[] =
const char * const x264_avcintra_class_names[] = { "50", "100", "200", 0 };
const char * const x264_cqm_names[] = { "flat", "jvt", 0 };
const char * const x264_log_level_names[] = { "none", "error", "warning", "info", "debug", 0 };
const char * const x264_partition_names[] = { "p8x8", "p4x4", "b8x8", "i8x8", "i4x4", "none", "all", 0 };
const char * const x264_pulldown_names[] = { "none", "22", "32", "64", "double", "triple", "euro", 0 };
const char * const x264_range_names[] = { "auto", "tv", "pc", 0 };
const char * const x264_output_csp_names[] =
{
"auto",
"raw",
"y4m",
#if !X264_CHROMA_FORMAT || X264_CHROMA_FORMAT == X264_CSP_I400
"i400",
#endif
#if !X264_CHROMA_FORMAT || X264_CHROMA_FORMAT == X264_CSP_I420
"i420",
#endif
#if !X264_CHROMA_FORMAT || X264_CHROMA_FORMAT == X264_CSP_I422
"i422",
#endif
#if !X264_CHROMA_FORMAT || X264_CHROMA_FORMAT == X264_CSP_I444
"i444", "rgb",
#endif
0
};
const char * const x264_valid_profile_names[] =
{
#if !X264_CHROMA_FORMAT || X264_CHROMA_FORMAT <= X264_CSP_I420
#if HAVE_BITDEPTH8
#if !X264_CHROMA_FORMAT || X264_CHROMA_FORMAT == X264_CSP_I420
"baseline", "main",
#endif
"high",
#endif
#if HAVE_BITDEPTH10
"high10",
#endif
#endif
#if !X264_CHROMA_FORMAT || X264_CHROMA_FORMAT == X264_CSP_I422
"high422",
#endif
"high444", 0
};
const char * const x264_demuxer_names[] =
{
"auto", "raw", "y4m",
#if HAVE_AVS
"avs",
#endif
......@@ -183,36 +224,15 @@ static const char * const demuxer_names[] =
0
};
static const char * const muxer_names[] =
const char * const x264_muxer_names[] =
{
"auto",
"raw",
"mkv",
"flv",
"auto", "raw", "mkv", "flv",
#if HAVE_GPAC || HAVE_LSMASH
"mp4",
#endif
0
};
static const char * const pulldown_names[] = { "none", "22", "32", "64", "double", "triple", "euro", 0 };
static const char * const log_level_names[] = { "none", "error", "warning", "info", "debug", 0 };
static const char * const output_csp_names[] =
{
#if !X264_CHROMA_FORMAT || X264_CHROMA_FORMAT == X264_CSP_I400
"i400",
#endif
#if !X264_CHROMA_FORMAT || X264_CHROMA_FORMAT == X264_CSP_I420
"i420",
#endif
#if !X264_CHROMA_FORMAT || X264_CHROMA_FORMAT == X264_CSP_I422
"i422",
#endif
#if !X264_CHROMA_FORMAT || X264_CHROMA_FORMAT == X264_CSP_I444
"i444", "rgb",
#endif
0
};
static const char * const chroma_format_names[] =
{
[0] = "all",
......@@ -222,8 +242,6 @@ static const char * const chroma_format_names[] =
[X264_CSP_I444] = "i444"
};
static const char * const range_names[] = { "auto", "tv", "pc", 0 };
typedef struct
{
int mod;
......@@ -357,6 +375,9 @@ static void print_version_info( void )
static int main_internal( int argc, char **argv )
{
if( argc == 4 && !strcmp( argv[1], "--autocomplete" ) )
return x264_cli_autocomplete( argv[2], argv[3] );
x264_param_t param;
cli_opt_t opt = {0};
int ret = 0;
......@@ -588,23 +609,7 @@ static void help( x264_param_t *defaults, int longhelp )
" - high444:\n"
" Support for bit depth 8-10.\n"
" Support for 4:2:0/4:2:2/4:4:4 chroma subsampling.\n" );
else H0(
" - "
#if !X264_CHROMA_FORMAT || X264_CHROMA_FORMAT <= X264_CSP_I420
#if HAVE_BITDEPTH8
#if !X264_CHROMA_FORMAT || X264_CHROMA_FORMAT == X264_CSP_I420
"baseline,main,"
#endif
"high,"
#endif
#if HAVE_BITDEPTH10
"high10,"
#endif
#endif
#if !X264_CHROMA_FORMAT || X264_CHROMA_FORMAT == X264_CSP_I422
"high422,"
#endif
"high444\n" );
else H0( " - %s\n", stringify_names( buf, x264_valid_profile_names ) );
H0( " --preset <string> Use a preset to select encoding settings [medium]\n"
" Overridden by user settings.\n" );
H2( " - ultrafast:\n"
......@@ -791,9 +796,8 @@ static void help( x264_param_t *defaults, int longhelp )
H1( "Analysis:\n" );
H1( "\n" );
H1( " -A, --partitions <string> Partitions to consider [\"p8x8,b8x8,i8x8,i4x4\"]\n"
" - p8x8, p4x4, b8x8, i8x8, i4x4\n"
" - none, all\n"
" (p4x4 requires p8x8. i8x8 requires --8x8dct.)\n" );
" - %s\n"
" (p4x4 requires p8x8. i8x8 requires --8x8dct.)\n", stringify_names( buf, x264_partition_names ) );
H1( " --direct <string> Direct MV prediction mode [\"%s\"]\n"
" - none, spatial, temporal, auto\n",
strtable_lookup( x264_direct_pred_names, defaults->analyse.i_direct_mv_pred ) );
......@@ -845,8 +849,8 @@ static void help( x264_param_t *defaults, int longhelp )
H2( " --deadzone-inter <int> Set the size of the inter luma quantization deadzone [%d]\n", defaults->analyse.i_luma_deadzone[0] );
H2( " --deadzone-intra <int> Set the size of the intra luma quantization deadzone [%d]\n", defaults->analyse.i_luma_deadzone[1] );
H2( " Deadzones should be in the range 0 - 32.\n" );
H2( " --cqm <string> Preset quant matrices [\"flat\"]\n"
" - jvt, flat\n" );
H2( " --cqm <string> Preset quant matrices [\"%s\"]\n"
" - %s\n", x264_cqm_names[0], stringify_names( buf, x264_cqm_names ) );
H1( " --cqmfile <string> Read custom quant matrices from a JM-compatible file\n" );
H2( " Overrides any other --cqm* options.\n" );
H2( " --cqm4 <list> Set all 4x4 quant matrices\n"
......@@ -869,7 +873,7 @@ static void help( x264_param_t *defaults, int longhelp )
" - component, pal, ntsc, secam, mac, undef\n",
strtable_lookup( x264_vidformat_names, defaults->vui.i_vidformat ) );
H2( " --range <string> Specify color range [\"%s\"]\n"
" - %s\n", range_names[0], stringify_names( buf, range_names ) );
" - %s\n", x264_range_names[0], stringify_names( buf, x264_range_names ) );
H2( " --colorprim <string> Specify color primaries [\"%s\"]\n"
" - undef, bt709, bt470m, bt470bg, smpte170m,\n"
" smpte240m, film, bt2020, smpte428,\n"
......@@ -907,24 +911,24 @@ static void help( x264_param_t *defaults, int longhelp )
H0( "\n" );
H0( " -o, --output <string> Specify output file\n" );
H1( " --muxer <string> Specify output container format [\"%s\"]\n"
" - %s\n", muxer_names[0], stringify_names( buf, muxer_names ) );
" - %s\n", x264_muxer_names[0], stringify_names( buf, x264_muxer_names ) );
H1( " --demuxer <string> Specify input container format [\"%s\"]\n"
" - %s\n", demuxer_names[0], stringify_names( buf, demuxer_names ) );
" - %s\n", x264_demuxer_names[0], stringify_names( buf, x264_demuxer_names ) );
H1( " --input-fmt <string> Specify input file format (requires lavf support)\n" );
H1( " --input-csp <string> Specify input colorspace format for raw input\n" );
print_csp_names( longhelp );
H1( " --output-csp <string> Specify output colorspace [\"%s\"]\n"
" - %s\n",
#if X264_CHROMA_FORMAT
output_csp_names[0],
x264_output_csp_names[0],
#else
"i420",
#endif
stringify_names( buf, output_csp_names ) );
stringify_names( buf, x264_output_csp_names ) );
H1( " --input-depth <integer> Specify input bit depth for raw input\n" );
H1( " --output-depth <integer> Specify output bit depth\n" );
H1( " --input-range <string> Specify input color range [\"%s\"]\n"
" - %s\n", range_names[0], stringify_names( buf, range_names ) );
" - %s\n", x264_range_names[0], stringify_names( buf, x264_range_names ) );
H1( " --input-res <intxint> Specify input resolution (width x height)\n" );
H1( " --index <string> Filename for input index file\n" );
H0( " --sar width:height Specify Sample Aspect Ratio\n" );
......@@ -934,7 +938,7 @@ static void help( x264_param_t *defaults, int longhelp )
H0( " --level <string> Specify level (as defined by Annex A)\n" );
H1( " --bluray-compat Enable compatibility hacks for Blu-ray support\n" );
H1( " --avcintra-class <integer> Use compatibility hacks for AVC-Intra class\n"
" - 50, 100, 200\n" );
" - %s\n", stringify_names( buf, x264_avcintra_class_names )