decoders: add HDMV text subtitles decoder
authorFrancois Cartegnie <fcvlcdev@free.fr>
Mon, 30 Jan 2017 12:25:17 +0000 (13:25 +0100)
committerFrancois Cartegnie <fcvlcdev@free.fr>
Mon, 30 Jan 2017 16:41:07 +0000 (17:41 +0100)
NEWS
modules/MODULES_LIST
modules/codec/Makefile.am
modules/codec/textst.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 8c684c6..c589109 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -90,6 +90,7 @@ Decoder:
  * JPEG images correctly oriented using embedded orientation tag, if present
  * Support VPX high bit depth support
  * Extend MicroDVD support with color, fontname, size, position extensions
+ * BluRay text subtitles are now decoded
 
 Demuxers:
  * Support HD-DVD .evo (H.264, VC-1, MPEG-2, PCM, AC-3, E-AC3, MLP, DTS)
index 02ea857..42e83d0 100644 (file)
@@ -397,6 +397,7 @@ $Id$
  * tcp: TCP Network access module
  * tdummy: dummy text renderer
  * telx: teletext subtitles decoder
+ * textst: HDMV text subtitles decoder
  * theora: a theora video decoder/packetizer/encoder using the libtheora
  * timecode: clock/timecode as a subtitle input
  * tizen_audio: audio output for Tizen
index 2154906..b201be4 100644 (file)
@@ -233,6 +233,10 @@ libtelx_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(codecdir)'
 EXTRA_LTLIBRARIES += libtelx_plugin.la
 codec_LTLIBRARIES += $(LTLIBtelx)
 
+libtextst_plugin_la_SOURCES = codec/textst.c
+libtextst_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(codecdir)'
+codec_LTLIBRARIES += libtextst_plugin.la
+
 libzvbi_plugin_la_SOURCES = codec/zvbi.c
 libzvbi_plugin_la_CFLAGS = $(AM_CFLAGS) $(ZVBI_CFLAGS) $(CFLAGS_zvbi)
 libzvbi_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(codecdir)'
