* modules/codec/spudec/*: modified the spu decoder to handle text subtitles.
authorGildas Bazin <gbazin@videolan.org>
Wed, 6 Nov 2002 21:48:24 +0000 (21:48 +0000)
committerGildas Bazin <gbazin@videolan.org>
Wed, 6 Nov 2002 21:48:24 +0000 (21:48 +0000)
   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.

12 files changed:
AUTHORS
modules/codec/ffmpeg/video.c
modules/codec/ffmpeg/video.h
modules/codec/spudec/Modules.am
modules/codec/spudec/parse.c
modules/codec/spudec/render.c
modules/codec/spudec/spudec.c
modules/codec/spudec/spudec.h
modules/codec/spudec/subtitler.c [new file with mode: 0644]
modules/codec/spudec/text.c [new file with mode: 0644]
modules/demux/ogg.c
share/font-eutopiabold36.rle [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
index c81a62d..4833878 100644 (file)
--- 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
index 99a2ecd..068214a 100644 (file)
@@ -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;
index bd1bdbc..497c4b2 100644 (file)
@@ -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;
 
 
index 8c5deab..78a219d 100644 (file)
@@ -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
index 02e2cd4..7b72cb3 100644 (file)
@@ -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>
  *
 #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"
 
 /*****************************************************************************
index c913280..60bec34 100644 (file)
@@ -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>
 #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"
 
 /*****************************************************************************
index 0338390..50da469 100644 (file)
@@ -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>
  *
 #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 );
 }
-
index 887380e..53d434a 100644 (file)
@@ -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 );
diff --git a/modules/codec/spudec/subtitler.c b/modules/codec/spudec/subtitler.c
new file mode 100644 (file)
index 0000000..9d10405
--- /dev/null
@@ -0,0 +1,577 @@
+/*****************************************************************************
+ * 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 );
+}
diff --git a/modules/codec/spudec/text.c b/modules/codec/spudec/text.c
new file mode 100644 (file)
index 0000000..be7fd00
--- /dev/null
@@ -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 <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 );
+}
index e9b8a34..0f44fc2 100644 (file)
@@ -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 <gbazin@netcourrier.com>
  * 
@@ -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 (file)
index 0000000..2f0cca6
Binary files /dev/null and b/share/font-eutopiabold36.rle differ