[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