From 65d8709152c5e3942e1a4db958be7cae80ea05b0 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 14 Jul 2013 23:44:50 +0200 Subject: demux_lavf: add terrible hack to make DVD playback just work DVD playback had some trouble with PTS resets: libavformat's genpts feature would try reading until EOF (worst case) to find a new usable PTS in case a packet's PTS is not set correctly. Especially with slow DVD access, this would make the player to appear frozen. Reimplement it partially in demux_lavf.c, and use that code in the DVD case. This is heavily "inspired" by the code in av_read_frame from libavformat/utils.c. The difference is that we stop reading if no PTS has been found after 50 packets (consider this a heuristic). Also, we don't bother with the PTS wrapping and last-frame-before-EOF handling. Even with normal PTS wraps, the player frontend will go to hell for the duration of a frame anyway, and should recover quickly after that. The terribleness of this commit is mostly that we duplicate libavformat functionality, and that we suddenly need a packet queue. --- demux/demux_lavf.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 85 insertions(+), 8 deletions(-) (limited to 'demux') diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c index 82ca2e24d0..e7d144eb71 100644 --- a/demux/demux_lavf.c +++ b/demux/demux_lavf.c @@ -58,6 +58,8 @@ const m_option_t lavfdopts_conf[] = { OPT_FLAG("allow-mimetype", lavfdopts.allow_mimetype, 0), OPT_INTRANGE("probescore", lavfdopts.probescore, 0, 0, 100), OPT_STRING("cryptokey", lavfdopts.cryptokey, 0), + OPT_CHOICE("genpts-mode", lavfdopts.genptsmode, 0, + ({"auto", 0}, {"lavf", 1}, {"builtin", 2}, {"no", 3})), OPT_STRING("o", lavfdopts.avopt, 0), {NULL, NULL, 0, 0, 0, 0, NULL} }; @@ -66,6 +68,8 @@ const m_option_t lavfdopts_conf[] = { // libavformat (almost) always reads data in blocks of this size. #define BIO_BUFFER_SIZE 32768 +#define MAX_PKT_QUEUE 50 + typedef struct lavf_priv { char *filename; const struct format_hack *format_hack; @@ -81,6 +85,9 @@ typedef struct lavf_priv { bool seek_by_bytes; int bitrate; char *mime_type; + bool genpts_hack; + AVPacket *packets[MAX_PKT_QUEUE]; + int num_packets; } lavf_priv_t; struct format_hack { @@ -522,8 +529,13 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check) priv->use_dts = true; demuxer->timestamp_type = TIMESTAMP_TYPE_SORT; } else { - if (opts->user_correct_pts != 0) + int mode = lavfdopts->genptsmode; + if (mode == 0 && opts->user_correct_pts != 0) + mode = demuxer->stream->uncached_type == STREAMTYPE_DVD ? 2 : 1; + if (mode == 1) avfc->flags |= AVFMT_FLAG_GENPTS; + if (mode == 2) + priv->genpts_hack = true; } if (opts->index_mode == 0) avfc->flags |= AVFMT_FLAG_IGNIDX; @@ -646,36 +658,45 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check) return 0; } +static void seek_reset(demuxer_t *demux) +{ + lavf_priv_t *priv = demux->priv; + + for (int n = 0; n < priv->num_packets; n++) + talloc_free(priv->packets[n]); + priv->num_packets = 0; +} + static int destroy_avpacket(void *pkt) { av_free_packet(pkt); return 0; } -static int demux_lavf_fill_buffer(demuxer_t *demux) +static int read_more_av_packets(demuxer_t *demux) { lavf_priv_t *priv = demux->priv; - demux_packet_t *dp; - mp_msg(MSGT_DEMUX, MSGL_DBG2, "demux_lavf_fill_buffer()\n"); + + if (priv->num_packets >= MAX_PKT_QUEUE) + return -1; demux->filepos = stream_tell(demux->stream); AVPacket *pkt = talloc(NULL, AVPacket); if (av_read_frame(priv->avfc, pkt) < 0) { talloc_free(pkt); - return 0; + return -2; // eof } talloc_set_destructor(pkt, destroy_avpacket); add_new_streams(demux); assert(pkt->stream_index >= 0 && pkt->stream_index < priv->num_streams); - AVStream *st = priv->avfc->streams[pkt->stream_index]; struct sh_stream *stream = priv->streams[pkt->stream_index]; if (!demuxer_stream_is_selected(demux, stream)) { talloc_free(pkt); - return 1; + return 0; // skip } // If the packet has pointers to temporary fields that could be @@ -683,8 +704,61 @@ static int demux_lavf_fill_buffer(demuxer_t *demux) // allocations so we can safely queue the packet for any length of time. if (av_dup_packet(pkt) < 0) abort(); + + priv->packets[priv->num_packets++] = pkt; + talloc_steal(priv, pkt); + return 1; +} + +static int read_av_packet(demuxer_t *demux, AVPacket **pkt) +{ + lavf_priv_t *priv = demux->priv; + + if (priv->num_packets < 1) { + int r = read_more_av_packets(demux); + if (r <= 0) + return r; + } + + AVPacket *next = priv->packets[0]; + if (priv->genpts_hack && next->dts != AV_NOPTS_VALUE) { + int n = 1; + while (next->pts == AV_NOPTS_VALUE) { + while (n >= priv->num_packets) { + if (read_more_av_packets(demux) < 0) + goto end; // queue limit or EOF reached - just use as is + } + AVPacket *cur = priv->packets[n]; + if (cur->stream_index == next->stream_index) { + if (next->dts < cur->dts && cur->pts != cur->dts) + next->pts = cur->dts; + } + n++; + } + } + +end: + MP_TARRAY_REMOVE_AT(priv->packets, priv->num_packets, 0); + *pkt = next; + return 1; +} + +static int demux_lavf_fill_buffer(demuxer_t *demux) +{ + lavf_priv_t *priv = demux->priv; + demux_packet_t *dp; + mp_msg(MSGT_DEMUX, MSGL_DBG2, "demux_lavf_fill_buffer()\n"); + + AVPacket *pkt; + int r = read_av_packet(demux, &pkt); + if (r <= 0) + return r == 0; // don't signal EOF if skipping a packet + + AVStream *st = priv->avfc->streams[pkt->stream_index]; + struct sh_stream *stream = priv->streams[pkt->stream_index]; + dp = new_demux_packet_fromdata(pkt->data, pkt->size); - dp->avpacket = pkt; + dp->avpacket = talloc_steal(dp, pkt); int64_t ts = priv->use_dts ? pkt->dts : pkt->pts; if (ts != AV_NOPTS_VALUE) { @@ -714,6 +788,8 @@ static void demux_seek_lavf(demuxer_t *demuxer, float rel_seek_secs, mp_msg(MSGT_DEMUX, MSGL_DBG2, "demux_seek_lavf(%p, %f, %f, %d)\n", demuxer, rel_seek_secs, audio_delay, flags); + seek_reset(demuxer); + if (priv->seek_by_bytes) { int64_t pos = demuxer->filepos; rel_seek_secs *= priv->bitrate / 8; @@ -877,6 +953,7 @@ redo: avio_flush(priv->avfc->pb); av_seek_frame(priv->avfc, 0, stream_tell(demuxer->stream), AVSEEK_FLAG_BYTE); + seek_reset(demuxer); avio_flush(priv->avfc->pb); return DEMUXER_CTRL_OK; default: -- cgit v1.2.3