summaryrefslogtreecommitdiffstats
path: root/demux
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2013-07-14 23:44:50 +0200
committerwm4 <wm4@nowhere>2013-07-14 23:44:50 +0200
commit65d8709152c5e3942e1a4db958be7cae80ea05b0 (patch)
treed40bddf44bf69f53319ff9d664e24ea90082a5be /demux
parente18ffd6b998858ca0797cb0ce4aabd4a509655dd (diff)
downloadmpv-65d8709152c5e3942e1a4db958be7cae80ea05b0.tar.bz2
mpv-65d8709152c5e3942e1a4db958be7cae80ea05b0.tar.xz
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.
Diffstat (limited to 'demux')
-rw-r--r--demux/demux_lavf.c93
1 files changed, 85 insertions, 8 deletions
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: