[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