ffserver_config: postpone codec context creation

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 is gathered.

Partially fixes #1275
pull/71/merge
Lukasz Marek 10 years ago
parent fe72622819
commit ed1f8915da
  1. 386
      ffserver_config.c
  2. 9
      ffserver_config.h

@ -18,6 +18,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include <float.h>
#include "libavutil/opt.h" #include "libavutil/opt.h"
#include "libavutil/parseutils.h" #include "libavutil/parseutils.h"
#include "libavutil/avstring.h" #include "libavutil/avstring.h"
@ -238,9 +239,8 @@ static void add_codec(FFServerStream *stream, AVCodecContext *av)
st = av_mallocz(sizeof(AVStream)); st = av_mallocz(sizeof(AVStream));
if (!st) if (!st)
return; return;
st->codec = avcodec_alloc_context3(NULL); st->codec = av;
stream->streams[stream->nb_streams++] = st; stream->streams[stream->nb_streams++] = st;
memcpy(st->codec, av, sizeof(AVCodecContext));
} }
static enum AVCodecID opt_codec(const char *name, enum AVMediaType type) 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; FILE *f=NULL;
char filename[1000], tmp[1000], tmp2[1000], line[1000]; char filename[1000], tmp[1000], tmp2[1000], line[1000];
int ret = 0; 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, if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
codec ? codec->name : NULL))) { codec ? codec->name : NULL))) {
fprintf(stderr, "File for preset '%s' not found\n", arg); fprintf(stderr, "File for preset '%s' not found\n", arg);
return 1; return AVERROR(EINVAL);
} }
while(!feof(f)){ 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; e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2;
if(e){ if(e){
fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line); fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line);
ret = 1; ret = AVERROR(EINVAL);
break; break;
} }
if(!strcmp(tmp, "acodec")){ if (audio_id && !strcmp(tmp, "acodec")) {
*audio_id = opt_codec(tmp2, AVMEDIA_TYPE_AUDIO); *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); *video_id = opt_codec(tmp2, AVMEDIA_TYPE_VIDEO);
}else if(!strcmp(tmp, "scodec")){ } else if(!strcmp(tmp, "scodec")) {
/* opt_subtitle_codec(tmp2); */ /* 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); fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2);
ret = 1;
break; break;
} }
} }
@ -323,15 +325,78 @@ static AVOutputFormat *ffserver_guess_format(const char *short_name, const char
return fmt; 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, ...) static void report_config_error(const char *filename, int line_num, int log_level, int *errors, const char *fmt, ...)
{ {
va_list vl; va_list vl;
va_start(vl, fmt); va_start(vl, fmt);
av_log(NULL, log_level, "%s:%d: ", filename, line_num); vreport_config_error(filename, line_num, log_level, errors, fmt, vl);
av_vlog(NULL, log_level, fmt, vl);
va_end(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__) #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; 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, static int ffserver_parse_config_stream(FFServerConfig *config, const char *cmd, const char **p,
int line_num, FFServerStream **pstream) 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); 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) { if (stream->fmt) {
config->audio_id = stream->fmt->audio_codec; config->audio_id = stream->fmt->audio_codec;
config->video_id = stream->fmt->video_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; *pstream = stream;
return 0; return 0;
@ -587,23 +731,20 @@ static int ffserver_parse_config_stream(FFServerConfig *config, const char *cmd,
!av_strcasecmp(cmd, "Copyright") || !av_strcasecmp(cmd, "Copyright") ||
!av_strcasecmp(cmd, "Title")) { !av_strcasecmp(cmd, "Title")) {
char key[32]; char key[32];
int i, ret; int i;
ffserver_get_arg(arg, sizeof(arg), p); ffserver_get_arg(arg, sizeof(arg), p);
for (i = 0; i < strlen(cmd); i++) for (i = 0; i < strlen(cmd); i++)
key[i] = av_tolower(cmd[i]); key[i] = av_tolower(cmd[i]);
key[i] = 0; key[i] = 0;
WARNING("'%s' option in configuration file is deprecated, " WARNING("'%s' option in configuration file is deprecated, "
"use 'Metadata %s VALUE' instead\n", cmd, key); "use 'Metadata %s VALUE' instead\n", cmd, key);
if ((ret = av_dict_set(&stream->metadata, key, arg, 0)) < 0) if (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)); goto nomem;
} else if (!av_strcasecmp(cmd, "Metadata")) { } else if (!av_strcasecmp(cmd, "Metadata")) {
int ret;
ffserver_get_arg(arg, sizeof(arg), p); ffserver_get_arg(arg, sizeof(arg), p);
ffserver_get_arg(arg2, sizeof(arg2), p); ffserver_get_arg(arg2, sizeof(arg2), p);
if ((ret = av_dict_set(&stream->metadata, arg, arg2, 0)) < 0) { if (av_dict_set(&stream->metadata, arg, arg2, 0) < 0)
ERROR("Could not set metadata '%s' to value '%s': %s\n", goto nomem;
arg, arg2, av_err2str(ret));
}
} else if (!av_strcasecmp(cmd, "Preroll")) { } else if (!av_strcasecmp(cmd, "Preroll")) {
ffserver_get_arg(arg, sizeof(arg), p); ffserver_get_arg(arg, sizeof(arg), p);
stream->prebuffer = atof(arg) * 1000; 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; stream->max_time = atof(arg) * 1000;
} else if (!av_strcasecmp(cmd, "AudioBitRate")) { } else if (!av_strcasecmp(cmd, "AudioBitRate")) {
ffserver_get_arg(arg, sizeof(arg), p); 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")) { } else if (!av_strcasecmp(cmd, "AudioChannels")) {
ffserver_get_arg(arg, sizeof(arg), p); 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")) { } else if (!av_strcasecmp(cmd, "AudioSampleRate")) {
ffserver_get_arg(arg, sizeof(arg), p); 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")) { } else if (!av_strcasecmp(cmd, "VideoBitRateRange")) {
int minrate, maxrate; int minrate, maxrate;
ffserver_get_arg(arg, sizeof(arg), p); ffserver_get_arg(arg, sizeof(arg), p);
if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) { if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
config->video_enc.rc_min_rate = minrate * 1000; if (av_dict_set_int(&config->video_conf, "VideoBitRateRangeMin", minrate, 0) < 0 ||
config->video_enc.rc_max_rate = maxrate * 1000; av_dict_set_int(&config->video_conf, "VideoBitRateRangeMax", maxrate, 0) < 0)
goto nomem;
} else } else
ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg); ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg);
} else if (!av_strcasecmp(cmd, "Debug")) { } else if (!av_strcasecmp(cmd, "Debug")) {
ffserver_get_arg(arg, sizeof(arg), p); 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")) { } else if (!av_strcasecmp(cmd, "Strict")) {
ffserver_get_arg(arg, sizeof(arg), p); 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")) { } else if (!av_strcasecmp(cmd, "VideoBufferSize")) {
ffserver_get_arg(arg, sizeof(arg), p); 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")) { } else if (!av_strcasecmp(cmd, "VideoBitRateTolerance")) {
ffserver_get_arg(arg, sizeof(arg), p); 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")) { } else if (!av_strcasecmp(cmd, "VideoBitRate")) {
ffserver_get_arg(arg, sizeof(arg), p); 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")) { } else if (!av_strcasecmp(cmd, "VideoSize")) {
int ret; int ret, w, h;
ffserver_get_arg(arg, sizeof(arg), p); 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) if (ret < 0)
ERROR("Invalid video size '%s'\n", arg); 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"); 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")) { } else if (!av_strcasecmp(cmd, "VideoFrameRate")) {
AVRational frame_rate; AVRational frame_rate;
ffserver_get_arg(arg, sizeof(arg), p); ffserver_get_arg(arg, sizeof(arg), p);
if (av_parse_video_rate(&frame_rate, arg) < 0) { if (av_parse_video_rate(&frame_rate, arg) < 0) {
ERROR("Incorrect frame rate: %s\n", arg); ERROR("Incorrect frame rate: %s\n", arg);
} else { } else {
config->video_enc.time_base.num = frame_rate.den; if (av_dict_set_int(&config->video_conf, "VideoFrameRateNum", frame_rate.num, 0) < 0 ||
config->video_enc.time_base.den = frame_rate.num; av_dict_set_int(&config->video_conf, "VideoFrameRateDen", frame_rate.den, 0) < 0)
goto nomem;
} }
} else if (!av_strcasecmp(cmd, "PixelFormat")) { } else if (!av_strcasecmp(cmd, "PixelFormat")) {
enum AVPixelFormat pix_fmt;
ffserver_get_arg(arg, sizeof(arg), p); ffserver_get_arg(arg, sizeof(arg), p);
config->video_enc.pix_fmt = av_get_pix_fmt(arg); pix_fmt = av_get_pix_fmt(arg);
if (config->video_enc.pix_fmt == AV_PIX_FMT_NONE) if (pix_fmt == AV_PIX_FMT_NONE)
ERROR("Unknown pixel format: %s\n", arg); 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")) { } else if (!av_strcasecmp(cmd, "VideoGopSize")) {
ffserver_get_arg(arg, sizeof(arg), p); 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")) { } 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")) { } 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")) { } else if (!av_strcasecmp(cmd, "Video4MotionVector")) {
config->video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove if (av_dict_set(&config->video_conf, cmd, "", 0) < 0)
config->video_enc.flags |= CODEC_FLAG_4MV; goto nomem;
} else if (!av_strcasecmp(cmd, "AVOptionVideo") || } else if (!av_strcasecmp(cmd, "AVOptionVideo") ||
!av_strcasecmp(cmd, "AVOptionAudio")) { !av_strcasecmp(cmd, "AVOptionAudio")) {
AVCodecContext *avctx; AVDictionary **dict;
int type;
ffserver_get_arg(arg, sizeof(arg), p); ffserver_get_arg(arg, sizeof(arg), p);
ffserver_get_arg(arg2, sizeof(arg2), p); ffserver_get_arg(arg2, sizeof(arg2), p);
if (!av_strcasecmp(cmd, "AVOptionVideo")) { if (!av_strcasecmp(cmd, "AVOptionVideo"))
avctx = &config->video_enc; dict = &config->video_opts;
type = AV_OPT_FLAG_VIDEO_PARAM; else
} else { dict = &config->audio_opts;
avctx = &config->audio_enc; if (av_dict_set(dict, arg, arg2, 0) < 0)
type = AV_OPT_FLAG_AUDIO_PARAM; goto nomem;
}
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);
}
} else if (!av_strcasecmp(cmd, "AVPresetVideo") || } else if (!av_strcasecmp(cmd, "AVPresetVideo") ||
!av_strcasecmp(cmd, "AVPresetAudio")) { !av_strcasecmp(cmd, "AVPresetAudio")) {
AVCodecContext *avctx; char **preset = NULL;
int type;
ffserver_get_arg(arg, sizeof(arg), p); ffserver_get_arg(arg, sizeof(arg), p);
if (!av_strcasecmp(cmd, "AVPresetVideo")) { if (!av_strcasecmp(cmd, "AVPresetVideo")) {
avctx = &config->video_enc; preset = &config->video_preset;
config->video_enc.codec_id = config->video_id; ffserver_opt_preset(arg, NULL, 0, NULL, &config->video_id);
type = AV_OPT_FLAG_VIDEO_PARAM;
} else { } else {
avctx = &config->audio_enc; preset = &config->audio_preset;
config->audio_enc.codec_id = config->audio_id; ffserver_opt_preset(arg, NULL, 0, &config->audio_id, NULL);
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 = av_strdup(arg);
if (!preset)
return AVERROR(ENOMEM);
} else if (!av_strcasecmp(cmd, "VideoTag")) { } else if (!av_strcasecmp(cmd, "VideoTag")) {
ffserver_get_arg(arg, sizeof(arg), p); ffserver_get_arg(arg, sizeof(arg), p);
if (strlen(arg) == 4) if (strlen(arg) == 4) {
config->video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]); if (av_dict_set(&config->video_conf, "VideoTag", "arg", 0) < 0)
goto nomem;
}
} else if (!av_strcasecmp(cmd, "BitExact")) { } 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")) { } 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")) { } 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")) { } else if (!av_strcasecmp(cmd, "Qscale")) {
ffserver_get_arg(arg, sizeof(arg), p); ffserver_get_arg(arg, sizeof(arg), p);
config->video_enc.flags |= CODEC_FLAG_QSCALE; if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0)
config->video_enc.global_quality = FF_QP2LAMBDA * atoi(arg); goto nomem;
} else if (!av_strcasecmp(cmd, "VideoQDiff")) { } else if (!av_strcasecmp(cmd, "VideoQDiff")) {
ffserver_get_arg(arg, sizeof(arg), p); ffserver_get_arg(arg, sizeof(arg), p);
config->video_enc.max_qdiff = atoi(arg); ffserver_set_int_param(NULL, arg, 0, 1, 31, config, line_num, "%s out of range\n", cmd);
if (config->video_enc.max_qdiff < 1 || config->video_enc.max_qdiff > 31) if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0)
ERROR("VideoQDiff out of range\n"); goto nomem;
} else if (!av_strcasecmp(cmd, "VideoQMax")) { } else if (!av_strcasecmp(cmd, "VideoQMax")) {
ffserver_get_arg(arg, sizeof(arg), p); ffserver_get_arg(arg, sizeof(arg), p);
config->video_enc.qmax = atoi(arg); ffserver_set_int_param(NULL, arg, 0, 1, 31, config, line_num, "%s out of range\n", cmd);
if (config->video_enc.qmax < 1 || config->video_enc.qmax > 31) if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0)
ERROR("VideoQMax out of range\n"); goto nomem;
} else if (!av_strcasecmp(cmd, "VideoQMin")) { } else if (!av_strcasecmp(cmd, "VideoQMin")) {
ffserver_get_arg(arg, sizeof(arg), p); ffserver_get_arg(arg, sizeof(arg), p);
config->video_enc.qmin = atoi(arg); ffserver_set_int_param(NULL, arg, 0, 1, 31, config, line_num, "%s out of range\n", cmd);
if (config->video_enc.qmin < 1 || config->video_enc.qmin > 31) if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0)
ERROR("VideoQMin out of range\n"); goto nomem;
} else if (!av_strcasecmp(cmd, "LumiMask")) { } else if (!av_strcasecmp(cmd, "LumiMask")) {
ffserver_get_arg(arg, sizeof(arg), p); 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")) { } else if (!av_strcasecmp(cmd, "DarkMask")) {
ffserver_get_arg(arg, sizeof(arg), p); 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")) { } else if (!av_strcasecmp(cmd, "NoVideo")) {
config->video_id = AV_CODEC_ID_NONE; config->video_id = AV_CODEC_ID_NONE;
} else if (!av_strcasecmp(cmd, "NoAudio")) { } 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>")) { } else if (!av_strcasecmp(cmd, "</Stream>")) {
if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) { if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
if (config->audio_id != AV_CODEC_ID_NONE) { if (config->audio_id != AV_CODEC_ID_NONE) {
config->audio_enc.codec_type = AVMEDIA_TYPE_AUDIO; AVCodecContext *audio_enc = avcodec_alloc_context3(avcodec_find_encoder(config->audio_id));
config->audio_enc.codec_id = config->audio_id; if (config->audio_preset &&
add_codec(stream, &config->audio_enc); 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) { if (config->video_id != AV_CODEC_ID_NONE) {
config->video_enc.codec_type = AVMEDIA_TYPE_VIDEO; AVCodecContext *video_enc = avcodec_alloc_context3(avcodec_find_encoder(config->video_id));
config->video_enc.codec_id = config->video_id; if (config->video_preset &&
add_codec(stream, &config->video_enc); 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; *pstream = NULL;
} else if (!av_strcasecmp(cmd, "File") || !av_strcasecmp(cmd, "ReadOnlyFile")) { } else if (!av_strcasecmp(cmd, "File") || !av_strcasecmp(cmd, "ReadOnlyFile")) {
ffserver_get_arg(stream->feed_filename, sizeof(stream->feed_filename), p); 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); ERROR("Invalid entry '%s' inside <Stream></Stream>\n", cmd);
} }
return 0; 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, static int ffserver_parse_config_redirect(FFServerConfig *config, const char *cmd, const char **p,

@ -107,11 +107,14 @@ typedef struct FFServerConfig {
int errors; int errors;
int warnings; int warnings;
// Following variables MUST NOT be used outside configuration parsing code. // Following variables MUST NOT be used outside configuration parsing code.
AVCodecContext audio_enc;
AVCodecContext video_enc;
enum AVCodecID audio_id; enum AVCodecID audio_id;
enum AVCodecID video_id; enum AVCodecID video_id;
AVDictionary *video_opts; /* AVOptions for video encoder */
AVDictionary *video_conf; /* Values stored in video AVCodecContext.fields */
AVDictionary *audio_opts; /* AVOptions for audio encoder */
AVDictionary *audio_conf; /* Values stored in audio AVCodecContext.fields */
char *video_preset;
char *audio_preset;
} FFServerConfig; } FFServerConfig;
void ffserver_get_arg(char *buf, int buf_size, const char **pp); void ffserver_get_arg(char *buf, int buf_size, const char **pp);

Loading…
Cancel
Save