[PATCH] ffmpeg preset support

Dennis Schridde <[email protected]> Sun, 6 Jun 2010 17:23:55 +0200
Newsgroups gmane.comp.video.transcode.devel
Message-ID <[email protected]>
--nextPart1595698.fXnU5UVfEi
Content-Type: multipart/mixed;
  boundary="Boundary-01=_L27CMXyb8oY2MZg"
Content-Transfer-Encoding: 7bit


--Boundary-01=_L27CMXyb8oY2MZg
Content-Type: text/plain;
  charset="us-ascii"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline

Hi!

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

The attached patch adds two config parameters to ffmpeg.cfg, "vpre" and=20
"ffmpeg_datadir".
The former sets the video presets and accepts a comma separated list, e.g.=
=20
"fast,baseline", default being "medium".
The latter is used to determine the path to the system ffmpeg presets, defa=
ult=20
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=20
(thus any transcode options will be overriden by any presets loaded), becau=
se=20
apparently transcode's internal defaults (which are in turn used to overrid=
e=20
ffmpeg's defaults) are just as broken as the ffmpeg ones... (See the FIXME =
I=20
included)

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

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

Kind regards,
Dennis

--Boundary-01=_L27CMXyb8oY2MZg
Content-Type: text/x-patch; charset="utf-8";
	name="transcode-1.1.5-ffmpeg-presets.patch"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="transcode-1.1.5-ffmpeg-presets.patch"

diff -wur transcode-1.1.5.orig//export/export_ffmpeg.c transcode-1.1.5/expo=
rt/export_ffmpeg.c
=2D-- transcode-1.1.5.orig//export/export_ffmpeg.c	2010-06-06 10:57:38.1289=
96193 +0200
+++ transcode-1.1.5/export/export_ffmpeg.c	2010-06-06 14:43:50.492250764 +0=
200
@@ -168,6 +168,191 @@
     return -10.0 * log(d) / log(10);
 }
