OSDN Git Service

Support for reading VOB subtitle tracks from file inputs initital implementation.
authordynaflash <dynaflash@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Thu, 20 May 2010 15:28:27 +0000 (15:28 +0000)
committerdynaflash <dynaflash@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Thu, 20 May 2010 15:28:27 +0000 (15:28 +0000)
- Patch by davidfstr, Nice Work!
- Adds support for reading VOB subtitle tracks from file inputs.
Tested with:
- MKV VOB -> MKV VOB passthru.
- MKV VOB -> MKV VOB burned in.
VOB subtitle palette moved from per-title to per-track.
Discussion leading up to commit can be referenced here: http://forum.handbrake.fr/viewtopic.php?f=4&t=16267

git-svn-id: svn://localhost/HandBrake/trunk@3308 b64f7644-9d1e-0410-96f1-a4d463321fa5

libhb/common.c
libhb/common.h
libhb/decavcodec.c
libhb/decvobsub.c
libhb/dvd.c
libhb/dvdnav.c
libhb/internal.h
libhb/muxmkv.c
libhb/stream.c

index 06fdedd..f7b9ee3 100644 (file)
@@ -1021,3 +1021,71 @@ char * hb_strdup_printf( char * fmt, ... )
     }
 }
 
+/**********************************************************************
+ * hb_yuv2rgb
+ **********************************************************************
+ * Converts a YCbCr pixel to an RGB pixel.
+ * 
+ * This conversion is lossy (due to rounding and clamping).
+ * 
+ * Algorithm:
+ *   http://en.wikipedia.org/w/index.php?title=YCbCr&oldid=361987695#Technical_details
+ *********************************************************************/
+int hb_yuv2rgb(int yuv)
+{
+    double y, Cr, Cb;
+    int r, g, b;
+
+    y  = (yuv >> 16) & 0xff;
+    Cb = (yuv >>  8) & 0xff;
+    Cr = (yuv      ) & 0xff;
+
+    r = 1.164 * (y - 16)                      + 2.018 * (Cb - 128);
+    g = 1.164 * (y - 16) - 0.813 * (Cr - 128) - 0.391 * (Cb - 128);
+    b = 1.164 * (y - 16) + 1.596 * (Cr - 128);
+    
+    r = (r < 0) ? 0 : r;
+    g = (g < 0) ? 0 : g;
+    b = (b < 0) ? 0 : b;
+    
+    r = (r > 255) ? 255 : r;
+    g = (g > 255) ? 255 : g;
+    b = (b > 255) ? 255 : b;
+    
+    return (r << 16) | (g << 8) | b;
+}
+
+/**********************************************************************
+ * hb_rgb2yuv
+ **********************************************************************
+ * Converts an RGB pixel to a YCbCr pixel.
+ * 
+ * This conversion is lossy (due to rounding and clamping).
+ * 
+ * Algorithm:
+ *   http://en.wikipedia.org/w/index.php?title=YCbCr&oldid=361987695#Technical_details
+ *********************************************************************/
+int hb_rgb2yuv(int rgb)
+{
+    double r, g, b;
+    int y, Cr, Cb;
+    
+    r = (rgb >> 16) & 0xff;
+    g = (rgb >>  8) & 0xff;
+    b = (rgb      ) & 0xff;
+
+    y  =  16. + ( 0.257 * r) + (0.504 * g) + (0.098 * b);
+    Cb = 128. + (-0.148 * r) - (0.291 * g) + (0.439 * b);
+    Cr = 128. + ( 0.439 * r) - (0.368 * g) - (0.071 * b);
+    
+    y = (y < 0) ? 0 : y;
+    Cb = (Cb < 0) ? 0 : Cb;
+    Cr = (Cr < 0) ? 0 : Cr;
+    
+    y = (y > 255) ? 255 : y;
+    Cb = (Cb > 255) ? 255 : Cb;
+    Cr = (Cr > 255) ? 255 : Cr;
+    
+    return (y << 16) | (Cb << 8) | Cr;
+}
+
index cdb4d9f..e70550e 100644 (file)
@@ -478,7 +478,7 @@ struct hb_chapter_s
  * > format
  *     - format of the packets the subtitle decoder work-object sends to sub->fifo_raw
  *     - for passthru subtitles, is also the format of the final packets sent to sub->fifo_out
- *     - PICTURESUB for banded 8-bit YAUV pixels
+ *     - PICTURESUB for banded 8-bit YAUV pixels; see decvobsub.c documentation for more info
  *     - TEXTSUB for UTF-8 text marked up with <b>, <i>, or <u>
  *     - read by the muxers, and by the subtitle burn-in logic in the hb_sync_video work-object
  * > source
@@ -508,6 +508,10 @@ struct hb_subtitle_s
     char lang[1024];
     char iso639_2[4];
     uint8_t type; /* Closed Caption, Childrens, Directors etc */
+    
+    // Color lookup table for VOB subtitle tracks. Each entry is in YCbCr format.
+    // Must be filled out by the demuxer for VOB subtitle tracks.
+    uint32_t    palette[16];
 
     int hits;     /* How many hits/occurrences of this subtitle */
     int forced_hits; /* How many forced hits in this subtitle */
@@ -577,8 +581,6 @@ struct hb_title_s
     const char  *container_name;
     int         data_rate;
 
-    uint32_t    palette[16];
-
     hb_metadata_t *metadata;
 
     hb_list_t * list_chapter;
@@ -780,4 +782,7 @@ extern void hb_register_error_handler( hb_error_handler_t * handler );
 
 char * hb_strdup_printf( char * fmt, ... );
 
+int hb_yuv2rgb(int yuv);
+int hb_rgb2yuv(int rgb);
+
 #endif
index 31c0b75..09f15ec 100644 (file)
@@ -561,7 +561,20 @@ static void flushDelayQueue( hb_work_private_t *pv )
     }
 }
 
-static int decodeFrame( hb_work_private_t *pv, uint8_t *data, int size )
+/*
+ * Decodes a video frame from the specified raw packet data ('data', 'size', 'sequence').
+ * The output of this function is stored in 'pv->list', which contains a list
+ * of zero or more decoded packets.
+ * 
+ * The returned packets are guaranteed to have their timestamps in the correct order,
+ * even if the original packets decoded by libavcodec have misordered timestamps,
+ * due to the use of 'packed B-frames'.
+ * 
+ * Internally the set of decoded packets may be buffered in 'pv->delayq'
+ * until enough packets have been decoded so that the timestamps can be
+ * correctly rewritten, if this is necessary.
+ */
+static int decodeFrame( hb_work_private_t *pv, uint8_t *data, int size, int sequence )
 {
     int got_picture, oldlevel = 0;
     AVFrame frame;
@@ -634,6 +647,7 @@ static int decodeFrame( hb_work_private_t *pv, uint8_t *data, int size )
         {
             buf = copy_frame( pv, &frame );
             buf->start = pts;
+            buf->sequence = sequence;
             hb_list_add( pv->list, buf );
             ++pv->nframes;
             return got_picture;
@@ -681,7 +695,9 @@ static int decodeFrame( hb_work_private_t *pv, uint8_t *data, int size )
         }
 
         // add the new frame to the delayq & push its timestamp on the heap
-        pv->delayq[slot] = copy_frame( pv, &frame );
+        buf = copy_frame( pv, &frame );
+        buf->sequence = sequence;
+        pv->delayq[slot] = buf;
         heap_push( &pv->pts_heap, pts );
 
         ++pv->nframes;
@@ -690,7 +706,7 @@ static int decodeFrame( hb_work_private_t *pv, uint8_t *data, int size )
     return got_picture;
 }
 
-static void decodeVideo( hb_work_private_t *pv, uint8_t *data, int size,
+static void decodeVideo( hb_work_private_t *pv, uint8_t *data, int size, int sequence,
                          int64_t pts, int64_t dts )
 {
     /*
@@ -710,20 +726,24 @@ static void decodeVideo( hb_work_private_t *pv, uint8_t *data, int size,
         if ( pout_len > 0 )
         {
             pv->pts = pv->parser->pts;
-            decodeFrame( pv, pout, pout_len );
+            decodeFrame( pv, pout, pout_len, sequence );
         }
     } while ( pos < size );
 
     /* the stuff above flushed the parser, now flush the decoder */
     if ( size <= 0 )
     {
-        while ( decodeFrame( pv, NULL, 0 ) )
+        while ( decodeFrame( pv, NULL, 0, sequence ) )
         {
         }
         flushDelayQueue( pv );
     }
 }
 
+/*
+ * Removes all packets from 'pv->list', links them together into
+ * a linked-list, and returns the first packet in the list.
+ */
 static hb_buffer_t *link_buf_list( hb_work_private_t *pv )
 {
     hb_buffer_t *head = hb_list_item( pv->list, 0 );
@@ -863,7 +883,7 @@ static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
     /* if we got an empty buffer signaling end-of-stream send it downstream */
     if ( in->size == 0 )
     {
-        decodeVideo( pv, in->data, in->size, pts, dts );
+        decodeVideo( pv, in->data, in->size, in->sequence, pts, dts );
         hb_list_add( pv->list, in );
         *buf_out = link_buf_list( pv );
         return HB_WORK_DONE;
@@ -900,7 +920,7 @@ static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
         pv->new_chap = in->new_chap;
         pv->chap_time = pts >= 0? pts : pv->pts_next;
     }
-    decodeVideo( pv, in->data, in->size, pts, dts );
+    decodeVideo( pv, in->data, in->size, in->sequence, pts, dts );
     hb_buffer_close( &in );
     *buf_out = link_buf_list( pv );
     return HB_WORK_OK;
@@ -1115,7 +1135,7 @@ static int decavcodecviWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
     if ( in->size == 0 )
     {
         /* flush any frames left in the decoder */
-        while ( pv->context && decodeFrame( pv, NULL, 0 ) )
+        while ( pv->context && decodeFrame( pv, NULL, 0, in->sequence ) )
         {
         }
         flushDelayQueue( pv );
@@ -1146,7 +1166,7 @@ static int decavcodecviWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
         pv->chap_time = pts >= 0? pts : pv->pts_next;
     }
     prepare_ffmpeg_buffer( in );
-    decodeFrame( pv, in->data, in->size );
+    decodeFrame( pv, in->data, in->size, in->sequence );
     hb_buffer_close( &in );
     *buf_out = link_buf_list( pv );
     return HB_WORK_OK;
index e180b23..3b5177c 100644 (file)
@@ -4,6 +4,26 @@
    Homepage: <http://handbrake.fr/>.
    It may be used under the terms of the GNU General Public License. */
 
+/*
+ * Decoder for DVD bitmap subtitles, also known as "VOB subtitles" within the HandBrake source code.
+ * 
+ * Input format of the subtitle packets is described here:
+ *   http://sam.zoy.org/writings/dvd/subtitles/
+ *
+ * An auxiliary input is the color palette lookup table, in 'subtitle->palette'.
+ * The demuxer implementation must fill out this table appropriately.
+ * - In the case of a true DVD input, the palette is read from the IFO file.
+ * - In the case of an MKV file input, the palette is read from the codec private data of the subtitle track.
+ *
+ * Output format of this decoder is PICTURESUB, which is:
+ *   struct PictureSubPacket {
+ *       uint8_t lum[pixelCount];
+ *       uint8_t alpha[pixelCount];
+ *       uint8_t chromaU[pixelCount];
+ *       uint8_t chromaV[pixelCount];
+ *   }
+ */
+
 #include "hb.h"
 
 struct hb_work_private_s
@@ -42,6 +62,21 @@ int decsubInit( hb_work_object_t * w, hb_job_t * job )
 
     pv->job = job;
     pv->pts = -1;
+    
+    // Warn if the input color palette is empty
+    int paletteEmpty = 1;
+    int i;
+    for (i=0; i<16; i++)
+    {
+        if (w->subtitle->palette[i])
+        {
+            paletteEmpty = 0;
+            break;
+        }
+    }
+    if (paletteEmpty) {
+        hb_log( "decvobsub: input color palette is empty; not demuxed properly?" );
+    }
 
     return 0;
 }
@@ -262,7 +297,7 @@ static void ParseControls( hb_work_object_t * w )
                          * work, but I get the right colours by doing
                          * no conversion.
                          */
-                        uint32_t color = title->palette[colors[j]];
+                        uint32_t color = w->subtitle->palette[colors[j]];
                         uint8_t Cr, Cb, y;
                         y = (color>>16) & 0xff;
                         Cr = (color>>8) & 0xff;
index e51fd95..ee10dd5 100644 (file)
@@ -443,10 +443,6 @@ static hb_title_t * hb_dvdread_title_scan( hb_dvd_t * e, int t )
         hb_list_add( title->list_audio, audio );
     }
 
-    memcpy( title->palette,
-            vts->vts_pgcit->pgci_srp[pgc_id-1].pgc->palette,
-            16 * sizeof( uint32_t ) );
-
     /* Check for subtitles */
     for( i = 0; i < vts->vtsi_mat->nr_of_vts_subp_streams; i++ )
     {
@@ -502,6 +498,10 @@ static hb_title_t * hb_dvdread_title_scan( hb_dvd_t * e, int t )
         subtitle->config.dest   = RENDERSUB;  // By default render (burn-in) the VOBSUB.
 
         subtitle->type = lang_extension;
+        
+        memcpy( subtitle->palette,
+            vts->vts_pgcit->pgci_srp[pgc_id-1].pgc->palette,
+            16 * sizeof( uint32_t ) );
 
         switch( lang_extension )
         {  
index 84b4003..67e7b8b 100644 (file)
@@ -591,10 +591,6 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t )
         hb_list_add( title->list_audio, audio );
     }
 
-    memcpy( title->palette,
-            ifo->vts_pgcit->pgci_srp[title_pgcn-1].pgc->palette,
-            16 * sizeof( uint32_t ) );
-
     /* Check for subtitles */
     for( i = 0; i < ifo->vtsi_mat->nr_of_vts_subp_streams; i++ )
     {
@@ -650,6 +646,10 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t )
         subtitle->config.dest   = RENDERSUB;  // By default render (burn-in) the VOBSUB.
 
         subtitle->type = lang_extension;
+        
+        memcpy( subtitle->palette,
+            ifo->vts_pgcit->pgci_srp[title_pgcn-1].pgc->palette,
+            16 * sizeof( uint32_t ) );
 
         switch( lang_extension )
         {  
index dd21750..f7092f1 100644 (file)
@@ -51,12 +51,23 @@ struct hb_buffer_s
     uint8_t *     data;     // packet data
     int           cur;      // used internally by packet lists (hb_list_t)
 
+    /*
+     * Corresponds to the order that this packet was read from the demuxer.
+     * 
+     * It is important that video decoder work-objects pass this value through
+     * from their input packets to the output packets they generate. Otherwise
+     * RENDERSUB subtitles (especially VOB subtitles) will break.
+     * 
+     * Subtitle decoder work-objects that output a renderable subtitle
+     * format (ex: PICTURESUB) must also be careful to pass the sequence number
+     * through for the same reason.
+     */
     int64_t       sequence;
 
     int           id;           // ID of the track that the packet comes from
     int64_t       start;        // Video and subtitle packets: start time of frame/subtitle
     int64_t       stop;         // Video and subtitle packets: stop time of frame/subtitle
-    int           new_chap;     // Video packets: ???
+    int           new_chap;     // Video packets: if non-zero, is the index of the chapter whose boundary was crossed
 
 #define HB_FRAME_IDR    0x01
 #define HB_FRAME_I      0x02
index d30e7c0..8fcd30b 100644 (file)
@@ -34,27 +34,6 @@ struct hb_mux_data_s
     int       sub_format;
 };
 
-static int yuv2rgb(int yuv)
-{
-    double y, Cr, Cb;
-    int r, g, b;
-
-    y =  (yuv >> 16) & 0xff;
-    Cb = (yuv >>  8) & 0xff;
-    Cr = (yuv      ) & 0xff;
-
-    r = 1.164 * (y - 16)                      + 2.018 * (Cb - 128);
-    g = 1.164 * (y - 16) - 0.813 * (Cr - 128) - 0.391 * (Cb - 128);
-    b = 1.164 * (y - 16) + 1.596 * (Cr - 128);
-    r = (r < 0) ? 0 : r;
-    g = (g < 0) ? 0 : g;
-    b = (b < 0) ? 0 : b;
-    r = (r > 255) ? 255 : r;
-    g = (g > 255) ? 255 : g;
-    b = (b > 255) ? 255 : b;
-    return (r << 16) | (g << 8) | b;
-}
-
 /**********************************************************************
  * MKVInit
  **********************************************************************
@@ -296,7 +275,7 @@ static int MKVInit( hb_mux_object_t * m )
             case PICTURESUB:
                 track->codecID = MK_SUBTITLE_VOBSUB;
                 for (j = 0; j < 16; j++)
-                    rgb[j] = yuv2rgb(title->palette[j]);
+                    rgb[j] = hb_yuv2rgb(subtitle->palette[j]);
                 len = snprintf(subidx, 2048, subidx_fmt, 
                         title->width, title->height,
                         0, 0, "OFF",
index 4b1bd98..95bf719 100644 (file)
@@ -2831,6 +2831,73 @@ static void add_ffmpeg_audio( hb_title_t *title, hb_stream_t *stream, int id )
     }
 }
 
+/*
+ * Parses the 'subtitle->palette' information from the specific VOB subtitle track's private data.
+ * Returns 0 if successful or 1 if parsing failed or was incomplete.
+ * 
+ * Format:
+ *   MkvVobSubtitlePrivateData = ( Line )*
+ *   Line = FieldName ':' ' ' FieldValue '\n'
+ *   FieldName = [^:]+
+ *   FieldValue = [^\n]+
+ * 
+ * The line of interest is:
+ *   PaletteLine = "palette" ':' ' ' RRGGBB ( ',' ' ' RRGGBB )*
+ * 
+ * More information on the format at:
+ *   http://www.matroska.org/technical/specs/subtitles/images.html
+ */
+static int ffmpeg_parse_vobsub_extradata( AVCodecContext *codec, hb_subtitle_t *subtitle )
+{
+    if ( codec->extradata_size <= 0 )
+        return 1;
+    
+    // lines = (string) codec->extradata;
+    char *lines = malloc( codec->extradata_size + 1 );
+    if ( lines == NULL )
+        return 1;
+    memcpy( lines, codec->extradata, codec->extradata_size );
+    lines[codec->extradata_size] = '\0';
+    
+    uint32_t rgb[16];
+    int gotPalette = 0;
+    
+    char *curLine, *curLine_parserData;
+    for ( curLine = strtok_r( lines, "\n", &curLine_parserData );
+          curLine;
+          curLine = strtok_r( NULL, "\n", &curLine_parserData ) )
+    {
+        int numElementsRead = sscanf(curLine, "palette: "
+            "%06x, %06x, %06x, %06x, "
+            "%06x, %06x, %06x, %06x, "
+            "%06x, %06x, %06x, %06x, "
+            "%06x, %06x, %06x, %06x",
+            &rgb[0],  &rgb[1],  &rgb[2],  &rgb[3],
+            &rgb[4],  &rgb[5],  &rgb[6],  &rgb[7],
+            &rgb[8],  &rgb[9],  &rgb[10], &rgb[11],
+            &rgb[12], &rgb[13], &rgb[14], &rgb[15]);
+        
+        if (numElementsRead == 16) {
+            gotPalette = 1;
+            break;
+        }
+    }
+    
+    free( lines );
+    
+    if ( gotPalette )
+    {
+        int i;
+        for (i=0; i<16; i++)
+            subtitle->palette[i] = hb_rgb2yuv(rgb[i]);
+        return 0;
+    }
+    else
+    {
+        return 1;
+    }
+}
+
 static void add_ffmpeg_subtitle( hb_title_t *title, hb_stream_t *stream, int id )
 {
     AVStream *st = stream->ffmpeg_ic->streams[id];
@@ -2842,14 +2909,14 @@ static void add_ffmpeg_subtitle( hb_title_t *title, hb_stream_t *stream, int id
     
     switch ( codec->codec_id )
     {
-        // TODO(davidfstr): get universal VOB sub input working
-        /*
         case CODEC_ID_DVD_SUBTITLE:
             subtitle->format = PICTURESUB;
             subtitle->source = VOBSUB;
             subtitle->config.dest = RENDERSUB;  // By default render (burn-in) the VOBSUB.
+            if ( ffmpeg_parse_vobsub_extradata( codec, subtitle ) )
+                hb_log( "add_ffmpeg_subtitle: malformed extradata for VOB subtitle track; "
+                        "subtitle colors likely to be wrong" );
             break;
-        */
         case CODEC_ID_TEXT:
             subtitle->format = TEXTSUB;
             subtitle->source = UTF8SUB;
@@ -2869,7 +2936,7 @@ static void add_ffmpeg_subtitle( hb_title_t *title, hb_stream_t *stream, int id
             break;
         */
         default:
-            hb_log("add_ffmpeg_subtitle: unknown subtitle stream type: 0x%x", (int) codec->codec_id);
+            hb_log( "add_ffmpeg_subtitle: unknown subtitle stream type: 0x%x", (int) codec->codec_id );
             free(subtitle);
             return;
     }
@@ -3132,10 +3199,14 @@ static int ffmpeg_read( hb_stream_t *stream, hb_buffer_t *buf )
     /* 
      * Fill out buf->stop for subtitle packets
      * 
-     * libavcodec's MKV demuxer stores the duration of UTF-8 (TEXT) subtitles
+     * libavcodec's MKV demuxer stores the duration of UTF-8 subtitles (CODEC_ID_TEXT)
      * in the 'convergence_duration' field for some reason.
      * 
      * Other subtitles' durations are stored in the 'duration' field.
+     * 
+     * VOB subtitles (CODEC_ID_DVD_SUBTITLE) do not have their duration stored in
+     * either field. This is not a problem because the VOB decoder can extract this
+     * information from the packet payload itself.
      */
     enum CodecID ffmpeg_pkt_codec = stream->ffmpeg_ic->streams[stream->ffmpeg_pkt->stream_index]->codec->codec_id;
     if ( ffmpeg_pkt_codec == CODEC_ID_TEXT ) {