Commit 85071f34 authored by gbazin's avatar gbazin
Browse files

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