On date Monday 2014-10-20 23:57:02 +0200, Lukasz Marek encoded: > 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); \ > + } \ > + } In case of empty string errno is not set, so you should do: if (eptr[0]) ret = AVERROR(EINVAL); same below. Also eptr is a bit confusing, I'd prefer tailp[tr] or simply p[tr]. > +#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); Not sure, but looks like you're not moving the remaining options from conf to opts. > + 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); > + I'd prefer to exit in this case, after listening all the invalid values, since otherwise the user will ignore the error. > + 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"); Note: you can probably drop this check (but not in this patch). > + 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); Meaningful error message please, something like: "Could not apply preset '%s'. (Note: preset system is currently braindead and should be probably removed altogether). > + 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; please annotate what's the difference between options and configuration, which is a bit confused/ing. [...] -- FFmpeg = Free and Foolish Magnificient Plastic Educated Gnome _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel