Commits

Francesco Romani  committed 409adbc

[ffmpeg][encoder] initial preset support.

Using transcode with ffmpeg+h264 causes some trouble with libx264 erroring out
with "broken ffmpeg default settings detected". libx264 further suggests: "use
an encoding preset (vpre)".

The attached patch adds two config parameters to ffmpeg.cfg, "vpre" and
"ffmpeg_datadir".
The former sets the video presets and accepts a comma separated list, e.g.
"fast,baseline", default being "medium".
The latter is used to determine the path to the system ffmpeg presets, default
being "/usr/share/ffmpeg".
These presets are used to override the broken ffmpeg default values.

Important: I had to load the presets after the setup of all other options
(thus any transcode options will be overriden by any presets loaded), because
apparently transcode's internal defaults (which are in turn used to override
ffmpeg's defaults) are just as broken as the ffmpeg ones... (See the FIXME I
included)

Licensing and origin: All code (almost) is copied from ffmpeg-0.5_p22846,
files ffmpeg.c and cmdutils.c.

Known issues: None known, but the code may be buggy, especially since it is
only ripped from ffmpeg.
In the beginning I had some trouble with uninitialised pointers. This should
be fixed now (see the GLUE comments), but I don't guarantee for anything.

Patch courtesy of Dennis Schridde <devurandom <at> gmx.net>

  • Participants
  • Parent commits 03f84cf
  • Branches transcode-1_1

Comments (0)

Files changed (3)

File export/export_ffmpeg.c

     return -10.0 * log(d) / log(10);
 }
 
