[vlc-devel] [vlc-commits] decoders: add HDMV text subtitles decoder

Hugo Beauzée-Luyssen hugo at beauzee.fr
Tue Jan 31 09:56:35 CET 2017


On 01/30/2017 05:42 PM, Francois Cartegnie wrote:
> vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Mon Jan 30 13:25:17 2017 +0100| [7672393eee5539d0815e80329c7abea14f4ed480] | committer: Francois Cartegnie
>
> decoders: add HDMV text subtitles decoder
>
>> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=7672393eee5539d0815e80329c7abea14f4ed480
> ---
>
>  NEWS                      |   1 +
>  modules/MODULES_LIST      |   1 +
>  modules/codec/Makefile.am |   4 +
>  modules/codec/textst.c    | 282 ++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 288 insertions(+)
>
> diff --git a/NEWS b/NEWS
> index 8c684c6..c589109 100644
> --- 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)
> diff --git a/modules/MODULES_LIST b/modules/MODULES_LIST
> index 02ea857..42e83d0 100644
> --- a/modules/MODULES_LIST
> +++ b/modules/MODULES_LIST
> @@ -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
> diff --git a/modules/codec/Makefile.am b/modules/codec/Makefile.am
> index 2154906..b201be4 100644
> --- a/modules/codec/Makefile.am
> +++ b/modules/codec/Makefile.am
> @@ -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
> index 0000000..795d3b2
> --- /dev/null
> +++ b/modules/codec/textst.c
> @@ -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 at 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);

It seems simpler to create a text segment with a NULL text and assign 
psz to it afterward, instead of duplicating the text and freeing 
immediatly after.

> +                    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]);

"p_data" seems enough

> +    i_size = i_data = __MIN(i_data, i_size);
> +    if(i_data > 0)
> +    {
> +        p_data++; i_data--;

Shouldn't you skip 2 bytes here?

> +        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)

Duplicated check

> +    {
> +        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;
> +}
> +
>
> _______________________________________________
> vlc-commits mailing list
> vlc-commits at videolan.org
> https://mailman.videolan.org/listinfo/vlc-commits
>

Regards,


More information about the vlc-devel mailing list