
Property changes on: .
___________________________________________________________________
Name: svn:ignore
   + *.swp


Index: SOURCES
===================================================================
--- SOURCES	(revision 13210)
+++ SOURCES	(working copy)
@@ -16,3 +16,4 @@
 video_out_rockbox.c
 mpeg_settings.c
 mpegplayer.c
+subtitles.c
Index: mpegplayer.c
===================================================================
--- mpegplayer.c	(revision 13210)
+++ mpegplayer.c	(working copy)
@@ -167,6 +167,7 @@
 static mpeg2dec_t * mpeg2dec;
 static int total_offset = 0;
 
+
 /* Utility */
 
 /* Atomically add one long value to another - not core safe atm if ever needed */
@@ -281,6 +282,38 @@
 #define MPEG_GUARDBUF_SIZE (64*1024+1024) /* Keep a bit extra - excessive for now */
 #define MPEG_LOW_WATERMARK (1024*1024)
 
+#include "subtitles.h"
+#define OVERLAY_Y_BUFFER_SIZE (320*240) /* FIXME */
+#ifdef OVERLAY_CHROMA
+#   define OVERLAY_UV_BUFFER_SIZE (160*120) /* FIXME */
+#else
+#   define OVERLAY_UV_BUFFER_SIZE (0)
+#endif
+#define OVERLAY_BUFFER_SIZE (OVERLAY_Y_BUFFER_SIZE+2*OVERLAY_UV_BUFFER_SIZE)
+
+static subtitles_t subs;
+static overlay_t overlay;
+
+static bool init_subbuf( size_t buffer_size )
+{
+    subs.buffer = mpeg2_malloc( buffer_size, -2 );
+    if( !subs.buffer )
+        return false;
+    return true;
+}
+
+static bool init_overlaybuf( void )
+{
+    overlay.update = -1;
+    overlay.count = 0;
+    return ( overlay.Y_PIXELS = mpeg2_malloc( OVERLAY_Y_BUFFER_SIZE, -2) )
+#ifdef OVERLAY_CHROMA
+        && ( overlay.U_PIXELS = mpeg2_malloc( OVERLAY_UV_BUFFER_SIZE, -2) )
+        && ( overlay.V_PIXELS = mpeg2_malloc( OVERLAY_UV_BUFFER_SIZE, -2) )
+#endif
+    ;
+}
+
 static void pcm_playback_play_pause(bool play);
 
 static void button_loop(void)
@@ -1341,6 +1374,13 @@
         case STATE_SEQUENCE:
             /* New GOP, inform output of any changes */
             vo_setup(info->sequence);
+            /* Init the overlay stuff if needed */
+            if( overlay.update == -1 )
+                overlay_init( &overlay,
+                              info->sequence->width,
+                              info->sequence->height,
+                              info->sequence->chroma_width,
+                              info->sequence->chroma_height );
             break;
 
         case STATE_PICTURE:
@@ -1554,6 +1594,19 @@
             /* Record last frame time */
             last_render = *rb->current_tick;
 
+#if 1
+            /* Render overlay stuff in a cache */
+            overlay_render( &overlay, get_playback_time() );
+#endif
+#if 1
+            /* Blend the overlay cache on the video yuv buffer */
+            overlay_blend(  &overlay,
+                            info->display_fbuf->buf,
+                            info->sequence->width,
+                            info->sequence->height,
+                            info->sequence->chroma_width,
+                            info->sequence->chroma_height );
+#endif
             vo_draw_frame(info->display_fbuf->buf);
             num_drawn++;
 
@@ -1608,13 +1661,16 @@
     void* audiobuf;
     int audiosize;
     int in_file;
+    int subs_file;
     uint8_t* buffer;
     size_t file_remaining;
     size_t disk_buf_len;
+    size_t sub_buf_len;
 #ifndef HAVE_LCD_COLOR
     long graysize;
     int grayscales;
 #endif
+    char str[200];
 
     /* We define this here so it is on the main stack (in IRAM) */
     mad_fixed_t mad_frame_overlap[2][32][18];       /* 4608 bytes */
@@ -1646,10 +1702,20 @@
     /* Initialise our malloc buffer */
     mpeg2_alloc_init(audiobuf,audiosize);
 
