Commit 85071f34 authored by Gildas Bazin's avatar Gildas Bazin

* 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.
parent 8b09b55d
...@@ -419,3 +419,7 @@ C: ipkiss ...@@ -419,3 +419,7 @@ C: ipkiss
D: Win32 interface D: Win32 interface
S: France S: France
N: Andrew Flintham
E: amf@cus.org.uk
D: text subtitler and font scripts
S: United Kingdom
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* video.c: video decoder using ffmpeg library * video.c: video decoder using ffmpeg library
***************************************************************************** *****************************************************************************
* Copyright (C) 1999-2001 VideoLAN * 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 <fenrir@via.ecp.fr> * Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Gildas Bazin <gbazin@netcourrier.com> * Gildas Bazin <gbazin@netcourrier.com>
...@@ -349,6 +349,8 @@ int E_( InitThread_Video )( vdec_thread_t *p_vdec ) ...@@ -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->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; p_vdec->b_direct_rendering = 0;
#if LIBAVCODEC_BUILD > 4615 #if LIBAVCODEC_BUILD > 4615
if( (p_vdec->p_codec->capabilities & CODEC_CAP_DR1) if( (p_vdec->p_codec->capabilities & CODEC_CAP_DR1)
...@@ -668,6 +670,11 @@ void E_( DecodeThread_Video )( vdec_thread_t *p_vdec ) ...@@ -668,6 +670,11 @@ void E_( DecodeThread_Video )( vdec_thread_t *p_vdec )
*****************************************************************************/ *****************************************************************************/
void E_( EndThread_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 ) if( p_vdec->p_pp )
{ {
/* release postprocessing module */ /* release postprocessing module */
...@@ -797,8 +804,13 @@ static int ffmpeg_GetFrameBuf( struct AVCodecContext *avctx, int width, ...@@ -797,8 +804,13 @@ static int ffmpeg_GetFrameBuf( struct AVCodecContext *avctx, int width,
msleep( VOUT_OUTMEM_SLEEP ); msleep( VOUT_OUTMEM_SLEEP );
} }
/* FIXME: we may have to use link/unlinkPicture to fully support streams /* FIXME: We keep the last picture linked until the current one is decoded,
* with B FRAMES */ * 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->draw_horiz_band= NULL;
avctx->dr_buffer[0]= p_pic->p[0].p_pixels; avctx->dr_buffer[0]= p_pic->p[0].p_pixels;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* video.h: video decoder using ffmpeg library * video.h: video decoder using ffmpeg library
***************************************************************************** *****************************************************************************
* Copyright (C) 1999-2001 VideoLAN * 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 <fenrir@via.ecp.fr> * Authors: Laurent Aimar <fenrir@via.ecp.fr>
* *
...@@ -41,7 +41,8 @@ typedef struct vdec_thread_s ...@@ -41,7 +41,8 @@ typedef struct vdec_thread_s
/* for direct rendering */ /* for direct rendering */
int b_direct_rendering; int b_direct_rendering;
picture_t *p_lastpic;
picture_t *p_secondlastpic;
} vdec_thread_t; } vdec_thread_t;
......
SOURCES_spudec = \ SOURCES_spudec = \
modules/codec/spudec/spudec.c \ modules/codec/spudec/spudec.c \
modules/codec/spudec/parse.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 += \ noinst_HEADERS += \
modules/codec/spudec/spudec.h modules/codec/spudec/spudec.h
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* parse.c: SPU parser * parse.c: SPU parser
***************************************************************************** *****************************************************************************
* Copyright (C) 2000-2001 VideoLAN * 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 <sam@zoy.org> * Authors: Samuel Hocevar <sam@zoy.org>
* *
...@@ -31,14 +31,6 @@ ...@@ -31,14 +31,6 @@
#include <vlc/vout.h> #include <vlc/vout.h>
#include <vlc/decoder.h> #include <vlc/decoder.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h> /* getpid() */
#endif
#ifdef WIN32 /* getpid() for win32 is located in process.h */
# include <process.h>
#endif
#include "spudec.h" #include "spudec.h"
/***************************************************************************** /*****************************************************************************
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* render.c : SPU renderer * render.c : SPU renderer
***************************************************************************** *****************************************************************************
* Copyright (C) 2000-2001 VideoLAN * 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 <sam@zoy.org> * Authors: Samuel Hocevar <sam@zoy.org>
* Rudolf Cornelissen <rag.cornelissen@inter.nl.net> * Rudolf Cornelissen <rag.cornelissen@inter.nl.net>
...@@ -33,14 +33,6 @@ ...@@ -33,14 +33,6 @@
#include <vlc/vout.h> #include <vlc/vout.h>
#include <vlc/decoder.h> #include <vlc/decoder.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h> /* getpid() */
#endif
#ifdef WIN32 /* getpid() for win32 is located in process.h */
# include <process.h>
#endif
#include "spudec.h" #include "spudec.h"
/***************************************************************************** /*****************************************************************************
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* spudec.c : SPU decoder thread * spudec.c : SPU decoder thread
***************************************************************************** *****************************************************************************
* Copyright (C) 2000-2001 VideoLAN * 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 <sam@zoy.org> * Authors: Samuel Hocevar <sam@zoy.org>
* *
...@@ -31,14 +31,6 @@ ...@@ -31,14 +31,6 @@
#include <vlc/vout.h> #include <vlc/vout.h>
#include <vlc/decoder.h> #include <vlc/decoder.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h> /* getpid() */
#endif
#ifdef WIN32 /* getpid() for win32 is located in process.h */
# include <process.h>
#endif
#include "spudec.h" #include "spudec.h"
/***************************************************************************** /*****************************************************************************
...@@ -52,8 +44,16 @@ static void EndThread ( spudec_thread_t * ); ...@@ -52,8 +44,16 @@ static void EndThread ( spudec_thread_t * );
/***************************************************************************** /*****************************************************************************
* Module descriptor. * 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(); 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_capability( "decoder", 50 );
set_callbacks( OpenDecoder, NULL ); set_callbacks( OpenDecoder, NULL );
vlc_module_end(); vlc_module_end();
...@@ -69,11 +69,12 @@ static int OpenDecoder( vlc_object_t *p_this ) ...@@ -69,11 +69,12 @@ static int OpenDecoder( vlc_object_t *p_this )
decoder_fifo_t *p_fifo = (decoder_fifo_t*) p_this; decoder_fifo_t *p_fifo = (decoder_fifo_t*) p_this;
if( p_fifo->i_fourcc != VLC_FOURCC('s','p','u',' ') 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; return VLC_EGENERIC;
} }
p_fifo->pf_run = RunDecoder; p_fifo->pf_run = RunDecoder;
return VLC_SUCCESS; return VLC_SUCCESS;
...@@ -85,6 +86,8 @@ static int OpenDecoder( vlc_object_t *p_this ) ...@@ -85,6 +86,8 @@ static int OpenDecoder( vlc_object_t *p_this )
static int RunDecoder( decoder_fifo_t * p_fifo ) static int RunDecoder( decoder_fifo_t * p_fifo )
{ {
spudec_thread_t * p_spudec; spudec_thread_t * p_spudec;
subtitler_font_t * p_font;
char * psz_font;
/* Allocate the memory needed to store the thread's structure */ /* Allocate the memory needed to store the thread's structure */
p_spudec = (spudec_thread_t *)malloc( sizeof(spudec_thread_t) ); p_spudec = (spudec_thread_t *)malloc( sizeof(spudec_thread_t) );
...@@ -101,7 +104,7 @@ static int RunDecoder( decoder_fifo_t * p_fifo ) ...@@ -101,7 +104,7 @@ static int RunDecoder( decoder_fifo_t * p_fifo )
*/ */
p_spudec->p_vout = NULL; p_spudec->p_vout = NULL;
p_spudec->p_fifo = p_fifo; p_spudec->p_fifo = p_fifo;
/* /*
* Initialize thread and free configuration * Initialize thread and free configuration
*/ */
...@@ -111,14 +114,48 @@ static int RunDecoder( decoder_fifo_t * p_fifo ) ...@@ -111,14 +114,48 @@ static int RunDecoder( decoder_fifo_t * p_fifo )
* Main loop - it is not executed if an error occured during * Main loop - it is not executed if an error occured during
* initialization * 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 ) ...@@ -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 ) if( p_spudec->p_fifo->b_die || p_spudec->p_fifo->b_error )
{ {
/* Call InitBitstream anyway so p_spudec is in a known state /* Call InitBitstream anyway so p_spudec->bit_stream is in a known
* before calling CloseBitstream */ * state before calling CloseBitstream */
InitBitstream( &p_spudec->bit_stream, p_spudec->p_fifo, InitBitstream( &p_spudec->bit_stream, p_spudec->p_fifo,
NULL, NULL ); NULL, NULL );
return -1; return -1;
...@@ -209,4 +246,3 @@ static void EndThread( spudec_thread_t *p_spudec ) ...@@ -209,4 +246,3 @@ static void EndThread( spudec_thread_t *p_spudec )
CloseBitstream( &p_spudec->bit_stream ); CloseBitstream( &p_spudec->bit_stream );
free( p_spudec ); free( p_spudec );
} }
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* spudec.h : sub picture unit decoder thread interface * spudec.h : sub picture unit decoder thread interface
***************************************************************************** *****************************************************************************
* Copyright (C) 1999, 2000 VideoLAN * 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 <sam@zoy.org> * Authors: Samuel Hocevar <sam@zoy.org>
* *
...@@ -44,6 +44,18 @@ struct subpicture_sys_t ...@@ -44,6 +44,18 @@ struct subpicture_sys_t
int i_x_start, i_y_start, i_x_end, i_y_end; 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 * spudec_thread_t : sub picture unit decoder thread descriptor
*****************************************************************************/ *****************************************************************************/
...@@ -99,3 +111,9 @@ void E_(ParsePacket) ( spudec_thread_t * ); ...@@ -99,3 +111,9 @@ void E_(ParsePacket) ( spudec_thread_t * );
void E_(RenderSPU) ( vout_thread_t *, picture_t *, void E_(RenderSPU) ( vout_thread_t *, picture_t *,
const subpicture_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 );
/*****************************************************************************
* subtitler.c : subtitler font routines
*****************************************************************************
* Copyright (C) 1999, 2000 VideoLAN
*
* Authors: Andrew Flintham <amf@cus.org.uk>
*
* 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 <stdlib.h> /* malloc(), free() */
#include <string.h> /* memcpy(), memset() */
#include <errno.h> /* errno */
#include <fcntl.h> /* open() */
#include <ctype.h> /* toascii() */
#include <vlc/vlc.h>
#include <vlc/vout.h>
#include <vlc/decoder.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h> /* read(), close() */
#endif
#include <sys/types.h>
#include <sys/stat.h>
#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 );
}
/*****************************************************************************
* 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 <gbazin@netcourrier.com>
*
* 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 <stdlib.h> /* malloc(), free() */
#include <string.h> /* memcpy(), memset() */
#include <vlc/vlc.h>
#include <vlc/vout.h>
#include <vlc/decoder.h>
#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 );
}
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* ogg.c : ogg stream input module for vlc * ogg.c : ogg stream input module for vlc
***************************************************************************** *****************************************************************************
* Copyright (C) 2001 VideoLAN * 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 <gbazin@netcourrier.com> * Authors: Gildas Bazin <gbazin@netcourrier.com>
* *
...@@ -87,6 +87,7 @@ struct demux_sys_t ...@@ -87,6 +87,7 @@ struct demux_sys_t
/* current audio and video es */ /* current audio and video es */
logical_stream_t *p_stream_video; logical_stream_t *p_stream_video;
logical_stream_t *p_stream_audio; logical_stream_t *p_stream_audio;
logical_stream_t *p_stream_spu;
/* stream we use as a time reference for demux reading speed */ /* stream we use as a time reference for demux reading speed */
logical_stream_t *p_stream_timeref; logical_stream_t *p_stream_timeref;
...@@ -168,7 +169,6 @@ static int Demux ( input_thread_t * ); ...@@ -168,7 +169,6 @@ static int Demux ( input_thread_t * );
/* Stream managment */ /* Stream managment */
static int Ogg_StreamStart ( input_thread_t *, demux_sys_t *, int ); 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 ); static void Ogg_StreamStop ( input_thread_t *, demux_sys_t *, int );
/* Bitstream manipulation */ /* Bitstream manipulation */
...@@ -227,8 +227,6 @@ static int Ogg_StreamStart( input_thread_t *p_input, ...@@ -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 ); return( p_stream->i_activated );
#undef p_stream #undef p_stream
} }
...@@ -256,17 +254,6 @@ static void Ogg_StreamStop( input_thread_t *p_input, ...@@ -256,17 +254,6 @@ static void Ogg_StreamStop( input_thread_t *p_input,
#undef p_stream #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. * Ogg_Check: Check we are dealing with an ogg stream.
****************************************************************************/ ****************************************************************************/
...@@ -383,9 +370,20 @@ static void Ogg_DecodePacket( input_thread_t *p_input, ...@@ -383,9 +370,20 @@ static void Ogg_DecodePacket( input_thread_t *p_input,
} }
/* Convert the pcr into a pts */ /* Convert the pcr into a pts */
p_pes->i_pts = ( p_stream->i_pcr < 0 ) ? 0 : if( p_stream->i_cat != SPU_ES )
input_ClockGetTS( p_input, p_input->stream.p_selected_program, {
p_stream->i_pcr ); 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 */ /* Convert the next granule into a pcr */
if( p_oggpacket->granulepos < 0 ) if( p_oggpacket->granulepos < 0 )
...@@ -435,6 +433,7 @@ static void Ogg_DecodePacket( input_thread_t *p_input, ...@@ -435,6 +433,7 @@ static void Ogg_DecodePacket( input_thread_t *p_input,
p_oggpacket->bytes - i_header_len ); p_oggpacket->bytes - i_header_len );
p_data->p_payload_end = p_data->p_payload_start + p_pes->i_pes_size; 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 ); 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) ...@@ -649,10 +648,14 @@ static int Ogg_FindLogicalStreams( input_thread_t *p_input, demux_sys_t *p_ogg)
/* Check for text (subtitles) header */ /* Check for text (subtitles) header */
else if( !strncmp(st->streamtype, "text", 4) ) 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" ); msg_Dbg( p_input, "found text subtitles header" );
p_stream->i_cat = SPU_ES; p_stream->i_cat = SPU_ES;
p_stream->i_fourcc = p_stream->i_fourcc =
VLC_FOURCC( 's', 'u', 'b', 't' ); VLC_FOURCC( 's', 'u', 'b', 't' );
p_stream->i_rate = 1000; /* granulepos is in milisec */
} }
else else
{ {
...@@ -772,7 +775,7 @@ static int Activate( vlc_object_t * p_this ) ...@@ -772,7 +775,7 @@ static int Activate( vlc_object_t * p_this )
p_ogg->i_streams + 1, 0 ); p_ogg->i_streams + 1, 0 );
p_input->stream.i_mux_rate += (p_stream->i_bitrate / ( 8 * 50 )); p_input->stream.i_mux_rate += (p_stream->i_bitrate / ( 8 * 50 ));
vlc_mutex_unlock( &p_input->stream.stream_lock ); 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_fourcc = p_stream->i_fourcc;
p_stream->p_es->i_cat = p_stream->i_cat; p_stream->p_es->i_cat = p_stream->i_cat;
p_stream->p_es->p_demux_data = p_stream->p_bih ? p_stream->p_es->p_demux_data = p_stream->p_bih ?
...@@ -786,7 +789,6 @@ static int Activate( vlc_object_t * p_this ) ...@@ -786,7 +789,6 @@ static int Activate( vlc_object_t * p_this )
switch( p_stream->p_es->i_cat ) switch( p_stream->p_es->i_cat )
{ {
case( VIDEO_ES ): case( VIDEO_ES ):
if( (p_ogg->p_stream_video == NULL) ) if( (p_ogg->p_stream_video == NULL) )
{ {
p_ogg->p_stream_video = p_stream; p_ogg->p_stream_video = p_stream;
...@@ -798,9 +800,33 @@ static int Activate( vlc_object_t * p_this ) ...@@ -798,9 +800,33 @@ static int Activate( vlc_object_t * p_this )
case( AUDIO_ES ): case( AUDIO_ES ):
if( (p_ogg->p_stream_audio == NULL) ) if( (p_ogg->p_stream_audio == NULL) )
{ {
p_ogg->p_stream_audio = p_stream; int i_audio = config_GetInt( p_input, "audio-channel" );
p_ogg->p_stream_timeref = p_stream; if( i_audio == i_stream || i_audio <= 0 ||
Ogg_StreamStart( p_input, p_ogg, i_stream ); 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; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment