mpv icon indicating copy to clipboard operation
mpv copied to clipboard

Can't seek byterange MP4 HLS stream

Open nihaals opened this issue 1 year ago • 5 comments

Important Information

Provide following Information:

  • mpv version: 0.37.0
  • macOS Version: 13.6.2
  • Source of the mpv binary or bundle: Homebrew formula

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.

nihaals avatar Jan 02 '24 00:01 nihaals

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.

llyyr avatar Jan 02 '24 02:01 llyyr

Quick question. Has this been reported upstream?

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

stonerl avatar May 21 '24 04:05 stonerl

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

nihaals avatar Jun 10 '24 18:06 nihaals

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.

BlakeB415 avatar Aug 26 '24 15:08 BlakeB415

Ping the patch or resubmit it to the ML

llyyr avatar Aug 27 '24 02:08 llyyr

is it any way to fix it?

voronoff2803 avatar Oct 26 '24 15:10 voronoff2803

is it any way to fix it?

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

llyyr avatar Oct 26 '24 16:10 llyyr

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

voronoff2803 avatar Oct 26 '24 17:10 voronoff2803

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

llyyr avatar Oct 26 '24 17:10 llyyr

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 <[email protected]>
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 <[email protected]>
---
 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 <[email protected]>
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 <[email protected]>
---
 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

llyyr avatar Oct 27 '24 01:10 llyyr

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

debugzxcv avatar Nov 19 '24 22:11 debugzxcv

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. ^^

gungun974 avatar Nov 19 '24 22:11 gungun974

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

llyyr avatar Nov 19 '24 23:11 llyyr