Browse code

lavfi: add subtitles filter.

Clément Bœsch authored on 2012/11/29 11:28:37
Showing 7 changed files
... ...
@@ -33,6 +33,7 @@ version <next>:
33 33
 - BRSTM demuxer
34 34
 - animated GIF decoder and demuxer
35 35
 - PVF demuxer
36
+- subtitles filter
36 37
 
37 38
 
38 39
 version 1.0:
... ...
@@ -1970,6 +1970,7 @@ removelogo_filter_deps="avcodec avformat swscale"
1970 1970
 scale_filter_deps="swscale"
1971 1971
 smartblur_filter_deps="gpl swscale"
1972 1972
 showspectrum_filter_deps="avcodec rdft"
1973
+subtitles_filter_deps="avformat avcodec libass"
1973 1974
 super2xsai_filter_deps="gpl"
1974 1975
 tinterlace_filter_deps="gpl"
1975 1976
 yadif_filter_deps="gpl"
... ...
@@ -1274,38 +1274,9 @@ overlay to a video stream, consider the @var{overlay} filter instead.
1274 1274
 
1275 1275
 @section ass
1276 1276
 
1277
-Draw ASS (Advanced Substation Alpha) subtitles on top of input video
1278
-using the libass library.
1279
-
1280
-To enable compilation of this filter you need to configure FFmpeg with
1281
-@code{--enable-libass}.
1282
-
1283
-This filter accepts the following named options, expressed as a
1284
-sequence of @var{key}=@var{value} pairs, separated by ":".
1285
-
1286
-@table @option
1287
-@item filename, f
1288
-Set the filename of the ASS file to read. It must be specified.
1289
-
1290
-@item original_size
1291
-Specify the size of the original video, the video for which the ASS file
1292
-was composed. Due to a misdesign in ASS aspect ratio arithmetic, this is
1293
-necessary to correctly scale the fonts if the aspect ratio has been changed.
1294
-@end table
1295
-
1296
-If the first key is not specified, it is assumed that the first value
1297
-specifies the @option{filename}.
1298
-
1299
-For example, to render the file @file{sub.ass} on top of the input
1300
-video, use the command:
1301
-@example
1302
-ass=sub.ass
1303
-@end example
1304
-
1305
-which is equivalent to:
1306
-@example
1307
-ass=filename=sub.ass
1308
-@end example
1277
+Same as the @ref{subtitles} filter, except that it doesn't require libavcodec
1278
+and libavformat to work. On the other hand, it is limited to ASS (Advanced
1279
+Substation Alpha) subtitles files.
1309 1280
 
1310 1281
 @section bbox
1311 1282
 
... ...
@@ -3745,6 +3716,43 @@ a pixel should be blurred or not. A value of 0 will filter all the
3745 3745
 image, a value included in [0,30] will filter flat areas and a value
3746 3746
 included in [-30,0] will filter edges.
3747 3747
 
3748
+@anchor{subtitles}
3749
+@section subtitles
3750
+
3751
+Draw subtitles on top of input video using the libass library.
3752
+
3753
+To enable compilation of this filter you need to configure FFmpeg with
3754
+@code{--enable-libass}. This filter also requires a build with libavcodec and
3755
+libavformat to convert the passed subtitles file to ASS (Advanced Substation
3756
+Alpha) subtitles format.
3757
+
3758
+This filter accepts the following named options, expressed as a
3759
+sequence of @var{key}=@var{value} pairs, separated by ":".
3760
+
3761
+@table @option
3762
+@item filename, f
3763
+Set the filename of the subtitle file to read. It must be specified.
3764
+
3765
+@item original_size
3766
+Specify the size of the original video, the video for which the ASS file
3767
+was composed. Due to a misdesign in ASS aspect ratio arithmetic, this is
3768
+necessary to correctly scale the fonts if the aspect ratio has been changed.
3769
+@end table
3770
+
3771
+If the first key is not specified, it is assumed that the first value
3772
+specifies the @option{filename}.
3773
+
3774
+For example, to render the file @file{sub.srt} on top of the input
3775
+video, use the command:
3776
+@example
3777
+subtitles=sub.srt
3778
+@end example
3779
+
3780
+which is equivalent to:
3781
+@example
3782
+subtitles=filename=sub.srt
3783
+@end example
3784
+
3748 3785
 @section split
3749 3786
 
3750 3787
 Split input video into several identical outputs.
... ...
@@ -134,6 +134,7 @@ OBJS-$(CONFIG_SETTB_FILTER)                  += f_settb.o
134 134
 OBJS-$(CONFIG_SHOWINFO_FILTER)               += vf_showinfo.o
135 135
 OBJS-$(CONFIG_SMARTBLUR_FILTER)              += vf_smartblur.o
136 136
 OBJS-$(CONFIG_SPLIT_FILTER)                  += split.o
137
+OBJS-$(CONFIG_SUBTITLES_FILTER)              += vf_ass.o
137 138
 OBJS-$(CONFIG_SUPER2XSAI_FILTER)             += vf_super2xsai.o
138 139
 OBJS-$(CONFIG_SWAPUV_FILTER)                 += vf_swapuv.o
139 140
 OBJS-$(CONFIG_THUMBNAIL_FILTER)              += vf_thumbnail.o
... ...
@@ -126,6 +126,7 @@ void avfilter_register_all(void)
126 126
     REGISTER_FILTER (SHOWINFO,    showinfo,    vf);
127 127
     REGISTER_FILTER (SMARTBLUR,   smartblur,   vf);
128 128
     REGISTER_FILTER (SPLIT,       split,       vf);
129
+    REGISTER_FILTER (SUBTITLES,   subtitles,   vf);
129 130
     REGISTER_FILTER (SUPER2XSAI,  super2xsai,  vf);
130 131
     REGISTER_FILTER (SWAPUV,      swapuv,      vf);
131 132
     REGISTER_FILTER (THUMBNAIL,   thumbnail,   vf);
... ...
@@ -29,8 +29,8 @@
29 29
 #include "libavutil/avutil.h"
30 30
 
31 31
 #define LIBAVFILTER_VERSION_MAJOR  3
32
-#define LIBAVFILTER_VERSION_MINOR  23
33
-#define LIBAVFILTER_VERSION_MICRO 105
32
+#define LIBAVFILTER_VERSION_MINOR  24
33
+#define LIBAVFILTER_VERSION_MICRO 100
34 34
 
35 35
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
36 36
                                                LIBAVFILTER_VERSION_MINOR, \
... ...
@@ -1,6 +1,7 @@
1 1
 /*
2 2
  * Copyright (c) 2011 Baptiste Coudurier
3 3
  * Copyright (c) 2011 Stefano Sabatini
4
+ * Copyright (c) 2012 Clément Bœsch
4 5
  *
5 6
  * This file is part of FFmpeg.
6 7
  *
... ...
@@ -28,6 +29,11 @@
28 28
 
29 29
 #include <ass/ass.h>
30 30
 
31
+#include "config.h"
32
+#if CONFIG_SUBTITLES_FILTER
33
+# include "libavcodec/avcodec.h"
34
+# include "libavformat/avformat.h"
35
+#endif
31 36
 #include "libavutil/avstring.h"
32 37
 #include "libavutil/imgutils.h"
33 38
 #include "libavutil/opt.h"
... ...
@@ -53,15 +59,13 @@ typedef struct {
53 53
 #define OFFSET(x) offsetof(AssContext, x)
54 54
 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
55 55
 
56
-static const AVOption ass_options[] = {
57
-    {"filename",       "set the filename of the ASS file to read",                 OFFSET(filename),   AV_OPT_TYPE_STRING,     {.str = NULL},  CHAR_MIN, CHAR_MAX, FLAGS },
58
-    {"f",              "set the filename of the ASS file to read",                 OFFSET(filename),   AV_OPT_TYPE_STRING,     {.str = NULL},  CHAR_MIN, CHAR_MAX, FLAGS },
56
+static const AVOption options[] = {
57
+    {"filename",       "set the filename of file to read",                         OFFSET(filename),   AV_OPT_TYPE_STRING,     {.str = NULL},  CHAR_MIN, CHAR_MAX, FLAGS },
58
+    {"f",              "set the filename of file to read",                         OFFSET(filename),   AV_OPT_TYPE_STRING,     {.str = NULL},  CHAR_MIN, CHAR_MAX, FLAGS },
59 59
     {"original_size",  "set the size of the original video (used to scale fonts)", OFFSET(original_w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL},  CHAR_MIN, CHAR_MAX, FLAGS },
60 60
     {NULL},
61 61
 };
62 62
 
63
-AVFILTER_DEFINE_CLASS(ass);
64
-
65 63
 /* libass supports a log level ranging from 0 to 7 */
66 64
 static const int ass_libavfilter_log_level_map[] = {
67 65
     AV_LOG_QUIET,               /* 0 */
... ...
@@ -82,13 +86,13 @@ static void ass_log(int ass_level, const char *fmt, va_list args, void *ctx)
82 82
     av_log(ctx, level, "\n");
83 83
 }
84 84
 
85
-static av_cold int init(AVFilterContext *ctx, const char *args)
85
+static av_cold int init(AVFilterContext *ctx, const char *args, const AVClass *class)
86 86
 {
87 87
     AssContext *ass = ctx->priv;
88 88
     static const char *shorthand[] = { "filename", NULL };
89 89
     int ret;
90 90
 
91
-    ass->class = &ass_class;
91
+    ass->class = class;
92 92
     av_opt_set_defaults(ass);
93 93
 
94 94
     if ((ret = av_opt_set_from_string(ass, args, shorthand, "=", ":")) < 0)
... ...
@@ -112,14 +116,6 @@ static av_cold int init(AVFilterContext *ctx, const char *args)
112 112
         return AVERROR(EINVAL);
113 113
     }
114 114
 
115
-    ass->track = ass_read_file(ass->library, ass->filename, NULL);
116
-    if (!ass->track) {
117
-        av_log(ctx, AV_LOG_ERROR,
118
-               "Could not create a libass track when reading file '%s'\n",
119
-               ass->filename);
120
-        return AVERROR(EINVAL);
121
-    }
122
-
123 115
     ass_set_fonts(ass->renderer, NULL, NULL, 1, NULL, 1);
124 116
     return 0;
125 117
 }
... ...
@@ -215,14 +211,143 @@ static const AVFilterPad ass_outputs[] = {
215 215
     { NULL }
216 216
 };
217 217
 
218
+#if CONFIG_ASS_FILTER
219
+
220
+#define ass_options options
221
+AVFILTER_DEFINE_CLASS(ass);
222
+
223
+static av_cold int init_ass(AVFilterContext *ctx, const char *args)
224
+{
225
+    AssContext *ass = ctx->priv;
226
+    int ret = init(ctx, args, &ass_class);
227
+
228
+    if (ret < 0)
229
+        return ret;
230
+
231
+    ass->track = ass_read_file(ass->library, ass->filename, NULL);
232
+    if (!ass->track) {
233
+        av_log(ctx, AV_LOG_ERROR,
234
+               "Could not create a libass track when reading file '%s'\n",
235
+               ass->filename);
236
+        return AVERROR(EINVAL);
237
+    }
238
+    return 0;
239
+}
240
+
218 241
 AVFilter avfilter_vf_ass = {
219 242
     .name          = "ass",
220 243
     .description   = NULL_IF_CONFIG_SMALL("Render subtitles onto input video using the libass library."),
221 244
     .priv_size     = sizeof(AssContext),
222
-    .init          = init,
245
+    .init          = init_ass,
223 246
     .uninit        = uninit,
224 247
     .query_formats = query_formats,
225 248
     .inputs        = ass_inputs,
226 249
     .outputs       = ass_outputs,
227 250
     .priv_class    = &ass_class,
228 251
 };
252
+#endif
253
+
254
+#if CONFIG_SUBTITLES_FILTER
255
+
256
+#define subtitles_options options
257
+AVFILTER_DEFINE_CLASS(subtitles);
258
+
259
+static av_cold int init_subtitles(AVFilterContext *ctx, const char *args)
260
+{
261
+    int ret, sid;
262
+    AVFormatContext *fmt = NULL;
263
+    AVCodecContext *dec_ctx = NULL;
264
+    AVCodec *dec = NULL;
265
+    AVStream *st;
266
+    AVPacket pkt;
267
+    AssContext *ass = ctx->priv;
268
+
269
+    /* Init libass */
270
+    ret = init(ctx, args, &subtitles_class);
271
+    if (ret < 0)
272
+        return ret;
273
+    ass->track = ass_new_track(ass->library);
274
+    if (!ass->track) {
275
+        av_log(ctx, AV_LOG_ERROR, "Could not create a libass track\n");
276
+        return AVERROR(EINVAL);
277
+    }
278
+
279
+    /* Open subtitles file */
280
+    ret = avformat_open_input(&fmt, ass->filename, NULL, NULL);
281
+    if (ret < 0) {
282
+        av_log(ctx, AV_LOG_ERROR, "Unable to open %s\n", ass->filename);
283
+        goto end;
284
+    }
285
+    ret = avformat_find_stream_info(fmt, NULL);
286
+    if (ret < 0)
287
+        goto end;
288
+
289
+    /* Locate subtitles stream */
290
+    ret = av_find_best_stream(fmt, AVMEDIA_TYPE_SUBTITLE, -1, -1, NULL, 0);
291
+    if (ret < 0) {
292
+        av_log(ctx, AV_LOG_ERROR, "Unable to locate subtitle stream in %s\n",
293
+               ass->filename);
294
+        goto end;
295
+    }
296
+    sid = ret;
297
+    st = fmt->streams[sid];
298
+
299
+    /* Open decoder */
300
+    dec_ctx = st->codec;
301
+    dec = avcodec_find_decoder(dec_ctx->codec_id);
302
+    if (!dec) {
303
+        av_log(ctx, AV_LOG_ERROR, "Failed to find subtitle codec %s\n",
304
+               avcodec_get_name(dec_ctx->codec_id));
305
+        return AVERROR(EINVAL);
306
+    }
307
+    ret = avcodec_open2(dec_ctx, dec, NULL);
308
+    if (ret < 0)
309
+        goto end;
310
+
311
+    /* Decode subtitles and push them into the renderer (libass) */
312
+    if (dec_ctx->subtitle_header)
313
+        ass_process_codec_private(ass->track,
314
+                                  dec_ctx->subtitle_header,
315
+                                  dec_ctx->subtitle_header_size);
316
+    av_init_packet(&pkt);
317
+    pkt.data = NULL;
318
+    pkt.size = 0;
319
+    while (av_read_frame(fmt, &pkt) >= 0) {
320
+        int i, got_subtitle;
321
+        AVSubtitle sub;
322
+
323
+        if (pkt.stream_index == sid) {
324
+            ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_subtitle, &pkt);
325
+            if (ret < 0 || !got_subtitle)
326
+                break;
327
+            for (i = 0; i < sub.num_rects; i++) {
328
+                char *ass_line = sub.rects[i]->ass;
329
+                if (!ass_line)
330
+                    break;
331
+                ass_process_data(ass->track, ass_line, strlen(ass_line));
332
+            }
333
+        }
334
+        av_free_packet(&pkt);
335
+        avsubtitle_free(&sub);
336
+    }
337
+
338
+end:
339
+    if (fmt)
340
+        avformat_close_input(&fmt);
341
+    if (dec_ctx)
342
+        avcodec_close(dec_ctx);
343
+    return ret;
344
+}
345
+
346
+AVFilter avfilter_vf_subtitles = {
347
+    .name          = "subtitles",
348
+    .description   = NULL_IF_CONFIG_SMALL("Render subtitles onto input video using the libass library."),
349
+    .priv_size     = sizeof(AssContext),
350
+    .init          = init_subtitles,
351
+    .uninit        = uninit,
352
+    .query_formats = query_formats,
353
+    .inputs        = ass_inputs,
354
+    .outputs       = ass_outputs,
355
+    .priv_class    = &subtitles_class,
356
+};
357
+#endif