[FFmpeg-devel] [PATCH] lavc: add text subtitles decoder.

Clément Bœsch ubitux at gmail.com
Sun Oct 14 03:18:20 CEST 2012


TODO: lavc bump
---
This patch makes possible to extract & convert text subtitles from formats like
Ogg, with a simple ffmpeg -i in.ogg out.ass.

At some point we might consider changing the AV_CODEC_ID_TEXT into
AV_CODEC_ID_SUBRIP in the Ogg demuxer and some others (if some samples with
SubRip markup appears, which is very likely). Still, I believe this decoder
will be useful for some other cases such as:

 - potential formats making an explicit difference between raw text subtitles
   and SubRip markup'ed text subtitles
 - a bunch of old formats like vplayer, mpl2, will have this common decoder
   (demuxers incoming when I'll have some time available to write them)
 - keeping compat with the fork which might continue to add some demuxers with
   text codec without the need for us to change it to subrip (until someone
   request it because he just found a sample with markup in the given format)
---
 libavcodec/Makefile    |  1 +
 libavcodec/allcodecs.c |  1 +
 libavcodec/textdec.c   | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 99 insertions(+)
 create mode 100644 libavcodec/textdec.c

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 47a01db..1811b7b 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -402,6 +402,7 @@ OBJS-$(CONFIG_SVQ3_DECODER)            += svq3.o svq13.o h263.o h264.o        \
                                           h264_loopfilter.o h264_direct.o     \
                                           h264_sei.o h264_ps.o h264_refs.o    \
                                           h264_cavlc.o h264_cabac.o cabac.o
+OBJS-$(CONFIG_TEXT_DECODER)            += textdec.o ass.o
 OBJS-$(CONFIG_TAK_DECODER)             += takdec.o tak.o
 OBJS-$(CONFIG_TARGA_DECODER)           += targa.o
 OBJS-$(CONFIG_TARGA_ENCODER)           += targaenc.o rle.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index ae88fab..c023f35 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -424,6 +424,7 @@ void avcodec_register_all(void)
     REGISTER_ENCDEC  (SRT, srt);
     REGISTER_ENCDEC  (SUBRIP, subrip);
     REGISTER_DECODER (SUBVIEWER, subviewer);
+    REGISTER_DECODER (TEXT, text);
     REGISTER_DECODER (WEBVTT, webvtt);
     REGISTER_ENCDEC  (XSUB, xsub);
 
diff --git a/libavcodec/textdec.c b/libavcodec/textdec.c
new file mode 100644
index 0000000..a154995
--- /dev/null
+++ b/libavcodec/textdec.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2012 Clément Bœsch
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Raw subtitles decoder
+ */
+
+#include "avcodec.h"
+#include "ass.h"
+#include "libavutil/bprint.h"
+#include "libavutil/opt.h"
+
+typedef struct {
+    AVClass *class;
+    char *linebreaks;
+    int escape_ass_tags;
+} TextContext;
+
+#define OFFSET(x) offsetof(TextContext, x)
+#define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM
+static const AVOption options[] = {
+    { "linebreaks",      "Extra line breaks characters",    OFFSET(linebreaks),      AV_OPT_TYPE_STRING, {.str=NULL}, .flags=SD },
+    { "escape_ass_tags", "Set if ASS tags must be escaped", OFFSET(escape_ass_tags), AV_OPT_TYPE_INT,    {.i64=1},    .flags=SD },
+    { NULL }
+};
+
+static const AVClass text_decoder_class = {
+    .class_name = "text decoder",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+static int text_event_to_ass(const AVCodecContext *avctx, AVBPrint *buf, const char *p)
+{
+    const TextContext *text = avctx->priv_data;
+
+    while (*p) {
+        if ((text->linebreaks && strchr(text->linebreaks, *p)) ||
+            (p[0] == '\n' && p[1]))
+            av_bprintf(buf, "\\N");
+        else if (text->escape_ass_tags && (*p == '{' || *p == '}'))
+            av_bprintf(buf, "\\%c", *p);
+        else if (*p != '\r')
+            av_bprint_chars(buf, *p, 1);
+        p++;
+    }
+    av_bprintf(buf, "\r\n");
+    return 0;
+}
+
+static int text_decode_frame(AVCodecContext *avctx, void *data,
+                             int *got_sub_ptr, AVPacket *avpkt)
+{
+    AVBPrint buf;
+    AVSubtitle *sub = data;
+    const char *ptr = avpkt->data;
+    const int ts_start     = av_rescale_q(avpkt->pts,      avctx->time_base, (AVRational){1,100});
+    const int ts_duration  = avpkt->duration != -1 ?
+                             av_rescale_q(avpkt->duration, avctx->time_base, (AVRational){1,100}) : -1;
+
+    av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
+    if (ptr && *ptr && avpkt->size > 0 &&
+        !text_event_to_ass(avctx, &buf, ptr))
+        ff_ass_add_rect(sub, buf.str, ts_start, ts_duration, 0);
+    *got_sub_ptr = sub->num_rects > 0;
+    av_bprint_finalize(&buf, NULL);
+    return avpkt->size;
+}
+
+AVCodec ff_text_decoder = {
+    .name      = "text",
+    .priv_data_size = sizeof(TextContext),
+    .long_name = NULL_IF_CONFIG_SMALL("Raw text subtitle"),
+    .type      = AVMEDIA_TYPE_SUBTITLE,
+    .id        = AV_CODEC_ID_TEXT,
+    .decode    = text_decode_frame,
+    .init      = ff_ass_subtitle_header_default,
+};
-- 
1.7.12.2



More information about the ffmpeg-devel mailing list