diff options
Diffstat (limited to 'demux/demux_lavf.c')
-rw-r--r-- | demux/demux_lavf.c | 68 |
1 files changed, 64 insertions, 4 deletions
diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c index 283e6fe6fb..e0bf24fd50 100644 --- a/demux/demux_lavf.c +++ b/demux/demux_lavf.c @@ -140,6 +140,7 @@ struct format_hack { bool fix_editlists : 1; bool is_network : 1; bool no_seek : 1; + bool no_pcm_seek : 1; }; #define BLACKLIST(fmt) {fmt, .ignore = true} @@ -161,8 +162,8 @@ static const struct format_hack format_hacks[] = { {"mpeg", .use_stream_ids = true}, {"mpegts", .use_stream_ids = true}, - {"mp4", .skipinfo = true, .fix_editlists = true}, - {"matroska", .skipinfo = true}, + {"mp4", .skipinfo = true, .fix_editlists = true, .no_pcm_seek = true}, + {"matroska", .skipinfo = true, .no_pcm_seek = true}, {"v4l2", .no_seek = true}, @@ -217,6 +218,10 @@ typedef struct lavf_priv { struct demux_lavf_opts *opts; double mf_fps; + bool pcm_seek_hack_disabled; + AVStream *pcm_seek_hack; + int pcm_seek_hack_packet_size; + // Proxying nested streams. struct nested_stream *nested; int num_nested; @@ -677,6 +682,12 @@ static void handle_new_stream(demuxer_t *demuxer, int i) } } + if (!sh->attached_picture) { + // A real video stream probably means it's a packet based format. + priv->pcm_seek_hack_disabled = true; + priv->pcm_seek_hack = NULL; + } + sh->codec->disp_w = codec->width; sh->codec->disp_h = codec->height; if (st->avg_frame_rate.num) @@ -785,6 +796,25 @@ static void handle_new_stream(demuxer_t *demuxer, int i) sh->missing_timestamps = !!(priv->avif_flags & AVFMT_NOTIMESTAMPS); mp_tags_copy_from_av_dictionary(sh->tags, st->metadata); demux_add_sh_stream(demuxer, sh); + + // Unfortunately, there is no better way to detect PCM codecs, other + // than listing them all manually. (Or other "frameless" codecs. Or + // rather, codecs with frames so small libavformat will put multiple of + // them into a single packet, but not preserve these artificial packet + // boundaries on seeking.) + if (sh->codec->codec && strncmp(sh->codec->codec, "pcm_", 4) == 0 && + codec->block_align && !priv->pcm_seek_hack_disabled && + priv->opts->hacks && !priv->format_hack.no_pcm_seek && + st->time_base.num == 1 && st->time_base.den == codec->sample_rate) + { + if (priv->pcm_seek_hack) { + // More than 1 audio stream => usually doesn't apply. + priv->pcm_seek_hack_disabled = true; + priv->pcm_seek_hack = NULL; + } else { + priv->pcm_seek_hack = st; + } + } } select_tracks(demuxer, i); @@ -1112,6 +1142,9 @@ static bool demux_lavf_read_packet(struct demuxer *demux, return true; } + if (priv->pcm_seek_hack == st && !priv->pcm_seek_hack_packet_size) + priv->pcm_seek_hack_packet_size = pkt->size; + if (pkt->pts != AV_NOPTS_VALUE) dp->pts = pkt->pts * av_q2d(st->time_base); if (pkt->dts != AV_NOPTS_VALUE) @@ -1139,6 +1172,7 @@ static void demux_seek_lavf(demuxer_t *demuxer, double seek_pts, int flags) lavf_priv_t *priv = demuxer->priv; int avsflags = 0; int64_t seek_pts_av = 0; + int seek_stream = -1; if (priv->optical_crap_hack) { if (flags & SEEK_FACTOR) @@ -1169,13 +1203,39 @@ static void demux_seek_lavf(demuxer_t *demuxer, double seek_pts, int flags) seek_pts_av = seek_pts * AV_TIME_BASE; } - int r = av_seek_frame(priv->avfc, -1, seek_pts_av, avsflags); + // Hack to make wav seeking "deterministic". Without this, features like + // backward playback won't work. + if (priv->pcm_seek_hack && !priv->pcm_seek_hack_packet_size) { + // This might for example be the initial seek. Fuck it up like the + // bullshit it is. + AVPacket pkt = {0}; + if (av_read_frame(priv->avfc, &pkt) >= 0) + priv->pcm_seek_hack_packet_size = pkt.size; + av_packet_unref(&pkt); + add_new_streams(demuxer); + } + if (priv->pcm_seek_hack && priv->pcm_seek_hack_packet_size && + !(avsflags & AVSEEK_FLAG_BYTE)) + { + int samples = priv->pcm_seek_hack_packet_size / + priv->pcm_seek_hack->codecpar->block_align; + if (samples > 0) { + MP_VERBOSE(demuxer, "using bullshit libavformat PCM seek hack\n"); + double pts = seek_pts_av / (double)AV_TIME_BASE; + seek_pts_av = pts / av_q2d(priv->pcm_seek_hack->time_base); + int64_t align = seek_pts_av % samples; + seek_pts_av -= align; + seek_stream = priv->pcm_seek_hack->index; + } + } + + int r = av_seek_frame(priv->avfc, seek_stream, seek_pts_av, avsflags); if (r < 0 && (avsflags & AVSEEK_FLAG_BACKWARD)) { // When seeking before the beginning of the file, and seeking fails, // try again without the backwards flag to make it seek to the // beginning. avsflags &= ~AVSEEK_FLAG_BACKWARD; - r = av_seek_frame(priv->avfc, -1, seek_pts_av, avsflags); + r = av_seek_frame(priv->avfc, seek_stream, seek_pts_av, avsflags); } if (r < 0) { |