diff --git a/doc/ffserver.conf b/doc/ffserver.conf index 2fbfecd683..217117005c 100644 --- a/doc/ffserver.conf +++ b/doc/ffserver.conf @@ -306,6 +306,27 @@ StartSendOnKey # +# Transcode an incoming live feed to another live feed, +# using libx264 and video presets + +# +#Format rtp +#Feed feed1.ffm +#VideoCodec libx264 +#VideoFrameRate 24 +#VideoBitRate 100 +#VideoSize 480x272 +#AVPresetVideo default +#AVPresetVideo baseline +#AVOptionVideo flags +global_header +# +#AudioCodec libfaac +#AudioBitRate 32 +#AudioChannels 2 +#AudioSampleRate 22050 +#AVOptionAudio flags +global_header +# + ################################################################## # SDP/multicast examples # diff --git a/ffserver.c b/ffserver.c index 6d1e710c6a..77340af3df 100644 --- a/ffserver.c +++ b/ffserver.c @@ -3949,6 +3949,65 @@ static int ffserver_opt_default(const char *opt, const char *arg, return ret; } +static int ffserver_opt_preset(const char *arg, + AVCodecContext *avctx, int type, + enum CodecID *audio_id, enum CodecID *video_id) +{ + FILE *f=NULL; + char filename[1000], tmp[1000], tmp2[1000], line[1000]; + int i, ret = 0; + const char *base[3]= { getenv("FFMPEG_DATADIR"), + getenv("HOME"), + FFMPEG_DATADIR, + }; + + for(i=0; i<3 && !f; i++){ + if(!base[i]) + continue; + snprintf(filename, sizeof(filename), "%s%s/%s.ffpreset", base[i], i != 1 ? "" : "/.ffmpeg", arg); + f= fopen(filename, "r"); + if(!f){ + AVCodec *codec = avcodec_find_encoder(avctx->codec_id); + if (codec) { + snprintf(filename, sizeof(filename), "%s%s/%s-%s.ffpreset", base[i], i != 1 ? "" : "/.ffmpeg", codec->name, arg); + f= fopen(filename, "r"); + } + } + } + + if(!f){ + fprintf(stderr, "File for preset '%s' not found\n", arg); + return 1; + } + + while(!feof(f)){ + int e= fscanf(f, "%999[^\n]\n", line) - 1; + if(line[0] == '#' && !e) + continue; + e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2; + if(e){ + fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line); + ret = 1; + break; + } + if(!strcmp(tmp, "acodec")){ + *audio_id = opt_audio_codec(tmp2); + }else if(!strcmp(tmp, "vcodec")){ + *video_id = opt_video_codec(tmp2); + }else if(!strcmp(tmp, "scodec")){ + /* opt_subtitle_codec(tmp2); */ + }else if(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; + } + } + + fclose(f); + + return ret; +} + static AVOutputFormat *ffserver_guess_format(const char *short_name, const char *filename, const char *mime_type) { @@ -4403,6 +4462,23 @@ static int parse_ffconfig(const char *filename) if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) { ERROR("AVOption error: %s %s\n", arg, arg2); } + } else if (!strcasecmp(cmd, "AVPresetVideo") || + !strcasecmp(cmd, "AVPresetAudio")) { + AVCodecContext *avctx; + int type; + get_arg(arg, sizeof(arg), &p); + if (!strcasecmp(cmd, "AVPresetVideo")) { + avctx = &video_enc; + video_enc.codec_id = video_id; + type = AV_OPT_FLAG_VIDEO_PARAM; + } else { + avctx = &audio_enc; + audio_enc.codec_id = audio_id; + type = AV_OPT_FLAG_AUDIO_PARAM; + } + if (ffserver_opt_preset(arg, avctx, type|AV_OPT_FLAG_ENCODING_PARAM, &audio_id, &video_id)) { + ERROR("AVPreset error: %s\n", arg); + } } else if (!strcasecmp(cmd, "VideoTag")) { get_arg(arg, sizeof(arg), &p); if ((strlen(arg) == 4) && stream)