mirror of https://git.ffmpeg.org/ffmpeg.git
279 lines
7.1 KiB
C
279 lines
7.1 KiB
C
/*
|
|
* LGPL
|
|
*/
|
|
|
|
/*
|
|
* typical parsed command line:
|
|
* msmpeg4:bitrate=720000:qmax=16
|
|
*
|
|
*/
|
|
|
|
#include "avcodec.h"
|
|
#ifdef OPTS_MAIN
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#endif
|
|
|
|
/*
|
|
* todo - use for decoder options also
|
|
*/
|
|
|
|
static int parse_bool(avc_config_t* c, char* s)
|
|
{
|
|
int b = 1; /* by default -on- when present */
|
|
if (s) {
|
|
if (!strcasecmp(s, "off") || !strcasecmp(s, "false")
|
|
|| !strcmp(s, "0"))
|
|
b = 0;
|
|
else if (!strcasecmp(s, "on") || !strcasecmp(s, "true")
|
|
|| !strcmp(s, "1"))
|
|
b = 1;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
if (c && c->val)
|
|
*(int*)(c->val) = b;
|
|
return 0;
|
|
}
|
|
|
|
static int parse_double(avc_config_t* c, char* s)
|
|
{
|
|
double d;
|
|
if (!s)
|
|
return -1;
|
|
d = atof(s);
|
|
if (c->min != c->max) {
|
|
if (d < c->min || d > c->max) {
|
|
fprintf(stderr, "Option: %s double value: %f out of range <%f, %f>\n",
|
|
c->name, d, c->min, c->max);
|
|
return -1;
|
|
}
|
|
}
|
|
if (c && c->val)
|
|
*(double*)(c->val) = d;
|
|
return 0;
|
|
}
|
|
|
|
static int parse_int(avc_config_t* c, char* s)
|
|
{
|
|
int i;
|
|
if (!s)
|
|
return -1;
|
|
i = atoi(s);
|
|
if (c->min != c->max) {
|
|
if (i < (int)c->min || i > (int)c->max) {
|
|
fprintf(stderr, "Option: %s integer value: %d out of range <%d, %d>\n",
|
|
c->name, i, (int)c->min, (int)c->max);
|
|
return -1;
|
|
}
|
|
}
|
|
if (c && c->val)
|
|
*(int*)(c->val) = i;
|
|
return 0;
|
|
}
|
|
|
|
static int parse_string(AVCodecContext* avctx, avc_config_t* c, char* s)
|
|
{
|
|
if (!s)
|
|
return -1;
|
|
|
|
if (c->type == FF_CONF_TYPE_RCOVERIDE) {
|
|
int sf, ef, qs;
|
|
float qf;
|
|
if (sscanf(s, "%d,%d,%d,%f", &sf, &ef, &qs, &qf) == 4 && sf < ef) {
|
|
RcOverride* o;
|
|
*((RcOverride**)c->val) =
|
|
realloc(*((RcOverride**)c->val),
|
|
sizeof(RcOverride) * (avctx->rc_override_count + 1));
|
|
o = *((RcOverride**)c->val) + avctx->rc_override_count++;
|
|
o->start_frame = sf;
|
|
o->end_frame = ef;
|
|
o->qscale = qs;
|
|
o->quality_factor = qf;
|
|
|
|
//printf("parsed Rc: %d,%d,%d,%f (%d)\n", sf,ef,qs,qf, avctx->rc_override_count);
|
|
} else {
|
|
printf("incorrect/unparsable Rc: \"%s\"\n", s);
|
|
}
|
|
} else
|
|
(char*)(c->val) = strdup(s);
|
|
return 0;
|
|
}
|
|
|
|
static int parse(AVCodecContext* avctx, avc_config_t* config, char* str)
|
|
{
|
|
while (str && *str) {
|
|
avc_config_t* c = config;
|
|
char* e = strchr(str, ':');
|
|
char* p;
|
|
if (e)
|
|
*e++ = 0;
|
|
|
|
p = strchr(str, '=');
|
|
if (p)
|
|
*p++ = 0;
|
|
|
|
while (c->name) {
|
|
if (!strcmp(c->name, str)) {
|
|
switch (c->type & FF_CONF_TYPE_MASK) {
|
|
case FF_CONF_TYPE_BOOL:
|
|
parse_bool(c, p);
|
|
break;
|
|
case FF_CONF_TYPE_DOUBLE:
|
|
parse_double(c, p);
|
|
break;
|
|
case FF_CONF_TYPE_INT:
|
|
parse_int(c, p);
|
|
break;
|
|
case FF_CONF_TYPE_STRING:
|
|
parse_string(avctx, c, p);
|
|
break;
|
|
default:
|
|
abort();
|
|
break;
|
|
}
|
|
}
|
|
c++;
|
|
}
|
|
str = e;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* \param avctx where to store parsed results
|
|
* \param str string with options for parsing
|
|
* or selectional string (pick only options appliable
|
|
* for codec - use ,msmpeg4, (with commas to avoid mismatch)
|
|
* \param config allocated avc_config_t for external parsing
|
|
* i.e. external program might learn about all available
|
|
* options for given codec
|
|
**/
|
|
void avcodec_getopt(AVCodecContext* avctx, const char* str, avc_config_t** config)
|
|
{
|
|
AVCodecContext avctx_tmp;
|
|
AVCodecContext* ctx = (avctx) ? avctx : &avctx_tmp;
|
|
static const char* class_h263 = ",msmpeg4,";
|
|
//"huffyuv,wmv1,msmpeg4v2,msmpeg4,mpeg4,mpeg1,mpeg1video,mjpeg,rv10,h263,h263p"
|
|
|
|
avc_config_t cnf[] =
|
|
{
|
|
// FIXME: sorted by importance!!!
|
|
// expert option should follow more common ones
|
|
{
|
|
"bitrate", "desired video bitrate",
|
|
FF_CONF_TYPE_INT, &ctx->bit_rate, 4, 240000000, 800000, NULL, class_h263
|
|
}, {
|
|
"vhq", "very high quality",
|
|
FF_CONF_TYPE_FLAG, &ctx->flags, 0, CODEC_FLAG_HQ, 0, NULL, class_h263
|
|
}, {
|
|
"ratetol", "number of bits the bitstream is allowed to diverge from the reference"
|
|
"the reference can be CBR (for CBR pass1) or VBR (for pass2)",
|
|
FF_CONF_TYPE_INT, &ctx->bit_rate_tolerance, 4, 240000000, 8000, NULL, class_h263
|
|
}, {
|
|
"qmin", "minimum quantizer", FF_CONF_TYPE_INT, &ctx->qmin, 1, 31, 2, NULL, class_h263
|
|
}, {
|
|
"qmax", "maximum qunatizer", FF_CONF_TYPE_INT, &ctx->qmax, 1, 31, 31, NULL, class_h263
|
|
}, {
|
|
"rc_eq", "rate control equation",
|
|
FF_CONF_TYPE_STRING, &ctx->rc_eq, 0, 0, 0, "tex^qComp" /* FILLME options */, class_h263
|
|
}, {
|
|
"rc_minrate", "rate control minimum bitrate",
|
|
FF_CONF_TYPE_INT, &ctx->rc_min_rate, 4, 24000000, 0, NULL, class_h263
|
|
}, {
|
|
"rc_maxrate", "rate control maximum bitrate",
|
|
FF_CONF_TYPE_INT, &ctx->rc_max_rate, 4, 24000000, 0, NULL, class_h263
|
|
}, {
|
|
"psnr", "calculate PSNR of compressed frames",
|
|
FF_CONF_TYPE_FLAG, &ctx->flags, 0, CODEC_FLAG_PSNR, 0, NULL, class_h263
|
|
}, {
|
|
"rc_override", "ratecontrol override (=startframe,endframe,qscale,quality_factor)",
|
|
FF_CONF_TYPE_RCOVERIDE, &ctx->rc_override, 0, 0, 0, "0,0,0,0", class_h263
|
|
},
|
|
|
|
{ NULL, NULL, 0, NULL, 0, 0, 0, NULL, NULL }
|
|
};
|
|
|
|
if (config) {
|
|
*config = malloc(sizeof(cnf));
|
|
if (*config) {
|
|
avc_config_t* src = cnf;
|
|
avc_config_t* dst = *config;
|
|
while (src->name) {
|
|
if (!str || !src->supported || strstr(src->supported, str))
|
|
memcpy(dst++, src, sizeof(avc_config_t));
|
|
src++;
|
|
}
|
|
memset(dst, 0, sizeof(avc_config_t));
|
|
}
|
|
} else if (str) {
|
|
char* s = strdup(str);
|
|
if (s) {
|
|
parse(avctx, cnf, s);
|
|
free(s);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef OPTS_MAIN
|
|
/*
|
|
* API test -
|
|
* arg1: options
|
|
* arg2: codec type
|
|
*
|
|
* compile standalone: make CFLAGS="-DOPTS_MAIN" opts
|
|
*/
|
|
int main(int argc, char* argv[])
|
|
{
|
|
AVCodecContext avctx;
|
|
avc_config_t* config;
|
|
char* def = malloc(5000);
|
|
const char* col = "";
|
|
int i = 0;
|
|
|
|
memset(&avctx, 0, sizeof(avctx));
|
|
*def = 0;
|
|
avcodec_getopt(&avctx, argv[1], NULL);
|
|
|
|
avcodec_getopt(NULL, (argc > 2) ? argv[2] : NULL, &config);
|
|
if (config)
|
|
while (config->name) {
|
|
int t = config->type & FF_CONF_TYPE_MASK;
|
|
printf("Config %s %s\n", config->name,
|
|
t == FF_CONF_TYPE_BOOL ? "bool" :
|
|
t == FF_CONF_TYPE_DOUBLE ? "double" :
|
|
t == FF_CONF_TYPE_INT ? "integer" :
|
|
t == FF_CONF_TYPE_STRING ? "string" :
|
|
"unknown??");
|
|
switch (t) {
|
|
case FF_CONF_TYPE_BOOL:
|
|
i += sprintf(def + i, "%s%s=%s",
|
|
col, config->name,
|
|
config->defval != 0. ? "on" : "off");
|
|
break;
|
|
case FF_CONF_TYPE_DOUBLE:
|
|
i += sprintf(def + i, "%s%s=%f",
|
|
col, config->name, config->defval);
|
|
break;
|
|
case FF_CONF_TYPE_INT:
|
|
i += sprintf(def + i, "%s%s=%d",
|
|
col, config->name, (int) config->defval);
|
|
break;
|
|
case FF_CONF_TYPE_STRING:
|
|
i += sprintf(def + i, "%s%s=%s",
|
|
col, config->name, config->defstr);
|
|
break;
|
|
}
|
|
col = ":";
|
|
config++;
|
|
}
|
|
|
|
printf("Default Options: %s\n", def);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|