|
|
@@ -18,6 +18,7 @@ |
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
|
|
*/ |
|
|
|
|
|
|
|
#include <float.h> |
|
|
|
#include "libavutil/opt.h" |
|
|
|
#include "libavutil/parseutils.h" |
|
|
|
#include "libavutil/avstring.h" |
|
|
@@ -238,9 +239,8 @@ static void add_codec(FFServerStream *stream, AVCodecContext *av) |
|
|
|
st = av_mallocz(sizeof(AVStream)); |
|
|
|
if (!st) |
|
|
|
return; |
|
|
|
st->codec = avcodec_alloc_context3(NULL); |
|
|
|
st->codec = av; |
|
|
|
stream->streams[stream->nb_streams++] = st; |
|
|
|
memcpy(st->codec, av, sizeof(AVCodecContext)); |
|
|
|
} |
|
|
|
|
|
|
|
static enum AVCodecID opt_codec(const char *name, enum AVMediaType type) |
|
|
@@ -269,12 +269,15 @@ static int ffserver_opt_preset(const char *arg, |
|
|
|
FILE *f=NULL; |
|
|
|
char filename[1000], tmp[1000], tmp2[1000], line[1000]; |
|
|
|
int ret = 0; |
|
|
|
AVCodec *codec = avcodec_find_encoder(avctx->codec_id); |
|
|
|
AVCodec *codec = NULL; |
|
|
|
|
|
|
|
if (avctx) |
|
|
|
codec = avcodec_find_encoder(avctx->codec_id); |
|
|
|
|
|
|
|
if (!(f = get_preset_file(filename, sizeof(filename), arg, 0, |
|
|
|
codec ? codec->name : NULL))) { |
|
|
|
fprintf(stderr, "File for preset '%s' not found\n", arg); |
|
|
|
return 1; |
|
|
|
return AVERROR(EINVAL); |
|
|
|
} |
|
|
|
|
|
|
|
while(!feof(f)){ |
|
|
@@ -284,18 +287,17 @@ static int ffserver_opt_preset(const char *arg, |
|
|
|
e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2; |
|
|
|
if(e){ |
|
|
|
fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line); |
|
|
|
ret = 1; |
|
|
|
ret = AVERROR(EINVAL); |
|
|
|
break; |
|
|
|
} |
|
|
|
if(!strcmp(tmp, "acodec")){ |
|
|
|
if (audio_id && !strcmp(tmp, "acodec")) { |
|
|
|
*audio_id = opt_codec(tmp2, AVMEDIA_TYPE_AUDIO); |
|
|
|
}else if(!strcmp(tmp, "vcodec")){ |
|
|
|
} else if (video_id && !strcmp(tmp, "vcodec")){ |
|
|
|
*video_id = opt_codec(tmp2, AVMEDIA_TYPE_VIDEO); |
|
|
|
}else if(!strcmp(tmp, "scodec")){ |
|
|
|
} else if(!strcmp(tmp, "scodec")) { |
|
|
|
/* opt_subtitle_codec(tmp2); */ |
|
|
|
}else if(ffserver_opt_default(tmp, tmp2, avctx, type) < 0){ |
|
|
|
} else if (avctx && (ret = ffserver_opt_default(tmp, tmp2, avctx, type)) < 0) { |
|
|
|
fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2); |
|
|
|
ret = 1; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
@@ -323,15 +325,78 @@ static AVOutputFormat *ffserver_guess_format(const char *short_name, const char |
|
|
|
return fmt; |
|
|
|
} |
|
|
|
|
|
|
|
static void vreport_config_error(const char *filename, int line_num, int log_level, int *errors, const char *fmt, va_list vl) |
|
|
|
{ |
|
|
|
av_log(NULL, log_level, "%s:%d: ", filename, line_num); |
|
|
|
av_vlog(NULL, log_level, fmt, vl); |
|
|
|
(*errors)++; |
|
|
|
} |
|
|
|
|
|
|
|
static void report_config_error(const char *filename, int line_num, int log_level, int *errors, const char *fmt, ...) |
|
|
|
{ |
|
|
|
va_list vl; |
|
|
|
va_start(vl, fmt); |
|
|
|
av_log(NULL, log_level, "%s:%d: ", filename, line_num); |
|
|
|
av_vlog(NULL, log_level, fmt, vl); |
|
|
|
vreport_config_error(filename, line_num, log_level, errors, fmt, vl); |
|
|
|
va_end(vl); |
|
|
|
} |
|
|
|
|
|
|
|
(*errors)++; |
|
|
|
static int ffserver_set_int_param(int *dest, const char *value, int factor, int min, int max, |
|
|
|
FFServerConfig *config, int line_num, const char *error_msg, ...) |
|
|
|
{ |
|
|
|
int tmp; |
|
|
|
char *tailp; |
|
|
|
if (!value || !value[0]) |
|
|
|
goto error; |
|
|
|
errno = 0; |
|
|
|
tmp = strtol(value, &tailp, 0); |
|
|
|
if (tmp < min || tmp > max) |
|
|
|
goto error; |
|
|
|
if (factor) { |
|
|
|
if (FFABS(tmp) > INT_MAX / FFABS(factor)) |
|
|
|
goto error; |
|
|
|
tmp *= factor; |
|
|
|
} |
|
|
|
if (tailp[0] || errno) |
|
|
|
goto error; |
|
|
|
if (dest) |
|
|
|
*dest = tmp; |
|
|
|
return 0; |
|
|
|
error: |
|
|
|
if (config) { |
|
|
|
va_list vl; |
|
|
|
va_start(vl, error_msg); |
|
|
|
vreport_config_error(config->filename, line_num, AV_LOG_ERROR, &config->errors, error_msg, vl); |
|
|
|
va_end(vl); |
|
|
|
} |
|
|
|
return AVERROR(EINVAL); |
|
|
|
} |
|
|
|
|
|
|
|
static int ffserver_set_float_param(float *dest, const char *value, float factor, float min, float max, |
|
|
|
FFServerConfig *config, int line_num, const char *error_msg, ...) |
|
|
|
{ |
|
|
|
double tmp; |
|
|
|
char *tailp; |
|
|
|
if (!value || !value[0]) |
|
|
|
goto error; |
|
|
|
errno = 0; |
|
|
|
tmp = strtod(value, &tailp); |
|
|
|
if (tmp < min || tmp > max) |
|
|
|
goto error; |
|
|
|
if (factor) |
|
|
|
tmp *= factor; |
|
|
|
if (tailp[0] || errno) |
|
|
|
goto error; |
|
|
|
if (dest) |
|
|
|
*dest = tmp; |
|
|
|
return 0; |
|
|
|
error: |
|
|
|
if (config) { |
|
|
|
va_list vl; |
|
|
|
va_start(vl, error_msg); |
|
|
|
vreport_config_error(config->filename, line_num, AV_LOG_ERROR, &config->errors, error_msg, vl); |
|
|
|
va_end(vl); |
|
|
|
} |
|
|
|
return AVERROR(EINVAL); |
|
|
|
} |
|
|
|
|
|
|
|
#define ERROR(...) report_config_error(config->filename, line_num, AV_LOG_ERROR, &config->errors, __VA_ARGS__) |
|
|
@@ -502,6 +567,87 @@ static int ffserver_parse_config_feed(FFServerConfig *config, const char *cmd, c |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static int ffserver_apply_stream_config(AVCodecContext *enc, const AVDictionary *conf, AVDictionary **opts) |
|
|
|
{ |
|
|
|
AVDictionaryEntry *e; |
|
|
|
int ret = 0; |
|
|
|
|
|
|
|
/* Return values from ffserver_set_*_param are ignored. |
|
|
|
Values are initially parsed and checked before inserting to AVDictionary. */ |
|
|
|
|
|
|
|
//video params |
|
|
|
if ((e = av_dict_get(conf, "VideoBitRateRangeMin", NULL, 0))) |
|
|
|
ffserver_set_int_param(&enc->rc_min_rate, e->value, 1000, INT_MIN, INT_MAX, NULL, 0, NULL); |
|
|
|
if ((e = av_dict_get(conf, "VideoBitRateRangeMax", NULL, 0))) |
|
|
|
ffserver_set_int_param(&enc->rc_max_rate, e->value, 1000, INT_MIN, INT_MAX, NULL, 0, NULL); |
|
|
|
if ((e = av_dict_get(conf, "Debug", NULL, 0))) |
|
|
|
ffserver_set_int_param(&enc->debug, e->value, 0, INT_MIN, INT_MAX, NULL, 0, NULL); |
|
|
|
if ((e = av_dict_get(conf, "Strict", NULL, 0))) |
|
|
|
ffserver_set_int_param(&enc->strict_std_compliance, e->value, 0, INT_MIN, INT_MAX, NULL, 0, NULL); |
|
|
|
if ((e = av_dict_get(conf, "VideoBufferSize", NULL, 0))) |
|
|
|
ffserver_set_int_param(&enc->rc_buffer_size, e->value, 8*1024, INT_MIN, INT_MAX, NULL, 0, NULL); |
|
|
|
if ((e = av_dict_get(conf, "VideoBitRateTolerance", NULL, 0))) |
|
|
|
ffserver_set_int_param(&enc->bit_rate_tolerance, e->value, 1000, INT_MIN, INT_MAX, NULL, 0, NULL); |
|
|
|
if ((e = av_dict_get(conf, "VideoBitRate", NULL, 0))) |
|
|
|
ffserver_set_int_param(&enc->bit_rate, e->value, 1000, INT_MIN, INT_MAX, NULL, 0, NULL); |
|
|
|
if ((e = av_dict_get(conf, "VideoSizeWidth", NULL, 0))) |
|
|
|
ffserver_set_int_param(&enc->width, e->value, 0, INT_MIN, INT_MAX, NULL, 0, NULL); |
|
|
|
if ((e = av_dict_get(conf, "VideoSizeHeight", NULL, 0))) |
|
|
|
ffserver_set_int_param(&enc->height, e->value, 0, INT_MIN, INT_MAX, NULL, 0, NULL); |
|
|
|
if ((e = av_dict_get(conf, "PixelFormat", NULL, 0))) |
|
|
|
ffserver_set_int_param(&enc->pix_fmt, e->value, 0, INT_MIN, INT_MAX, NULL, 0, NULL); |
|
|
|
if ((e = av_dict_get(conf, "VideoGopSize", NULL, 0))) |
|
|
|
ffserver_set_int_param(&enc->gop_size, e->value, 0, INT_MIN, INT_MAX, NULL, 0, NULL); |
|
|
|
if ((e = av_dict_get(conf, "VideoFrameRateNum", NULL, 0))) |
|
|
|
ffserver_set_int_param(&enc->time_base.num, e->value, 0, INT_MIN, INT_MAX, NULL, 0, NULL); |
|
|
|
if ((e = av_dict_get(conf, "VideoFrameRateDen", NULL, 0))) |
|
|
|
ffserver_set_int_param(&enc->time_base.den, e->value, 0, INT_MIN, INT_MAX, NULL, 0, NULL); |
|
|
|
if ((e = av_dict_get(conf, "VideoQDiff", NULL, 0))) |
|
|
|
ffserver_set_int_param(&enc->max_qdiff, e->value, 0, INT_MIN, INT_MAX, NULL, 0, NULL); |
|
|
|
if ((e = av_dict_get(conf, "VideoQMax", NULL, 0))) |
|
|
|
ffserver_set_int_param(&enc->qmax, e->value, 0, INT_MIN, INT_MAX, NULL, 0, NULL); |
|
|
|
if ((e = av_dict_get(conf, "VideoQMin", NULL, 0))) |
|
|
|
ffserver_set_int_param(&enc->qmin, e->value, 0, INT_MIN, INT_MAX, NULL, 0, NULL); |
|
|
|
if ((e = av_dict_get(conf, "LumiMask", NULL, 0))) |
|
|
|
ffserver_set_float_param(&enc->lumi_masking, e->value, 0, -FLT_MAX, FLT_MAX, NULL, 0, NULL); |
|
|
|
if ((e = av_dict_get(conf, "DarkMask", NULL, 0))) |
|
|
|
ffserver_set_float_param(&enc->dark_masking, e->value, 0, -FLT_MAX, FLT_MAX, NULL, 0, NULL); |
|
|
|
if (av_dict_get(conf, "BitExact", NULL, 0)) |
|
|
|
enc->flags |= CODEC_FLAG_BITEXACT; |
|
|
|
if (av_dict_get(conf, "DctFastint", NULL, 0)) |
|
|
|
enc->dct_algo = FF_DCT_FASTINT; |
|
|
|
if (av_dict_get(conf, "IdctSimple", NULL, 0)) |
|
|
|
enc->idct_algo = FF_IDCT_SIMPLE; |
|
|
|
if (av_dict_get(conf, "VideoHighQuality", NULL, 0)) |
|
|
|
enc->mb_decision = FF_MB_DECISION_BITS; |
|
|
|
if ((e = av_dict_get(conf, "VideoTag", NULL, 0))) |
|
|
|
enc->codec_tag = MKTAG(e->value[0], e->value[1], e->value[2], e->value[3]); |
|
|
|
if (av_dict_get(conf, "Qscale", NULL, 0)) { |
|
|
|
enc->flags |= CODEC_FLAG_QSCALE; |
|
|
|
ffserver_set_int_param(&enc->global_quality, e->value, FF_QP2LAMBDA, INT_MIN, INT_MAX, NULL, 0, NULL); |
|
|
|
} |
|
|
|
if (av_dict_get(conf, "Video4MotionVector", NULL, 0)) { |
|
|
|
enc->mb_decision = FF_MB_DECISION_BITS; //FIXME remove |
|
|
|
enc->flags |= CODEC_FLAG_4MV; |
|
|
|
} |
|
|
|
//audio params |
|
|
|
if ((e = av_dict_get(conf, "AudioChannels", NULL, 0))) |
|
|
|
ffserver_set_int_param(&enc->channels, e->value, 0, INT_MIN, INT_MAX, NULL, 0, NULL); |
|
|
|
if ((e = av_dict_get(conf, "AudioSampleRate", NULL, 0))) |
|
|
|
ffserver_set_int_param(&enc->sample_rate, e->value, 0, INT_MIN, INT_MAX, NULL, 0, NULL); |
|
|
|
if ((e = av_dict_get(conf, "AudioBitRate", NULL, 0))) |
|
|
|
ffserver_set_int_param(&enc->bit_rate, e->value, 0, INT_MIN, INT_MAX, NULL, 0, NULL); |
|
|
|
|
|
|
|
av_opt_set_dict2(enc, opts, AV_OPT_SEARCH_CHILDREN); |
|
|
|
e = NULL; |
|
|
|
while (e = av_dict_get(*opts, "", e, AV_DICT_IGNORE_SUFFIX)) { |
|
|
|
av_log(NULL, AV_LOG_ERROR, "Provided AVOption '%s' doesn't match any existing option.\n", e->key); |
|
|
|
ret = AVERROR(EINVAL); |
|
|
|
} |
|
|
|
|
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
static int ffserver_parse_config_stream(FFServerConfig *config, const char *cmd, const char **p, |
|
|
|
int line_num, FFServerStream **pstream) |
|
|
|
{ |
|
|
@@ -528,14 +674,12 @@ static int ffserver_parse_config_stream(FFServerConfig *config, const char *cmd, |
|
|
|
} |
|
|
|
|
|
|
|
stream->fmt = ffserver_guess_format(NULL, stream->filename, NULL); |
|
|
|
avcodec_get_context_defaults3(&config->video_enc, NULL); |
|
|
|
avcodec_get_context_defaults3(&config->audio_enc, NULL); |
|
|
|
|
|
|
|
config->audio_id = AV_CODEC_ID_NONE; |
|
|
|
config->video_id = AV_CODEC_ID_NONE; |
|
|
|
if (stream->fmt) { |
|
|
|
config->audio_id = stream->fmt->audio_codec; |
|
|
|
config->video_id = stream->fmt->video_codec; |
|
|
|
} else { |
|
|
|
config->audio_id = AV_CODEC_ID_NONE; |
|
|
|
config->video_id = AV_CODEC_ID_NONE; |
|
|
|
} |
|
|
|
*pstream = stream; |
|
|
|
return 0; |
|
|
@@ -587,23 +731,20 @@ static int ffserver_parse_config_stream(FFServerConfig *config, const char *cmd, |
|
|
|
!av_strcasecmp(cmd, "Copyright") || |
|
|
|
!av_strcasecmp(cmd, "Title")) { |
|
|
|
char key[32]; |
|
|
|
int i, ret; |
|
|
|
int i; |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
for (i = 0; i < strlen(cmd); i++) |
|
|
|
key[i] = av_tolower(cmd[i]); |
|
|
|
key[i] = 0; |
|
|
|
WARNING("'%s' option in configuration file is deprecated, " |
|
|
|
"use 'Metadata %s VALUE' instead\n", cmd, key); |
|
|
|
if ((ret = av_dict_set(&stream->metadata, key, arg, 0)) < 0) |
|
|
|
ERROR("Could not set metadata '%s' to value '%s': %s\n", key, arg, av_err2str(ret)); |
|
|
|
if (av_dict_set(&stream->metadata, key, arg, 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "Metadata")) { |
|
|
|
int ret; |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
ffserver_get_arg(arg2, sizeof(arg2), p); |
|
|
|
if ((ret = av_dict_set(&stream->metadata, arg, arg2, 0)) < 0) { |
|
|
|
ERROR("Could not set metadata '%s' to value '%s': %s\n", |
|
|
|
arg, arg2, av_err2str(ret)); |
|
|
|
} |
|
|
|
if (av_dict_set(&stream->metadata, arg, arg2, 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "Preroll")) { |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
stream->prebuffer = atof(arg) * 1000; |
|
|
@@ -624,136 +765,166 @@ static int ffserver_parse_config_stream(FFServerConfig *config, const char *cmd, |
|
|
|
stream->max_time = atof(arg) * 1000; |
|
|
|
} else if (!av_strcasecmp(cmd, "AudioBitRate")) { |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
config->audio_enc.bit_rate = lrintf(atof(arg) * 1000); |
|
|
|
float f; |
|
|
|
ffserver_set_float_param(&f, arg, 1000, 0, FLT_MAX, config, line_num, "Invalid %s: %s\n", cmd, arg); |
|
|
|
if (av_dict_set_int(&config->audio_conf, cmd, lrintf(f), 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "AudioChannels")) { |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
config->audio_enc.channels = atoi(arg); |
|
|
|
ffserver_set_int_param(NULL, arg, 0, 1, 8, config, line_num, "Invalid %s: %s, valid range is 1-8.", cmd, arg); |
|
|
|
if (av_dict_set(&config->audio_conf, cmd, arg, 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "AudioSampleRate")) { |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
config->audio_enc.sample_rate = atoi(arg); |
|
|
|
ffserver_set_int_param(NULL, arg, 0, 0, INT_MAX, config, line_num, "Invalid %s: %s", cmd, arg); |
|
|
|
if (av_dict_set(&config->audio_conf, cmd, arg, 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "VideoBitRateRange")) { |
|
|
|
int minrate, maxrate; |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) { |
|
|
|
config->video_enc.rc_min_rate = minrate * 1000; |
|
|
|
config->video_enc.rc_max_rate = maxrate * 1000; |
|
|
|
if (av_dict_set_int(&config->video_conf, "VideoBitRateRangeMin", minrate, 0) < 0 || |
|
|
|
av_dict_set_int(&config->video_conf, "VideoBitRateRangeMax", maxrate, 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else |
|
|
|
ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg); |
|
|
|
} else if (!av_strcasecmp(cmd, "Debug")) { |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
config->video_enc.debug = strtol(arg,0,0); |
|
|
|
ffserver_set_int_param(NULL, arg, 0, INT_MIN, INT_MAX, config, line_num, "Invalid %s: %s", cmd, arg); |
|
|
|
if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "Strict")) { |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
config->video_enc.strict_std_compliance = atoi(arg); |
|
|
|
ffserver_set_int_param(NULL, arg, 0, INT_MIN, INT_MAX, config, line_num, "Invalid %s: %s", cmd, arg); |
|
|
|
if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "VideoBufferSize")) { |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
config->video_enc.rc_buffer_size = atoi(arg) * 8*1024; |
|
|
|
ffserver_set_int_param(NULL, arg, 8*1024, 0, INT_MAX, config, line_num, "Invalid %s: %s", cmd, arg); |
|
|
|
if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "VideoBitRateTolerance")) { |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
config->video_enc.bit_rate_tolerance = atoi(arg) * 1000; |
|
|
|
ffserver_set_int_param(NULL, arg, 1000, INT_MIN, INT_MAX, config, line_num, "Invalid %s: %s", cmd, arg); |
|
|
|
if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "VideoBitRate")) { |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
config->video_enc.bit_rate = atoi(arg) * 1000; |
|
|
|
ffserver_set_int_param(NULL, arg, 1000, 0, INT_MAX, config, line_num, "Invalid %s: %s", cmd, arg); |
|
|
|
if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "VideoSize")) { |
|
|
|
int ret; |
|
|
|
int ret, w, h; |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
ret = av_parse_video_size(&config->video_enc.width, &config->video_enc.height, arg); |
|
|
|
ret = av_parse_video_size(&w, &h, arg); |
|
|
|
if (ret < 0) |
|
|
|
ERROR("Invalid video size '%s'\n", arg); |
|
|
|
else if ((config->video_enc.width % 16) != 0 || (config->video_enc.height % 16) != 0) |
|
|
|
else if ((w % 16) || (h % 16)) |
|
|
|
ERROR("Image size must be a multiple of 16\n"); |
|
|
|
if (av_dict_set_int(&config->video_conf, "VideoSizeWidth", w, 0) < 0 || |
|
|
|
av_dict_set_int(&config->video_conf, "VideoSizeHeight", h, 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "VideoFrameRate")) { |
|
|
|
AVRational frame_rate; |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
if (av_parse_video_rate(&frame_rate, arg) < 0) { |
|
|
|
ERROR("Incorrect frame rate: %s\n", arg); |
|
|
|
} else { |
|
|
|
config->video_enc.time_base.num = frame_rate.den; |
|
|
|
config->video_enc.time_base.den = frame_rate.num; |
|
|
|
if (av_dict_set_int(&config->video_conf, "VideoFrameRateNum", frame_rate.num, 0) < 0 || |
|
|
|
av_dict_set_int(&config->video_conf, "VideoFrameRateDen", frame_rate.den, 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} |
|
|
|
} else if (!av_strcasecmp(cmd, "PixelFormat")) { |
|
|
|
enum AVPixelFormat pix_fmt; |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
config->video_enc.pix_fmt = av_get_pix_fmt(arg); |
|
|
|
if (config->video_enc.pix_fmt == AV_PIX_FMT_NONE) |
|
|
|
pix_fmt = av_get_pix_fmt(arg); |
|
|
|
if (pix_fmt == AV_PIX_FMT_NONE) |
|
|
|
ERROR("Unknown pixel format: %s\n", arg); |
|
|
|
if (av_dict_set_int(&config->video_conf, cmd, pix_fmt, 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "VideoGopSize")) { |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
config->video_enc.gop_size = atoi(arg); |
|
|
|
ffserver_set_int_param(NULL, arg, 0, INT_MIN, INT_MAX, config, line_num, "Invalid %s: %s", cmd, arg); |
|
|
|
if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "VideoIntraOnly")) { |
|
|
|
config->video_enc.gop_size = 1; |
|
|
|
if (av_dict_set(&config->video_conf, cmd, "1", 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "VideoHighQuality")) { |
|
|
|
config->video_enc.mb_decision = FF_MB_DECISION_BITS; |
|
|
|
if (av_dict_set(&config->video_conf, cmd, "", 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "Video4MotionVector")) { |
|
|
|
config->video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove |
|
|
|
config->video_enc.flags |= CODEC_FLAG_4MV; |
|
|
|
if (av_dict_set(&config->video_conf, cmd, "", 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "AVOptionVideo") || |
|
|
|
!av_strcasecmp(cmd, "AVOptionAudio")) { |
|
|
|
AVCodecContext *avctx; |
|
|
|
int type; |
|
|
|
AVDictionary **dict; |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
ffserver_get_arg(arg2, sizeof(arg2), p); |
|
|
|
if (!av_strcasecmp(cmd, "AVOptionVideo")) { |
|
|
|
avctx = &config->video_enc; |
|
|
|
type = AV_OPT_FLAG_VIDEO_PARAM; |
|
|
|
} else { |
|
|
|
avctx = &config->audio_enc; |
|
|
|
type = AV_OPT_FLAG_AUDIO_PARAM; |
|
|
|
} |
|
|
|
if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) { |
|
|
|
ERROR("Error setting %s option to %s %s\n", cmd, arg, arg2); |
|
|
|
} |
|
|
|
if (!av_strcasecmp(cmd, "AVOptionVideo")) |
|
|
|
dict = &config->video_opts; |
|
|
|
else |
|
|
|
dict = &config->audio_opts; |
|
|
|
if (av_dict_set(dict, arg, arg2, 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "AVPresetVideo") || |
|
|
|
!av_strcasecmp(cmd, "AVPresetAudio")) { |
|
|
|
AVCodecContext *avctx; |
|
|
|
int type; |
|
|
|
char **preset = NULL; |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
if (!av_strcasecmp(cmd, "AVPresetVideo")) { |
|
|
|
avctx = &config->video_enc; |
|
|
|
config->video_enc.codec_id = config->video_id; |
|
|
|
type = AV_OPT_FLAG_VIDEO_PARAM; |
|
|
|
preset = &config->video_preset; |
|
|
|
ffserver_opt_preset(arg, NULL, 0, NULL, &config->video_id); |
|
|
|
} else { |
|
|
|
avctx = &config->audio_enc; |
|
|
|
config->audio_enc.codec_id = config->audio_id; |
|
|
|
type = AV_OPT_FLAG_AUDIO_PARAM; |
|
|
|
} |
|
|
|
if (ffserver_opt_preset(arg, avctx, type|AV_OPT_FLAG_ENCODING_PARAM, &config->audio_id, &config->video_id)) { |
|
|
|
ERROR("AVPreset error: %s\n", arg); |
|
|
|
preset = &config->audio_preset; |
|
|
|
ffserver_opt_preset(arg, NULL, 0, &config->audio_id, NULL); |
|
|
|
} |
|
|
|
*preset = av_strdup(arg); |
|
|
|
if (!preset) |
|
|
|
return AVERROR(ENOMEM); |
|
|
|
} else if (!av_strcasecmp(cmd, "VideoTag")) { |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
if (strlen(arg) == 4) |
|
|
|
config->video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]); |
|
|
|
if (strlen(arg) == 4) { |
|
|
|
if (av_dict_set(&config->video_conf, "VideoTag", "arg", 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} |
|
|
|
} else if (!av_strcasecmp(cmd, "BitExact")) { |
|
|
|
config->video_enc.flags |= CODEC_FLAG_BITEXACT; |
|
|
|
if (av_dict_set(&config->video_conf, cmd, "", 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "DctFastint")) { |
|
|
|
config->video_enc.dct_algo = FF_DCT_FASTINT; |
|
|
|
if (av_dict_set(&config->video_conf, cmd, "", 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "IdctSimple")) { |
|
|
|
config->video_enc.idct_algo = FF_IDCT_SIMPLE; |
|
|
|
if (av_dict_set(&config->video_conf, cmd, "", 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "Qscale")) { |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
config->video_enc.flags |= CODEC_FLAG_QSCALE; |
|
|
|
config->video_enc.global_quality = FF_QP2LAMBDA * atoi(arg); |
|
|
|
if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "VideoQDiff")) { |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
config->video_enc.max_qdiff = atoi(arg); |
|
|
|
if (config->video_enc.max_qdiff < 1 || config->video_enc.max_qdiff > 31) |
|
|
|
ERROR("VideoQDiff out of range\n"); |
|
|
|
ffserver_set_int_param(NULL, arg, 0, 1, 31, config, line_num, "%s out of range\n", cmd); |
|
|
|
if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "VideoQMax")) { |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
config->video_enc.qmax = atoi(arg); |
|
|
|
if (config->video_enc.qmax < 1 || config->video_enc.qmax > 31) |
|
|
|
ERROR("VideoQMax out of range\n"); |
|
|
|
ffserver_set_int_param(NULL, arg, 0, 1, 31, config, line_num, "%s out of range\n", cmd); |
|
|
|
if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "VideoQMin")) { |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
config->video_enc.qmin = atoi(arg); |
|
|
|
if (config->video_enc.qmin < 1 || config->video_enc.qmin > 31) |
|
|
|
ERROR("VideoQMin out of range\n"); |
|
|
|
ffserver_set_int_param(NULL, arg, 0, 1, 31, config, line_num, "%s out of range\n", cmd); |
|
|
|
if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "LumiMask")) { |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
config->video_enc.lumi_masking = atof(arg); |
|
|
|
ffserver_set_float_param(NULL, arg, 0, -FLT_MAX, FLT_MAX, config, line_num, "Invalid %s: %s", cmd, arg); |
|
|
|
if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "DarkMask")) { |
|
|
|
ffserver_get_arg(arg, sizeof(arg), p); |
|
|
|
config->video_enc.dark_masking = atof(arg); |
|
|
|
ffserver_set_float_param(NULL, arg, 0, -FLT_MAX, FLT_MAX, config, line_num, "Invalid %s: %s", cmd, arg); |
|
|
|
if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0) |
|
|
|
goto nomem; |
|
|
|
} else if (!av_strcasecmp(cmd, "NoVideo")) { |
|
|
|
config->video_id = AV_CODEC_ID_NONE; |
|
|
|
} else if (!av_strcasecmp(cmd, "NoAudio")) { |
|
|
@@ -783,16 +954,32 @@ static int ffserver_parse_config_stream(FFServerConfig *config, const char *cmd, |
|
|
|
} else if (!av_strcasecmp(cmd, "</Stream>")) { |
|
|
|
if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) { |
|
|
|
if (config->audio_id != AV_CODEC_ID_NONE) { |
|
|
|
config->audio_enc.codec_type = AVMEDIA_TYPE_AUDIO; |
|
|
|
config->audio_enc.codec_id = config->audio_id; |
|
|
|
add_codec(stream, &config->audio_enc); |
|
|
|
AVCodecContext *audio_enc = avcodec_alloc_context3(avcodec_find_encoder(config->audio_id)); |
|
|
|
if (config->audio_preset && |
|
|
|
ffserver_opt_preset(arg, audio_enc, AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_ENCODING_PARAM, |
|
|
|
NULL, NULL) < 0) |
|
|
|
ERROR("Could not apply preset '%s'\n", arg); |
|
|
|
if (ffserver_apply_stream_config(audio_enc, config->audio_conf, &config->audio_opts) < 0) |
|
|
|
config->errors++; |
|
|
|
add_codec(stream, audio_enc); |
|
|
|
} |
|
|
|
if (config->video_id != AV_CODEC_ID_NONE) { |
|
|
|
config->video_enc.codec_type = AVMEDIA_TYPE_VIDEO; |
|
|
|
config->video_enc.codec_id = config->video_id; |
|
|
|
add_codec(stream, &config->video_enc); |
|
|
|
AVCodecContext *video_enc = avcodec_alloc_context3(avcodec_find_encoder(config->video_id)); |
|
|
|
if (config->video_preset && |
|
|
|
ffserver_opt_preset(arg, video_enc, AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_ENCODING_PARAM, |
|
|
|
NULL, NULL) < 0) |
|
|
|
ERROR("Could not apply preset '%s'\n", arg); |
|
|
|
if (ffserver_apply_stream_config(video_enc, config->video_conf, &config->video_opts) < 0) |
|
|
|
config->errors++; |
|
|
|
add_codec(stream, video_enc); |
|
|
|
} |
|
|
|
} |
|
|
|
av_dict_free(&config->video_opts); |
|
|
|
av_dict_free(&config->video_conf); |
|
|
|
av_dict_free(&config->audio_opts); |
|
|
|
av_dict_free(&config->audio_conf); |
|
|
|
av_freep(&config->video_preset); |
|
|
|
av_freep(&config->audio_preset); |
|
|
|
*pstream = NULL; |
|
|
|
} else if (!av_strcasecmp(cmd, "File") || !av_strcasecmp(cmd, "ReadOnlyFile")) { |
|
|
|
ffserver_get_arg(stream->feed_filename, sizeof(stream->feed_filename), p); |
|
|
@@ -800,6 +987,15 @@ static int ffserver_parse_config_stream(FFServerConfig *config, const char *cmd, |
|
|
|
ERROR("Invalid entry '%s' inside <Stream></Stream>\n", cmd); |
|
|
|
} |
|
|
|
return 0; |
|
|
|
nomem: |
|
|
|
av_log(NULL, AV_LOG_ERROR, "Out of memory. Aborting.\n"); |
|
|
|
av_dict_free(&config->video_opts); |
|
|
|
av_dict_free(&config->video_conf); |
|
|
|
av_dict_free(&config->audio_opts); |
|
|
|
av_dict_free(&config->audio_conf); |
|
|
|
av_freep(&config->video_preset); |
|
|
|
av_freep(&config->audio_preset); |
|
|
|
return AVERROR(ENOMEM); |
|
|
|
} |
|
|
|
|
|
|
|
static int ffserver_parse_config_redirect(FFServerConfig *config, const char *cmd, const char **p, |
|
|
|