mirror of https://git.ffmpeg.org/ffmpeg.git
avformat/dvdvideodec: discard duplicate or partial AC3 samples
Some DVD muxers signal segments to start with duplicate audio samples when starting extraction from a chapter marker or terminate seamless PGs on partial audio samples (causing corrupt AC3 frames). Clean up after these muxers by tracking frames with duplicate PTS and eliminating partial AC3 frames. This results in smoother chapter extraction and overall seeking experience, with linear PTS and AC3 delay within 32ms (1 frame) away from the video. The issue was not apparent until the flushing pattern was replaced with a full subdemux reset, as the flushing dropped the frames prematurely, along side others, as such they were never present to begin with.
This commit is contained in:
parent
f2f238c3a4
commit
0912407b9d
|
@ -38,6 +38,7 @@
|
|||
#include <dvdread/ifo_types.h>
|
||||
#include <dvdread/nav_read.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue