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
D: Win32 interface
S: France
N: Andrew Flintham
E: amf@cus.org.uk
D: text subtitler and font scripts
S: United Kingdom
......@@ -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 <fenrir@via.ecp.fr>
* Gildas Bazin <gbazin@netcourrier.com>
......@@ -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;
......
......@@ -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 <fenrir@via.ecp.fr>
*
......@@ -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;
......
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
......@@ -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 <sam@zoy.org>
*
......@@ -31,14 +31,6 @@
#include <vlc/vout.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"
/*****************************************************************************
......
......@@ -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 <sam@zoy.org>
* Rudolf Cornelissen <rag.cornelissen@inter.nl.net>
......@@ -33,14 +33,6 @@
#include <vlc/vout.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"
/*****************************************************************************
......
......@@ -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 <sam@zoy.org>
*
......@@ -31,14 +31,6 @@
#include <vlc/vout.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"
/*****************************************************************************
......@@ -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 );
}
......@@ -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 <sam@zoy.org>
*
......@@ -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 );
/*****************************************************************************
* 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;