Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  stl.c   Sprache: C

 

/*****************************************************************************
 * stl.c: EBU STL decoder
 *****************************************************************************
 * Copyright (C) 2010 Laurent Aimar
 *
 * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
 *
 * 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 <assert.h>

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_codec.h>
#include <vlc_charset.h>

#include "substext.h" /* required for font scaling / updater */

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/

static int  Open (vlc_object_t *);
static void Close(vlc_object_t *);

vlc_module_begin()
    set_description(N_("EBU STL subtitles decoder"))
    set_subcategory(SUBCAT_INPUT_SCODEC)
    set_capability("spu decoder", 10)
    set_callbacks(Open, Close)
vlc_module_end()

/*****************************************************************************
 * Local definitions/prototypes
 *****************************************************************************/

#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,
    CCT_ISO_8859_6 = 0x3032,
    CCT_ISO_8859_7 = 0x3033,
    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;
    vlc_tick_t i_start;
    vlc_tick_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;

typedef struct
{
    stl_sg_t groups[STL_GROUPS_MAX + 1];
    cct_number_value_t cct;
    uint8_t i_fps;
} decoder_sys_t;

static cct_number_t cct_nums[] = { {CCT_ISO_6937_2, "ISO_6937-2"},
                                   {CCT_ISO_8859_5, "ISO_8859-5"},
                                   {CCT_ISO_8859_6, "ISO_8859-6"},
                                   {CCT_ISO_8859_7, "ISO_8859-7"},
                                   {CCT_ISO_8859_8, "ISO_8859-8"} };

static text_style_t * CreateGroupStyle(void)
{
    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 void TextBufferFlush(stl_sg_t *p_group, uint8_t *p_buf, uint8_t *pi_buf,
                            const char *psz_charset)
{
    if(*pi_buf == 0)
        return;

    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;
}

static void GroupParseTeletext(stl_sg_t *p_group, uint8_t code)
{
    if(p_group->p_style == NULL &&
      !(p_group->p_style = CreateGroupStyle()))
        return;

    /* See ETS 300 706 Table 26 as EBU 3264 does only name values
       and does not explain at all */


    static const uint32_t 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;

        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)
            {
                p_group->p_style->i_font_color = colors[code];
                p_group->p_style->i_features |= STYLE_HAS_FONT_COLOR;
            }

            /* Need to handle Mosaic ? Really ? */
            break;
    }

}

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;
    }
}

static vlc_tick_t ParseTimeCode(const uint8_t *data, double fps)
{
    return vlc_tick_from_sec( data[0] * 3600 +
                         data[1] *   60 +
                         data[2] *    1 +
                         data[3] /  fps);
}

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);
    }
}

/* 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, double fps)
{
    uint8_t p_buffer[STL_TEXTFIELD_SIZE];
    uint8_t i_buffer = 0;

    /* Header */
    uint8_t ebn = p_data[3];
    if(ebn > 0xef && ebn != 0xff)
        return false;

    if(p_data[15] != 0x00) /* comment flag */
        return false;

    if(p_data[14] > 0x00)
        p_group->i_justify = p_data[14];

    /* 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], fps );
    p_group->i_end = ParseTimeCode( &p_data[9], fps );

    /* Text Field */
    for (size_t i = STL_TTI_HEADER_SIZE; i < STL_TTI_SIZE; i++)
    {
        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;
        }
    }

    TextBufferFlush(p_group, p_buffer, &i_buffer, psz_charset);

    return false;
}

static void FillSubpictureUpdater(stl_sg_t *p_group, subtext_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.b_absolute = false;
    p_spu_sys->region.b_in_window = false;
    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++)
    {
        stl_sg_t *p_group = &p_sys->groups[i];
        if(p_group->p_segment)
        {
            text_segment_ChainDelete(p_group->p_segment);
            p_group->p_segment = NULL;
            p_group->pp_segment_last = &p_group->p_segment;
        }

        if(p_group->p_style)
        {
            text_style_Delete(p_group->p_style);
            p_group->p_style = NULL;
        }

        p_group->i_accumulating = false;
        p_group->i_end = VLC_TICK_INVALID;
        p_group->i_start = VLC_TICK_INVALID;
        p_group->i_justify = 0;
    }
}

static int Decode(decoder_t *p_dec, block_t *p_block)
{
    if (p_block == NULL) /* No Drain */
        return VLCDEC_SUCCESS;

    decoder_sys_t *p_sys = p_dec->p_sys;

    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 VLCDEC_SUCCESS;
        }
    }

    const char *psz_charset = cct_nums[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_sys->groups[p_block->p_buffer[0]];
        if(ParseTTI(p_group, &p_block->p_buffer[i * STL_TTI_SIZE], psz_charset, p_sys->i_fps) &&
           p_group->p_segment != NULL )
        {
            /* output */
            subpicture_t *p_sub = decoder_NewSubpictureText(p_dec);
            if( p_sub )
            {
                FillSubpictureUpdater(p_group, p_sub->updater.sys );

                if(p_group->i_end != VLC_TICK_INVALID && p_group->i_start >= p_block->i_dts)
                {
                    p_sub->i_start = VLC_TICK_0 + p_group->i_start;
                    p_sub->i_stop =  VLC_TICK_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);
                }
                decoder_QueueSub(p_dec, p_sub);
            }
        }
    }

    ResetGroups(p_sys);

    block_Release(p_block);
    return VLCDEC_SUCCESS;
}

static int ParseGSI(decoder_t *dec, decoder_sys_t *p_sys)
{
    uint8_t *header = dec->fmt_in->p_extra;
    if (!header) {
        msg_Err(dec, "NULL EBU header (GSI block)\n");
        return VLC_EGENERIC;
    }

    if (GSI_BLOCK_SIZE != dec->fmt_in->i_extra) {
        msg_Err(dec, "EBU header is not in expected size (%zu)\n", dec->fmt_in->i_extra);
        return VLC_EGENERIC;
    }

    char dfc_fps_str[] = { header[6], header[7], '\0' };
    int fps = strtol(dfc_fps_str, NULL, 10);
    if (1 > fps || 60 < fps) {
        msg_Warn(dec, "EBU header contains unsupported DFC fps ('%s'); falling back to 25\n", dfc_fps_str);
        fps = 25;
    }

    int cct = (header[12] << 8) | header[13];
    if (CCT_BEGIN > cct || CCT_END < cct) {
        msg_Err(dec, "EBU header contains illegal CCT (0x%x)\n", cct);
        return VLC_EGENERIC;
    }

    msg_Dbg(dec, "DFC fps=%d, CCT=0x%x", fps, cct);
    p_sys->i_fps = fps;
    p_sys->cct = cct;

    return VLC_SUCCESS;
}

static int Open(vlc_object_t *object)
{
    decoder_t *dec = (decoder_t*)object;

    if (dec->fmt_in->i_codec != VLC_CODEC_EBU_STL)
        return VLC_EGENERIC;

    decoder_sys_t *sys = calloc(1, sizeof(*sys));
    if (!sys)
        return VLC_ENOMEM;

    int rc = ParseGSI(dec, sys);
    if (VLC_SUCCESS != rc) {
        free(sys);
        return rc;
    }

    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 = Decode;
    dec->fmt_out.i_codec = 0;
    return VLC_SUCCESS;
}

static void Close(vlc_object_t *object)
{
    decoder_t *dec = (decoder_t*)object;
    decoder_sys_t *p_sys = dec->p_sys;

    ResetGroups(p_sys);
    free(p_sys);
}

Messung
CHZG
849510093

¤ Dauer der Verarbeitung: 0.9 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104842
#Domains=499162