+    /* Try loading subtitles in file with same name except extension */
+    rb->snprintf( str, sizeof(str), "%s", (char*)parameter );
+    rb->snprintf( rb->strrchr( str, '.' ), 10/*FIXME*/, ".srt" );
+    subs_file = rb->open( str, O_RDONLY );
+    if( subs_file >= 0 )
+        sub_buf_len = rb->filesize(subs_file);
+    else
+        sub_buf_len = 0;
+
     /* Grab most of the buffer for the compressed video - leave some for
        PCM audio data and some for libmpeg2 malloc use. */
     buffer_size = audiosize - (PCMBUFFER_SIZE+PCMBUFFER_GUARD_SIZE+
-                               MPABUF_SIZE+LIBMPEG2BUFFER_SIZE);
+                               MPABUF_SIZE+LIBMPEG2BUFFER_SIZE+sub_buf_len
+                               +OVERLAY_BUFFER_SIZE);
 
     DEBUGF("audiosize=%d, buffer_size=%ld\n",audiosize,buffer_size);
     buffer = mpeg2_malloc(buffer_size,-1);
@@ -1679,6 +1745,22 @@
     if (!init_pcmbuf())
         return PLUGIN_ERROR;
 
+    if( !init_subbuf(sub_buf_len) )
+        return PLUGIN_ERROR;
+
+    if( !init_overlaybuf())
+        return PLUGIN_ERROR;
+
+    subs_init( &subs );
+    overlay.subs = &subs;
+
+    if( subs_file >= 0 ){
+        /* load the subs */
+        if( rb->read( subs_file, subs.buffer, sub_buf_len ) )
+            subs.cur = subs.buffer;
+        rb->close( subs_file );
+    }
+
     /* The remaining buffer is for use by libmpeg2 */
 
     /* Open the video file */
Index: subtitles.c
===================================================================
--- subtitles.c	(revision 0)
+++ subtitles.c	(revision 0)
@@ -0,0 +1,386 @@
+#include "plugin.h"
+#include "subtitles.h"
+
+extern struct plugin_api *rb;
+
+static inline void uint8_buffer_putsxyofs(
+                                    uint8_t *, int, int, int, int, int,
+                                    const unsigned char *, struct font* );
+
+void subs_init( subtitles_t *subs )
+{
+    subs->cur = NULL;
+    subs->i_start = 0;
+    subs->i_stop = 0;
+    subs->i_prev_date = 0;
+    subs->pf = rb->font_get( FONT_UI );
+    if( !subs->pf ) subs->pf = rb->font_get( FONT_SYSFIXED );
+}
+
+extern struct plugin_api *rb;
+
+static inline char *strchr2( char *str, const char *strend, const char c )
+{
+    while( *str && *str != c && str < strend ) str ++;
+    return str;
+}
+
+static inline uint32_t subs_convert_date( int h, int m, int s, int d )
+{
+    return h*3600*44100 + m*60*44100 + s*44100 + d*44;
+}
+
+static inline int strtol10( char *str, char **end )
+{
+    int r = 0;
+    while( *str && *str >= '0' && *str <= '9' )
+    {
+        r = r*10+*str-'0';
+        str++;
+    }
+    if( end ) *end = str;
+    return r;
+}
+
+/**
+ * Parse SRT subs. Format is:
+ *
+ * n
+ * h1:m1:s1,d1 --> h2:m2:s2,d2
+ * Line1
+ * Line2
+ * ...
+ * [empty line]
+ */
+static inline char *subs_get_next( subtitles_t *subs )
+{
+    int h,m,s,d;
+    char *cur = subs->cur;
+
+    /* TODO: add some error checking */
+
+#define skipline(a) (rb->strchr(a,'\n')+1)
+    if( cur[0] != '1' && cur[1] != '\n' && cur[1] != '\r' )
+    {
+        /* Skip all text lines */
+        while( *cur != '\n' && *cur != '\r' )
+            cur = skipline( cur );
+        /* This is the empty line. Skip it */
+        cur = skipline( cur );
+    }
+    cur = skipline( cur );
+    h = strtol10( cur, &cur ); cur ++; /* ":" */
+    m = strtol10( cur, &cur ); cur ++; /* ":" */
+    s = strtol10( cur, &cur ); cur ++; /* "," */
+    d = strtol10( cur, &cur ); cur += 1 + 3 + 1; /* " --> " */
+    subs->i_start = subs_convert_date( h, m, s, d );
+    h = strtol10( cur, &cur ); cur ++; /* ":" */
+    m = strtol10( cur, &cur ); cur ++; /* ":" */
+    s = strtol10( cur, &cur ); cur ++; /* "," */
+    d = strtol10( cur, &cur ); cur = skipline( cur );
+    subs->i_stop = subs_convert_date( h, m, s, d );
+#undef skipline
+
+    subs->cur = cur;
+    return cur;
+}
+
+static int subs_need_update( subtitles_t *subs, uint32_t date )
+{
+    return  subs->cur
+            && (  !( subs->i_start || subs->i_stop )
+               || ( subs->i_stop < date )
+               || ( subs->i_start <= date
+                   && subs->i_prev_date < subs->i_start ) );
+}
+
+static int subs_render( subtitles_t *subs, uint32_t date,
+                        uint8_t *p_y, int i_y_width, int i_y_height )
+{
+    char *cur;
+    int x = 0, y = 10;
+    if( !subs->cur ) return 0;
+
+    /* FIXME: current code doesn't have any error checking */
+
+    if( subs->i_stop < date )
+    {
+        cur = subs_get_next( subs );
+        if( !cur ) return 0; /* Crap, something failed */
+        subs->i_prev_date = 0;
+    }
+    else
+    {
+        cur = subs->cur;
+    }
+
+    if( subs->i_start <= date
+        && subs->i_prev_date < subs->i_start )
+    {
+        char *end = cur;
+        do /* Loop on lines in srt buffer */
+        {
+            char *lineend = cur;
+            end = rb->strchr( end, '\n' );
+            while( lineend != end ) /* We need to skip lines at ever pipe */
+            {
+                char c = 0;
+                int ugly_hack;
+                lineend = strchr2( cur, end, '|' );
+
+                /* \r\n handling ... gra */
+                ugly_hack = *lineend == '\n' && *(lineend-1) == '\r';
+
+                if( ugly_hack )
+                    *(lineend-1) = '\0';
+                else
+                {
+                    c = *lineend;
+                    *lineend = '\0';
+                }
+
+                /* Render in the buffer */
+                uint8_buffer_putsxyofs( p_y, i_y_width, i_y_height,
+                                        x, y, 0, cur, subs->pf );
+
+                if( ugly_hack )
+                    *(lineend-1) = '\r';
+                else
+                    *lineend = c;
+                cur = lineend+1;
+                y += subs->pf->height;
+            }
+            end++;
+        } while( *end != '\n' && *end != '\r' );
+    }
+    else return 0;
+    subs->i_prev_date = date;
+    return y;
+}
+
+/***********************************************************************
+ * Offscreen buffer/Text/Fonts handling
+ *
+ * Parts of code taken from firmware/drivers/lcd-16bit.c
+ ***********************************************************************/
+static void uint8_buffer_mono_bitmap_part(
+                              uint8_t *buf, int buf_width, int buf_height,
+                              const unsigned char *src, int src_x, int src_y,
+                              int stride, int x, int y, int width, int height )
+/* this function only draws the foreground part of the bitmap */
+/* TODO: add a black border */
+{
+    const unsigned char *src_end;
+    uint8_t *dst, *dst_end;
+    const uint8_t fgcolor = 0xff;
+    const uint8_t shcolor = 0x01;
+
+    /* nothing to draw? */
+    if( ( width <= 0 ) || ( height <= 0 ) || ( x >= buf_width )
+        || ( y >= buf_height ) || ( x + width <= 0 ) || ( y + height <= 0 ) )
+        return;
+
+    /* clipping */
+    if( x < 0 )
+    {
+        width += x;
+        src_x -= x;
+        x = 0;
+    }
+    if( y < 0 )
+    {
+        height += y;
+        src_y -= y;
+        y = 0;
+    }
+    if( x + width > buf_width )
+        width = buf_width - x;
+    if( y + height > buf_height )
+        height = buf_height - y;
+
+    src += stride * (src_y >> 3) + src_x; /* move starting point */
+    src_y &= 7;
+    src_end = src + width;
+
+    dst = buf + y*buf_width + x;
+
+    do
+    {
+        const unsigned char *src_col = src++;
+        unsigned data = *src_col >> src_y;
+        uint8_t *dst_col = dst++;
+        int numbits = 8 - src_y;
+
+        dst_end = dst_col + height * buf_width;
+        do
+        {
+            if( data & 0x01 )
+            {
+                dst_col[0] = fgcolor;
+                dst_col[1] = shcolor;
+            }
+
+            dst_col += buf_width;
+
+            data >>= 1;
+            if( --numbits == 0 )
+            {
+                src_col += stride;
+                data = *src_col;
+                numbits = 8;
+            }
+        } while( dst_col < dst_end );
+    } while( src < src_end );
+}
+
+static inline void uint8_buffer_putsxyofs(
+                              uint8_t *buf, int buf_width, int buf_height,
+                              int x, int y, int ofs, const unsigned char *str,
+                              struct font *pf )
+{
+    unsigned short ch;
+    unsigned short *ucs;
+
+    ucs = rb->bidi_l2v( str, 1 );
+
+    while( (ch = *ucs++) != 0 && x < buf_width )
+    {
+        int width;
+        const unsigned char *bits;
+
+        /* get proportional width and glyph bits */
+        width = rb->font_get_width( pf, ch );
+
+        if( ofs > width )
+        {
+            ofs -= width;
+            continue;
+        }
+
+        bits = rb->font_get_bits( pf, ch );
+
+        uint8_buffer_mono_bitmap_part( buf, buf_width, buf_height, bits,
+                                       ofs, 0, width, x, y,
+                                       width - ofs, pf->height);
+
+        x += width - ofs;
+        ofs = 0;
+    }
+}
+
+static void overlay_init_plane( overlay_plane_t *p, int width, int height )
+{
+    p->width     = width;
+    p->height    = height;
+    p->status    = PLANE_DIRTY;
+    p->bb_y      = 0;
+    p->bb_height = 0;
+}
+
+/**
+ * Overlay framework
+ */
+void overlay_init( overlay_t *o, int width, int height, int chroma_width, int chroma_height )
+{
+    overlay_init_plane( o->plane + Y_PLANE, width, height );
+#ifdef OVERLAY_CHROMA
+    overlay_init_plane( o->plane + U_PLANE, chroma_width, chroma_height );
+    overlay_init_plane( o->plane + V_PLANE, chroma_width, chroma_height );
+#else
+    (void)chroma_width;
+    (void)chroma_height;
+#endif
+
+    o->update = 1;
+    o->count = 0;
+}
+
+static inline void overlay_clear_plane( overlay_plane_t *op )
+{
+    if( op->status != PLANE_CLEAN )
+    {
+        if( op->status == PLANE_DIRTY )
+            rb->memset( op->buf, OVERLAY_TRANSPARENT, op->width * op->height );
+        else /* PLANE_USED */
+            rb->memset( op->buf + op->bb_y*op->width, OVERLAY_TRANSPARENT,
+                        op->bb_height*op->width );
+        op->status = PLANE_CLEAN;
+        op->bb_y = 0;
+        op->bb_height = 0;
+    }
+}
+
+void overlay_render( overlay_t *o, uint32_t date )
+{
+    int r;
+
+    if( o->update == -1 ) return;
+    o->update |= subs_need_update( o->subs, date );
+
+    if( !o->update ) return;
+    o->update = 0;
+    o->count = 0;
+
+    overlay_clear_plane( o->plane+Y_PLANE );
+#ifdef OVERLAY_CHROMA
+    overlay_clear_plane( o->plane+U_PLANE );
+    overlay_clear_plane( o->plane+V_PLANE );
+#endif
+
+    if( ( r = subs_render( o->subs, date, o->Y_PIXELS,
+                           o->plane[Y_PLANE].width,
+                           o->plane[Y_PLANE].height ) ) )
+    {
+        o->plane[Y_PLANE].bb_y = 10;
+        o->plane[Y_PLANE].bb_height = r;
+        o->count ++;
+        o->plane[Y_PLANE].status = PLANE_USED;
+    }
+}
+
+static inline void overlay_blend_plane( uint8_t *dst, overlay_plane_t *p )
+{
+    uint8_t *src = p->buf+p->bb_y*p->width;
+    const uint8_t *srcend = src + p->bb_height*p->width;
+    dst += p->bb_y*p->width;
+    while( src < srcend )
+    {
+        if( *src != OVERLAY_TRANSPARENT ) *dst = *src; dst++; src++;
+        if( *src != OVERLAY_TRANSPARENT ) *dst = *src; dst++; src++;
+        if( *src != OVERLAY_TRANSPARENT ) *dst = *src; dst++; src++;
+        if( *src != OVERLAY_TRANSPARENT ) *dst = *src; dst++; src++;
+        if( *src != OVERLAY_TRANSPARENT ) *dst = *src; dst++; src++;
+        if( *src != OVERLAY_TRANSPARENT ) *dst = *src; dst++; src++;
+        if( *src != OVERLAY_TRANSPARENT ) *dst = *src; dst++; src++;
+    }
+}
+
+void overlay_blend( overlay_t *o, uint8_t **buf,
+                    int width, int height,
+                    int chroma_width, int chroma_height )
+{
+#ifndef OVERLAY_CHROMA
+    (void)chroma_width;
+    (void)chroma_height;
+#endif
+
+    if( !o->count ) return;
+    if( o->plane[Y_PLANE].width != width || o->plane[Y_PLANE].height != height
+#ifdef OVERLAY_CHROMA
+     || o->plane[U_PLANE].width != chroma_width
+     || o->plane[U_PLANE].height != chroma_height
+     || o->plane[V_PLANE].width != chroma_width
+     || o->plane[V_PLANE].height != chroma_height
+#endif
+     )
+        return; /* TODO: make it work even if dimensions don't match */
+
+    if( o->plane[Y_PLANE].status == PLANE_USED )
+    overlay_blend_plane( buf[0], o->plane+Y_PLANE );
+#ifdef OVERLAY_CHROMA
+    if( o->plane[U_PLANE].status == PLANE_USED )
+    overlay_blend_plane( buf[1], o->plane+U_PLANE );
+    if( o->plane[V_PLANE].status == PLANE_USED )
+    overlay_blend_plane( buf[2], o->plane+V_PLANE );
+#endif
+}
Index: subtitles.h
===================================================================
--- subtitles.h	(revision 0)
+++ subtitles.h	(revision 0)
@@ -0,0 +1,71 @@
+#ifndef _SUBTITLES_H
+#define _SUBTITLES_H
+
+/**
+ * Subtitles
+ */
+typedef struct {
+    char *buffer;
+    char *cur;
+    uint32_t i_start; /* don't display before date >= i_start */
+    uint32_t i_stop; /* if date >= i_stop, read next subs block (and stop displaying the previous one) */
+    uint32_t i_prev_date; /* Use if b_render_on_video is false only */
+    struct font *pf;
+} subtitles_t;
+
+void subs_init( subtitles_t * );
+
+/**
+ * Video overlay
+ *
+ * Do we need a seperate A_PLANE? (alpha)
+ */
+#define OVERLAY_TRANSPARENT 0
+
+#define Y_PLANE 0
+#define Y_PIXELS plane[Y_PLANE].buf
+#ifdef OVERLAY_CHROMA
+#   define U_PLANE 1
+#   define U_PIXELS plane[U_PLANE].buf
+#   define V_PLANE 2
+#   define V_PIXELS plane[V_PLANE].buf
+#endif
+
+enum {
+    PLANE_DIRTY = -1,
+    PLANE_CLEAN = 0,
+    PLANE_USED  = 1
+};
+
+typedef struct {
+    uint8_t *buf;
+    int width;
+    int height;
+    int status;
+
+    int bb_y;
+    int bb_height;
+} overlay_plane_t;
+
+typedef struct {
+#ifdef OVERLAY_CHROMA
+    overlay_plane_t plane[3];
+#else
+    overlay_plane_t plane[1];
+#endif
+    /* TODO: add "overlay bounding box" coordinates so we only try blending
+     *       pixels that are likely to be non-transparent. */
+
+    int count; /* Blend only when count > 1 */
+    int update;
+
+    /* List stuff that can be rendered in the overlay buffer here */
+    subtitles_t *subs;
+    /* TODO: add playback info widgets (like volume, seek bar, ...) */
+} overlay_t;
+
+void overlay_init( overlay_t *, int, int, int, int );
+void overlay_render( overlay_t *, uint32_t );
+void overlay_blend( overlay_t *, uint8_t **buf, int, int, int, int );
+
+#endif
