Created
December 11, 2011 20:07
-
-
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" )
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 <[email protected]> | |
+* | |
+* 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" ) ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
How to install in ubuntu os ?