+
+// Could be using GNU extension 'strchrnul' instead:
+static char *tc_strchrnul(const char *s, int c) {
+	char *tmp = strchr(s, c);
+	if (tmp == NULL) {
+		tmp = s + strlen(s);
+	}
+	return tmp;
+}
+
+
+/* START: COPIED FROM ffmpeg-0.5_p22846(ffmpeg.c, cmdutils.c) */
+#include <libavcodec/opt.h>
+#include <libavutil/avstring.h>
+#include <libswscale/swscale.h>
+
+/* GLUE: */
+#define FFMPEG_DATADIR lavc_param_ffmpeg_datadir
+
+/* GLUE: */
+static AVCodecContext *avcodec_opts[AVMEDIA_TYPE_NB] = {NULL};
+
+static // GLUE
+const char **opt_names;
+static int opt_name_count;
+
+static char  *audio_codec_name = NULL;
+static char *subtitle_codec_name = NULL;
+static char *video_codec_name = NULL;
+static int audio_stream_copy = 0;
+static int video_stream_copy = 0;
+static int subtitle_stream_copy = 0;
+
+static int av_exit(int ret)
+{
+    av_free(opt_names);
+
+    av_free(video_codec_name);
+    av_free(audio_codec_name);
+    av_free(subtitle_codec_name);
+
+    exit(ret); /* not all OS-es handle main() return value */
+    return ret;
+}
+
+static void opt_codec(int *pstream_copy, char **pcodec_name,
+                      int codec_type, const char *arg)
+{
+    av_freep(pcodec_name);
+    if (!strcmp(arg, "copy")) {
+        *pstream_copy = 1;
+    } else {
+        *pcodec_name = av_strdup(arg);
+    }
+}
+
+static void opt_audio_codec(const char *arg)
+{
+    opt_codec(&audio_stream_copy, &audio_codec_name, AVMEDIA_TYPE_AUDIO, arg);
+}
+
+static void opt_video_codec(const char *arg)
+{
+    opt_codec(&video_stream_copy, &video_codec_name, AVMEDIA_TYPE_VIDEO, arg);
+}
+
+static void opt_subtitle_codec(const char *arg)
+{
+    opt_codec(&subtitle_stream_copy, &subtitle_codec_name, AVMEDIA_TYPE_SUBTITLE, arg);
+}
+
+static
+int opt_default(const char *opt, const char *arg){
+    int type;
+    int ret= 0;
+    const AVOption *o= NULL;
+    int opt_types[]={AV_OPT_FLAG_VIDEO_PARAM, AV_OPT_FLAG_AUDIO_PARAM, 0, AV_OPT_FLAG_SUBTITLE_PARAM, 0};
+
+    for(type=0; type<AVMEDIA_TYPE_NB && ret>= 0; type++){
+		/* GLUE: +if */
+		if (type == AVMEDIA_TYPE_VIDEO) {
+        const AVOption *o2 = av_find_opt(avcodec_opts[0], opt, NULL, opt_types[type], opt_types[type]);
+        if(o2)
+            ret = av_set_string3(avcodec_opts[type], opt, arg, 1, &o);
+		/* GLUE: +if */
+		}
+    }
+    /* GLUE: disabling
+    if(!o)
+        ret = av_set_string3(avformat_opts, opt, arg, 1, &o);
+    if(!o && sws_opts)
+        ret = av_set_string3(sws_opts, opt, arg, 1, &o);
+	*/
+    if(!o){
+		/* GLUE: disabling
+        if(opt[0] == 'a')
+            ret = av_set_string3(avcodec_opts[AVMEDIA_TYPE_AUDIO], opt+1, arg, 1, &o);
+        else */ if(opt[0] == 'v')
+            ret = av_set_string3(avcodec_opts[AVMEDIA_TYPE_VIDEO], opt+1, arg, 1, &o);
+		/* GLUE: disabling
+        else if(opt[0] == 's')
+            ret = av_set_string3(avcodec_opts[AVMEDIA_TYPE_SUBTITLE], opt+1, arg, 1, &o);
+		*/
+    }
+    if (o && ret < 0) {
+        fprintf(stderr, "Invalid value '%s' for option '%s'\n", arg, opt);
+        exit(1);
+    }
+    if (!o) {
+        fprintf(stderr, "Unrecognized option '%s'\n", opt);
+        exit(1);
+    }
+
+//    av_log(NULL, AV_LOG_ERROR, "%s:%s: %f 0x%0X\n", opt, arg, av_get_double(avcodec_opts, opt, NULL), (int)av_get_int(avcodec_opts, opt, NULL));
+
+    //FIXME we should always use avcodec_opts, ... for storing options so there will not be any need to keep track of what i set over this
+    opt_names= av_realloc(opt_names, sizeof(void*)*(opt_name_count+1));
+    opt_names[opt_name_count++]= o->name;
+
+	/* GLUE: disabling
+    if(avcodec_opts[0]->debug || avformat_opts->debug)
+        av_log_set_level(AV_LOG_DEBUG);
+	*/
+    return 0;
+}
+
+static int opt_preset(const char *opt, const char *arg)
+{
+    FILE *f=NULL;
+    char filename[1000], tmp[1000], tmp2[1000], line[1000];
+    int i;
+    const char *base[2]= { getenv("HOME"),
+                           FFMPEG_DATADIR,
+                         };
+
+    if (*opt != 'f') {
+        for(i=!base[0]; i<2 && !f; i++){
+            snprintf(filename, sizeof(filename), "%s%s/%s.ffpreset", base[i], i ? "" : "/.ffmpeg", arg);
+            f= fopen(filename, "r");
+            if(!f){
+                char *codec_name= *opt == 'v' ? video_codec_name :
+                                  *opt == 'a' ? audio_codec_name :
+                                                subtitle_codec_name;
+                snprintf(filename, sizeof(filename), "%s%s/%s-%s.ffpreset", base[i],  i ? "" : "/.ffmpeg", codec_name, arg);
+                f= fopen(filename, "r");
+            }
+        }
+    } else {
+        av_strlcpy(filename, arg, sizeof(filename));
+        f= fopen(filename, "r");
+    }
+
+    if(!f){
+        fprintf(stderr, "File for preset '%s' not found\n", arg);
+        av_exit(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);
+            av_exit(1);
+        }
+        if(!strcmp(tmp, "acodec")){
+            opt_audio_codec(tmp2);
+        }else if(!strcmp(tmp, "vcodec")){
+            opt_video_codec(tmp2);
+        }else if(!strcmp(tmp, "scodec")){
+            opt_subtitle_codec(tmp2);
+        }else if(opt_default(tmp, tmp2) < 0){
+            fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2);
+            av_exit(1);
+        }
+    }
+
+    fclose(f);
+
+    return 0;
+}
+/* END: COPIED FROM ffmpeg-0.5_p22846(ffmpeg.c, cmdutils.c) */
+
+
 /* ------------------------------------------------------------
  *
  * init codec
 
     lavc_venc_context->me_method = ME_ZERO + lavc_param_vme;
 
+
+	/* FIXME: transcode itself contains "broken ffmpeg default settings", thus we need to override them! */
+	if (lavc_param_video_preset) {
+		avcodec_opts[AVMEDIA_TYPE_VIDEO] = lavc_venc_context;
+		video_codec_name = ffmpeg_codec_name(codec->name);
+
+		const char *preset_start = lavc_param_video_preset;
+		while (preset_start) {
+			const char *preset_end = tc_strchrnul(preset_start, ',');
+			char preset_name[255] = {'\0'};
+
+			if (strncpy(preset_name, preset_start, preset_end-preset_start) != preset_name) {
+				tc_log_warn(MOD_NAME, "Extracting preset name failed");
+				return TC_EXPORT_ERROR;
+			}
+
+			if (verbose) {
+				tc_log_info(MOD_NAME, "Parsing ffmpeg preset '%s'", preset_name);
+			}
+			if (opt_preset("vpre", preset_name) != 0) {
+				tc_log_warn(MOD_NAME, "Parsing ffmpeg preset '%s' failed", preset_name);
+			}
+			if (verbose) {
+				int i;
+				tc_log_info(MOD_NAME, "After parsing preset '%s', %i options are overridden:", preset_name, opt_name_count);
+				for (i=0; i < opt_name_count; i++)
+					tc_log_info(MOD_NAME, "-- %s", opt_names[i]);
+			}
+
+			if (*preset_end != '\0') {
+				preset_start = preset_end+1;
+			}
+			else {
+				preset_start = NULL;
+			}
+		}
+	}
+
+
     //-- open codec --
     //----------------
     TC_LOCK_LIBAVCODEC;

File export/ffmpeg_cfg.c

 //int lavc_param_atag = 0;
 //int lavc_param_abitrate = 224;
 
+char *lavc_param_video_preset = "medium";
+char *lavc_param_ffmpeg_datadir = "/usr/share/ffmpeg";
+
 TCConfigEntry lavcopts_conf[]={
 //    {"acodec", &lavc_param_acodec, TCCONF_TYPE_STRING, 0, 0, 0},
 //    {"abitrate", &lavc_param_abitrate, TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 1, 1000},
     {"skip_top", &lavc_param_skip_top, TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 1000},
     {"skip_bottom", &lavc_param_skip_bottom, TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 1000},
     {"fps_code", &lavc_param_fps_code, TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 9},
+    {"vpre", &lavc_param_video_preset, TCCONF_TYPE_STRING, 0, 0, 0},
+    {"ffmpeg_datadir", &lavc_param_ffmpeg_datadir, TCCONF_TYPE_STRING, 0, 0, 0},
     {NULL, NULL, 0, 0, 0, 0}
 };

File export/ffmpeg_cfg.h

 extern int lavc_param_skip_top;
 extern int lavc_param_skip_bottom;
 
+extern char *lavc_param_video_preset;
+extern char *lavc_param_ffmpeg_datadir;
+
 extern TCConfigEntry lavcopts_conf[];
 
 #endif