From 8fb91511b1e69a0121d33ab48974e0a4b8151858 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Wed, 10 Nov 2010 07:17:51 +0200 Subject: demux_lavf: add simple seek-by-bytes mode for MPEG Seeking in MPEG files with pts resets could fail completely, as it was always done by timestamps and those of course don't unambiguously specify a file position in such files. Add basic functionality for byte-based seeking and playback position reporting, and decide whether to use that functionality based on a simple heuristic (could be improved). --- libmpdemux/demux_lavf.c | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/libmpdemux/demux_lavf.c b/libmpdemux/demux_lavf.c index be184a8f64..7f50e2de4a 100644 --- a/libmpdemux/demux_lavf.c +++ b/libmpdemux/demux_lavf.c @@ -77,6 +77,8 @@ typedef struct lavf_priv { int nb_streams_last; bool internet_radio_hack; bool use_dts; + bool seek_by_bytes; + int bitrate; }lavf_priv_t; static int mp_read(void *opaque, uint8_t *buf, int size) { @@ -612,7 +614,22 @@ static demuxer_t* demux_open_lavf(demuxer_t *demuxer){ demuxer->video->id=-2; // audio-only } //else if (best_video > 0 && demuxer->video->id == -1) demuxer->video->id = best_video; - demuxer->accurate_seek = true; + /* libavformat sets bitrate for mpeg based on pts at start and end + * of file, which fails for files with pts resets. So calculate our + * own bitrate estimate. */ + if (priv->avif->flags & AVFMT_TS_DISCONT) { + for (int i = 0; i < avfc->nb_streams; i++) + priv->bitrate += avfc->streams[i]->codec->bit_rate; + /* pts-based is more accurate if there are no resets; try to make + * a somewhat reasonable guess */ + if (!avfc->duration || avfc->duration == AV_NOPTS_VALUE + || priv->bitrate && (avfc->bit_rate < priv->bitrate / 2 + || avfc->bit_rate > priv->bitrate * 2)) + priv->seek_by_bytes = true; + if (!priv->bitrate) + priv->bitrate = 1440000; + } + demuxer->accurate_seek = !priv->seek_by_bytes; return demuxer; } @@ -751,6 +768,14 @@ static void demux_seek_lavf(demuxer_t *demuxer, float rel_seek_secs, float audio int avsflags = 0; mp_msg(MSGT_DEMUX,MSGL_DBG2,"demux_seek_lavf(%p, %f, %f, %d)\n", demuxer, rel_seek_secs, audio_delay, flags); + if (priv->seek_by_bytes) { + int64_t pos = demuxer->filepos; + rel_seek_secs *= priv->bitrate / 8; + pos += rel_seek_secs; + av_seek_frame(priv->avfc, -1, pos, AVSEEK_FLAG_BYTE); + return; + } + if (flags & SEEK_ABSOLUTE) { priv->last_pts = 0; } else { @@ -780,14 +805,25 @@ static int demux_lavf_control(demuxer_t *demuxer, int cmd, void *arg) switch (cmd) { case DEMUXER_CTRL_CORRECT_PTS: return DEMUXER_CTRL_OK; - case DEMUXER_CTRL_GET_TIME_LENGTH: + case DEMUXER_CTRL_GET_TIME_LENGTH: + if (priv->seek_by_bytes) { + /* Our bitrate estimate may be better than would be used in + * otherwise similar fallback code at higher level */ + if (demuxer->movi_end <= 0) + return DEMUXER_CTRL_DONTKNOW; + *(double *)arg = (demuxer->movi_end - demuxer->movi_start) * 8 / + priv->bitrate; + return DEMUXER_CTRL_GUESS; + } if (priv->avfc->duration == 0 || priv->avfc->duration == AV_NOPTS_VALUE) return DEMUXER_CTRL_DONTKNOW; *((double *)arg) = (double)priv->avfc->duration / AV_TIME_BASE; return DEMUXER_CTRL_OK; - case DEMUXER_CTRL_GET_PERCENT_POS: + case DEMUXER_CTRL_GET_PERCENT_POS: + if (priv->seek_by_bytes) + return DEMUXER_CTRL_DONTKNOW; // let it use the fallback code if (priv->avfc->duration == 0 || priv->avfc->duration == AV_NOPTS_VALUE) return DEMUXER_CTRL_DONTKNOW; -- cgit v1.2.3