From: Gildas Bazin Date: Wed, 6 Nov 2002 21:48:24 +0000 (+0000) Subject: * modules/codec/spudec/*: modified the spu decoder to handle text subtitles. X-Git-Tag: 0.5.0~764 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=85071f3419dec4837538a25f0ab8853cd6ddf531;p=vlc * modules/codec/spudec/*: modified the spu decoder to handle text subtitles. Only one format of text subtitles is supported right now but we should be able to expand this by modifying modules/codec/spudec/text.c. Most of this work comes from by Andrew Flintham ( thanks a bunch Andrew :). * share/font-eutopiabold36.rle: new font for the text subtitler, courtesy of Andrew Flintham. * AUTHORS: added Andrew Flintham to the authors file. * modules/demux/ogg.c: modified the ogg demuxer to handle subtitles. * modules/codec/ffmpeg/*: modified the ffmpeg decoder to always keep the last decoded frame linked. --- diff --git a/AUTHORS b/AUTHORS index c81a62d535..48338788d4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -419,3 +419,7 @@ C: ipkiss D: Win32 interface S: France +N: Andrew Flintham +E: amf@cus.org.uk +D: text subtitler and font scripts +S: United Kingdom diff --git a/modules/codec/ffmpeg/video.c b/modules/codec/ffmpeg/video.c index 99a2ecdce3..068214a1e3 100644 --- a/modules/codec/ffmpeg/video.c +++ b/modules/codec/ffmpeg/video.c @@ -2,7 +2,7 @@ * video.c: video decoder using ffmpeg library ***************************************************************************** * Copyright (C) 1999-2001 VideoLAN - * $Id: video.c,v 1.2 2002/11/05 10:07:56 gbazin Exp $ + * $Id: video.c,v 1.3 2002/11/06 21:48:24 gbazin Exp $ * * Authors: Laurent Aimar * Gildas Bazin @@ -349,6 +349,8 @@ int E_( InitThread_Video )( vdec_thread_t *p_vdec ) p_vdec->b_hurry_up = config_GetInt(p_vdec->p_fifo, "ffmpeg-hurry-up"); + p_vdec->p_lastpic = NULL; + p_vdec->p_secondlastpic = NULL; p_vdec->b_direct_rendering = 0; #if LIBAVCODEC_BUILD > 4615 if( (p_vdec->p_codec->capabilities & CODEC_CAP_DR1) @@ -668,6 +670,11 @@ void E_( DecodeThread_Video )( vdec_thread_t *p_vdec ) *****************************************************************************/ void E_( EndThread_Video )( vdec_thread_t *p_vdec ) { + if( p_vdec->p_secondlastpic ) + vout_UnlinkPicture( p_vdec->p_vout, p_vdec->p_secondlastpic ); + if( p_vdec->p_lastpic ) + vout_UnlinkPicture( p_vdec->p_vout, p_vdec->p_lastpic ); + if( p_vdec->p_pp ) { /* release postprocessing module */ @@ -797,8 +804,13 @@ static int ffmpeg_GetFrameBuf( struct AVCodecContext *avctx, int width, msleep( VOUT_OUTMEM_SLEEP ); } - /* FIXME: we may have to use link/unlinkPicture to fully support streams - * with B FRAMES */ + /* FIXME: We keep the last picture linked until the current one is decoded, + * this trick won't work with streams with B frames though. */ + vout_LinkPicture( p_vdec->p_vout, p_pic ); + if( p_vdec->p_secondlastpic ) + vout_UnlinkPicture( p_vdec->p_vout, p_vdec->p_secondlastpic ); + p_vdec->p_secondlastpic = p_vdec->p_lastpic; + p_vdec->p_lastpic = p_pic; avctx->draw_horiz_band= NULL; avctx->dr_buffer[0]= p_pic->p[0].p_pixels; diff --git a/modules/codec/ffmpeg/video.h b/modules/codec/ffmpeg/video.h index bd1bdbcfd7..497c4b2531 100644 --- a/modules/codec/ffmpeg/video.h +++ b/modules/codec/ffmpeg/video.h @@ -2,7 +2,7 @@ * video.h: video decoder using ffmpeg library ***************************************************************************** * Copyright (C) 1999-2001 VideoLAN - * $Id: video.h,v 1.2 2002/11/05 10:07:56 gbazin Exp $ + * $Id: video.h,v 1.3 2002/11/06 21:48:24 gbazin Exp $ * * Authors: Laurent Aimar * @@ -41,7 +41,8 @@ typedef struct vdec_thread_s /* for direct rendering */ int b_direct_rendering; - + picture_t *p_lastpic; + picture_t *p_secondlastpic; } vdec_thread_t; diff --git a/modules/codec/spudec/Modules.am b/modules/codec/spudec/Modules.am index 8c5deab2b5..78a219d899 100644 --- a/modules/codec/spudec/Modules.am +++ b/modules/codec/spudec/Modules.am @@ -1,7 +1,9 @@ SOURCES_spudec = \ modules/codec/spudec/spudec.c \ modules/codec/spudec/parse.c \ - modules/codec/spudec/render.c + modules/codec/spudec/render.c \ + modules/codec/spudec/text.c \ + modules/codec/spudec/subtitler.c noinst_HEADERS += \ modules/codec/spudec/spudec.h diff --git a/modules/codec/spudec/parse.c b/modules/codec/spudec/parse.c index 02e2cd48da..7b72cb3845 100644 --- a/modules/codec/spudec/parse.c +++ b/modules/codec/spudec/parse.c @@ -2,7 +2,7 @@ * parse.c: SPU parser ***************************************************************************** * Copyright (C) 2000-2001 VideoLAN - * $Id: parse.c,v 1.4 2002/11/06 18:07:57 sam Exp $ + * $Id: parse.c,v 1.5 2002/11/06 21:48:24 gbazin Exp $ * * Authors: Samuel Hocevar * @@ -31,14 +31,6 @@ #include #include -#ifdef HAVE_UNISTD_H -# include /* getpid() */ -#endif - -#ifdef WIN32 /* getpid() for win32 is located in process.h */ -# include -#endif - #include "spudec.h" /***************************************************************************** diff --git a/modules/codec/spudec/render.c b/modules/codec/spudec/render.c index c913280b71..60bec344be 100644 --- a/modules/codec/spudec/render.c +++ b/modules/codec/spudec/render.c @@ -2,7 +2,7 @@ * render.c : SPU renderer ***************************************************************************** * Copyright (C) 2000-2001 VideoLAN - * $Id: render.c,v 1.3 2002/11/06 18:07:57 sam Exp $ + * $Id: render.c,v 1.4 2002/11/06 21:48:24 gbazin Exp $ * * Authors: Samuel Hocevar * Rudolf Cornelissen @@ -33,14 +33,6 @@ #include #include -#ifdef HAVE_UNISTD_H -# include /* getpid() */ -#endif - -#ifdef WIN32 /* getpid() for win32 is located in process.h */ -# include -#endif - #include "spudec.h" /***************************************************************************** diff --git a/modules/codec/spudec/spudec.c b/modules/codec/spudec/spudec.c index 03383909a5..50da46991f 100644 --- a/modules/codec/spudec/spudec.c +++ b/modules/codec/spudec/spudec.c @@ -2,7 +2,7 @@ * spudec.c : SPU decoder thread ***************************************************************************** * Copyright (C) 2000-2001 VideoLAN - * $Id: spudec.c,v 1.7 2002/11/06 18:07:57 sam Exp $ + * $Id: spudec.c,v 1.8 2002/11/06 21:48:24 gbazin Exp $ * * Authors: Samuel Hocevar * @@ -31,14 +31,6 @@ #include #include -#ifdef HAVE_UNISTD_H -# include /* getpid() */ -#endif - -#ifdef WIN32 /* getpid() for win32 is located in process.h */ -# include -#endif - #include "spudec.h" /***************************************************************************** @@ -52,8 +44,16 @@ static void EndThread ( spudec_thread_t * ); /***************************************************************************** * Module descriptor. *****************************************************************************/ +#define FONT_TEXT N_("Font used by the text subtitler") +#define FONT_LONGTEXT N_(\ + "When the subtitles are coded in text form then, you can choose " \ + "which font will be used to display them.") + vlc_module_begin(); - set_description( _("DVD subtitles decoder module") ); + add_category_hint( N_("subtitles"), NULL ); + add_file( "spudec-font", "./share/font-eutopiabold36.rle", NULL, + FONT_TEXT, FONT_LONGTEXT ); + set_description( _("subtitles decoder module") ); set_capability( "decoder", 50 ); set_callbacks( OpenDecoder, NULL ); vlc_module_end(); @@ -69,11 +69,12 @@ static int OpenDecoder( vlc_object_t *p_this ) decoder_fifo_t *p_fifo = (decoder_fifo_t*) p_this; if( p_fifo->i_fourcc != VLC_FOURCC('s','p','u',' ') - && p_fifo->i_fourcc != VLC_FOURCC('s','p','u','b') ) - { + && p_fifo->i_fourcc != VLC_FOURCC('s','p','u','b') + && p_fifo->i_fourcc != VLC_FOURCC('s','u','b','t') ) + { return VLC_EGENERIC; } - + p_fifo->pf_run = RunDecoder; return VLC_SUCCESS; @@ -85,6 +86,8 @@ static int OpenDecoder( vlc_object_t *p_this ) static int RunDecoder( decoder_fifo_t * p_fifo ) { spudec_thread_t * p_spudec; + subtitler_font_t * p_font; + char * psz_font; /* Allocate the memory needed to store the thread's structure */ p_spudec = (spudec_thread_t *)malloc( sizeof(spudec_thread_t) ); @@ -101,7 +104,7 @@ static int RunDecoder( decoder_fifo_t * p_fifo ) */ p_spudec->p_vout = NULL; p_spudec->p_fifo = p_fifo; - + /* * Initialize thread and free configuration */ @@ -111,14 +114,48 @@ static int RunDecoder( decoder_fifo_t * p_fifo ) * Main loop - it is not executed if an error occured during * initialization */ - while( (!p_spudec->p_fifo->b_die) && (!p_spudec->p_fifo->b_error) ) + if( p_fifo->i_fourcc == VLC_FOURCC('s','u','b','t') ) { - if( E_(SyncPacket)( p_spudec ) ) + /* Here we are dealing with text subtitles */ + + if( (psz_font = config_GetPsz( p_fifo, "spudec-font" )) == NULL ) + { + msg_Err( p_fifo, "no default font selected" ); + p_font = NULL; + p_spudec->p_fifo->b_error; + } + else { - continue; + p_font = subtitler_LoadFont( p_spudec->p_vout, psz_font ); + if ( p_font == NULL ) + { + msg_Err( p_fifo, "unable to load font: %s", psz_font ); + p_spudec->p_fifo->b_error; + } } + if( psz_font ) free( psz_font ); - E_(ParsePacket)( p_spudec ); + while( (!p_spudec->p_fifo->b_die) && (!p_spudec->p_fifo->b_error) ) + { + E_(ParseText)( p_spudec, p_font ); + } + + if( p_font ) subtitler_UnloadFont( p_spudec->p_vout, p_font ); + + } + else + { + /* Here we are dealing with sub-pictures subtitles*/ + + while( (!p_spudec->p_fifo->b_die) && (!p_spudec->p_fifo->b_error) ) + { + if( E_(SyncPacket)( p_spudec ) ) + { + continue; + } + + E_(ParsePacket)( p_spudec ); + } } /* @@ -154,8 +191,8 @@ static int InitThread( spudec_thread_t *p_spudec ) { if( p_spudec->p_fifo->b_die || p_spudec->p_fifo->b_error ) { - /* Call InitBitstream anyway so p_spudec is in a known state - * before calling CloseBitstream */ + /* Call InitBitstream anyway so p_spudec->bit_stream is in a known + * state before calling CloseBitstream */ InitBitstream( &p_spudec->bit_stream, p_spudec->p_fifo, NULL, NULL ); return -1; @@ -209,4 +246,3 @@ static void EndThread( spudec_thread_t *p_spudec ) CloseBitstream( &p_spudec->bit_stream ); free( p_spudec ); } - diff --git a/modules/codec/spudec/spudec.h b/modules/codec/spudec/spudec.h index 887380e787..53d434a700 100644 --- a/modules/codec/spudec/spudec.h +++ b/modules/codec/spudec/spudec.h @@ -2,7 +2,7 @@ * spudec.h : sub picture unit decoder thread interface ***************************************************************************** * Copyright (C) 1999, 2000 VideoLAN - * $Id: spudec.h,v 1.3 2002/11/06 18:07:57 sam Exp $ + * $Id: spudec.h,v 1.4 2002/11/06 21:48:24 gbazin Exp $ * * Authors: Samuel Hocevar * @@ -44,6 +44,18 @@ struct subpicture_sys_t int i_x_start, i_y_start, i_x_end, i_y_end; }; +/***************************************************************************** + * subtitler_font_t : proportional font + *****************************************************************************/ +typedef struct subtitler_font_s +{ + int i_height; /* character height in pixels */ + int i_width[256]; /* character widths in pixels */ + int i_memory[256]; /* amount of memory used by character */ + int * p_length[256]; /* line byte widths */ + u16 ** p_offset[256]; /* pointer to RLE data */ +} subtitler_font_t; + /***************************************************************************** * spudec_thread_t : sub picture unit decoder thread descriptor *****************************************************************************/ @@ -99,3 +111,9 @@ void E_(ParsePacket) ( spudec_thread_t * ); void E_(RenderSPU) ( vout_thread_t *, picture_t *, const subpicture_t * ); +void E_(ParseText) ( spudec_thread_t *, subtitler_font_t * ); + +subtitler_font_t *E_(subtitler_LoadFont) ( vout_thread_t *, const char * ); +void E_(subtitler_UnloadFont) ( vout_thread_t *, subtitler_font_t * ); +void E_(subtitler_PlotSubtitle) ( vout_thread_t *, char *, subtitler_font_t *, + mtime_t, mtime_t ); diff --git a/modules/codec/spudec/subtitler.c b/modules/codec/spudec/subtitler.c new file mode 100644 index 0000000000..9d10405553 --- /dev/null +++ b/modules/codec/spudec/subtitler.c @@ -0,0 +1,577 @@ +/***************************************************************************** + * subtitler.c : subtitler font routines + ***************************************************************************** + * Copyright (C) 1999, 2000 VideoLAN + * + * Authors: Andrew Flintham + * + * 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 + * (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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#include /* malloc(), free() */ +#include /* memcpy(), memset() */ +#include /* errno */ +#include /* open() */ +#include /* toascii() */ + +#include +#include +#include + +#ifdef HAVE_UNISTD_H +# include /* read(), close() */ +#endif + +#include +#include + +#include "spudec.h" + +/***************************************************************************** + * subtitler_line : internal structure for an individual line in a subtitle + *****************************************************************************/ +typedef struct subtitler_line_s +{ + struct subtitler_line_s * p_next; + char * p_text; +} subtitler_line_t; + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static uint16_t *PlotSubtitleLine( char *, subtitler_font_t *, int, + uint16_t * ); +static void DestroySPU ( subpicture_t * ); + +/***************************************************************************** + * subtitler_LoadFont: load a run-length encoded font file into memory + ***************************************************************************** + * RLE font files have the following format: + * + * 2 bytes : magic number: 0x36 0x05 + * 1 byte : font height in rows + * + * then, per character: + * 1 byte : character + * 1 byte : character width in pixels + * + * then, per row: + * 1 byte : length of row, in entries + * + * then, per entry + * 1 byte : colour + * 1 byte : number of pixels of that colour + * + * to end: + * 1 byte : 0xff + *****************************************************************************/ +subtitler_font_t* subtitler_LoadFont( vout_thread_t * p_vout, + const char * psz_name ) +{ + subtitler_font_t * p_font; + + int i; + int i_file; + int i_char; + int i_length; + int i_line; + int i_total_length; + + byte_t pi_buffer[512]; /* file buffer */ + + msg_Dbg( p_vout, "loading font '%s'", psz_name ); + + i_file = open( psz_name, O_RDONLY ); + + if( i_file == -1 ) + { + msg_Err( p_vout, "can't open font file '%s' (%s)", psz_name, + strerror(errno) ); + return( NULL ); + } + + /* Read magick number */ + if( read( i_file, pi_buffer, 2 ) != 2 ) + { + msg_Err( p_vout, "unexpected end of font file '%s'", psz_name ); + close( i_file ); + return( NULL ); + } + if( pi_buffer[0] != 0x36 || pi_buffer[1] != 0x05 ) + { + msg_Err( p_vout, "file '%s' is not a font file", psz_name ); + close( i_file ); + return( NULL ); + } + + p_font = malloc( sizeof( subtitler_font_t ) ); + + if( p_font == NULL ) + { + msg_Err( p_vout, "out of memory" ); + close( i_file ); + return NULL; + } + + /* Read font height */ + if( read( i_file, pi_buffer, 1 ) != 1 ) + { + msg_Err( p_vout, "unexpected end of font file '%s'", psz_name ); + free( p_font ); + close( i_file ); + return( NULL ); + } + p_font->i_height = pi_buffer[0]; + + /* Initialise font character data */ + for( i = 0; i < 256; i++ ) + { + p_font->i_width[i] = 0; + p_font->i_memory[i] = 0; + p_font->p_offset[i] = NULL; + p_font->p_length[i] = NULL; + } + + while(1) + { + /* Read character number */ + if( read( i_file, pi_buffer, 1 ) != 1) + { + msg_Err( p_vout, "unexpected end of font file '%s'", psz_name ); + close( i_file ); + subtitler_UnloadFont( p_vout, p_font ); + return( NULL ); + } + i_char = pi_buffer[0]; + + /* Character 255 signals the end of the font file */ + if(i_char == 255) + { + break; + } + + /* Read character width */ + if( read( i_file, pi_buffer, 1 ) != 1 ) + { + msg_Err( p_vout, "unexpected end of font file '%s'", psz_name ); + close( i_file ); + subtitler_UnloadFont( p_vout, p_font ); + return( NULL ); + } + p_font->i_width[ i_char ] = pi_buffer[0]; + + p_font->p_length[ i_char ] = (int *) malloc( + sizeof(int) * p_font->i_height ); + p_font->p_offset[ i_char ] = (uint16_t **) malloc( + sizeof(uint16_t *) * p_font->i_height); + + if( p_font->p_length[ i_char] == NULL || + p_font->p_offset[ i_char ] == NULL ) + { + msg_Err( p_vout, "out of memory" ); + close( i_file ); + subtitler_UnloadFont( p_vout, p_font ); + return NULL; + } + for( i_line=0; i_line < p_font->i_height; i_line ++ ) + { + p_font->p_offset[ i_char ][ i_line ] = NULL; + } + + i_total_length=0; + for( i_line = 0; i_line < p_font->i_height; i_line ++ ) + { + /* Read line length */ + if( read( i_file, pi_buffer, 1 ) != 1) + { + msg_Err( p_vout, "unexpected end of font file '%s'", psz_name); + subtitler_UnloadFont( p_vout, p_font ); + close( i_file ); + return( NULL ); + } + i_length = pi_buffer[0]; + p_font->p_length[ i_char ][ i_line ] = i_length; + + i_total_length += i_length; + + /* Read line RLE data */ + if( read( i_file, pi_buffer, i_length*2 ) != i_length*2) + { + msg_Err( p_vout, "unexpected end of font file '%s'", psz_name); + subtitler_UnloadFont( p_vout, p_font ); + close( i_file ); + return( NULL ); + } + p_font->p_offset[ i_char ][ i_line ] = + (uint16_t *) malloc( sizeof( uint16_t ) * i_length ); + if( p_font->p_offset[ i_char ][ i_line ] == NULL ) + { + msg_Err( p_vout, "out of memory" ); + close( i_file ); + subtitler_UnloadFont( p_vout, p_font ); + return NULL; + } + for( i = 0; i < i_length; i++ ) + { + *( p_font->p_offset[ i_char ][ i_line ] + i ) = + (uint16_t) ( pi_buffer[ i * 2 ] + + ( pi_buffer[ i * 2 + 1 ] << 2 ) ); + } + + } + + /* Set total memory size of character */ + p_font->i_memory[ i_char ] = i_total_length; + + } + + close(i_file); + + return p_font; +} + +/***************************************************************************** + * subtitler_UnloadFont: unload a run-length encoded font file from memory + *****************************************************************************/ +void subtitler_UnloadFont( vout_thread_t * p_vout, subtitler_font_t * p_font ) +{ + int i_char; + int i_line; + + msg_Dbg( p_vout, "unloading font" ); + + if( p_font == NULL ) + { + return; + } + + for( i_char = 0; i_char < 256; i_char ++ ) + { + if( p_font->p_offset[ i_char ] != NULL ) + { + for( i_line = 0; i_line < p_font->i_height; i_line++ ) + { + if( p_font->p_offset[ i_char ][ i_line ] != NULL ) + { + free( p_font->p_offset[ i_char ][ i_line ] ); + } + } + free( p_font->p_offset[ i_char ] ); + } + if( p_font->p_length[ i_char ] != NULL ) + { + free( p_font->p_length[ i_char ] ); + } + } + + free( p_font ); +} + +/***************************************************************************** + * subtitler_PlotSubtitle: create a subpicture containing the subtitle + *****************************************************************************/ +void subtitler_PlotSubtitle ( vout_thread_t *p_vout , char *psz_subtitle, + subtitler_font_t *p_font, mtime_t i_start, + mtime_t i_stop ) +{ + subpicture_t * p_spu; + + int i_x; + int i_width; + int i_lines; + int i_longest_width; + int i_total_length; + int i_char; + + uint16_t * p_data; + + char * p_line_start; + char * p_word_start; + char * p_char; + + subtitler_line_t * p_first_line; + subtitler_line_t * p_previous_line; + subtitler_line_t * p_line; + + if( p_font == NULL ) + { + msg_Err( p_vout, "attempt to use NULL font in subtitle" ); + return; + } + + p_first_line = NULL; + p_previous_line = NULL; + + p_line_start = psz_subtitle; + + while( *p_line_start != 0 ) + { + i_width = 0; + p_word_start = p_line_start; + p_char = p_line_start; + + while( *p_char != '\n' && *p_char != 0 ) + { + i_width += p_font->i_width[ toascii( *p_char ) ]; + + if( i_width > p_vout->output.i_width ) + { + /* If the line has more than one word, break at the end of + the previous one. If the line is one very long word, + display as much as we can of it */ + if( p_word_start != p_line_start ) + { + p_char=p_word_start; + } + break; + } + + if( *p_char == ' ' ) + { + p_word_start = p_char+1; + } + + p_char++; + } + + p_line = malloc(sizeof(subtitler_line_t)); + + if( p_line == NULL ) + { + msg_Err( p_vout, "out of memory" ); + return; + } + + if( p_first_line == NULL ) + { + p_first_line = p_line; + } + + if( p_previous_line != NULL ) + { + p_previous_line->p_next = p_line; + } + + p_previous_line = p_line; + + p_line->p_next = NULL; + + p_line->p_text = malloc(( p_char - p_line_start ) +1 ); + + if( p_line == NULL ) + { + msg_Err( p_vout, "out of memory" ); + return; + } + + /* Copy only the part of the text that is in this line */ + strncpy( p_line->p_text , p_line_start , p_char - p_line_start ); + *( p_line->p_text + ( p_char - p_line_start )) = 0; + + /* If we had to break a line because it was too long, ensure that + no characters are lost */ + if( *p_char != '\n' && *p_char != 0 ) + { + p_char--; + } + + p_line_start = p_char; + if( *p_line_start != 0 ) + { + p_line_start ++; + } + } + + i_lines = 0; + i_longest_width = 0; + i_total_length = 0; + p_line = p_first_line; + + /* Find the width of the longest line, count the total number of lines, + and calculate the amount of memory we need to allocate for the RLE + data */ + while( p_line != NULL ) + { + i_lines++; + i_width = 0; + for( i_x = 0; i_x < strlen( p_line->p_text ); i_x++ ) + { + i_char = toascii(*(( p_line->p_text )+ i_x )); + i_width += p_font->i_width[ i_char ]; + i_total_length += p_font->i_memory[ i_char ]; + } + if(i_width > i_longest_width) + { + i_longest_width = i_width; + } + p_line = p_line->p_next; + } + + /* Allow space for the padding bytes at either edge */ + i_total_length += p_font->i_height * 2 * i_lines; + + /* Allocate the subpicture internal data. */ + p_spu = vout_CreateSubPicture( p_vout, MEMORY_SUBPICTURE ); + if( p_spu == NULL ) + { + return; + } + + /* Rationale for the "p_spudec->i_rle_size * 4": we are going to + * expand the RLE stuff so that we won't need to read nibbles later + * on. This will speed things up a lot. Plus, we'll only need to do + * this stupid interlacing stuff once. */ + p_spu->p_sys = malloc( sizeof( subpicture_sys_t ) + + i_total_length * sizeof(uint16_t) ); + if( p_spu->p_sys == NULL ) + { + vout_DestroySubPicture( p_vout, p_spu ); + return; + } + + /* Fill the p_spu structure */ + p_spu->pf_render = E_(RenderSPU); + p_spu->pf_destroy = DestroySPU; + p_spu->p_sys->p_data = (uint8_t *)p_spu->p_sys + sizeof(subpicture_sys_t); + + p_spu->i_start = i_start; + p_spu->i_stop = i_stop; + + p_spu->b_ephemer = i_stop ? VLC_FALSE : VLC_TRUE; + + /* FIXME: Do we need these two? */ + p_spu->p_sys->pi_offset[0] = 0; + p_spu->p_sys->pi_offset[1] = 0; + + p_spu->p_sys->b_palette = 1; + + /* Colour 0 is transparent */ + p_spu->p_sys->pi_yuv[0][0] = 0xff; + p_spu->p_sys->pi_yuv[0][1] = 0x80; + p_spu->p_sys->pi_yuv[0][2] = 0x80; + p_spu->p_sys->pi_yuv[0][3] = 0x80; + p_spu->p_sys->pi_alpha[0] = 0x0; + + /* Colour 1 is grey */ + p_spu->p_sys->pi_yuv[1][0] = 0x80; + p_spu->p_sys->pi_yuv[1][1] = 0x80; + p_spu->p_sys->pi_yuv[1][2] = 0x80; + p_spu->p_sys->pi_yuv[1][3] = 0x80; + p_spu->p_sys->pi_alpha[1] = 0xf; + + /* Colour 2 is white */ + p_spu->p_sys->pi_yuv[2][0] = 0xff; + p_spu->p_sys->pi_yuv[2][1] = 0xff; + p_spu->p_sys->pi_yuv[2][2] = 0xff; + p_spu->p_sys->pi_yuv[2][3] = 0xff; + p_spu->p_sys->pi_alpha[2] = 0xf; + + /* Colour 3 is black */ + p_spu->p_sys->pi_yuv[3][0] = 0x00; + p_spu->p_sys->pi_yuv[3][1] = 0x00; + p_spu->p_sys->pi_yuv[3][2] = 0x00; + p_spu->p_sys->pi_yuv[3][3] = 0x00; + p_spu->p_sys->pi_alpha[3] = 0xf; + + p_spu->p_sys->b_crop = VLC_FALSE; + + p_spu->i_x = (p_vout->output.i_width - i_longest_width) / 2; + p_spu->i_y = p_vout->output.i_height - (p_font->i_height * i_lines); + p_spu->i_width = i_longest_width; + p_spu->i_height = p_font->i_height*i_lines; + + p_data = (uint16_t *)(p_spu->p_sys->p_data); + + p_line = p_first_line; + while( p_line != NULL ) + { + p_data = PlotSubtitleLine( p_line->p_text, + p_font, i_longest_width, p_data ); + p_previous_line = p_line; + p_line = p_line->p_next; + free( p_previous_line->p_text ); + free( p_previous_line ); + } + + /* SPU is finished - we can ask the video output to display it */ + vout_DisplaySubPicture( p_vout, p_spu ); + +} + +/***************************************************************************** + * PlotSubtitleLine: plot a single line of a subtitle + *****************************************************************************/ +static uint16_t * PlotSubtitleLine ( char *psz_line, subtitler_font_t *p_font, + int i_total_width, uint16_t *p_data ) +{ + int i_x; + int i_y; + int i_length; + int i_line_width; + int i_char; + + uint16_t * p_rle; + + i_line_width = 0; + for( i_x = 0; i_x< strlen( psz_line ); i_x++ ) + { + i_line_width += p_font->i_width[ toascii( *( psz_line+i_x ) ) ]; } + + for( i_y = 0; i_y < p_font->i_height; i_y++ ) + { + /* Pad line to fit box */ + if( i_line_width < i_total_width ) + { + *p_data++ = ((( i_total_width - i_line_width)/2) << 2 ); + } + + for(i_x = 0; i_x < strlen(psz_line); i_x ++) + { + i_char = toascii( *(psz_line + i_x) ); + + if( p_font->i_width[ i_char ] != 0 ) + { + p_rle = p_font->p_offset[ i_char ][ i_y ]; + i_length = p_font->p_length[ i_char ][ i_y ]; + + if(p_rle != NULL ) + { + memcpy(p_data, p_rle, i_length * sizeof(uint16_t) ); + p_data+=i_length; + } + } + } + + /* Pad line to fit box */ + if( i_line_width < i_total_width ) + { + *p_data++ = ((( i_total_width - i_line_width) + - (( i_total_width - i_line_width)/2)) << 2 ); + } + } + + return p_data; +} + +/***************************************************************************** + * DestroySPU: subpicture destructor + *****************************************************************************/ +static void DestroySPU( subpicture_t *p_spu ) +{ + free( p_spu->p_sys ); +} diff --git a/modules/codec/spudec/text.c b/modules/codec/spudec/text.c new file mode 100644 index 0000000000..be7fd00a26 --- /dev/null +++ b/modules/codec/spudec/text.c @@ -0,0 +1,85 @@ +/***************************************************************************** + * text.c: text subtitles parser + ***************************************************************************** + * Copyright (C) 2000-2001 VideoLAN + * $Id: text.c,v 1.1 2002/11/06 21:48:24 gbazin Exp $ + * + * Authors: Gildas Bazin + * + * 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 + * (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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#include /* malloc(), free() */ +#include /* memcpy(), memset() */ + +#include +#include +#include + +#include "spudec.h" + +/***************************************************************************** + * Local prototypes. + *****************************************************************************/ + +/***************************************************************************** + * ParseText: parse an text subtitle packet and send it to the video output + *****************************************************************************/ +void E_(ParseText)( spudec_thread_t *p_spudec, subtitler_font_t *p_font ) +{ + char * psz_subtitle; + mtime_t i_pts; + + /* We cannot display a subpicture with no date */ + i_pts = p_spudec->bit_stream.p_pes->i_pts; + if( i_pts == 0 ) + { + /* Dump the packet */ + NextDataPacket( p_spudec->p_fifo, &p_spudec->bit_stream ); + msg_Warn( p_spudec->p_fifo, "subtitle without a date" ); + return; + } + else + + /* Check validity of packet data */ + if( (p_spudec->bit_stream.p_data->p_payload_end + - p_spudec->bit_stream.p_data->p_payload_start) <= 0 + || (strlen(p_spudec->bit_stream.p_data->p_payload_start) + > p_spudec->bit_stream.p_data->p_payload_end + - p_spudec->bit_stream.p_data->p_payload_start) ) + { + /* Dump the packet */ + NextDataPacket( p_spudec->p_fifo, &p_spudec->bit_stream ); + msg_Warn( p_spudec->p_fifo, "invalid subtitle" ); + return; + } + psz_subtitle = p_spudec->bit_stream.p_data->p_payload_start; + + if( psz_subtitle[0] != '\0' ) + { + subtitler_PlotSubtitle( p_spudec->p_vout, + psz_subtitle, p_font, + i_pts, + 0 ); + } + + /* Prepare for next time. No need to check that + * p_spudec->bit_stream->p_data is valid since we check later on + * for b_die and b_error */ + NextDataPacket( p_spudec->p_fifo, &p_spudec->bit_stream ); +} diff --git a/modules/demux/ogg.c b/modules/demux/ogg.c index e9b8a342bb..0f44fc2609 100644 --- a/modules/demux/ogg.c +++ b/modules/demux/ogg.c @@ -2,7 +2,7 @@ * ogg.c : ogg stream input module for vlc ***************************************************************************** * Copyright (C) 2001 VideoLAN - * $Id: ogg.c,v 1.7 2002/11/05 21:57:41 gbazin Exp $ + * $Id: ogg.c,v 1.8 2002/11/06 21:48:23 gbazin Exp $ * * Authors: Gildas Bazin * @@ -87,6 +87,7 @@ struct demux_sys_t /* current audio and video es */ logical_stream_t *p_stream_video; logical_stream_t *p_stream_audio; + logical_stream_t *p_stream_spu; /* stream we use as a time reference for demux reading speed */ logical_stream_t *p_stream_timeref; @@ -168,7 +169,6 @@ static int Demux ( input_thread_t * ); /* Stream managment */ static int Ogg_StreamStart ( input_thread_t *, demux_sys_t *, int ); -static int Ogg_StreamSeek ( input_thread_t *, demux_sys_t *, int, mtime_t ); static void Ogg_StreamStop ( input_thread_t *, demux_sys_t *, int ); /* Bitstream manipulation */ @@ -227,8 +227,6 @@ static int Ogg_StreamStart( input_thread_t *p_input, } } - //Ogg_StreamSeek( p_input, p_ogg, i_stream, p_ogg->i_time ); - return( p_stream->i_activated ); #undef p_stream } @@ -256,17 +254,6 @@ static void Ogg_StreamStop( input_thread_t *p_input, #undef p_stream } -static int Ogg_StreamSeek( input_thread_t *p_input, demux_sys_t *p_ogg, - int i_stream, mtime_t i_date ) -{ -#define p_stream p_ogg->pp_stream[i_stream] - - /* FIXME: todo */ - - return 1; -#undef p_stream -} - /**************************************************************************** * Ogg_Check: Check we are dealing with an ogg stream. ****************************************************************************/ @@ -383,9 +370,20 @@ static void Ogg_DecodePacket( input_thread_t *p_input, } /* Convert the pcr into a pts */ - p_pes->i_pts = ( p_stream->i_pcr < 0 ) ? 0 : - input_ClockGetTS( p_input, p_input->stream.p_selected_program, - p_stream->i_pcr ); + if( p_stream->i_cat != SPU_ES ) + { + p_pes->i_pts = ( p_stream->i_pcr < 0 ) ? 0 : + input_ClockGetTS( p_input, p_input->stream.p_selected_program, + p_stream->i_pcr ); + } + else + { + /* Of course subtitles had to be different! */ + p_pes->i_pts = ( p_oggpacket->granulepos < 0 ) ? 0 : + input_ClockGetTS( p_input, p_input->stream.p_selected_program, + p_oggpacket->granulepos * 90000 / + p_stream->i_rate ); + } /* Convert the next granule into a pcr */ if( p_oggpacket->granulepos < 0 ) @@ -435,6 +433,7 @@ static void Ogg_DecodePacket( input_thread_t *p_input, p_oggpacket->bytes - i_header_len ); p_data->p_payload_end = p_data->p_payload_start + p_pes->i_pes_size; + p_data->b_discard_payload = 0; input_DecodePES( p_stream->p_es->p_decoder_fifo, p_pes ); } @@ -649,10 +648,14 @@ static int Ogg_FindLogicalStreams( input_thread_t *p_input, demux_sys_t *p_ogg) /* Check for text (subtitles) header */ else if( !strncmp(st->streamtype, "text", 4) ) { + /* We need to get rid of the header packet */ + ogg_stream_packetout( &p_stream->os, &oggpacket ); + msg_Dbg( p_input, "found text subtitles header" ); p_stream->i_cat = SPU_ES; p_stream->i_fourcc = VLC_FOURCC( 's', 'u', 'b', 't' ); + p_stream->i_rate = 1000; /* granulepos is in milisec */ } else { @@ -772,7 +775,7 @@ static int Activate( vlc_object_t * p_this ) p_ogg->i_streams + 1, 0 ); p_input->stream.i_mux_rate += (p_stream->i_bitrate / ( 8 * 50 )); vlc_mutex_unlock( &p_input->stream.stream_lock ); - p_stream->p_es->i_stream_id = i_stream; + p_stream->p_es->i_stream_id = p_stream->p_es->i_id = i_stream; p_stream->p_es->i_fourcc = p_stream->i_fourcc; p_stream->p_es->i_cat = p_stream->i_cat; p_stream->p_es->p_demux_data = p_stream->p_bih ? @@ -786,7 +789,6 @@ static int Activate( vlc_object_t * p_this ) switch( p_stream->p_es->i_cat ) { case( VIDEO_ES ): - if( (p_ogg->p_stream_video == NULL) ) { p_ogg->p_stream_video = p_stream; @@ -798,9 +800,33 @@ static int Activate( vlc_object_t * p_this ) case( AUDIO_ES ): if( (p_ogg->p_stream_audio == NULL) ) { - p_ogg->p_stream_audio = p_stream; - p_ogg->p_stream_timeref = p_stream; - Ogg_StreamStart( p_input, p_ogg, i_stream ); + int i_audio = config_GetInt( p_input, "audio-channel" ); + if( i_audio == i_stream || i_audio <= 0 || + i_audio >= p_ogg->i_streams || + p_ogg->pp_stream[i_audio]->p_es->i_cat != AUDIO_ES ) + { + p_ogg->p_stream_audio = p_stream; + p_ogg->p_stream_timeref = p_stream; + Ogg_StreamStart( p_input, p_ogg, i_stream ); + } + } + break; + + case( SPU_ES ): + if( (p_ogg->p_stream_spu == NULL) ) + { + /* for spu, default is none */ + int i_spu = config_GetInt( p_input, "spu-channel" ); + if( i_spu < 0 || i_spu >= p_ogg->i_streams || + p_ogg->pp_stream[i_spu]->p_es->i_cat != SPU_ES ) + { + break; + } + else if( i_spu == i_stream ) + { + p_ogg->p_stream_spu = p_stream; + Ogg_StreamStart( p_input, p_ogg, i_stream ); + } } break; diff --git a/share/font-eutopiabold36.rle b/share/font-eutopiabold36.rle new file mode 100644 index 0000000000..2f0cca6b22 Binary files /dev/null and b/share/font-eutopiabold36.rle differ