[vlc-commits] demux: stl: rework
Francois Cartegnie
git at videolan.org
Thu Jan 26 19:56:08 CET 2017
vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Thu Jan 26 15:36:25 2017 +0100| [79594cfe0d03ab81bb3636bfeb167761bce0cb00] | committer: Francois Cartegnie
demux: stl: rework
Implements teletext styles, accumulation, multiple spu per block
and removes block duplication.
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=79594cfe0d03ab81bb3636bfeb167761bce0cb00
---
modules/codec/stl.c | 420 ++++++++++++++++++++++++++++++++++++++++------------
1 file changed, 324 insertions(+), 96 deletions(-)
diff --git a/modules/codec/stl.c b/modules/codec/stl.c
index ca628ca..d51a520 100644
--- a/modules/codec/stl.c
+++ b/modules/codec/stl.c
@@ -35,6 +35,8 @@
#include <vlc_memory.h>
#include <vlc_charset.h>
+#include "substext.h" /* required for font scaling / updater */
+
/*****************************************************************************
* Module descriptor
*****************************************************************************/
@@ -54,6 +56,26 @@ vlc_module_end()
*****************************************************************************/
#define GSI_BLOCK_SIZE 1024
+#define STL_GROUPS_MAX 255
+
+#define STL_TEXTFIELD_SIZE 112
+#define STL_TTI_HEADER_SIZE 16
+#define STL_TTI_SIZE (STL_TTI_HEADER_SIZE + STL_TEXTFIELD_SIZE)
+
+#define STL_TF_TELETEXT_FIRST 0x00
+#define STL_TF_TELETEXT_LAST 0x1f
+#define STL_TF_CHARCODE1_FIRST 0x20
+#define STL_TF_CHARCODE1_LAST 0x7f
+#define STL_TF_ITALICS_ON 0x80
+#define STL_TF_ITALICS_OFF 0x81
+#define STL_TF_UNDERLINE_ON 0x82
+#define STL_TF_UNDERLINE_OFF 0x83
+#define STL_TF_BOXING_ON 0x84
+#define STL_TF_BOXING_OFF 0x85
+#define STL_TF_LINEBREAK 0x8a
+#define STL_TF_END_FILL 0x8f
+#define STL_TF_CHARCODE2_FIRST 0xa1
+
typedef enum {
CCT_ISO_6937_2 = 0x3030, CCT_BEGIN = CCT_ISO_6937_2,
CCT_ISO_8859_5 = 0x3031,
@@ -62,12 +84,24 @@ typedef enum {
CCT_ISO_8859_8 = 0x3034, CCT_END = CCT_ISO_8859_8
} cct_number_value_t;
+typedef struct
+{
+ uint8_t i_accumulating;
+ uint8_t i_justify;
+ int64_t i_start;
+ int64_t i_end;
+ text_style_t *p_style;
+ text_segment_t *p_segment;
+ text_segment_t **pp_segment_last;
+} stl_sg_t;
+
typedef struct {
cct_number_value_t value;
const char *str;
} cct_number_t;
struct decoder_sys_t {
+ stl_sg_t groups[STL_GROUPS_MAX + 1];
cct_number_value_t cct;
};
@@ -77,127 +111,318 @@ static cct_number_t cct_nums[] = { {CCT_ISO_6937_2, "ISO_6937-2"},
{CCT_ISO_8859_7, "ISO_8859-7"},
{CCT_ISO_8859_8, "ISO_8859-8"} };
+static text_style_t * CreateGroupStyle()
+{
+ text_style_t *p_style = text_style_Create(STYLE_NO_DEFAULTS);
+ if(p_style)
+ {
+ p_style->i_features = STYLE_HAS_FLAGS|STYLE_HAS_BACKGROUND_ALPHA|STYLE_HAS_BACKGROUND_COLOR;
+ /* Teletext needs default background to black */
+ p_style->i_background_alpha = STYLE_ALPHA_OPAQUE;
+ p_style->i_background_color = 0x000000;
+ p_style->i_font_size = 0;
+ p_style->f_font_relsize = STYLE_DEFAULT_REL_FONT_SIZE;
+ }
+ return p_style;
+}
-static text_segment_t *ParseText(const uint8_t *data, size_t size, const char *charset)
+static void TextBufferFlush(stl_sg_t *p_group, uint8_t *p_buf, uint8_t *pi_buf,
+ const char *psz_charset)
{
- text_style_t *style = NULL;
- char *text = malloc(size);
- if (text == NULL)
- return NULL;
+ if(*pi_buf == 0)
+ return;
- text_segment_t *segment = text_segment_New( NULL );
- if (segment == NULL)
- return NULL;
+ char *psz_utf8 = FromCharset(psz_charset, p_buf, *pi_buf);
+ if(psz_utf8)
+ {
+ *p_group->pp_segment_last = text_segment_New(psz_utf8);
+ if(*p_group->pp_segment_last)
+ {
+ if(p_group->p_style)
+ (*p_group->pp_segment_last)->style = text_style_Duplicate(p_group->p_style);
+ p_group->pp_segment_last = &((*p_group->pp_segment_last)->p_next);
+ }
+ free(psz_utf8);
+ }
+
+ *pi_buf = 0;
+}
- size_t text_size = 0;
+static void GroupParseTeletext(stl_sg_t *p_group, uint8_t code)
+{
+ if(p_group->p_style == NULL &&
+ !(p_group->p_style = CreateGroupStyle()))
+ return;
- for (size_t i = 0; i < size; i++) {
- uint8_t code = data[i];
+ /* See ETS 300 706 Table 26 as EBU 3264 does only name values
+ and does not explain at all */
- if (code == 0x8f)
+ static const uint32_t const colors[] =
+ {
+ 0x000000,
+ 0xFF0000,
+ 0x00FF00,
+ 0xFFFF00,
+ 0x0000FF,
+ 0xFF00FF,
+ 0x00FFFF,
+ 0xFFFFFF,
+ };
+
+ /* Teletext data received, so we need to enable background */
+ p_group->p_style->i_style_flags |= STYLE_BACKGROUND;
+
+ switch(code)
+ {
+ case 0x0c:
+ p_group->p_style->f_font_relsize = STYLE_DEFAULT_REL_FONT_SIZE;
+ p_group->p_style->i_style_flags &= ~(STYLE_DOUBLEWIDTH|STYLE_HALFWIDTH);
break;
- if (code == 0x7f)
- continue;
- if (code & 0x60)
- text[text_size++] = code;
- /* italics begin/end 0x80/0x81, underline being/end 0x82/0x83 */
- if (code >= 0x80 && code <= 0x85 )
- {
- /* Style Change, we do a new segment */
- if( text_size != 0 )
- {
- segment->p_next = ParseText( &data[i], (size - i), charset);
- break;
- }
- else
+
+ case 0x0d: /* double height */
+ p_group->p_style->f_font_relsize = STYLE_DEFAULT_REL_FONT_SIZE * 2;
+ p_group->p_style->i_style_flags &= ~STYLE_DOUBLEWIDTH;
+ p_group->p_style->i_style_flags |= STYLE_HALFWIDTH;
+ break;
+
+ case 0x0e: /* double width */
+ p_group->p_style->f_font_relsize = STYLE_DEFAULT_REL_FONT_SIZE;
+ p_group->p_style->i_style_flags &= ~STYLE_HALFWIDTH;
+ p_group->p_style->i_style_flags |= STYLE_DOUBLEWIDTH;
+ break;
+
+ case 0x0f: /* double size */
+ p_group->p_style->f_font_relsize = STYLE_DEFAULT_REL_FONT_SIZE * 2;
+ p_group->p_style->i_style_flags &= ~(STYLE_DOUBLEWIDTH|STYLE_HALFWIDTH);
+ break;
+
+ case 0x1d:
+ p_group->p_style->i_background_color = p_group->p_style->i_font_color;
+ p_group->p_style->i_features &= ~STYLE_HAS_FONT_COLOR;
+ break;
+
+ case 0x1c:
+ p_group->p_style->i_background_color = colors[0];
+ break;
+
+ default:
+ if(code < 8)
{
- style = text_style_Create( STYLE_NO_DEFAULTS );
- if (code == 0x80)
- style->i_style_flags |= STYLE_ITALIC;
- if (code == 0x81)
- style->i_style_flags &= STYLE_ITALIC;
- if (code == 0x82)
- style->i_style_flags |= STYLE_UNDERLINE;
- if (code == 0x83)
- style->i_style_flags &= STYLE_UNDERLINE;
- if (code == 0x84)
- style->i_style_flags |= STYLE_BACKGROUND;
- if (code == 0x85)
- style->i_style_flags &= STYLE_BACKGROUND;
- style->i_features |= STYLE_HAS_FLAGS;
+ p_group->p_style->i_font_color = colors[code];
+ p_group->p_style->i_features |= STYLE_HAS_FONT_COLOR;
}
- }
- if (code == 0x8a)
- text[text_size++] = '\n';
+
+ /* Need to handle Mosaic ? Really ? */
+ break;
}
- segment->psz_text = FromCharset(charset, text, text_size);
- free(text);
+}
+
+static void GroupApplyStyle(stl_sg_t *p_group, uint8_t code)
+{
+ if(p_group->p_style == NULL &&
+ !(p_group->p_style = CreateGroupStyle()))
+ return;
+
+ switch(code)
+ {
+ case STL_TF_ITALICS_ON:
+ p_group->p_style->i_style_flags |= STYLE_ITALIC;
+ break;
+ case STL_TF_ITALICS_OFF:
+ p_group->p_style->i_style_flags &= ~STYLE_ITALIC;
+ break;
+ case STL_TF_UNDERLINE_ON:
+ p_group->p_style->i_style_flags |= STYLE_UNDERLINE;
+ break;
+ case STL_TF_UNDERLINE_OFF:
+ p_group->p_style->i_style_flags &= ~STYLE_UNDERLINE;
+ break;
+ case STL_TF_BOXING_ON:
+ case STL_TF_BOXING_OFF:
+ default:
+ break;
+ }
+}
- if( style )
- segment->style = style;
+static int64_t ParseTimeCode(const uint8_t *data, double fps)
+{
+ return INT64_C(1000000) * (data[0] * 3600 +
+ data[1] * 60 +
+ data[2] * 1 +
+ data[3] / fps);
+}
- return segment;
+static void ClearTeletextStyles(stl_sg_t *p_group)
+{
+ if(p_group->p_style)
+ {
+ p_group->p_style->i_features &= ~STYLE_HAS_FONT_COLOR;
+ p_group->p_style->i_background_color = 0x000000;
+ p_group->p_style->f_font_relsize = STYLE_DEFAULT_REL_FONT_SIZE;
+ p_group->p_style->i_style_flags &= ~(STYLE_DOUBLEWIDTH|STYLE_HALFWIDTH);
+ }
}
-static subpicture_t *Decode(decoder_t *dec, block_t **block)
+/* Returns true if group is we need to output group */
+static bool ParseTTI(stl_sg_t *p_group, const uint8_t *p_data, const char *psz_charset)
{
- if (block == NULL || *block == NULL)
- return NULL;
+ uint8_t p_buffer[STL_TEXTFIELD_SIZE];
+ uint8_t i_buffer = 0;
- subpicture_t *sub = NULL;
+ /* Header */
+ uint8_t ebn = p_data[3];
+ if(ebn > 0xef && ebn != 0xff)
+ return false;
- block_t *b = *block;
- *block = NULL;
- if (b->i_flags & (BLOCK_FLAG_CORRUPTED))
- goto exit;
- if (b->i_buffer < 128)
- goto exit;
+ if(p_data[15] != 0x00) /* comment flag */
+ return false;
- int payload_size = 0;
- uint8_t *payload = malloc((b->i_buffer / 128) * 112);
- if (!payload)
- goto exit;
+ if(p_data[14] > 0x00)
+ p_group->i_justify = p_data[14];
- /* */
- int j = 0;
- for (unsigned i = 0; i < b->i_buffer / 128; i++)
+ /* Accumulating started or continuing.
+ * We must not flush current segments on output and continue on next block */
+ p_group->i_accumulating = (p_data[4] == 0x01 || p_data[4] == 0x02);
+
+ p_group->i_start = ParseTimeCode( &p_data[5], 30 );
+ p_group->i_end = ParseTimeCode( &p_data[9], 30 );
+
+ /* Text Field */
+ for (size_t i = STL_TTI_HEADER_SIZE; i < STL_TTI_SIZE; i++)
{
- if( b->p_buffer[128 * i + 3] != 0xfe ) {
- memcpy(&payload[112 * j++], &b->p_buffer[128 * i + 16], 112);
- payload_size += 112;
+ const uint8_t code = p_data[i];
+ switch(code)
+ {
+ case STL_TF_LINEBREAK:
+ p_buffer[i_buffer++] = '\n';
+ TextBufferFlush(p_group, p_buffer, &i_buffer, psz_charset);
+ /* Clear teletext styles on each new row */
+ ClearTeletextStyles(p_group);
+ break;
+
+ case STL_TF_END_FILL:
+ TextBufferFlush(p_group, p_buffer, &i_buffer, psz_charset);
+ ClearTeletextStyles(p_group);
+ return true;
+
+ default:
+ if(code <= STL_TF_TELETEXT_LAST)
+ {
+ TextBufferFlush(p_group, p_buffer, &i_buffer, psz_charset);
+ GroupParseTeletext(p_group, code);
+ }
+ else if((code >= STL_TF_CHARCODE1_FIRST && code <= STL_TF_CHARCODE1_LAST) ||
+ code >= STL_TF_CHARCODE2_FIRST)
+ {
+ p_buffer[i_buffer++] = code;
+ }
+ else if(code >= STL_TF_ITALICS_ON && code <= STL_TF_BOXING_OFF)
+ {
+ TextBufferFlush(p_group, p_buffer, &i_buffer, psz_charset);
+ GroupApplyStyle(p_group, code);
+ }
+ break;
}
}
- sub = decoder_NewSubpicture(dec, NULL);
- if (!sub) {
- free(payload);
- goto exit;
+ TextBufferFlush(p_group, p_buffer, &i_buffer, psz_charset);
+
+ return false;
+}
+
+static void FillSubpictureUpdater(stl_sg_t *p_group, subpicture_updater_sys_t *p_spu_sys)
+{
+ if(p_group->i_accumulating)
+ {
+ p_spu_sys->region.p_segments = text_segment_Copy(p_group->p_segment);
+ }
+ else
+ {
+ p_spu_sys->region.p_segments = p_group->p_segment;
+ p_group->p_segment = NULL;
+ p_group->pp_segment_last = &p_group->p_segment;
+ }
+
+ p_spu_sys->region.align = SUBPICTURE_ALIGN_BOTTOM;
+ if(p_group->i_justify == 0x01)
+ p_spu_sys->region.inner_align = SUBPICTURE_ALIGN_LEFT;
+ else if(p_group->i_justify == 0x03)
+ p_spu_sys->region.inner_align = SUBPICTURE_ALIGN_RIGHT;
+}
+
+static void ResetGroups(decoder_sys_t *p_sys)
+{
+ for(size_t i=0; i<=STL_GROUPS_MAX; i++)
+ {
+ if(p_sys->groups[i].p_segment)
+ text_segment_ChainDelete(p_sys->groups[i].p_segment);
+ if(p_sys->groups[i].p_style)
+ text_style_Delete(p_sys->groups[i].p_style);
}
- sub->i_start = b->i_pts;
- sub->i_stop = b->i_pts + b->i_length;
- sub->b_ephemer = b->i_length == 0;
- sub->b_absolute = false;
- //sub->i_original_picture_width = 0;
- //sub->i_original_picture_height = 0;
-
- video_format_t fmt;
- video_format_Init(&fmt, VLC_CODEC_TEXT);
- sub->p_region = subpicture_region_New(&fmt);
- video_format_Clean(&fmt);
-
- if (sub->p_region) {
- sub->p_region->p_text = ParseText(payload,
- payload_size,
- cct_nums[dec->p_sys->cct - CCT_BEGIN].str);
- sub->p_region->i_align = SUBPICTURE_ALIGN_BOTTOM;
+ memset(p_sys->groups, 0, sizeof(stl_sg_t) * (STL_GROUPS_MAX + 1));
+}
+
+static subpicture_t *Decode(decoder_t *p_dec, block_t **pp_block)
+{
+ if (pp_block == NULL || *pp_block == NULL)
+ return NULL;
+
+ subpicture_t *p_sub_first = NULL;
+ subpicture_t **pp_sub_last = &p_sub_first;
+
+ block_t *p_block = *pp_block;
+ *pp_block = NULL;
+
+ if(p_block->i_buffer < STL_TTI_SIZE)
+ p_block->i_flags |= BLOCK_FLAG_CORRUPTED;
+
+ if(p_block->i_flags & (BLOCK_FLAG_CORRUPTED|BLOCK_FLAG_DISCONTINUITY))
+ {
+ ResetGroups(p_dec->p_sys);
+
+ if(p_block->i_flags & BLOCK_FLAG_CORRUPTED)
+ {
+ block_Release(p_block);
+ return NULL;
+ }
}
- free(payload);
+ const char *psz_charset = cct_nums[p_dec->p_sys->cct - CCT_BEGIN].str;
+ for (size_t i = 0; i < p_block->i_buffer / STL_TTI_SIZE; i++)
+ {
+ stl_sg_t *p_group = &p_dec->p_sys->groups[p_block->p_buffer[0]];
+ if(ParseTTI(p_group, &p_block->p_buffer[i * STL_TTI_SIZE], psz_charset) &&
+ p_group->p_segment != NULL )
+ {
+ /* output */
+ subpicture_t *p_sub = decoder_NewSubpictureText(p_dec);
+ if( p_sub )
+ {
+ FillSubpictureUpdater(p_group, p_sub->updater.p_sys );
+
+ p_sub->b_absolute = false;
+
+ if(p_group->i_end && p_group->i_start >= p_block->i_dts)
+ {
+ p_sub->i_start = VLC_TS_0 + p_group->i_start;
+ p_sub->i_stop = VLC_TS_0 + p_group->i_end;
+ }
+ else
+ {
+ p_sub->i_start = p_block->i_pts;
+ p_sub->i_stop = p_block->i_pts + p_block->i_length;
+ p_sub->b_ephemer = (p_block->i_length == 0);
+ }
+
+ *pp_sub_last = p_sub;
+ pp_sub_last = &p_sub->p_next;
+ }
+ }
+ }
-exit:
- block_Release(b);
- return sub;
+ block_Release(p_block);
+ return p_sub_first;
}
static int ExtractCCT(const decoder_t *dec, cct_number_value_t *cct_number)
@@ -238,11 +463,13 @@ static int Open(vlc_object_t *object)
msg_Dbg(dec, "CCT=0x%x", cct);
- decoder_sys_t *sys = malloc(sizeof(*sys));
+ decoder_sys_t *sys = calloc(1, sizeof(*sys));
if (!sys)
return VLC_ENOMEM;
sys->cct = cct;
+ for(size_t i=0; i<=STL_GROUPS_MAX; i++)
+ sys->groups[i].pp_segment_last = &sys->groups[i].p_segment;
dec->p_sys = sys;
dec->pf_decode_sub = Decode;
@@ -254,7 +481,8 @@ static int Open(vlc_object_t *object)
static void Close(vlc_object_t *object)
{
decoder_t *dec = (decoder_t*)object;
- decoder_sys_t *sys = dec->p_sys;
+ decoder_sys_t *p_sys = dec->p_sys;
- free(sys);
+ ResetGroups(p_sys);
+ free(p_sys);
}
More information about the vlc-commits
mailing list