summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Stebbins <[email protected]>2019-12-11 16:29:33 -0800
committerJohn Stebbins <[email protected]>2020-03-29 08:23:20 -0600
commit93ac83aca7220403b84fb88d4b0326f383e862b9 (patch)
tree983d995ec7c0d48e90cfeaa63dbdb7923f328c26
parent494a548865e20767076f2caaa4f23522ee750687 (diff)
decavsub: use libav to decode dvd subtitles
Simplifies code, removes encvobsub.c (was never used) and decvobsub.c.
-rw-r--r--contrib/ffmpeg/A14-dvdsubdec-fix-processing-of-partial-packets.patch49
-rw-r--r--libhb/decavsub.c108
-rw-r--r--libhb/decvobsub.c734
-rw-r--r--libhb/demuxmpeg.c7
-rw-r--r--libhb/dvd.c3
-rw-r--r--libhb/dvdnav.c3
-rw-r--r--libhb/encvobsub.c75
-rw-r--r--libhb/handbrake/common.h1
-rw-r--r--libhb/handbrake/internal.h1
-rw-r--r--libhb/hb.c2
-rw-r--r--libhb/rendersub.c4
-rw-r--r--libhb/stream.c25
12 files changed, 154 insertions, 858 deletions
diff --git a/contrib/ffmpeg/A14-dvdsubdec-fix-processing-of-partial-packets.patch b/contrib/ffmpeg/A14-dvdsubdec-fix-processing-of-partial-packets.patch
new file mode 100644
index 000000000..dbe1d8d7c
--- /dev/null
+++ b/contrib/ffmpeg/A14-dvdsubdec-fix-processing-of-partial-packets.patch
@@ -0,0 +1,49 @@
+From b5846bbdf2bea0ec40ab68fbb5440e17a9390f65 Mon Sep 17 00:00:00 2001
+From: John Stebbins <[email protected]>
+Date: Wed, 11 Dec 2019 14:10:09 -0800
+Subject: [PATCH] dvdsubdec: fix processing of partial packets
+
+Wait for a complete dvd subtitle before processing.
+
+If the input packet is large enough to start processing, but does not
+contain complete data, unfinished results are emitted and the following
+input packet causes an error because the stream is no longer in sync
+with the decoder.
+---
+ libavcodec/dvdsubdec.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/libavcodec/dvdsubdec.c b/libavcodec/dvdsubdec.c
+index 741ea9fd1e..c0c9962bad 100644
+--- a/libavcodec/dvdsubdec.c
++++ b/libavcodec/dvdsubdec.c
+@@ -232,7 +232,7 @@ static int decode_dvd_subtitles(DVDSubContext *ctx, AVSubtitle *sub_header,
+ int64_t offset1, offset2;
+
+ if (buf_size < 10)
+- return -1;
++ return AVERROR(EAGAIN);
+
+ if (AV_RB16(buf) == 0) { /* HD subpicture with 4-byte offsets */
+ big_offsets = 1;
+@@ -247,12 +247,12 @@ static int decode_dvd_subtitles(DVDSubContext *ctx, AVSubtitle *sub_header,
+ size = READ_OFFSET(buf + (big_offsets ? 2 : 0));
+ cmd_pos = READ_OFFSET(buf + cmd_pos);
+
+- if (cmd_pos < 0 || cmd_pos > buf_size - 2 - offset_size) {
+- if (cmd_pos > size) {
+- av_log(ctx, AV_LOG_ERROR, "Discarding invalid packet\n");
+- return 0;
+- }
++ if (buf_size < size)
+ return AVERROR(EAGAIN);
++
++ if (cmd_pos < 0 || cmd_pos > size) {
++ av_log(ctx, AV_LOG_ERROR, "Discarding invalid packet\n");
++ return AVERROR_INVALIDDATA;
+ }
+
+ while (cmd_pos > 0 && cmd_pos < buf_size - 2 - offset_size) {
+--
+2.23.0
+
diff --git a/libhb/decavsub.c b/libhb/decavsub.c
index 71baaf3a3..27e94099c 100644
--- a/libhb/decavsub.c
+++ b/libhb/decavsub.c
@@ -23,6 +23,20 @@ struct hb_avsub_context_s
hb_buffer_list_t list_pass;
// List of subtitle packets to be output by this decoder.
hb_buffer_list_t list;
+ // DVD subtitles: ffmpeg returns the timestamp of the last packet
+ // submitted when a DVD subtitle spans multiple packets. So we must
+ // keep track of the start time of the first packet. In this case
+ // sub_pts is reset when ffmpeg delivers us a subtitle.
+ //
+ // BUT, EIA 608 can have many nil packets before the actual subtitle
+ // begins and the ffmpeg decoder keeps track of the timestamp where
+ // the subtitle actually begins for us. In this case sub_pts must be
+ // reset for every packet. I.e. the opposite of what we have to do for
+ // DVD subtitle :(
+ //
+ // PGS subtitles should also reset this for each packet since the packet
+ // with the presentation segment defines the display time.
+ int64_t sub_pts;
// XXX: we may occasionally see subtitles with broken timestamps
// while this should really get fixed elsewhere,
// dropping subtitles should be avoided as much as possible
@@ -48,6 +62,7 @@ hb_avsub_context_t * decavsubInit( hb_work_object_t * w, hb_job_t * job )
}
ctx->seen_forced_sub = 0;
ctx->last_pts = AV_NOPTS_VALUE;
+ ctx->sub_pts = AV_NOPTS_VALUE;
ctx->job = job;
ctx->subtitle = w->subtitle;
@@ -69,6 +84,29 @@ hb_avsub_context_t * decavsubInit( hb_work_object_t * w, hb_job_t * job )
{
av_dict_set( &av_opts, "real_time", "1", 0 );
}
+ if (ctx->subtitle->source == VOBSUB && ctx->subtitle->palette_set)
+ {
+ char * palette = hb_strdup_printf(
+ "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x",
+ hb_yuv2rgb(ctx->subtitle->palette[0]),
+ hb_yuv2rgb(ctx->subtitle->palette[1]),
+ hb_yuv2rgb(ctx->subtitle->palette[2]),
+ hb_yuv2rgb(ctx->subtitle->palette[3]),
+ hb_yuv2rgb(ctx->subtitle->palette[4]),
+ hb_yuv2rgb(ctx->subtitle->palette[5]),
+ hb_yuv2rgb(ctx->subtitle->palette[6]),
+ hb_yuv2rgb(ctx->subtitle->palette[7]),
+ hb_yuv2rgb(ctx->subtitle->palette[8]),
+ hb_yuv2rgb(ctx->subtitle->palette[9]),
+ hb_yuv2rgb(ctx->subtitle->palette[10]),
+ hb_yuv2rgb(ctx->subtitle->palette[11]),
+ hb_yuv2rgb(ctx->subtitle->palette[12]),
+ hb_yuv2rgb(ctx->subtitle->palette[13]),
+ hb_yuv2rgb(ctx->subtitle->palette[14]),
+ hb_yuv2rgb(ctx->subtitle->palette[15]));
+ av_dict_set( &av_opts, "palette", palette, 0 );
+ free(palette);
+ }
if (hb_avcodec_open(ctx->context, codec, &av_opts, 0))
{
@@ -280,10 +318,14 @@ int decavsubWork( hb_avsub_context_t * ctx,
int64_t duration = AV_NOPTS_VALUE;
AVPacket avp;
+ if (ctx->sub_pts == AV_NOPTS_VALUE)
+ {
+ ctx->sub_pts = in->s.start;
+ }
av_init_packet( &avp );
avp.data = in->data;
avp.size = in->size;
- avp.pts = in->s.start;
+ avp.pts = ctx->sub_pts;
duration = in->s.duration;
if (duration <= 0 &&
@@ -305,6 +347,7 @@ int decavsubWork( hb_avsub_context_t * ctx,
return HB_WORK_OK;
}
+ // <UGLY_HACKS>
// Ugly hack, but ffmpeg doesn't consume a trailing 0xff in
// DVB subtitle buffers :(
if (ctx->subtitle->source == DVBSUB &&
@@ -314,10 +357,23 @@ int decavsubWork( hb_avsub_context_t * ctx,
usedBytes++;
}
// Another ugly hack, ffmpeg always returns 0 for CC decoder :(
- if (ctx->subtitle->source == CC608SUB)
+ //
+ // Also returns 0 for DVD subtitles, until it emits an AVSubtitle,
+ // then it returns the total number of bytes used to construct
+ // that subtitle :(
+ if (ctx->subtitle->source == CC608SUB ||
+ ctx->subtitle->source == VOBSUB)
{
usedBytes = avp.size;
}
+ // ffmpeg needs us to remember the timestamp of the first packet of
+ // DVD subtitles
+ if (ctx->subtitle->source != VOBSUB)
+ {
+ ctx->sub_pts = AV_NOPTS_VALUE;
+ }
+ // </UGLY_HACKS>
+
if (usedBytes <= avp.size)
{
avp.data += usedBytes;
@@ -332,6 +388,7 @@ int decavsubWork( hb_avsub_context_t * ctx,
{
continue;
}
+ ctx->sub_pts = AV_NOPTS_VALUE;
uint8_t forced_sub = 0;
uint8_t usable_sub = 0;
@@ -474,13 +531,6 @@ int decavsubWork( hb_avsub_context_t * ctx,
out = hb_buffer_init(0);
out->s.flags = HB_BUF_FLAG_EOS;
}
- out->s.frametype = HB_FRAME_SUBTITLE;
- out->s.start = pts;
- if (duration != AV_NOPTS_VALUE)
- {
- out->s.stop = pts + duration;
- out->s.duration = duration;
- }
hb_buffer_list_close(&ctx->list_pass);
}
else if (ctx->subtitle->config.dest == PASSTHRUSUB &&
@@ -522,11 +572,11 @@ int decavsubWork( hb_avsub_context_t * ctx,
b = b->next;
}
hb_buffer_list_close(&ctx->list_pass);
-
- out->s = in->s;
}
- out->s.frametype = HB_FRAME_SUBTITLE;
- out->s.start = pts;
+ if (clear_sub)
+ {
+ out->s.flags = HB_BUF_FLAG_EOS;
+ }
}
else
{
@@ -558,10 +608,6 @@ int decavsubWork( hb_avsub_context_t * ctx,
out = hb_frame_buffer_init(AV_PIX_FMT_YUVA420P, w, h);
memset(out->data, 0, out->size);
- out->s.frametype = HB_FRAME_SUBTITLE;
- out->s.id = in->s.id;
- out->s.start = pts;
- out->s.scr_sequence = in->s.scr_sequence;
out->f.x = x0;
out->f.y = y0;
out->f.window_width = ctx->context->width;
@@ -613,25 +659,29 @@ int decavsubWork( hb_avsub_context_t * ctx,
alpha += out->plane[3].stride;
}
}
- hb_buffer_list_append(&ctx->list, out);
- out = NULL;
}
else
{
out = hb_buffer_init( 0 );
- out->s.frametype = HB_FRAME_SUBTITLE;
- out->s.flags = HB_BUF_FLAG_EOS;
- out->s.id = in->s.id;
- out->s.start = pts;
- out->s.stop = pts;
- out->s.scr_sequence = in->s.scr_sequence;
- out->f.x = 0;
- out->f.y = 0;
- out->f.width = 0;
- out->f.height = 0;
+ out->s.flags = HB_BUF_FLAG_EOS;
+ out->f.x = 0;
+ out->f.y = 0;
+ out->f.width = 0;
+ out->f.height = 0;
+ duration = 0;
}
}
+ out->s.id = in->s.id;
+ out->s.scr_sequence = in->s.scr_sequence;
+ out->s.frametype = HB_FRAME_SUBTITLE;
+ out->s.start = pts;
+ if (duration != AV_NOPTS_VALUE)
+ {
+ out->s.stop = pts + duration;
+ out->s.duration = duration;
+ }
+
hb_buffer_list_append(&ctx->list, out);
avsubtitle_free(&subtitle);
}
diff --git a/libhb/decvobsub.c b/libhb/decvobsub.c
deleted file mode 100644
index c8b5f149a..000000000
--- a/libhb/decvobsub.c
+++ /dev/null
@@ -1,734 +0,0 @@
-/* decvobsub.c
-
- Copyright (c) 2003-2020 HandBrake Team
- This file is part of the HandBrake source code
- Homepage: <http://handbrake.fr/>.
- It may be used under the terms of the GNU General Public License v2.
- For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
- */
-
-/*
- * 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]; // Y
- * uint8_t alpha[pixelCount]; // alpha (max = 16)
- * uint8_t chromaU[pixelCount]; // Cb
- * uint8_t chromaV[pixelCount]; // Cr
- * }
- */
-
-#include "handbrake/handbrake.h"
-
-struct hb_work_private_s
-{
- hb_job_t * job;
-
- hb_buffer_t * buf;
- int size_sub;
- int size_got;
- int size_rle;
- int64_t pts;
- int current_scr_sequence;
- int64_t pts_start;
- int64_t pts_stop;
- int scr_sequence;
- int pts_forced;
- int x;
- int y;
- int width;
- int height;
- int stream_id;
-
- int offsets[2];
- uint8_t lum[4];
- uint8_t chromaU[4];
- uint8_t chromaV[4];
- uint8_t alpha[4];
- uint8_t palette_set;
-};
-
-static hb_buffer_t * Decode( hb_work_object_t * );
-
-int decsubInit( hb_work_object_t * w, hb_job_t * job )
-{
- hb_work_private_t * pv;
-
- pv = calloc( 1, sizeof( hb_work_private_t ) );
- w->private_data = pv;
-
- pv->job = job;
- pv->pts = 0;
-
- // Warn if the input color palette is empty
- pv->palette_set = w->subtitle->palette_set;
- if ( pv->palette_set )
- {
- // Make sure the entries in the palette are not all 0
- pv->palette_set = 0;
- int i;
- for (i=0; i<16; i++)
- {
- if (w->subtitle->palette[i])
- {
- pv->palette_set = 1;
- break;
- }
- }
- }
- if (!pv->palette_set) {
- hb_log( "decvobsub: input color palette is empty!" );
- }
-
- return 0;
-}
-
-int decsubWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
- hb_buffer_t ** buf_out )
-{
- hb_work_private_t * pv = w->private_data;
- hb_buffer_t * in = *buf_in;
- int size_sub, size_rle;
-
- if (in->s.flags & HB_BUF_FLAG_EOF)
- {
- /* EOF on input stream - send it downstream & say that we're done */
- *buf_out = in;
- *buf_in = NULL;
- return HB_WORK_DONE;
- }
-
- pv->stream_id = in->s.id;
-
- size_sub = ( in->data[0] << 8 ) | in->data[1];
- size_rle = ( in->data[2] << 8 ) | in->data[3];
-
- if( !pv->size_sub )
- {
- /* We are looking for the start of a new subtitle */
- if( size_sub && size_rle && size_sub > size_rle &&
- in->size <= size_sub )
- {
- /* Looks all right so far */
- pv->size_sub = size_sub;
- pv->size_rle = size_rle;
-
- pv->buf = hb_buffer_init( 0xFFFF );
- memcpy( pv->buf->data, in->data, in->size );
- pv->buf->s.id = in->s.id;
- pv->buf->s.frametype = HB_FRAME_SUBTITLE;
- pv->size_got = in->size;
- if( in->s.start >= 0 )
- {
- pv->pts = in->s.start;
- pv->current_scr_sequence = in->s.scr_sequence;
- }
- }
- }
- else
- {
- /* We are waiting for the end of the current subtitle */
- if( in->size <= pv->size_sub - pv->size_got )
- {
- memcpy( pv->buf->data + pv->size_got, in->data, in->size );
- pv->buf->s.id = in->s.id;
- pv->size_got += in->size;
- if( in->s.start >= 0 )
- {
- pv->pts = in->s.start;
- pv->current_scr_sequence = in->s.scr_sequence;
- }
- }
- else
- {
- // bad size, must have lost sync
- // force re-sync
- if ( pv->buf != NULL )
- hb_buffer_close( &pv->buf );
- pv->size_sub = 0;
- }
-
- }
-
- *buf_out = NULL;
-
- if( pv->size_sub && pv->size_sub == pv->size_got )
- {
- pv->buf->size = pv->size_sub;
-
- /* We got a complete subtitle, decode it */
- *buf_out = Decode( w );
-
- if ( *buf_out != NULL )
- (*buf_out)->s.id = in->s.id;
-
- /* Wait for the next one */
- pv->size_sub = 0;
- pv->size_got = 0;
- pv->size_rle = 0;
-
- if ( pv->pts_stop != AV_NOPTS_VALUE )
- {
- // If we don't get a valid next timestamp, use the stop time
- // of the current sub as the start of the next.
- // This can happen if reader invalidates timestamps while
- // waiting for an audio to update the SCR.
- pv->pts = pv->pts_stop;
- pv->current_scr_sequence = pv->scr_sequence;
- }
- }
-
- return HB_WORK_OK;
-}
-
-void decsubClose( hb_work_object_t * w )
-{
- hb_work_private_t * pv = w->private_data;
-
- if ( pv && pv->buf )
- hb_buffer_close( &pv->buf );
- free( w->private_data );
-}
-
-hb_work_object_t hb_decvobsub =
-{
- WORK_DECVOBSUB,
- "VOBSUB decoder",
- decsubInit,
- decsubWork,
- decsubClose
-};
-
-
-/***********************************************************************
- * ParseControls
- ***********************************************************************
- * Get the start and end dates (relative to the PTS from the PES
- * header), the width and height of the subpicture and the colors and
- * alphas used in it
- **********************************************************************/
-static int ParseControls( hb_work_object_t * w )
-{
- hb_work_private_t * pv = w->private_data;
- uint8_t * buf = pv->buf->data;
-
- int i;
- int command;
- int date, next;
-
- pv->pts_start = AV_NOPTS_VALUE;
- pv->pts_stop = AV_NOPTS_VALUE;
- pv->pts_forced = 0;
-
- pv->alpha[3] = 0;
- pv->alpha[2] = 0;
- pv->alpha[1] = 0;
- pv->alpha[0] = 0;
-
- for( i = pv->size_rle; ; )
- {
- if ( i + 1 >= pv->buf->size )
- {
- return 1;
- }
-
- date = ( buf[i] << 8 ) | buf[i+1]; i += 2;
- next = ( buf[i] << 8 ) | buf[i+1]; i += 2;
-
- for( ;; )
- {
- if ( i >= pv->buf->size )
- {
- return 1;
- }
- command = buf[i++];
-
- /*
- * There are eight commands available for
- * Sub-Pictures. The first SP_DCSQ should contain, as a
- * minimum, SET_COLOR, SET_CONTR, SET_DAREA, and
- * SET_DSPXA
- */
-
- if( command == 0xFF ) // 0xFF - CMD_END - ends one SP_DCSQ
- {
- break;
- }
-
- switch( command )
- {
- case 0x00: // 0x00 - FSTA_DSP - Forced Start Display, no arguments
- pv->pts_start = pv->pts + date * 1024;
- pv->scr_sequence = pv->current_scr_sequence;
- pv->pts_forced = 1;
- w->subtitle->hits++;
- w->subtitle->forced_hits++;
- break;
-
- case 0x01: // 0x01 - STA_DSP - Start Display, no arguments
- pv->pts_start = pv->pts + date * 1024;
- pv->scr_sequence = pv->current_scr_sequence;
- pv->pts_forced = 0;
- w->subtitle->hits++;
- break;
-
- case 0x02: // 0x02 - STP_DSP - Stop Display, no arguments
- if (pv->pts_stop == AV_NOPTS_VALUE)
- {
- pv->pts_stop = pv->pts + date * 1024;
- pv->scr_sequence = pv->current_scr_sequence;
- }
- break;
-
- case 0x03: // 0x03 - SET_COLOR - Set Colour indices
- {
- /*
- * SET_COLOR - provides four indices into the CLUT
- * for the current PGC to associate with the four
- * pixel values
- */
- int colors[4];
- int j;
-
- if ( i + 1 >= pv->buf->size )
- {
- return 1;
- }
-
- colors[0] = (buf[i+0]>>4)&0x0f;
- colors[1] = (buf[i+0])&0x0f;
- colors[2] = (buf[i+1]>>4)&0x0f;
- colors[3] = (buf[i+1])&0x0f;
-
- for( j = 0; j < 4; j++ )
- {
- /*
- * Not sure what is happening here, in theory
- * the palette is in YCbCr. And we want YUV.
- *
- * However it looks more like YCrCb (according
- * to pgcedit). And the scalers for YCrCb don't
- * work, but I get the right colours by doing
- * no conversion.
- */
- uint32_t color = w->subtitle->palette[colors[j]];
- uint8_t Cr, Cb, y;
- y = (color>>16) & 0xff;
- Cr = (color>>8) & 0xff;
- Cb = (color) & 0xff;
- pv->lum[3-j] = y;
- pv->chromaU[3-j] = Cb;
- pv->chromaV[3-j] = Cr;
- /* hb_log("color[%d] y = %d, u = %d, v = %d",
- 3-j,
- pv->lum[3-j],
- pv->chromaU[3-j],
- pv->chromaV[3-j]);
- */
- }
- i += 2;
- break;
- }
- case 0x04: // 0x04 - SET_CONTR - Set Contrast
- {
- /*
- * SET_CONTR - directly provides the four contrast
- * (alpha blend) values to associate with the four
- * pixel values
- */
- uint8_t alpha[4];
-
- if ( i + 1 >= pv->buf->size )
- {
- return 1;
- }
-
- alpha[3] = ((buf[i+0] >> 4) & 0x0f) << 4;
- alpha[2] = ((buf[i+0] ) & 0x0f) << 4;
- alpha[1] = ((buf[i+1] >> 4) & 0x0f) << 4;
- alpha[0] = ((buf[i+1] ) & 0x0f) << 4;
-
-
- int lastAlpha = pv->alpha[3] + pv->alpha[2] + pv->alpha[1] + pv->alpha[0];
- int currAlpha = alpha[3] + alpha[2] + alpha[1] + alpha[0];
-
- // fading-in, save the highest alpha value
- if( currAlpha > lastAlpha )
- {
- pv->alpha[3] = alpha[3];
- pv->alpha[2] = alpha[2];
- pv->alpha[1] = alpha[1];
- pv->alpha[0] = alpha[0];
- }
-
- // fading-out
- if (currAlpha < lastAlpha && pv->pts_stop == AV_NOPTS_VALUE)
- {
- pv->pts_stop = pv->pts + date * 1024;
- pv->scr_sequence = pv->current_scr_sequence;
- }
-
- i += 2;
- break;
- }
- case 0x05: // 0x05 - SET_DAREA - defines the display area
- {
- if ( i + 4 >= pv->buf->size )
- {
- return 1;
- }
-
- pv->x = (buf[i+0]<<4) | ((buf[i+1]>>4)&0x0f);
- pv->width = (((buf[i+1]&0x0f)<<8)| buf[i+2]) - pv->x + 1;
- pv->y = (buf[i+3]<<4)| ((buf[i+4]>>4)&0x0f);
- pv->height = (((buf[i+4]&0x0f)<<8)| buf[i+5]) - pv->y + 1;
- i += 6;
- break;
- }
- case 0x06: // 0x06 - SET_DSPXA - defines the pixel data addresses
- {
- pv->offsets[0] = ( buf[i] << 8 ) | buf[i+1]; i += 2;
- pv->offsets[1] = ( buf[i] << 8 ) | buf[i+1]; i += 2;
- break;
- }
- }
- }
-
-
-
- if( i > next )
- {
- break;
- }
- i = next;
- }
- // Generate timestamps if they are not set
- if( pv->pts_start == AV_NOPTS_VALUE )
- {
- // Set pts to end of last sub if the start time is unknown.
- pv->pts_start = pv->pts;
- pv->scr_sequence = pv->current_scr_sequence;
- }
-
- return 0;
-}
-
-/***********************************************************************
- * CropSubtitle
- ***********************************************************************
- * Given a raw decoded subtitle, detects transparent borders and
- * returns a cropped subtitle in a hb_buffer_t ready to be used by
- * the renderer, or NULL if the subtitle was completely transparent
- **********************************************************************/
-static int LineIsTransparent( hb_work_object_t * w, uint8_t * p )
-{
- hb_work_private_t * pv = w->private_data;
- int i;
- for( i = 0; i < pv->width; i++ )
- {
- if( p[i] )
- {
- return 0;
- }
- }
- return 1;
-}
-
-static int ColumnIsTransparent( hb_work_object_t * w, uint8_t * p )
-{
- hb_work_private_t * pv = w->private_data;
- int i;
- for( i = 0; i < pv->height; i++ )
- {
- if( p[i*pv->width] )
- {
- return 0;
- }
- }
- return 1;
-}
-
-// Brain dead resampler. This should really use swscale...
-// Uses Bresenham algo to pick source samples for averaging
-static void resample( uint8_t * dst, uint8_t * src, int dst_w, int src_w )
-{
- int dst_x, src_x, err, cnt, sum, val;
-
- if( dst_w < src_w )
- {
- // sample down
- sum = 0;
- val = 0;
- cnt = 0;
- err = src_w / 2;
- dst_x = 0;
- for( src_x = 0; src_x < src_w; src_x++ )
- {
- sum += src[src_x];
- cnt++;
- err -= dst_w;
- if( err < 0 )
- {
- val = sum / cnt;
- dst[dst_x++] = val;
- sum = cnt = 0;
- err += src_w;
- }
- }
- for( ; dst_x < dst_w; dst_x++ )
- {
- dst[dst_x] = val;
- }
- }
- else
- {
- // sample up
- err = dst_w / 2;
- src_x = 0;
- for( dst_x = 0; dst_x < dst_w; dst_x++ )
- {
- dst[dst_x] = src[src_x];
- err -= src_w;
- if( err < 0 )
- {
- src_x++;
- err += dst_w;
- }
- }
- }
-}
-
-static hb_buffer_t * CropSubtitle( hb_work_object_t * w, uint8_t * raw )
-{
- hb_work_private_t * pv = w->private_data;
- int i;
- int crop[4] = { -1,-1,-1,-1 };
- uint8_t * alpha;
- int realwidth, realheight;
- hb_buffer_t * buf;
- uint8_t * lum_in, * alpha_in, * u_in, * v_in;
-
- alpha = raw + pv->width * pv->height;
-
- /* Top */
- for( i = 0; i < pv->height; i++ )
- {
- if( !LineIsTransparent( w, &alpha[i*pv->width] ) )
- {
- crop[0] = i;
- break;
- }
- }
-
- if( crop[0] < 0 )
- {
- /* Empty subtitle */
- return NULL;
- }
-
- /* Bottom */
- for( i = pv->height - 1; i >= 0; i-- )
- {
- if( !LineIsTransparent( w, &alpha[i*pv->width] ) )
- {
- crop[1] = i;
- break;
- }
- }
-
- /* Left */
- for( i = 0; i < pv->width; i++ )
- {
- if( !ColumnIsTransparent( w, &alpha[i] ) )
- {
- crop[2] = i;
- break;
- }
- }
-
- /* Right */
- for( i = pv->width - 1; i >= 0; i-- )
- {
- if( !ColumnIsTransparent( w, &alpha[i] ) )
- {
- crop[3] = i;
- break;
- }
- }
-
- realwidth = crop[3] - crop[2] + 1;
- realheight = crop[1] - crop[0] + 1;
-
- buf = hb_frame_buffer_init( AV_PIX_FMT_YUVA420P, realwidth, realheight );
- buf->s.frametype = HB_FRAME_SUBTITLE;
- buf->s.start = pv->pts_start;
- buf->s.stop = pv->pts_stop;
- buf->s.scr_sequence = pv->scr_sequence;
-
- buf->f.x = pv->x + crop[2];
- buf->f.y = pv->y + crop[0];
- buf->f.window_width = w->subtitle->width;
- buf->f.window_height = w->subtitle->height;
-
- lum_in = raw + crop[0] * pv->width + crop[2];
- alpha_in = lum_in + pv->width * pv->height;
- u_in = alpha_in + pv->width * pv->height;
- v_in = u_in + pv->width * pv->height;
-
- uint8_t *dst;
- for( i = 0; i < realheight; i++ )
- {
- // Luma
- dst = buf->plane[0].data + buf->plane[0].stride * i;
- memcpy( dst, lum_in, realwidth );
-
- if( ( i & 1 ) == 0 )
- {
- // chroma U (resample to YUV420)
- dst = buf->plane[1].data + buf->plane[1].stride * ( i >> 1 );
- resample( dst, u_in, buf->plane[1].width, realwidth );
-
- // chroma V (resample to YUV420)
- dst = buf->plane[2].data + buf->plane[2].stride * ( i >> 1 );
- resample( dst, v_in, buf->plane[2].width, realwidth );
- }
- // Alpha
- dst = buf->plane[3].data + buf->plane[3].stride * i;
- memcpy( dst, alpha_in, realwidth );
-
- lum_in += pv->width;
- alpha_in += pv->width;
- u_in += pv->width;
- v_in += pv->width;
- }
-
- return buf;
-}
-
-static hb_buffer_t * Decode( hb_work_object_t * w )
-{
- hb_work_private_t * pv = w->private_data;
- int code, line, col;
- int offsets[2];
- int * offset;
- hb_buffer_t * buf;
- uint8_t * buf_raw = NULL;
- hb_job_t * job = pv->job;
-
- /* Get infos about the subtitle */
- if ( ParseControls( w ) )
- {
- /*
- * Couldn't parse the info
- */
- hb_deep_log( 2, "decvobsub: Could not parse info!" );
-
- hb_buffer_close( &pv->buf );
- return NULL;
- }
-
- if( job->indepth_scan || ( w->subtitle->config.force && pv->pts_forced == 0 ) )
- {
- /*
- * Don't encode subtitles when doing a scan.
- *
- * When forcing subtitles, ignore all those that don't
- * have the forced flag set.
- */
- hb_buffer_close( &pv->buf );
- return NULL;
- }
-
- if (w->subtitle->config.dest == PASSTHRUSUB)
- {
- pv->buf->s.start = pv->pts_start;
- pv->buf->s.stop = pv->pts_stop;
- pv->buf->s.scr_sequence = pv->scr_sequence;
- buf = pv->buf;
- pv->buf = NULL;
- return buf;
- }
-
- /* Do the actual decoding now */
- buf_raw = malloc( ( pv->width * pv->height ) * 4 );
-
-#define GET_NEXT_NIBBLE code = ( code << 4 ) | ( ( ( *offset & 1 ) ? \
-( pv->buf->data[((*offset)>>1)] & 0xF ) : ( pv->buf->data[((*offset)>>1)] >> 4 ) ) ); \
-(*offset)++
-
- offsets[0] = pv->offsets[0] * 2;
- offsets[1] = pv->offsets[1] * 2;
-
- for( line = 0; line < pv->height; line++ )
- {
- /* Select even or odd field */
- offset = ( line & 1 ) ? &offsets[1] : &offsets[0];
-
- for( col = 0; col < pv->width; col += code >> 2 )
- {
- uint8_t * lum, * alpha, * chromaU, * chromaV;
- int idx, len;
-
- code = 0;
- GET_NEXT_NIBBLE;
- if( code < 0x4 )
- {
- GET_NEXT_NIBBLE;
- if( code < 0x10 )
- {
- GET_NEXT_NIBBLE;
- if( code < 0x40 )
- {
- GET_NEXT_NIBBLE;
- if( code < 0x100 )
- {
- /* End of line */
- code |= ( pv->width - col ) << 2;
- }
- }
- }
- }
-
- lum = buf_raw;
- alpha = lum + pv->width * pv->height;
- chromaU = alpha + pv->width * pv->height;
- chromaV = chromaU + pv->width * pv->height;
- idx = code & 3;
- len = code >> 2;
- // Protect against malformed VOBSUB with invalid run length
- if (len > pv->width - col)
- {
- len = pv->width - col;
- }
-
- memset( lum + line * pv->width + col, pv->lum[idx], len );
- memset( alpha + line * pv->width + col, pv->alpha[idx], len );
- memset( chromaU + line * pv->width + col, pv->chromaU[idx], len );
- memset( chromaV + line * pv->width + col, pv->chromaV[idx], len );
- }
-
- /* Byte-align */
- if( *offset & 1 )
- {
- (*offset)++;
- }
- }
-
- hb_buffer_close( &pv->buf );
-
- /* Crop subtitle (remove transparent borders) */
- buf = CropSubtitle( w, buf_raw );
-
- free( buf_raw );
-
- return buf;
-}
diff --git a/libhb/demuxmpeg.c b/libhb/demuxmpeg.c
index 17ade5742..2c9c25f50 100644
--- a/libhb/demuxmpeg.c
+++ b/libhb/demuxmpeg.c
@@ -222,10 +222,11 @@ static void demux_dvd_ps( hb_buffer_t * buf, hb_buffer_list_t * list_es,
/* Here we hit we ES payload */
buf_es = hb_buffer_init( pes_packet_end - pos );
- buf_es->s.id = id;
- buf_es->s.start = pts;
+ buf_es->s.id = id;
+ buf_es->s.start = pts;
buf_es->s.renderOffset = dts;
- buf_es->s.stop = AV_NOPTS_VALUE;
+ buf_es->s.duration = (int64_t)AV_NOPTS_VALUE;
+ buf_es->s.stop = AV_NOPTS_VALUE;
if ( state && id == 0xE0)
{
// Consume a chapter break, and apply it to the ES.
diff --git a/libhb/dvd.c b/libhb/dvd.c
index 29ba0d54d..206c1c51e 100644
--- a/libhb/dvd.c
+++ b/libhb/dvd.c
@@ -205,7 +205,8 @@ static void add_subtitle( hb_list_t * list_subtitle, int position,
subtitle->config.dest = RENDERSUB;
subtitle->stream_type = 0xbd;
subtitle->substream_type = 0x20 + position;
- subtitle->codec = WORK_DECVOBSUB;
+ subtitle->codec = WORK_DECAVSUB;
+ subtitle->codec_param = AV_CODEC_ID_DVD_SUBTITLE;
subtitle->timebase.num = 1;
subtitle->timebase.den = 90000;
diff --git a/libhb/dvdnav.c b/libhb/dvdnav.c
index 779f234ec..970ee29c5 100644
--- a/libhb/dvdnav.c
+++ b/libhb/dvdnav.c
@@ -351,7 +351,8 @@ static void add_subtitle( hb_list_t * list_subtitle, int position,
subtitle->config.dest = RENDERSUB;
subtitle->stream_type = 0xbd;
subtitle->substream_type = 0x20 + position;
- subtitle->codec = WORK_DECVOBSUB;
+ subtitle->codec = WORK_DECAVSUB;
+ subtitle->codec_param = AV_CODEC_ID_DVD_SUBTITLE;
subtitle->timebase.num = 1;
subtitle->timebase.den = 90000;
diff --git a/libhb/encvobsub.c b/libhb/encvobsub.c
deleted file mode 100644
index 01315aa90..000000000
--- a/libhb/encvobsub.c
+++ /dev/null
@@ -1,75 +0,0 @@
-/* encvobsub.c
-
- Copyright (c) 2003-2020 HandBrake Team
- This file is part of the HandBrake source code
- Homepage: <http://handbrake.fr/>.
- It may be used under the terms of the GNU General Public License v2.
- For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
- */
-
-#include "handbrake/handbrake.h"
-#include "handbrake/hbffmpeg.h"
-
-struct hb_work_private_s
-{
- hb_job_t * job;
-};
-
-int encsubInit( hb_work_object_t * w, hb_job_t * job )
-{
- hb_work_private_t * pv;
-
- pv = calloc( 1, sizeof( hb_work_private_t ) );
- w->private_data = pv;
-
- pv->job = job;
-
- return 0;
-}
-
-int encsubWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
- hb_buffer_t ** buf_out )
-{
- hb_buffer_t * in = *buf_in;
-
- if (w->subtitle->source != VOBSUB)
- {
- // Invalid source, send EOF, this shouldn't ever happen
- hb_log("encvobsub: invalid subtitle source");
- hb_buffer_close( buf_in );
- *buf_out = hb_buffer_eof_init();
- }
- if (in->s.flags & HB_BUF_FLAG_EOF)
- {
- /* EOF on input stream - send it downstream & say that we're done */
- *buf_out = in;
- *buf_in = NULL;
- return HB_WORK_DONE;
- }
-
- /*
- * Not much to do, just pass the buffer on.
- * Some day, we may re-encode bd subtitles here ;)
- */
- if (buf_out)
- {
- *buf_out = in;
- *buf_in = NULL;
- }
-
- return HB_WORK_OK;
-}
-
-void encsubClose( hb_work_object_t * w )
-{
- free( w->private_data );
-}
-
-hb_work_object_t hb_encvobsub =
-{
- WORK_ENCVOBSUB,
- "VOBSUB encoder",
- encsubInit,
- encsubWork,
- encsubClose
-};
diff --git a/libhb/handbrake/common.h b/libhb/handbrake/common.h
index 391e6e369..3d91be0da 100644
--- a/libhb/handbrake/common.h
+++ b/libhb/handbrake/common.h
@@ -1262,7 +1262,6 @@ extern hb_work_object_t hb_sync_video;
extern hb_work_object_t hb_sync_audio;
extern hb_work_object_t hb_sync_subtitle;
extern hb_work_object_t hb_decvobsub;
-extern hb_work_object_t hb_encvobsub;
extern hb_work_object_t hb_decsrtsub;
extern hb_work_object_t hb_decutf8sub;
extern hb_work_object_t hb_dectx3gsub;
diff --git a/libhb/handbrake/internal.h b/libhb/handbrake/internal.h
index 2f8fc0369..b0f1dd9fe 100644
--- a/libhb/handbrake/internal.h
+++ b/libhb/handbrake/internal.h
@@ -438,7 +438,6 @@ enum
WORK_DECSRTSUB,
WORK_DECTX3GSUB,
WORK_DECSSASUB,
- WORK_ENCVOBSUB,
WORK_RENDER,
WORK_ENCAVCODEC,
WORK_ENCQSV,
diff --git a/libhb/hb.c b/libhb/hb.c
index a62052436..fcd57c3e6 100644
--- a/libhb/hb.c
+++ b/libhb/hb.c
@@ -1713,8 +1713,6 @@ int hb_global_init()
hb_register(&hb_decsrtsub);
hb_register(&hb_decssasub);
hb_register(&hb_dectx3gsub);
- hb_register(&hb_decvobsub);
- hb_register(&hb_encvobsub);
hb_register(&hb_encavcodec);
hb_register(&hb_encavcodeca);
#ifdef __APPLE__
diff --git a/libhb/rendersub.c b/libhb/rendersub.c
index a04adbe90..7f1a4badb 100644
--- a/libhb/rendersub.c
+++ b/libhb/rendersub.c
@@ -22,7 +22,7 @@ struct hb_filter_private_s
int sws_width;
int sws_height;
- // VOBSUB
+ // VOBSUB && PGSSUB
hb_list_t * sub_list; // List of active subs
// SSA
@@ -345,6 +345,8 @@ static void ApplyVOBSubs( hb_filter_private_t * pv, hb_buffer_t * buf )
int ii;
hb_buffer_t *sub, *next;
+ // Note that VOBSUBs can overlap in time.
+ // I.e. more than one may be rendered to the screen at once.
for( ii = 0; ii < hb_list_count(pv->sub_list); )
{
sub = hb_list_item( pv->sub_list, ii );
diff --git a/libhb/stream.c b/libhb/stream.c
index bf96090bc..bb99688e0 100644
--- a/libhb/stream.c
+++ b/libhb/stream.c
@@ -1992,6 +1992,11 @@ static void pes_add_subtitle_to_title(
subtitle->format = PICTURESUB;
subtitle->config.dest = RENDERSUB;
break;
+ case AV_CODEC_ID_DVD_SUBTITLE:
+ subtitle->source = VOBSUB;
+ subtitle->format = PICTURESUB;
+ subtitle->config.dest = RENDERSUB;
+ break;
default:
// Unrecognized, don't add to list
hb_log("unrecognized subtitle!");
@@ -1999,11 +2004,6 @@ static void pes_add_subtitle_to_title(
return;
}
} break;
- case WORK_DECVOBSUB:
- subtitle->source = VOBSUB;
- subtitle->format = PICTURESUB;
- subtitle->config.dest = RENDERSUB;
- break;
default:
// Unrecognized, don't add to list
hb_log("unrecognized subtitle!");
@@ -3875,7 +3875,8 @@ static void hb_ps_stream_find_streams(hb_stream_t *stream)
int idx = update_ps_streams( stream, pes_info.stream_id,
pes_info.bd_substream_id, 0, -1 );
stream->pes.list[idx].stream_kind = S;
- stream->pes.list[idx].codec = WORK_DECVOBSUB;
+ stream->pes.list[idx].codec = WORK_DECAVSUB;
+ stream->pes.list[idx].codec_param = AV_CODEC_ID_DVD_SUBTITLE;
strncpy(stream->pes.list[idx].codec_name,
"DVD Subtitle", 80);
continue;
@@ -4719,6 +4720,7 @@ static hb_buffer_t * generate_output_data(hb_stream_t *stream, int curstream)
stream->ts.pcr = AV_NOPTS_VALUE;
buf->s.start = ts_stream->pes_info.pts;
buf->s.renderOffset = ts_stream->pes_info.dts;
+ buf->s.duration = (int64_t)AV_NOPTS_VALUE;
}
else
{
@@ -5514,13 +5516,16 @@ static void add_ffmpeg_subtitle( hb_title_t *title, hb_stream_t *stream, int id
switch ( codecpar->codec_id )
{
case AV_CODEC_ID_DVD_SUBTITLE:
- subtitle->format = PICTURESUB;
- subtitle->source = VOBSUB;
- subtitle->config.dest = RENDERSUB; // By default render (burn-in) the VOBSUB.
- subtitle->codec = WORK_DECVOBSUB;
+ subtitle->format = PICTURESUB;
+ subtitle->source = VOBSUB;
+ subtitle->config.dest = RENDERSUB;
+ subtitle->codec = WORK_DECAVSUB;
+ subtitle->codec_param = AV_CODEC_ID_DVD_SUBTITLE;
if (ffmpeg_parse_vobsub_extradata(codecpar, subtitle))
+ {
hb_log( "add_ffmpeg_subtitle: malformed extradata for VOB subtitle track; "
"subtitle colors likely to be wrong" );
+ }
break;
case AV_CODEC_ID_DVB_SUBTITLE:
subtitle->format = PICTURESUB;