=20
+
+// Could be using GNU extension 'strchrnul' instead:
+static char *tc_strchrnul(const char *s, int c) {
+	char *tmp =3D strchr(s, c);
+	if (tmp =3D=3D NULL) {
+		tmp =3D 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] =3D {NULL};
+
+static // GLUE
+const char **opt_names;
+static int opt_name_count;
+
+static char  *audio_codec_name =3D NULL;
+static char *subtitle_codec_name =3D NULL;
+static char *video_codec_name =3D NULL;
+static int audio_stream_copy =3D 0;
+static int video_stream_copy =3D 0;
+static int subtitle_stream_copy =3D 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 =3D 1;
+    } else {
+        *pcodec_name =3D av_strdup(arg);
+    }
+}
+
+static void opt_audio_codec(const char *arg)
+{
+    opt_codec(&audio_stream_copy, &audio_codec_name, AVMEDIA_TYPE_AUDIO, a=
rg);
+}
+
+static void opt_video_codec(const char *arg)
+{
+    opt_codec(&video_stream_copy, &video_codec_name, AVMEDIA_TYPE_VIDEO, a=
rg);
+}
+
+static void opt_subtitle_codec(const char *arg)
+{
+    opt_codec(&subtitle_stream_copy, &subtitle_codec_name, AVMEDIA_TYPE_SU=
BTITLE, arg);
+}
+
+static
+int opt_default(const char *opt, const char *arg){
+    int type;
+    int ret=3D 0;
+    const AVOption *o=3D NULL;
+    int opt_types[]=3D{AV_OPT_FLAG_VIDEO_PARAM, AV_OPT_FLAG_AUDIO_PARAM, 0=
, AV_OPT_FLAG_SUBTITLE_PARAM, 0};
+
+    for(type=3D0; type<AVMEDIA_TYPE_NB && ret>=3D 0; type++){
+		/* GLUE: +if */
+		if (type =3D=3D AVMEDIA_TYPE_VIDEO) {
+        const AVOption *o2 =3D av_find_opt(avcodec_opts[0], opt, NULL, opt=
_types[type], opt_types[type]);
+        if(o2)
+            ret =3D av_set_string3(avcodec_opts[type], opt, arg, 1, &o);
+		/* GLUE: +if */
+		}
+    }
+    /* GLUE: disabling
+    if(!o)
+        ret =3D av_set_string3(avformat_opts, opt, arg, 1, &o);
+    if(!o && sws_opts)
+        ret =3D av_set_string3(sws_opts, opt, arg, 1, &o);
+	*/
+    if(!o){
+		/* GLUE: disabling
+        if(opt[0] =3D=3D 'a')
+            ret =3D av_set_string3(avcodec_opts[AVMEDIA_TYPE_AUDIO], opt+1=
, arg, 1, &o);
+        else */ if(opt[0] =3D=3D 'v')
+            ret =3D av_set_string3(avcodec_opts[AVMEDIA_TYPE_VIDEO], opt+1=
, arg, 1, &o);
+		/* GLUE: disabling
+        else if(opt[0] =3D=3D 's')
+            ret =3D av_set_string3(avcodec_opts[AVMEDIA_TYPE_SUBTITLE], op=
t+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_dou=
ble(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=3D av_realloc(opt_names, sizeof(void*)*(opt_name_count+1));
+    opt_names[opt_name_count++]=3D 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=3DNULL;
+    char filename[1000], tmp[1000], tmp2[1000], line[1000];
+    int i;
+    const char *base[2]=3D { getenv("HOME"),
+                           FFMPEG_DATADIR,
+                         };
+
+    if (*opt !=3D 'f') {
+        for(i=3D!base[0]; i<2 && !f; i++){
+            snprintf(filename, sizeof(filename), "%s%s/%s.ffpreset", base[=
i], i ? "" : "/.ffmpeg", arg);
+            f=3D fopen(filename, "r");
+            if(!f){
+                char *codec_name=3D *opt =3D=3D 'v' ? video_codec_name :
+                                  *opt =3D=3D 'a' ? audio_codec_name :
+                                                subtitle_codec_name;
+                snprintf(filename, sizeof(filename), "%s%s/%s-%s.ffpreset"=
, base[i],  i ? "" : "/.ffmpeg", codec_name, arg);
+                f=3D fopen(filename, "r");
+            }
+        }
+    } else {
+        av_strlcpy(filename, arg, sizeof(filename));
+        f=3D fopen(filename, "r");
+    }
+
+    if(!f){
+        fprintf(stderr, "File for preset '%s' not found\n", arg);
+        av_exit(1);
+    }
+
+    while(!feof(f)){
+        int e=3D fscanf(f, "%999[^\n]\n", line) - 1;
+        if(line[0] =3D=3D '#' && !e)
+            continue;
+        e|=3D sscanf(line, "%999[^=3D]=3D%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' =3D '%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
@@ -1020,6 +1205,45 @@
=20
     lavc_venc_context->me_method =3D ME_ZERO + lavc_param_vme;
=20
+
+	/* FIXME: transcode itself contains "broken ffmpeg default settings", thu=
s we need to override them! */
+	if (lavc_param_video_preset) {
+		avcodec_opts[AVMEDIA_TYPE_VIDEO] =3D lavc_venc_context;
+		video_codec_name =3D ffmpeg_codec_name(codec->name);
+
+		const char *preset_start =3D lavc_param_video_preset;
+		while (preset_start) {
+			const char *preset_end =3D tc_strchrnul(preset_start, ',');
+			char preset_name[255] =3D {'\0'};
+
+			if (strncpy(preset_name, preset_start, preset_end-preset_start) !=3D pr=
eset_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) !=3D 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 overr=
idden:", preset_name, opt_name_count);
+				for (i=3D0; i < opt_name_count; i++)
+					tc_log_info(MOD_NAME, "-- %s", opt_names[i]);
+			}
+
+			if (*preset_end !=3D '\0') {
+				preset_start =3D preset_end+1;
+			}
+			else {
+				preset_start =3D NULL;
+			}
+		}
+	}
+
+
     //-- open codec --
     //----------------
     TC_LOCK_LIBAVCODEC;
diff -wur transcode-1.1.5.orig//export/ffmpeg_cfg.c transcode-1.1.5/export/=
ffmpeg_cfg.c
=2D-- transcode-1.1.5.orig//export/ffmpeg_cfg.c	2010-06-06 10:57:38.1289961=
93 +0200
+++ transcode-1.1.5/export/ffmpeg_cfg.c	2010-06-06 14:32:32.675996285 +0200
@@ -126,6 +126,9 @@
 //int lavc_param_atag =3D 0;
 //int lavc_param_abitrate =3D 224;
=20
+char *lavc_param_video_preset =3D "medium";
+char *lavc_param_ffmpeg_datadir =3D "/usr/share/ffmpeg";
+
 TCConfigEntry lavcopts_conf[]=3D{
 //    {"acodec", &lavc_param_acodec, TCCONF_TYPE_STRING, 0, 0, 0},
 //    {"abitrate", &lavc_param_abitrate, TCCONF_TYPE_INT, TCCONF_FLAG_RANG=
E, 1, 1000},
@@ -234,5 +237,7 @@
     {"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}
 };
diff -wur transcode-1.1.5.orig//export/ffmpeg_cfg.h transcode-1.1.5/export/=
ffmpeg_cfg.h
=2D-- transcode-1.1.5.orig//export/ffmpeg_cfg.h	2010-06-06 10:57:38.1269943=
75 +0200
+++ transcode-1.1.5/export/ffmpeg_cfg.h	2010-06-06 14:29:41.327995477 +0200
@@ -100,6 +100,9 @@
 extern int lavc_param_skip_top;
 extern int lavc_param_skip_bottom;
=20
+extern char *lavc_param_video_preset;
+extern char *lavc_param_ffmpeg_datadir;
+
 extern TCConfigEntry lavcopts_conf[];
=20
 #endif

--Boundary-01=_L27CMXyb8oY2MZg--

--nextPart1595698.fXnU5UVfEi
Content-Type: application/pgp-signature; name=signature.asc 
Content-Description: This is a digitally signed message part.

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.15 (GNU/Linux)

iEYEABECAAYFAkwLvYsACgkQjqfyF1DtJW4ZiQCfWVUVWwWO0v6u/cDjkd5wnbWN
gWkAmwdScJ1+pq2zNUihKxzGSqI83qZt
=9UXt
-----END PGP SIGNATURE-----

--nextPart1595698.fXnU5UVfEi--