diff --git a/modules/codec/textst.c b/modules/codec/textst.c
new file mode 100644 (file)
index 0000000..795d3b2
--- /dev/null
@@ -0,0 +1,282 @@
+/*****************************************************************************
+ * textst.c: HDMV TextST subtitles decoder
+ *****************************************************************************
+ * Copyright (C) 2017 Videolan Authors
+ *
+ * Adapted from libluray textst_decode.c
+ * Copyright (C) 2013  Petri Hintukainen <phintuka@users.sourceforge.net>
+ *
+ * This program 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.
+ *
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_codec.h>
+
+#include "substext.h"
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+static int  Open (vlc_object_t *);
+static void Close(vlc_object_t *);
+
+struct decoder_sys_t
+{
+    uint32_t palette[256];
+};
+
+vlc_module_begin()
+    set_description(N_("HDMV TextST subtitles decoder"))
+    set_category(CAT_INPUT)
+    set_subcategory(SUBCAT_INPUT_SCODEC)
+    set_capability("decoder", 10)
+    set_callbacks(Open, Close)
+vlc_module_end()
+
+#define BD_TEXTST_DATA_STRING      1
+#define BD_TEXTST_DATA_FONT_ID     2
+#define BD_TEXTST_DATA_FONT_STYLE  3
+#define BD_TEXTST_DATA_FONT_SIZE   4
+#define BD_TEXTST_DATA_FONT_COLOR  5
+#define BD_TEXTST_DATA_NEWLINE     0x0a
+#define BD_TEXTST_DATA_RESET_STYLE 0x0b
+
+static size_t textst_FillRegion(decoder_t *p_dec, const uint8_t *p_data, size_t i_data,
+                                subpicture_updater_sys_region_t *p_region)
+{
+    VLC_UNUSED(p_dec);
+    text_segment_t **pp_last = &p_region->p_segments;
+    text_style_t *p_style = NULL;
+
+     /* p_data[0] */
+     /*   continous_present_flag b1 */
+     /*   forced_on_flag b1 */
+     /*   ? b6 */
+
+     //uint8_t region_style_id_ref = p_data[1];
+     uint16_t i_data_length = GetWBE(&p_data[2]);
+
+     p_data += 4; i_data -= 4;
+     if( i_data < i_data_length )
+         return i_data;
+     else
+         i_data = i_data_length;
+
+     while (i_data > 3)
+     {
+         /* parse header */
+         uint8_t code = p_data[0];
+         if (code != 0x1b) {
+             p_data++; i_data--;
+             continue;
+         }
+
+         uint8_t type   = p_data[1];
+         uint8_t length = p_data[2];
+
+         p_data += 3; i_data -= 3;
+
+         if(length > i_data)
+             break;
+
+         switch (type)
+         {
+             case BD_TEXTST_DATA_STRING:
+                {
+                    char *psz = strndup((char *)p_data, length);
+                    *pp_last = text_segment_New(psz);
+                    free(psz);
+                    if(p_style && *pp_last)
+                        (*pp_last)->style = text_style_Duplicate(p_style);
+                }
+                break;
+             case BD_TEXTST_DATA_FONT_ID:
+                 //p_data[0] font_id;
+                 break;
+             case BD_TEXTST_DATA_FONT_STYLE:
+                 if(p_style || (p_style = text_style_Create( STYLE_NO_DEFAULTS )))
+                 {
+                    if(p_data[0] & 0x01)
+                        p_style->i_style_flags |= STYLE_BOLD;
+                    if(p_data[0] & 0x02)
+                        p_style->i_style_flags |= STYLE_ITALIC;
+                    if(p_data[0] & 0x04)
+                        p_style->i_style_flags |= STYLE_OUTLINE;
+                    p_style->i_outline_color = p_dec->p_sys->palette[p_data[1]] & 0x00FFFFFF;
+                    p_style->i_outline_alpha = p_dec->p_sys->palette[p_data[1]] >> 24;
+                    p_style->i_features |= STYLE_HAS_FLAGS | STYLE_HAS_OUTLINE_ALPHA | STYLE_HAS_OUTLINE_COLOR;
+                    //p_data[2] outline__thickness
+                 }
+                 break;
+             case BD_TEXTST_DATA_FONT_SIZE:
+                 /*p_style->f_font_relsize = STYLE_DEFAULT_REL_FONT_SIZE *
+                                           (p_data[0] << 4) / STYLE_DEFAULT_FONT_SIZE;*/
+                 break;
+             case BD_TEXTST_DATA_FONT_COLOR:
+                 p_style->i_font_color = p_dec->p_sys->palette[p_data[1]] & 0x00FFFFFF;
+                 p_style->i_font_alpha = p_dec->p_sys->palette[p_data[1]] >> 24;
+                 p_style->i_features |= STYLE_HAS_FONT_ALPHA | STYLE_HAS_FONT_COLOR;
+                 break;
+             case BD_TEXTST_DATA_NEWLINE:
+                 *pp_last = text_segment_New("\n");
+                 break;
+             case BD_TEXTST_DATA_RESET_STYLE:
+                 if(p_style)
+                 {
+                     text_style_Delete(p_style);
+                     p_style = NULL;
+                 }
+                 break;
+             default:
+                 break;
+         }
+
+         if(*pp_last)
+             pp_last = &(*pp_last)->p_next;
+
+         p_data += length; i_data -= length;
+     }
+
+     if(p_style)
+        text_style_Delete(p_style);
+
+     return i_data_length;
+}
+
+static size_t textst_Decode_palette(decoder_t *p_dec, const uint8_t *p_data, size_t i_data)
+{
+    uint16_t i_size = GetWBE(&p_data[0]);
+    i_size = i_data = __MIN(i_data, i_size);
+    if(i_data > 0)
+    {
+        p_data++; i_data--;
+        while (i_data > 4)
+        {
+            p_dec->p_sys->palette[p_data[0]] = /* YCrCbT to ARGB */
+                ( (uint32_t)((float)p_data[1] +1.402f * (p_data[2]-128)) << 16 ) |
+                ( (uint32_t)((float)p_data[1] -0.34414 * (p_data[3]-128) -0.71414 * (p_data[2]-128)) << 8 ) |
+                ( (uint32_t)((float)p_data[1] +1.722 * (p_data[3]-128)) ) |
+                ( (0xFF - p_data[4]) << 24 );
+            p_data += 5; i_data -= 5;
+        }
+    }
+    return i_size;
+}
+
+static void textst_FillRegions(decoder_t *p_dec, const uint8_t *p_data, size_t i_data,
+                               subpicture_updater_sys_region_t *p_region)
+{
+    subpicture_updater_sys_region_t **pp_last = &p_region;
+    bool palette_update_flag = p_data[0] >> 7;
+    p_data++; i_data--;
+
+    if (palette_update_flag)
+    {
+        size_t i_read = textst_Decode_palette(p_dec, p_data, i_data);
+        p_data += i_read; i_data -= i_read;
+    }
+
+    if(i_data > 2)
+    {
+        uint8_t i_region_count = p_data[0];
+        p_data++; i_data--;
+
+        for(uint8_t i=0; i<i_region_count && i_data > 0; i++)
+        {
+            if(*pp_last == NULL)
+            {
+                *pp_last = SubpictureUpdaterSysRegionNew();
+                if(!*pp_last)
+                    break;
+            }
+            size_t i_read = textst_FillRegion(p_dec, p_data, i_data, *pp_last);
+            (*pp_last)->align = SUBPICTURE_ALIGN_BOTTOM;
+            pp_last = &(*pp_last)->p_next;
+            p_data += i_read; i_data -= i_read;
+        }
+    }
+}
+
+static subpicture_t *Decode(decoder_t *p_dec, block_t **pp_block)
+{
+    subpicture_t *p_sub = NULL;
+    if (pp_block == NULL || *pp_block == NULL)
+        return NULL;
+
+    block_t *p_block = *pp_block;
+    *pp_block = NULL;
+
+    if(p_block)
+    {
+        if(p_block->i_buffer > 18 &&
+           (p_block->i_flags & BLOCK_FLAG_CORRUPTED) == 0 &&
+           (p_sub = decoder_NewSubpictureText(p_dec)))
+        {
+            p_sub->i_start = ((int64_t) (p_block->p_buffer[3] & 0x01) << 32) | GetDWBE(&p_block->p_buffer[4]);
+            p_sub->i_stop = ((int64_t) (p_block->p_buffer[8] & 0x01) << 32) | GetDWBE(&p_block->p_buffer[9]);
+            p_sub->i_start = VLC_TS_0 + p_sub->i_start * 100 / 9;
+            p_sub->i_stop = VLC_TS_0 + p_sub->i_stop * 100 / 9;
+            if(p_sub->i_start < p_block->i_dts)
+            {
+                p_sub->i_stop += p_block->i_dts - p_sub->i_start;
+                p_sub->i_start = p_block->i_dts;
+            }
+
+            textst_FillRegions(p_dec, &p_block->p_buffer[13], p_block->i_buffer - 13,
+                               &p_sub->updater.p_sys->region);
+
+            p_sub->b_absolute = false;
+        }
+
+        block_Release(p_block);
+    }
+
+    return p_sub;
+}
+
+static void Close(vlc_object_t *object)
+{
+    decoder_t *p_dec = (decoder_t*)object;
+    free(p_dec->p_sys);
+}
+
+static int Open(vlc_object_t *object)
+{
+    decoder_t *p_dec = (decoder_t*)object;
+
+    if (p_dec->fmt_in.i_codec != VLC_CODEC_BD_TEXT)
+        return VLC_EGENERIC;
+
+    decoder_sys_t *p_sys = malloc(sizeof(decoder_sys_t));
+    if(!p_sys)
+        return VLC_ENOMEM;
+    memset(p_sys->palette, 0xFF, 256 * sizeof(uint32_t));
+
+    p_dec->p_sys = p_sys;
+    p_dec->pf_decode_sub = Decode;
+    p_dec->fmt_out.i_cat = SPU_ES;
+    p_dec->fmt_out.i_codec = 0;
+
+    return VLC_SUCCESS;
+}
+