Skip to content

Instantly share code, notes, and snippets.

@astrataro
Created December 11, 2011 20:07
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save astrataro/1462458 to your computer and use it in GitHub Desktop.
Save astrataro/1462458 to your computer and use it in GitHub Desktop.
[x264-patch] Render subtitles in x264 with VSFilter ( --vf subtitles --sub "subtitles.ass" )
diff --git a/Makefile b/Makefile
index cf05d15..a29d37c 100644
--- a/Makefile
+++ b/Makefile
@@ -42,7 +42,7 @@ endif
# Optional module sources
ifneq ($(findstring HAVE_AVS 1, $(CONFIG)),)
-SRCCLI += input/avs.c
+SRCCLI += input/avs.c filters/video/subtitles.c
endif
ifneq ($(findstring HAVE_THREAD 1, $(CONFIG)),)
diff --git a/configure b/configure
index 3ab4669..cd00e5a 100755
--- a/configure
+++ b/configure
@@ -1256,7 +1256,7 @@ Libs: $pclibs
Cflags: -I$includedir
EOF
-filters="crop select_every hqdn3d pad vflip"
+filters="crop select_every hqdn3d pad vflip subtitles"
gpl_filters="yadif"
[ $swscale = yes ] && filters="resize $filters"
[ $gpl = yes ] && filters="$filters $gpl_filters"
diff --git a/filters/video/subtitles.c b/filters/video/subtitles.c
new file mode 100644
index 0000000..8d31e33
--- /dev/null
+++ b/filters/video/subtitles.c
@@ -0,0 +1,274 @@
+/*****************************************************************************
+* subtitles.c: subtitles render filter using vsfilter
+*****************************************************************************
+* Copyright (C) 2011 Zhou Zongyi <zhouzy_wuxi@hotmail.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
+*****************************************************************************/
+
+#include <Windows.h>
+#include "x264cli.h"
+#include "subtitles.h"
+#include "video.h"
+
+csri_open_file_t csri_open_file;
+csri_add_file_t csri_add_file;
+csri_request_fmt_t csri_request_fmt;
+csri_render_t csri_render;
+csri_close_t csri_close;
+
+//csri_openflag flags={};
+char* subfilename[16];
+int subtotal = 0;
+HMODULE hVSFilter = 0;
+
+int add_sub(char *filename)
+{
+ if (subtotal<16)
+ {
+ subfilename[subtotal++] = filename;
+ return 1;
+ }
+ return 0;
+}
+
+const char* get_csri_fmt_name(unsigned int fmt)
+{
+ switch(fmt)
+ {
+ case CSRI_F_RGBA:
+ return "RGBA";
+ case CSRI_F_ARGB:
+ return "ARGB";
+ case CSRI_F_BGRA:
+ return "BGRA";
+ case CSRI_F_ABGR:
+ return "ABGR";
+ case CSRI_F_RGB_:
+ case CSRI_F__RGB:
+ case CSRI_F_RGB:
+ return "RGB24";
+ case CSRI_F_BGR_:
+ case CSRI_F__BGR:
+ case CSRI_F_BGR:
+ return "BGR24";
+// case CSRI_F_AYUV:
+// return "AYUV";
+// case CSRI_F_YUVA:
+// return "YUVA";
+// case CSRI_F_YVUA:
+// return "YVUA";
+ case CSRI_F_YUY2:
+ return "YUY2";
+// case CSRI_F_YV12A:
+// return "YV12A";
+ case CSRI_F_YV12:
+ return "YV12";
+ case CSRI_F_NV12:
+ return "NV12";
+ case CSRI_F_NV21:
+ return "NV21";
+ }
+ return "Unknown";
+}
+
+void* subtitles_new_renderer(const csri_fmt *fmt, uint32_t sarw, uint32_t sarh)
+{
+ int i;
+ csri_openflag flag;
+ void *subrenderinst;
+ if (!subtotal)
+ return 0;
+
+ if (!hVSFilter)
+ {
+#if ARCH_X86_64
+ if (NULL == (hVSFilter = LoadLibraryA("VSFilter64.dll")))
+ {
+ x264_cli_log("subtitles", X264_LOG_ERROR, "failed to load VSFilter64.dll\n");
+ return 0;
+ }
+#else
+ if (NULL == (hVSFilter = LoadLibraryA("VSFilter.dll")))
+ {
+ x264_cli_log("subtitles", X264_LOG_ERROR, "failed to load VSFilter.dll\n");
+ return 0;
+ }
+#endif
+ csri_open_file = (csri_open_file_t)GetProcAddress(hVSFilter,"csri_open_file");
+ csri_close = (csri_close_t)GetProcAddress(hVSFilter,"csri_close");
+ csri_request_fmt = (csri_request_fmt_t)GetProcAddress(hVSFilter,"csri_request_fmt");
+ csri_render = (csri_render_t)GetProcAddress(hVSFilter,"csri_render");
+ csri_add_file = (csri_add_file_t)GetProcAddress(hVSFilter,"csri_add_file");
+ }
+ if (sarw != sarh) // non-square par
+ {
+ flag.name = "PAR";
+ flag.data.dval = (double)sarw / sarh;
+ flag.next = NULL;
+ }
+ else
+ flag.name = NULL;
+
+ if (NULL == (subrenderinst = csri_open_file((void*)"vsfilter", subfilename[0], flag.name?&flag:NULL)))
+ {
+ x264_cli_log("subtitles", X264_LOG_ERROR, "failed to create subtitles renderer\n");
+ return 0;
+ }
+ if (csri_request_fmt(subrenderinst, fmt))
+ {
+ x264_cli_log("subtitles", X264_LOG_ERROR, "csri does not support %s input\n", get_csri_fmt_name(fmt->pixfmt));
+ return 0;
+ }
+ x264_cli_log("subtitles", X264_LOG_INFO, "loaded subtitles \"%s\"\n", subfilename[0]);
+ if (csri_add_file)
+ for (i=1; i<subtotal; i++)
+ {
+ if (csri_add_file(subrenderinst, subfilename[i], flag.name?&flag:NULL))
+ x264_cli_log("subtitles", X264_LOG_INFO, "loaded subtitles \"%s\"\n", subfilename[0]);
+ else
+ x264_cli_log("subtitles", X264_LOG_WARNING, "failed to load subtitles \"%s\"\n", subfilename[0]);
+ }
+ else
+ x264_cli_log("subtitles", X264_LOG_WARNING, "no csri_add_file interface, fail to render subtitles\n");
+ return subrenderinst;
+}
+
+#define NAME "subtitles"
+#define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, NAME, __VA_ARGS__ )
+
+cli_vid_filter_t subtitles_filter;
+
+typedef struct
+{
+ hnd_t prev_hnd;
+ cli_vid_filter_t prev_filter;
+ void *subrenderinst;
+ int csp;
+ unsigned int fmt;
+ double scale_factor;
+} subtitles_hnd_t;
+
+static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string )
+{
+ subtitles_hnd_t *h;
+ csri_fmt fmt;
+ if (!(h = calloc(1, sizeof(subtitles_hnd_t))))
+ return -1;
+ fmt.width = info->width;
+ fmt.height = info->height;
+ h->csp = info->csp & X264_CSP_MASK;
+ switch(h->csp)
+ {
+ case X264_CSP_I420:
+ case X264_CSP_YV12:
+ fmt.pixfmt = CSRI_F_YV12;
+ break;
+ case X264_CSP_NV12:
+ fmt.pixfmt = CSRI_F_NV12;
+ break;
+ case X264_CSP_BGR:
+ fmt.pixfmt = CSRI_F_BGR;
+ break;
+ case X264_CSP_BGRA:
+ fmt.pixfmt = CSRI_F_BGRA;
+ break;
+ case X264_CSP_RGB:
+ fmt.pixfmt = CSRI_F_RGB;
+ break;
+ default:
+ x264_cli_log( NAME, X264_LOG_ERROR, "unsupported colorspace\n");
+ fmt.pixfmt = -1;
+ }
+ if (fmt.pixfmt == -1 || !(h->subrenderinst = subtitles_new_renderer(&fmt, info->sar_height, info->sar_height)))
+ {
+ free(h);
+ return -1;
+ }
+ h->fmt = fmt.pixfmt;
+ h->scale_factor = (double)(info->timebase_num) / info->timebase_den;
+
+ h->prev_filter = *filter;
+ h->prev_hnd = *handle;
+ *handle = h;
+ *filter = subtitles_filter;
+ return 0;
+}
+
+static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
+{
+ subtitles_hnd_t *h = handle;
+ csri_frame fr;
+ if( h->prev_filter.get_frame( h->prev_hnd, output, frame ) )
+ return -1;
+ fr.planes[0] = output->img.plane[0];
+ fr.strides[0] = output->img.stride[0];
+ switch(h->csp)
+ {
+ case X264_CSP_I420:
+ fr.planes[1] = output->img.plane[2];
+ fr.planes[2] = output->img.plane[1];
+ goto L_YV12;
+ case X264_CSP_YV12:
+ fr.planes[1] = output->img.plane[1];
+ fr.planes[2] = output->img.plane[2];
+ L_YV12:
+ fr.strides[1] = fr.strides[2] = output->img.stride[1];
+ break;
+ case X264_CSP_NV12:
+ fr.planes[1] = output->img.plane[1];
+ fr.planes[2] = 0;
+ fr.strides[1] = output->img.stride[1];
+ break;
+ }
+ fr.pixfmt = h->fmt;
+ subtitles_render_frame(h->subrenderinst, &fr, output->pts * h->scale_factor);
+ return 0;
+}
+
+static int release_frame( hnd_t handle, cli_pic_t *pic, int frame )
+{
+ subtitles_hnd_t *h = handle;
+ /* NO filter should ever have a dependent release based on the plane pointers,
+ * so avoid unnecessary unshifting */
+ return h->prev_filter.release_frame( h->prev_hnd, pic, frame );
+}
+
+static void free_filter( hnd_t handle )
+{
+ subtitles_hnd_t *h = handle;
+ h->prev_filter.free( h->prev_hnd );
+ free( h );
+}
+
+static void help( int longhelp )
+{
+ printf( " "NAME":\n" );
+ if( longhelp )
+ {
+ printf( " renders subtitles using VSFilter (requires lavf/ffms demuxer)\n" );
+#if ARCH_X86_64
+ printf( " need VSFilter64.dll\n" );
+#else
+ printf( " need VSFilter.dll\n" );
+#endif
+ }
+ printf( "\n"
+ " --sub <string> Load subtitles file (used with video filter \"subtitles\")\n");
+ if( longhelp )
+ printf( " can be called more than once to load multiple subtitles\n" );
+}
+
+cli_vid_filter_t subtitles_filter = { NAME, help, init, get_frame, release_frame, free_filter, NULL };
diff --git a/filters/video/subtitles.h b/filters/video/subtitles.h
new file mode 100644
index 0000000..604e787
--- /dev/null
+++ b/filters/video/subtitles.h
@@ -0,0 +1,71 @@
+enum {
+ CSRI_F_RGBA = 0,
+ CSRI_F_ARGB,
+ CSRI_F_BGRA,
+ CSRI_F_ABGR,
+
+ CSRI_F_RGB_ = 0x100,
+ CSRI_F__RGB,
+ CSRI_F_BGR_, /**< Windows "RGB32" */
+ CSRI_F__BGR,
+
+ CSRI_F_RGB = 0x200,
+ CSRI_F_BGR, /**< Windows "RGB24" */
+
+ CSRI_F_AYUV = 0x1000,
+ CSRI_F_YUVA,
+ CSRI_F_YVUA,
+
+ CSRI_F_YUY2 = 0x1100,
+
+ CSRI_F_YV12A = 0x2011, /**< planar YUV 2x2 + alpha plane */
+ CSRI_F_YV12 = 0x2111, /**< planar YUV 2x2 */
+ CSRI_F_NV12,
+ CSRI_F_NV21
+};
+
+typedef struct {
+ unsigned pixfmt;
+ unsigned width;
+ unsigned height;
+} csri_fmt;
+
+typedef struct {
+ unsigned pixfmt;
+ unsigned char *planes[4];
+ ptrdiff_t strides[4];
+} csri_frame;
+
+typedef struct {
+ const char *name;
+ const char *specific;
+ const char *longname;
+ const char *author;
+ const char *copyright;
+} csri_info;
+
+typedef union {
+ int32_t lval;
+ double dval;
+ const char *utf8val;
+ void *otherval;
+} csri_vardata;
+
+typedef struct {
+ const char *name;
+ csri_vardata data;
+ struct csri_openflag *next;
+} csri_openflag;
+
+typedef void* (*csri_open_file_t)(void *renderer, const char *filename, csri_openflag *flags);
+typedef int (*csri_add_file_t)(void *inst, const char *filename, csri_openflag *flags);
+typedef void (*csri_close_t)(void *inst);
+typedef int (*csri_request_fmt_t)(void *inst, const csri_fmt *fmt);
+typedef void (*csri_render_t)(void *inst, csri_frame *frame, double time);
+
+extern csri_render_t csri_render;
+#define subtitles_render_frame csri_render
+extern csri_close_t csri_close;
+#define subtitles_close csri_close
+
+void* subtitles_new_renderer(const csri_fmt *fmt, uint32_t sarw, uint32_t sarh);
diff --git a/filters/video/video.c b/filters/video/video.c
index 60a493e..19872ff 100644
--- a/filters/video/video.c
+++ b/filters/video/video.c
@@ -60,6 +60,10 @@ void x264_register_vid_filters( void )
REGISTER_VFILTER( select_every );
REGISTER_VFILTER( vflip );
REGISTER_GPL_VFILTER( yadif );
+
+#if HAVE_AVS
+ REGISTER_VFILTER( subtitles );
+#endif
}
int x264_init_vid_filter( const char *name, hnd_t *handle, cli_vid_filter_t *filter,
diff --git a/x264.c b/x264.c
index 5a67f61..67b01b2 100644
--- a/x264.c
+++ b/x264.c
@@ -88,6 +88,7 @@ typedef struct {
/* file i/o operation structs */
cli_input_t cli_input;
static cli_output_t cli_output;
+int add_sub(char *filename);
/* video filter operation struct */
static cli_vid_filter_t filter;
@@ -1024,6 +1025,9 @@ typedef enum
OPT_OUTPUT_CSP,
OPT_INPUT_RANGE,
OPT_RANGE,
+#if HAVE_AVS
+ OPT_SUB,
+#endif
OPT_AUDIOFILE,
OPT_AUDIODEMUXER,
OPT_AUDIOTRACK,
@@ -1208,6 +1212,9 @@ static struct option long_options[] =
{ "dts-compress", no_argument, NULL, OPT_DTS_COMPRESSION },
{ "output-csp", required_argument, NULL, OPT_OUTPUT_CSP },
{ "input-range", required_argument, NULL, OPT_INPUT_RANGE },
+#if HAVE_AVS
+ { "sub", required_argument, NULL, OPT_SUB},
+#endif
{ "audiofile", required_argument, NULL, OPT_AUDIOFILE },
{ "ademuxer", required_argument, NULL, OPT_AUDIODEMUXER },
{ "atrack", required_argument, NULL, OPT_AUDIOTRACK },
@@ -1708,6 +1715,12 @@ static int parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt )
#endif
param->i_csp = output_csp = output_csp_fix[output_csp];
break;
+#if HAVE_AVS
+ case OPT_SUB:
+ if (!add_sub(optarg))
+ x264_cli_log( "x264", X264_LOG_WARNING, "too many subtitles, \"%s\" ignored\n", optarg );
+ break;
+#endif
case OPT_AUDIOCODEC:
audio_enc = optarg;
if( !strcmp( audio_enc, "none" ) )
@POWARAJ
Copy link

POWARAJ commented Dec 14, 2012

How to install in ubuntu os ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment