mpv-player / mpv

🎥 Command line video player
https://mpv.io
Other
28.73k stars 2.93k forks source link

Can't seek byterange MP4 HLS stream #13211

Open nihaals opened 11 months ago

nihaals commented 11 months ago

Important Information

Provide following Information:

Reproduction steps

Try to reproduce your issue with --no-config first. If it isn't reproducible with --no-config try to first find out which option or script causes your issue.

Describe the reproduction steps as precise as possible. It's very likely that the bug you experience wasn't reproduced by the developer because the workflow differs from your own.

  1. Run mpv --no-config "https://framatube.org/static/streaming-playlists/hls/66fcff64-d8f9-49c1-8deb-011b115786de/73451ad8-d30e-4beb-aa92-3dba23ba07c8-720.m3u8"
  2. Notice skipping ahead to cached parts of the video works fine
  3. Skip to a part of the video that isn't cached
  4. Notice MPV freezes
  5. Notice the errors (shown below)
  6. Notice MPV crashes

Expected behavior

Video plays

Actual behavior

Video freezes and errors are logged:

[ffmpeg] NULL: Invalid NAL unit size (-20372128 > 35).
[ffmpeg] NULL: missing picture in access unit with size 39

MPV then crashes

Log file

mpv.log

Sample files

https://framatube.org/static/streaming-playlists/hls/66fcff64-d8f9-49c1-8deb-011b115786de/73451ad8-d30e-4beb-aa92-3dba23ba07c8-720.m3u8

Extra context

The example video is from #10029 as I'm not able to share the video I found the issue with, but it doesn't seem to be a duplicate of that issue. Based on experiments, this seems to only happen with playlists that use byte ranges on a single MP4 file. This might be caused by the boundaries not being in ideal places but I don't know much about how MP4 files work. VLC can play these streams as expected but prints errors like mp4: Fragment sequence discontinuity detected 677 != 0. It looks like VLC might download the previous chunk to get the data it needs to play the actual timestamp skipped to but I'm unsure.

llyyr commented 11 months ago

This is a feature request for ffmpeg. The same issue can be reproduced with ffplay where it'll try to download all the segments sequentially even if you seek way ahead of the current pts but still fail to continue anyway.

  1. Notice MPV crashes

This shouldn't and doesn't happen, in my case mpv and ffplay continuously download the file until they reach the end but the playback never resumes. Looking at the logs, it seems to be the case for you too.

[  51.911][v][lavf] EOF reached.
[  51.911][v][vf] filter input EOF
[  51.911][v][vf] filter output EOF
[  51.911][v][cplayer] video EOF reached
[  51.911][d][cplayer] video EOF (status=4)
[  51.911][v][af] filter input EOF
[  51.911][v][af] filter output EOF
[  51.911][v][cplayer] audio filter EOF
[  51.911][d][cplayer] video EOF (status=4)
[  51.911][v][cplayer] audio ready (and EOF)
[  51.911][d][cplayer] video EOF (status=4)
[  51.911][v][cplayer] starting audio playback
[  51.911][v][cplayer] audio draining
[  51.911][v][cplayer] playback restart complete @ -9223372036854775808.000000, audio=draining, video=eof
[  51.911][v][cplayer] audio EOF reached
[  51.911][d][cplayer] video EOF (status=4)
[  51.911][v][cplayer] EOF code: 1  

mpv reaches EOF so it exits, that's not a crash.

stonerl commented 6 months ago

Quick question. Has this been reported upstream?

I searched their bug tracker but haven't found a ticket that resembles this one.

nihaals commented 5 months ago

I haven't had a chance to so there might not be a ffmpeg issue yet.

BlakeB415 commented 3 months ago

It's an open defect with fmp4 handling. https://trac.ffmpeg.org/ticket/7359 https://ffmpeg.org/pipermail/ffmpeg-devel/2020-April/261343.html There's a patch from 2020 but it hasn't been merged.

llyyr commented 3 months ago

Ping the patch or resubmit it to the ML

voronoff2803 commented 1 month ago

is it any way to fix it?

llyyr commented 1 month ago

is it any way to fix it?

Yes, ping the patch above and try to get it merged into ffmpeg

voronoff2803 commented 1 month ago

is it any way to fix it?

Yes, ping the patch above and try to get it merged into ffmpeg

for what ffmpeg version this patch? I have errors when I apply it

llyyr commented 1 month ago

You did something wrong because it applies with no conflicts on ffmpeg master

llyyr commented 1 month ago

Sorry, you're right that it doesn't compile even if it does apply, because it's accessing items that aren't part of the public AVStream anymore. I rebased the patches

From 0c14ac8d5f19f22f31ae9505e3db5eb417e8d6e0 Mon Sep 17 00:00:00 2001
From: llyyr <llyyr.public@gmail.com>
Date: Sun, 27 Oct 2024 06:52:42 +0530
Subject: [PATCH 1/2] avformat/hls: always return keyframe if not
 AVSEEK_FLAG_ANY

Co-Authored-by: vectronic <hello.vectronic@gmail.com>
---
 libavformat/hls.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/libavformat/hls.c b/libavformat/hls.c
index 62473a15ddb5..4d02faa9e49a 100644
--- a/libavformat/hls.c
+++ b/libavformat/hls.c
@@ -2350,8 +2350,10 @@ static int hls_read_packet(AVFormatContext *s, AVPacket *pkt)
                     ts_diff = av_rescale_rnd(pls->pkt->dts, AV_TIME_BASE,
                                             tb.den, AV_ROUND_DOWN) -
                             pls->seek_timestamp;
-                    if (ts_diff >= 0 && (pls->seek_flags  & AVSEEK_FLAG_ANY ||
-                                        pls->pkt->flags & AV_PKT_FLAG_KEY)) {
+                    /* If AVSEEK_FLAG_ANY, keep reading until ts_diff >= 0,
+                     * otherwise return the first keyframe encountered */
+                    if ((ts_diff >= 0 && (pls->seek_flags & AVSEEK_FLAG_ANY)) ||
+                        (!(pls->seek_flags & AVSEEK_FLAG_ANY) && (pls->pkt->flags & AV_PKT_FLAG_KEY)))  {
                         pls->seek_timestamp = AV_NOPTS_VALUE;
                         break;
                     }
@@ -2502,7 +2504,7 @@ static int hls_read_seek(AVFormatContext *s, int stream_index,
         pb->eof_reached = 0;
         /* Clear any buffered data */
         pb->buf_end = pb->buf_ptr = pb->buffer;
-        /* Reset the pos, to let the mpegts demuxer know we've seeked. */
+        /* Reset the pos, to let the mpegts/mov demuxer know we've seeked. */
         pb->pos = 0;
         /* Flush the packet queue of the subdemuxer. */
         ff_read_frame_flush(pls->ctx);
-- 
2.47.0
From 07045b8245cfc5822005c716db8a84b710579787 Mon Sep 17 00:00:00 2001
From: llyyr <llyyr.public@gmail.com>
Date: Sun, 27 Oct 2024 06:44:51 +0530
Subject: [PATCH 2/2] avformat/mov: handle stream position resets

If the stream position has been reset, clear fragment index, the index
entries and the current sample and search for the next root.

Co-Authored-by: vectronic <hello.vectronic@gmail.com>
---
 libavformat/mov.c | 37 +++++++++++++++++++++++++++++++++----
 1 file changed, 33 insertions(+), 4 deletions(-)

diff --git a/libavformat/mov.c b/libavformat/mov.c
index 8c3329b81596..78540bec1d11 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -10612,15 +10612,15 @@ static int mov_switch_root(AVFormatContext *s, int64_t target, int index)

     if (index >= 0 && index < mov->frag_index.nb_items)
         target = mov->frag_index.item[index].moof_offset;
-    if (avio_seek(s->pb, target, SEEK_SET) != target) {
+    if (target >= 0 && avio_seek(s->pb, target, SEEK_SET) != target) {
         av_log(mov->fc, AV_LOG_ERROR, "root atom offset 0x%"PRIx64": partial file\n", target);
         return AVERROR_INVALIDDATA;
     }

     mov->next_root_atom = 0;
-    if (index < 0 || index >= mov->frag_index.nb_items)
+    if ((index < 0 && target >= 0) || index >= mov->frag_index.nb_items)
         index = search_frag_moof_offset(&mov->frag_index, target);
-    if (index < mov->frag_index.nb_items &&
+    if (index >= 0 && index < mov->frag_index.nb_items &&
         mov->frag_index.item[index].moof_offset == target) {
         if (index + 1 < mov->frag_index.nb_items)
             mov->next_root_atom = mov->frag_index.item[index + 1].moof_offset;
@@ -10751,9 +10751,38 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
     AVIndexEntry *sample;
     AVStream *st = NULL;
     int64_t current_index;
-    int ret;
+    int ret, i;
     mov->fc = s;
  retry:
+    if (s->pb->pos == 0) {
+        // Discard current fragment index
+        if (mov->frag_index.allocated_size > 0) {
+            av_freep(&mov->frag_index.item);
+            mov->frag_index.nb_items = 0;
+            mov->frag_index.allocated_size = 0;
+            mov->frag_index.current = -1;
+            mov->frag_index.complete = 0;
+        }
+
+        for (i = 0; i < s->nb_streams; i++) {
+            AVStream *avst = s->streams[i];
+            FFStream *sti = ffstream(avst);
+            MOVStreamContext *msc = avst->priv_data;
+
+            // Clear current sample
+            mov_current_sample_set(msc, 0);
+
+            // Discard current index entries
+            if (sti->index_entries_allocated_size > 0) {
+                av_freep(&sti->index_entries);
+                sti->index_entries_allocated_size = 0;
+                sti->nb_index_entries = 0;
+            }
+        }
+
+        if ((ret = mov_switch_root(s, -1, -1)) < 0)
+            return ret;
+    }
     sample = mov_find_next_sample(s, &st);
     if (!sample || (mov->next_root_atom && sample->pos > mov->next_root_atom)) {
         if (!mov->next_root_atom)
-- 
2.47.0
debugzxcv commented 1 week ago

Looks like fixed by https://github.com/FFmpeg/FFmpeg/commit/380a518c439d4e5e3cf17b97e4a06259e8048f99

gungun974 commented 1 week ago

Thank you for the patch. I had just finished personally developing my HLS track generator for my music player and I couldn't understand why, when seeking faster than mpv's cache, mpv would skip the music.

With this patch I can say that the problem has disappeared and mpv now loads segments properly.

However, I have a question - for now I'm patching ffmpeg myself and then recompiling mpv with my version.

Around when can I be sure that the majority of mpv versions will definitely include the patch?

I'm not at all familiar with the release cycles of mpv and ffmpeg. ^^

llyyr commented 1 week ago

mpv doesn't publish any binaries, so ask wherever you're getting your binaries from