summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordynaflash <[email protected]>2010-05-20 15:28:27 +0000
committerdynaflash <[email protected]>2010-05-20 15:28:27 +0000
commit5c0877e87e58f15cf5fe86c43afaad0778a8817d (patch)
tree6100ce310d0bedebcc3c1bf50883ab2365f846e0
parentf6b620012c27bc2858ea0ad198bd3edf34965ca3 (diff)
Support for reading VOB subtitle tracks from file inputs initital implementation.
- 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://svn.handbrake.fr/HandBrake/trunk@3308 b64f7644-9d1e-0410-96f1-a4d463321fa5
-rw-r--r--libhb/common.c68
-rw-r--r--libhb/common.h11
-rw-r--r--libhb/decavcodec.c38
-rw-r--r--libhb/decvobsub.c37
-rw-r--r--libhb/dvd.c8
-rw-r--r--libhb/dvdnav.c8
-rw-r--r--libhb/internal.h13
-rw-r--r--libhb/muxmkv.c23
-rw-r--r--libhb/stream.c81
9 files changed, 238 insertions, 49 deletions
diff --git a/libhb/common.c b/libhb/common.c
index 06fdedded..f7b9ee384 100644
--- a/libhb/common.c
+++ b/libhb/common.c
@@ -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;
+}
+
diff --git a/libhb/common.h b/libhb/common.h
index cdb4d9fe1..e70550eb8 100644
--- a/libhb/common.h
+++ b/libhb/common.h
@@ -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
diff --git a/libhb/decavcodec.c b/libhb/decavcodec.c
index 31c0b751a..09f15ecd8 100644
--- a/libhb/decavcodec.c
+++ b/libhb/decavcodec.c
@@ -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;
diff --git a/libhb/decvobsub.c b/libhb/decvobsub.c
index e180b232f..3b5177ca3 100644
--- a/libhb/decvobsub.c
+++ b/libhb/decvobsub.c
@@ -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;
diff --git a/libhb/dvd.c b/libhb/dvd.c
index e51fd95bf..ee10dd507 100644
--- a/libhb/dvd.c
+++ b/libhb/dvd.c
@@ -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 )
{
diff --git a/libhb/dvdnav.c b/libhb/dvdnav.c
index 84b400309..67e7b8b18 100644
--- a/libhb/dvdnav.c
+++ b/libhb/dvdnav.c
@@ -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 )
{
diff --git a/libhb/internal.h b/libhb/internal.h
index dd217507d..f7092f104 100644
--- a/libhb/internal.h
+++ b/libhb/internal.h
@@ -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
diff --git a/libhb/muxmkv.c b/libhb/muxmkv.c
index d30e7c01d..8fcd30b82 100644
--- a/libhb/muxmkv.c
+++ b/libhb/muxmkv.c
@@ -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",
diff --git a/libhb/stream.c b/libhb/stream.c
index 4b1bd98eb..95bf719ec 100644
--- a/libhb/stream.c
+++ b/libhb/stream.c
@@ -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 ) {