diff --git a/libavformat/dvdvideodec.c b/libavformat/dvdvideodec.c index f20d2aa5cb..179181ce69 100644 --- a/libavformat/dvdvideodec.c +++ b/libavformat/dvdvideodec.c @@ -38,6 +38,7 @@ #include #include +#include "libavcodec/ac3_parser.h" #include "libavutil/avstring.h" #include "libavutil/avutil.h" #include "libavutil/intreadwrite.h" @@ -166,6 +167,7 @@ typedef struct DVDVideoDemuxContext { int64_t first_pts; /* the PTS of the first video keyframe */ int play_started; /* signal that playback has started */ DVDVideoPlaybackState play_state; /* the active playback state */ + int64_t *prev_pts; /* track the previous PTS emitted per stream */ int64_t pts_offset; /* PTS discontinuity offset (ex. VOB change) */ int seek_warned; /* signal that we warned about seeking limits */ int subdemux_reset; /* signal that subdemuxer should be reset */ @@ -1566,7 +1568,7 @@ static int dvdvideo_read_header(AVFormatContext *s) (ret = dvdvideo_subdemux_open(s)) < 0) return ret; - return 0; + goto end_ready; } if (c->opt_pgc && (c->opt_chapter_start > 1 || c->opt_chapter_end > 0 || c->opt_preindex)) { @@ -1603,6 +1605,14 @@ static int dvdvideo_read_header(AVFormatContext *s) (ret = dvdvideo_subdemux_open(s)) < 0) return ret; +end_ready: + c->prev_pts = av_malloc(s->nb_streams * sizeof(int64_t)); + if (!c->prev_pts) + return AVERROR(ENOMEM); + + for (int i = 0; i < s->nb_streams; i++) + c->prev_pts[i] = AV_NOPTS_VALUE; + return 0; } @@ -1614,6 +1624,8 @@ static int dvdvideo_read_packet(AVFormatContext *s, AVPacket *pkt) int is_key = 0; int st_mapped = 0; AVStream *st_subdemux; + uint8_t ac3_bitstream_id; + uint16_t ac3_frame_size; ret = av_read_frame(c->mpeg_ctx, pkt); if (ret < 0) { @@ -1661,11 +1673,27 @@ static int dvdvideo_read_packet(AVFormatContext *s, AVPacket *pkt) if (pkt->pts < 0) goto discard; + /* clean up after DVD muxers which end seamless PGs on duplicate or partial AC3 samples */ + if (st_subdemux->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && + st_subdemux->codecpar->codec_id == AV_CODEC_ID_AC3) { + + if (pkt->pts <= c->prev_pts[pkt->stream_index]) + goto discard; + + ret = av_ac3_parse_header(pkt->buf->data, pkt->size, + &ac3_bitstream_id, &ac3_frame_size); + + if (ret < 0 || pkt->size != ac3_frame_size) + goto discard; + } + av_log(s, AV_LOG_TRACE, "st=%d pts=%" PRId64 " dts=%" PRId64 " " "pts_offset=%" PRId64 " first_pts=%" PRId64 "\n", pkt->stream_index, pkt->pts, pkt->dts, c->pts_offset, c->first_pts); + c->prev_pts[pkt->stream_index] = pkt->pts; + return 0; discard: @@ -1673,6 +1701,9 @@ discard: "Discarding frame @ st=%d pts=%" PRId64 " dts=%" PRId64 " is_key=%d st_mapped=%d\n", st_mapped ? pkt->stream_index : -1, pkt->pts, pkt->dts, is_key, st_mapped); + if (st_mapped) + c->prev_pts[pkt->stream_index] = pkt->pts; + return FFERROR_REDO; } @@ -1689,6 +1720,9 @@ static int dvdvideo_close(AVFormatContext *s) dvdvideo_ifo_close(s); + if (c->prev_pts) + av_freep(&c->prev_pts); + return 0; }