X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fcodec%2Fcc.c;h=8863f1c6f5aa40afc37aa01b45729bc9251ab96c;hb=56d4e908e1b71fc6161622701d1cf0db64e57aa6;hp=804899a3925f0fa589e9e3ccd73df021f96afa64;hpb=b426aaa95313933712cf386e15a09b7512060d6f;p=vlc diff --git a/modules/codec/cc.c b/modules/codec/cc.c index 804899a392..8863f1c6f5 100644 --- a/modules/codec/cc.c +++ b/modules/codec/cc.c @@ -1,24 +1,23 @@ /***************************************************************************** - * cc608.c : CC 608/708 subtitles decoder + * cc.c : CC 608/708 subtitles decoder ***************************************************************************** - * Copyright (C) 2007 Laurent Aimar - * $Id$ + * Copyright © 2007-2010 Laurent Aimar, 2011 VLC authors and VideoLAN * * Authors: Laurent Aimar < fenrir # via.ecp.fr> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * 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 General Public License for more details. + * 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 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. + * 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. *****************************************************************************/ /***************************************************************************** @@ -37,22 +36,14 @@ # include "config.h" #endif +#include + #include #include -#include #include -#include - -#include -#include -#include #include -#include -#include -#include -#include -#include +#include "substext.h" /***************************************************************************** * Module descriptor. @@ -144,15 +135,12 @@ typedef struct static void Eia608Init( eia608_t * ); static bool Eia608Parse( eia608_t *h, int i_channel_selected, const uint8_t data[2] ); static char *Eia608Text( eia608_t *h, bool b_html ); -static void Eia608Exit( eia608_t * ); /* It will be enough up to 63 B frames, which is far too high for * broadcast environment */ #define CC_MAX_REORDER_SIZE (64) struct decoder_sys_t { - int i; - int i_block; block_t *pp_block[CC_MAX_REORDER_SIZE]; @@ -210,7 +198,7 @@ static int Open( vlc_object_t *p_this ) Eia608Init( &p_sys->eia608 ); p_dec->fmt_out.i_cat = SPU_ES; - p_dec->fmt_out.i_codec = VLC_FOURCC('T','E','X','T'); + p_dec->fmt_out.i_codec = VLC_CODEC_TEXT; return VLC_SUCCESS; } @@ -252,11 +240,9 @@ static void Close( vlc_object_t *p_this ) { decoder_t *p_dec = (decoder_t *)p_this; decoder_sys_t *p_sys = p_dec->p_sys; - int i; - for( i = 0; i < p_sys->i_block; i++ ) + for( int i = 0; i < p_sys->i_block; i++ ) block_Release( p_sys->pp_block[i] ); - Eia608Exit( &p_sys->eia608 ); free( p_sys ); } @@ -280,7 +266,6 @@ static block_t *Pop( decoder_t *p_dec ) decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_block; int i_index; - int i; /* XXX Cc captions data are OUT OF ORDER (because we receive them in the bitstream * order (ie ordered by video picture dts) instead of the display order. * We will simulate a simple IPB buffer scheme @@ -295,11 +280,11 @@ static block_t *Pop( decoder_t *p_dec ) return NULL; p_block = p_sys->pp_block[i_index = 0]; - if( p_block->i_pts > 0 ) + if( p_block->i_pts > VLC_TS_INVALID ) { - for( i = 1; i < p_sys->i_block-1; i++ ) + for( int i = 1; i < p_sys->i_block-1; i++ ) { - if( p_sys->pp_block[i]->i_pts > 0 && p_block->i_pts > 0 && + if( p_sys->pp_block[i]->i_pts > VLC_TS_INVALID && p_block->i_pts > VLC_TS_INVALID && p_sys->pp_block[i]->i_pts < p_block->i_pts ) p_block = p_sys->pp_block[i_index = i]; } @@ -315,10 +300,9 @@ static subpicture_t *Subtitle( decoder_t *p_dec, char *psz_subtitle, char *psz_h { //decoder_sys_t *p_sys = p_dec->p_sys; subpicture_t *p_spu = NULL; - video_format_t fmt; /* We cannot display a subpicture with no date */ - if( i_pts == 0 ) + if( i_pts <= VLC_TS_INVALID ) { msg_Warn( p_dec, "subtitle without a date" ); return NULL; @@ -329,44 +313,27 @@ static subpicture_t *Subtitle( decoder_t *p_dec, char *psz_subtitle, char *psz_h EnsureUTF8( psz_html ); /* Create the subpicture unit */ - p_spu = decoder_NewSubpicture( p_dec ); + p_spu = decoder_NewSubpictureText( p_dec ); if( !p_spu ) { - msg_Warn( p_dec, "can't get spu buffer" ); free( psz_subtitle ); free( psz_html ); return NULL; } + p_spu->i_start = i_pts; + p_spu->i_stop = i_pts + 10000000; /* 10s max */ + p_spu->b_ephemer = true; + p_spu->b_absolute = false; - /* Create a new subpicture region */ - memset( &fmt, 0, sizeof(video_format_t) ); - fmt.i_chroma = VLC_FOURCC('T','E','X','T'); - fmt.i_aspect = 0; - fmt.i_width = fmt.i_height = 0; - fmt.i_x_offset = fmt.i_y_offset = 0; - p_spu->p_region = subpicture_region_New( &fmt ); - if( !p_spu->p_region ) - { - msg_Err( p_dec, "cannot allocate SPU region" ); - free( psz_subtitle ); - free( psz_html ); - decoder_DeleteSubpicture( p_dec, p_spu ); - return NULL; - } - - /* Decode and format the subpicture unit */ - /* Normal text subs, easy markup */ - p_spu->p_region->i_align = SUBPICTURE_ALIGN_BOTTOM;// | SUBPICTURE_ALIGN_LEFT;// | p_sys->i_align; - p_spu->p_region->i_x = 0; //p_sys->i_align ? 20 : 0; - p_spu->p_region->i_y = 10; + subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys; - p_spu->p_region->psz_text = psz_subtitle; - p_spu->p_region->psz_html = psz_html; - - p_spu->i_start = i_pts; - p_spu->i_stop = i_pts + 10000000; /* 10s max */ - p_spu->b_ephemer = true; - p_spu->b_absolute = false; + /* The "leavetext" alignment is a special mode where the subpicture + region itself gets aligned, but the text inside it does not */ + p_spu_sys->align = SUBPICTURE_ALIGN_LEAVETEXT; + p_spu_sys->text = psz_subtitle; + p_spu_sys->html = psz_html; + p_spu_sys->i_font_height_percent = 5; + p_spu_sys->renderbg = true; return p_spu; } @@ -394,7 +361,7 @@ static subpicture_t *Convert( decoder_t *p_dec, block_t *p_block ) if( b_changed ) { char *psz_subtitle = Eia608Text( &p_sys->eia608, false ); - char *psz_html = NULL;//Eia608Text( &p_sys->eia608, true ); + char *psz_html = Eia608Text( &p_sys->eia608, true ); return Subtitle( p_dec, psz_subtitle, psz_html, i_pts ); } return NULL; @@ -457,7 +424,6 @@ static void Eia608Cursor( eia608_t *h, int dx ) static void Eia608ClearScreenRowX( eia608_t *h, int i_screen, int i_row, int x ) { eia608_screen *screen = &h->screen[i_screen]; - int i; if( x == 0 ) { @@ -466,7 +432,7 @@ static void Eia608ClearScreenRowX( eia608_t *h, int i_screen, int i_row, int x ) else { screen->row_used[i_row] = false; - for( i = 0; i < x; i++ ) + for( int i = 0; i < x; i++ ) { if( screen->characters[i_row][i] != ' ' || screen->colors[i_row][i] != EIA608_COLOR_DEFAULT || @@ -493,8 +459,7 @@ static void Eia608ClearScreenRow( eia608_t *h, int i_screen, int i_row ) static void Eia608ClearScreen( eia608_t *h, int i_screen ) { - int i; - for( i = 0; i < EIA608_SCREEN_ROWS; i++ ) + for( int i = 0; i < EIA608_SCREEN_ROWS; i++ ) Eia608ClearScreenRow( h, i_screen, i ); } @@ -566,11 +531,13 @@ static void Eia608EraseToEndOfRow( eia608_t *h ) static void Eia608RollUp( eia608_t *h ) { + if( h->mode == EIA608_MODE_TEXT ) + return; + const int i_screen = Eia608GetWritingScreenIndex( h ); eia608_screen *screen = &h->screen[i_screen]; int keep_lines; - int i; /* Window size */ if( h->mode == EIA608_MODE_ROLLUP_2 ) @@ -586,11 +553,11 @@ static void Eia608RollUp( eia608_t *h ) h->cursor.i_column = 0; /* Erase lines above our window */ - for( i = 0; i < h->cursor.i_row - keep_lines; i++ ) + for( int i = 0; i < h->cursor.i_row - keep_lines; i++ ) Eia608ClearScreenRow( h, i_screen, i ); /* Move up */ - for( i = 0; i < keep_lines-1; i++ ) + for( int i = 0; i < keep_lines-1; i++ ) { const int i_row = h->cursor.i_row - keep_lines + i + 1; if( i_row < 0 ) @@ -604,7 +571,7 @@ static void Eia608RollUp( eia608_t *h ) /* Reset current row */ Eia608ClearScreenRow( h, i_screen, h->cursor.i_row ); } -static void Eia608ParseChannel( eia608_t *h, uint8_t d[2] ) +static void Eia608ParseChannel( eia608_t *h, const uint8_t d[2] ) { /* Check odd parity */ static const int p4[16] = { @@ -619,15 +586,10 @@ static void Eia608ParseChannel( eia608_t *h, uint8_t d[2] ) /* */ const int d1 = d[0] & 0x7f; - const int d2 = d[1] & 0x7f; - if( d1 == 0x14 ) - h->i_channel = 1; - else if( d1 == 0x1c ) - h->i_channel = 2; - else if( ( d1 >= 0x01 && d1 <= 0x0f ) || d1 == 0x15 ) + if( d1 >= 0x10 && d1 <= 0x1f ) + h->i_channel = 1 + ((d1 & 0x08) != 0); + else if( d1 < 0x10 ) h->i_channel = 3; - else if( d1 == 0x1d ) - h->i_channel = 4; } static bool Eia608ParseTextAttribute( eia608_t *h, uint8_t d2 ) { @@ -782,6 +744,9 @@ static bool Eia608ParsePac( eia608_t *h, uint8_t d1, uint8_t d2 ) else if( d2 >= 0x40 ) d2 -= 0x40; h->cursor.i_column = pac2_attribs[d2].i_column; + h->color = pac2_attribs[d2].i_color; + h->font = pac2_attribs[d2].i_font; + return false; } @@ -854,7 +819,7 @@ static void Eia608TextUtf8( char *psz_utf8, uint8_t c ) // Returns number of byt E2( 0x86, 0xc2,0xa3), // Pounds sterling E3( 0x87, 0xe2,0x99,0xaa), // Music note E2( 0x88, 0xc3,0xa0), // lowercase a, grave accent - E1( 0x89, 0x20), // transparent space, we make it regular + E2( 0x89, 0xc2,0xa0), // transparent space E2( 0x8a, 0xc3,0xa8), // lowercase e, grave accent E2( 0x8b, 0xc3,0xa2), // lowercase a, circumflex accent E2( 0x8c, 0xc3,0xaa), // lowercase e, circumflex accent @@ -870,15 +835,15 @@ static void Eia608TextUtf8( char *psz_utf8, uint8_t c ) // Returns number of byt E2( 0x94, 0xc3,0x9c), // capital letter U with diaresis E2( 0x95, 0xc3,0xbc), // lowercase letter U with diaeresis E1( 0x96, 0x27), // apostrophe - E2( 0x97, 0xc1,0xa1), // inverted exclamation mark + E2( 0x97, 0xc2,0xa1), // inverted exclamation mark E1( 0x98, 0x2a), // asterisk E1( 0x99, 0x27), // apostrophe (yes, duped). See CCADI source code. E1( 0x9a, 0x2d), // hyphen-minus E2( 0x9b, 0xc2,0xa9), // copyright sign E3( 0x9c, 0xe2,0x84,0xa0), // Service mark E1( 0x9d, 0x2e), // Full stop (.) - E1( 0x9e, 0x22), // Quoatation mark - E1( 0x9f, 0x22), // Quoatation mark + E3( 0x9e, 0xe2,0x80,0x9c), // Quotation mark + E3( 0x9f, 0xe2,0x80,0x9d), // Quotation mark E2( 0xa0, 0xc3,0x80), // uppercase A, grave accent E2( 0xa1, 0xc3,0x82), // uppercase A, circumflex E2( 0xa2, 0xc3,0x87), // uppercase C with cedilla @@ -936,23 +901,14 @@ static void Eia608TextUtf8( char *psz_utf8, uint8_t c ) // Returns number of byt #undef E2 #undef E1 - static const int i_c2utf8 = sizeof(c2utf8)/sizeof(*c2utf8); - int i; + for( size_t i = 0; i < ARRAY_SIZE(c2utf8) ; i++ ) + if( c2utf8[i].c == c ) { + strcpy( psz_utf8, c2utf8[i].utf8 ); + return; + } - for( i = 0; i < i_c2utf8; i++ ) - { - if( c2utf8[i].c == c ) - break; - } - if( i >= i_c2utf8 ) - { - psz_utf8[0] = c < 0x80 ? c : '?'; /* Normal : Unsupported */ - psz_utf8[1] = '\0'; - } - else - { - strcpy( psz_utf8, c2utf8[i].utf8 ); - } + psz_utf8[0] = c < 0x80 ? c : '?'; /* Normal : Unsupported */ + psz_utf8[1] = '\0'; } static void Eia608Strlcat( char *d, const char *s, int i_max ) @@ -963,6 +919,8 @@ static void Eia608Strlcat( char *d, const char *s, int i_max ) d[i_max-1] = '\0'; } +#define CAT(t) Eia608Strlcat( psz_text, t, i_text_max ) + static void Eia608TextLine( struct eia608_screen *screen, char *psz_text, int i_text_max, int i_row, bool b_html ) { const uint8_t *p_char = screen->characters[i_row]; @@ -974,11 +932,22 @@ static void Eia608TextLine( struct eia608_screen *screen, char *psz_text, int i_ eia608_color_t last_color = EIA608_COLOR_DEFAULT; bool b_last_italics = false; bool b_last_underline = false; + char utf8[4]; /* Search the start */ i_start = 0; - while( i_start < EIA608_SCREEN_COLUMNS-1 && p_char[i_start] == ' ' ) + + /* Ensure we get a monospaced font (required for accurate positioning */ + if( b_html ) + CAT( "" ); + + /* Convert leading spaces to non-breaking so that they don't get + stripped by the RenderHtml routine as regular whitespace */ + while( i_start < EIA608_SCREEN_COLUMNS && p_char[i_start] == ' ' ) { + Eia608TextUtf8( utf8, 0x89 ); + CAT( utf8 ); i_start++; + } /* Search the end */ i_end = EIA608_SCREEN_COLUMNS-1; @@ -986,13 +955,11 @@ static void Eia608TextLine( struct eia608_screen *screen, char *psz_text, int i_ i_end--; /* */ -#define CAT(t) Eia608Strlcat( psz_text, t, i_text_max ) for( x = i_start; x <= i_end; x++ ) { eia608_color_t color = p_color[x]; bool b_italics = p_font[x] & EIA608_FONT_ITALICS; bool b_underline = p_font[x] & EIA608_FONT_UNDERLINE; - char utf8[4]; /* */ if( b_html ) @@ -1006,7 +973,7 @@ static void Eia608TextLine( struct eia608_screen *screen, char *psz_text, int i_ /* Be sure to create valid html */ b_close_italics |= b_last_italics && b_close_color; - b_close_underline = b_last_underline && ( b_close_italics || b_close_color ); + b_close_underline |= b_last_underline && ( b_close_italics || b_close_color ); if( b_close_underline ) CAT( "" ); @@ -1027,9 +994,9 @@ static void Eia608TextLine( struct eia608_screen *screen, char *psz_text, int i_ "#ff00ff", // magenta "#ffffff", // user defined XXX we use white }; - CAT( "" ); + CAT( "\">" ); } if( ( b_close_italics && b_italics ) || ( b_italics && !b_last_italics ) ) CAT( "" ); @@ -1037,9 +1004,34 @@ static void Eia608TextLine( struct eia608_screen *screen, char *psz_text, int i_ CAT( "" ); } - /* */ - Eia608TextUtf8( utf8, p_char[x] ); - CAT( utf8 ); + if( b_html ) { + /* Escape XML reserved characters + http://www.w3.org/TR/xml/#syntax */ + switch (p_char[x]) { + case '>': + CAT( ">" ); + break; + case '<': + CAT( "<" ); + break; + case '"': + CAT( """ ); + break; + case '\'': + CAT( "'" ); + break; + case '&': + CAT( "&" ); + break; + default: + Eia608TextUtf8( utf8, p_char[x] ); + CAT( utf8 ); + break; + } + } else { + Eia608TextUtf8( utf8, p_char[x] ); + CAT( utf8 ); + } /* */ b_last_underline = b_underline; @@ -1054,6 +1046,7 @@ static void Eia608TextLine( struct eia608_screen *screen, char *psz_text, int i_ CAT( "" ); if( last_color != EIA608_COLOR_DEFAULT ) CAT( "" ); + CAT( "" ); } #undef CAT } @@ -1113,11 +1106,10 @@ static bool Eia608Parse( eia608_t *h, int i_channel_selected, const uint8_t data static char *Eia608Text( eia608_t *h, bool b_html ) { - const int i_size = EIA608_SCREEN_ROWS * 3 * EIA608_SCREEN_COLUMNS+1; + const int i_size = EIA608_SCREEN_ROWS * 10 * EIA608_SCREEN_COLUMNS+1; struct eia608_screen *screen = &h->screen[h->i_screen]; bool b_first = true; char *psz; - int i; /* We allocate a buffer big enough for normal case */ psz = malloc( i_size ); @@ -1126,11 +1118,8 @@ static char *Eia608Text( eia608_t *h, bool b_html ) *psz = '\0'; if( b_html ) Eia608Strlcat( psz, "", i_size ); - for( i = 0; i < EIA608_SCREEN_ROWS; i++ ) + for( int i = 0; i < EIA608_SCREEN_ROWS; i++ ) { - if( !screen->row_used[i] ) - continue; - if( !b_first ) Eia608Strlcat( psz, b_html ? "
" : "\n", i_size ); b_first = false; @@ -1141,8 +1130,3 @@ static char *Eia608Text( eia608_t *h, bool b_html ) Eia608Strlcat( psz, "
", i_size ); return psz; } - -static void Eia608Exit( eia608_t *h ) -{ -} -