So far AVCodecContext was created without codec specified.
This causes internal data to not be initialized to defaults.
This commit postpone context creation until all information are gathered.
Partially fixes #1275
---
ffserver.c | 8 +-
ffserver_config.c | 286 ++++++++++++++++++++++++++++++++----------------------
ffserver_config.h | 9 +-
3 files changed, 183 insertions(+), 120 deletions(-)
diff --git a/ffserver.c b/ffserver.c
index 22560ce..8c65d12 100644
--- a/ffserver.c
+++ b/ffserver.c
@@ -212,8 +212,12 @@ static FFServerConfig config = {
.warnings = 0,
.audio_id = AV_CODEC_ID_NONE,
.video_id = AV_CODEC_ID_NONE,
- .audio_enc = {0},
- .video_enc = {0},
+ .video_opts = NULL,
+ .video_conf = NULL,
+ .audio_opts = NULL,
+ .audio_conf = NULL,
+ .video_preset = NULL,
+ .audio_preset = NULL,
};
static void new_connection(int server_fd, int is_rtsp);
diff --git a/ffserver_config.c b/ffserver_config.c
index 18b1e72..87c91cd 100644
--- a/ffserver_config.c
+++ b/ffserver_config.c
@@ -238,9 +238,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 +268,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 +286,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;
}
}
@@ -510,6 +511,83 @@ 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;
+ char *eptr;
+
+#define SET_INT_PARAM(factor, param, key) \
+ if ((e = av_dict_get(conf, #key, NULL, 0))) { \
+ enc->param = strtol(e->value, &eptr, 0); \
+ if (factor) enc->param *= (factor); \
+ if (eptr[0] || errno) { \
+ av_log(NULL, AV_LOG_ERROR, "Cannot parse %s as number for %s
parameter.\n", e->value, #param); \
+ return AVERROR(errno); \
+ } \
+ }
+#define SET_DOUBLE_PARAM(factor, param, key) \
+ if ((e = av_dict_get(conf, #key, NULL, 0))) { \
+ enc->param = strtod(e->value, &eptr); \
+ if (factor) enc->param *= (factor); \
+ if (eptr[0] || errno) { \
+ av_log(NULL, AV_LOG_ERROR, "Cannot parse %s as number for %s
parameter.\n", e->value, #param); \
+ return AVERROR(errno); \
+ } \
+ }
+
+ errno = 0;
+ //video params
+ SET_INT_PARAM(0, rc_min_rate, VideoBitRateRangeMin)
+ SET_INT_PARAM(0, rc_max_rate, VideoBitRateRangeMax)
+ SET_INT_PARAM(0, debug, Debug)
+ SET_INT_PARAM(0, strict_std_compliance, Strict)
+ SET_INT_PARAM(8*1024, rc_buffer_size, VideoBufferSize)
+ SET_INT_PARAM(1000, bit_rate_tolerance, VideoBitRateTolerance)
+ SET_INT_PARAM(1000, bit_rate, VideoBitRate)
+ SET_INT_PARAM(0, width, VideoSizeWidth)
+ SET_INT_PARAM(0, height, VideoSizeHeight)
+ SET_INT_PARAM(0, pix_fmt, PixelFormat)
+ SET_INT_PARAM(0, gop_size, VideoGopSize)
+ SET_INT_PARAM(0, time_base.num, VideoFrameRateNum)
+ SET_INT_PARAM(0, time_base.den, VideoFrameRateDen)
+ SET_INT_PARAM(0, max_qdiff, VideoQDiff)
+ SET_INT_PARAM(0, qmax, VideoQMax)
+ SET_INT_PARAM(0, qmin, VideoQMin)
+ SET_DOUBLE_PARAM(0, lumi_masking, LumiMask)
+ SET_DOUBLE_PARAM(0, dark_masking, DarkMask)
+ 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;
+ SET_INT_PARAM(FF_QP2LAMBDA, global_quality, "Qscale")
+ }
+ if (av_dict_get(conf, "Video4MotionVector", NULL, 0)) {
+ enc->mb_decision = FF_MB_DECISION_BITS; //FIXME remove
+ enc->flags |= CODEC_FLAG_4MV;
+ }
+ //audio params
+ SET_INT_PARAM(0, channels, AudioChannels)
+ SET_INT_PARAM(0, sample_rate, AudioSampleRate)
+ SET_INT_PARAM(0, bit_rate, AudioBitRate)
+
+ 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_WARNING, "Provided option '%s' doesn't match any
existing option.\n", e->key);
+
+ return 0;
+#undef SET_INT_PARAM
+#undef SET_DOUBLE_PARAM
+}
+
static int ffserver_parse_config_stream(FFServerConfig *config, const char
*cmd, const char **p,
int line_num, FFServerStream **pstream)
{
@@ -537,14 +615,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;
@@ -638,136 +714,104 @@ 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);
- } else if (!av_strcasecmp(cmd, "AudioChannels")) {
- ffserver_get_arg(arg, sizeof(arg), p);
- config->audio_enc.channels = atoi(arg);
- } else if (!av_strcasecmp(cmd, "AudioSampleRate")) {
+ av_dict_set_int(&config->audio_conf, cmd, lrintf(atof(arg) * 1000), 0);
+ } else if (!av_strcasecmp(cmd, "AudioChannels") ||
+ !av_strcasecmp(cmd, "AudioSampleRate")) {
ffserver_get_arg(arg, sizeof(arg), p);
- config->audio_enc.sample_rate = atoi(arg);
+ av_dict_set(&config->audio_conf, cmd, arg, 0);
} 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;
+ av_dict_set_int(&config->video_conf, "VideoBitRateRangeMin",
minrate * 1000, 0);
+ av_dict_set_int(&config->video_conf, "VideoBitRateRangeMax",
maxrate * 1000, 0);
} 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);
- } else if (!av_strcasecmp(cmd, "Strict")) {
- ffserver_get_arg(arg, sizeof(arg), p);
- config->video_enc.strict_std_compliance = atoi(arg);
- } else if (!av_strcasecmp(cmd, "VideoBufferSize")) {
- ffserver_get_arg(arg, sizeof(arg), p);
- config->video_enc.rc_buffer_size = atoi(arg) * 8*1024;
- } else if (!av_strcasecmp(cmd, "VideoBitRateTolerance")) {
- ffserver_get_arg(arg, sizeof(arg), p);
- config->video_enc.bit_rate_tolerance = atoi(arg) * 1000;
- } else if (!av_strcasecmp(cmd, "VideoBitRate")) {
- ffserver_get_arg(arg, sizeof(arg), p);
- config->video_enc.bit_rate = atoi(arg) * 1000;
+ } else if (!av_strcasecmp(cmd, "Debug") ||
+ !av_strcasecmp(cmd, "Strict") ||
+ !av_strcasecmp(cmd, "VideoBufferSize") ||
+ !av_strcasecmp(cmd, "VideoBitRateTolerance") ||
+ !av_strcasecmp(cmd, "VideoBitRate") ||
+ !av_strcasecmp(cmd, "VideoGopSize") ||
+ !av_strcasecmp(cmd, "Qscale") ||
+ !av_strcasecmp(cmd, "LumiMask") ||
+ !av_strcasecmp(cmd, "DarkMask")){
+ ffserver_get_arg(arg, sizeof(arg), p);
+ av_dict_set(&config->video_conf, cmd, arg, 0);
} 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");
+ av_dict_set_int(&config->video_conf, "VideoSizeWidth", w, 0);
+ av_dict_set_int(&config->video_conf, "VideoSizeHeight", h, 0);
} 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;
+ av_dict_set_int(&config->video_conf, "VideoFrameRateNum",
frame_rate.num, 0);
+ av_dict_set_int(&config->video_conf, "VideoFrameRateDen",
frame_rate.den, 0);
}
} 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);
- } else if (!av_strcasecmp(cmd, "VideoGopSize")) {
- ffserver_get_arg(arg, sizeof(arg), p);
- config->video_enc.gop_size = atoi(arg);
+ av_dict_set_int(&config->video_conf, cmd, pix_fmt, 0);
} else if (!av_strcasecmp(cmd, "VideoIntraOnly")) {
- config->video_enc.gop_size = 1;
- } else if (!av_strcasecmp(cmd, "VideoHighQuality")) {
- config->video_enc.mb_decision = FF_MB_DECISION_BITS;
- } else if (!av_strcasecmp(cmd, "Video4MotionVector")) {
- config->video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
- config->video_enc.flags |= CODEC_FLAG_4MV;
+ av_dict_set(&config->video_conf, "VideoGopSize", "1", 0);
} 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;
+ av_dict_set(dict, arg, arg2, 0);
} 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]);
- } else if (!av_strcasecmp(cmd, "BitExact")) {
- config->video_enc.flags |= CODEC_FLAG_BITEXACT;
- } else if (!av_strcasecmp(cmd, "DctFastint")) {
- config->video_enc.dct_algo = FF_DCT_FASTINT;
- } else if (!av_strcasecmp(cmd, "IdctSimple")) {
- config->video_enc.idct_algo = FF_IDCT_SIMPLE;
- } 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);
- } 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");
- } 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");
- } 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");
- } else if (!av_strcasecmp(cmd, "LumiMask")) {
- ffserver_get_arg(arg, sizeof(arg), p);
- config->video_enc.lumi_masking = atof(arg);
- } else if (!av_strcasecmp(cmd, "DarkMask")) {
- ffserver_get_arg(arg, sizeof(arg), p);
- config->video_enc.dark_masking = atof(arg);
+ av_dict_set(&config->video_conf, "VideoTag", "arg", 0);
+ else
+ ERROR("Invalid VideoTag %s\n", arg);
+ } else if (!av_strcasecmp(cmd, "BitExact") ||
+ !av_strcasecmp(cmd, "DctFastint") ||
+ !av_strcasecmp(cmd, "IdctSimple") ||
+ !av_strcasecmp(cmd, "VideoHighQuality") ||
+ !av_strcasecmp(cmd, "Video4MotionVector")) {
+ av_dict_set(&config->video_conf, cmd, "", 0);
+ } else if (!av_strcasecmp(cmd, "VideoQDiff") ||
+ !av_strcasecmp(cmd, "VideoQMin") ||
+ !av_strcasecmp(cmd, "VideoQMax")) {
+ int val;
+ ffserver_get_arg(arg, sizeof(arg), p);
+ val = atoi(arg);
+ if (val < 1 || val > 31)
+ ERROR("%s out of range\n", cmd);
+ else
+ av_dict_set(&config->video_conf, cmd, arg, 0);
} else if (!av_strcasecmp(cmd, "NoVideo")) {
config->video_id = AV_CODEC_ID_NONE;
} else if (!av_strcasecmp(cmd, "NoAudio")) {
@@ -797,16 +841,28 @@ 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("AVPreset error: %s\n", arg);
+ ffserver_apply_stream_config(audio_enc, config->audio_conf,
&config->audio_opts);
+ 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("AVPreset error: %s\n", arg);
+ ffserver_apply_stream_config(video_enc, config->video_conf,
&config->video_opts);
+ 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);
*pstream = NULL;
} else if (!av_strcasecmp(cmd, "File") || !av_strcasecmp(cmd,
"ReadOnlyFile")) {
ffserver_get_arg(stream->feed_filename, sizeof(stream->feed_filename),
p);
diff --git a/ffserver_config.h b/ffserver_config.h
index 36d61d0..234e91a 100644
--- a/ffserver_config.h
+++ b/ffserver_config.h
@@ -107,11 +107,14 @@ typedef struct FFServerConfig {
int errors;
int warnings;
// Following variables MUST NOT be used outside configuration parsing code.
- AVCodecContext audio_enc;
- AVCodecContext video_enc;
enum AVCodecID audio_id;
enum AVCodecID video_id;
-
+ AVDictionary *video_opts;
+ AVDictionary *video_conf;
+ AVDictionary *audio_opts;
+ AVDictionary *audio_conf;
+ char *video_preset;
+ char *audio_preset;
} FFServerConfig;
void ffserver_get_arg(char *buf, int buf_size, const char **pp);
--
1.9.1
_______________________________________________
ffmpeg-devel mailing list
[email protected]
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel