diff options
author | John Stebbins <[email protected]> | 2019-12-11 16:29:33 -0800 |
---|---|---|
committer | John Stebbins <[email protected]> | 2020-03-29 08:23:20 -0600 |
commit | 93ac83aca7220403b84fb88d4b0326f383e862b9 (patch) | |
tree | 983d995ec7c0d48e90cfeaa63dbdb7923f328c26 | |
parent | 494a548865e20767076f2caaa4f23522ee750687 (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.patch | 49 | ||||
-rw-r--r-- | libhb/decavsub.c | 108 | ||||
-rw-r--r-- | libhb/decvobsub.c | 734 | ||||
-rw-r--r-- | libhb/demuxmpeg.c | 7 | ||||
-rw-r--r-- | libhb/dvd.c | 3 | ||||
-rw-r--r-- | libhb/dvdnav.c | 3 | ||||
-rw-r--r-- | libhb/encvobsub.c | 75 | ||||
-rw-r--r-- | libhb/handbrake/common.h | 1 | ||||
-rw-r--r-- | libhb/handbrake/internal.h | 1 | ||||
-rw-r--r-- | libhb/hb.c | 2 | ||||
-rw-r--r-- | libhb/rendersub.c | 4 | ||||
-rw-r--r-- | libhb/stream.c | 25 |
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; |