Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Speeding up YouTube video loading and seeking? #10601

Open
nyanpasu64 opened this issue Sep 2, 2022 · 13 comments
Open

Speeding up YouTube video loading and seeking? #10601

nyanpasu64 opened this issue Sep 2, 2022 · 13 comments
Labels

Comments

@nyanpasu64
Copy link

Important Information

Provide following Information:

  • mpv version: v0.34.1
  • Linux Distribution and Version: Void Linux
  • Source of the mpv binary: distro package, xbps-src rebuilt
  • Window Manager and version: xfwm4 4.16.1
  • GPU driver and version: i915, Linux 5.18.19, 3rd Gen Core processor Graphics Controller

Reproduction steps

  • Install yt-dlp (eg. using pipx).

Regular settings:

  • Optionally add hwdec to mpv.conf (slightly slower startup, insignificant unless my testing is inaccurate).
  • Load a YouTube URL in mpv.
  • Click the seek bar ahead of the currently buffered video, to seek into the future.

save-position-on-quit:

  • Add save-position-on-quit to mpv.conf.
  • Load a long YouTube video, seek multiple minutes into the future, and close mpv.
  • Reopen mpv with the same video.

Expected behavior

Loading videos in mpv should not be much slower than invoking yt-dlp to get the URL, and seeking should be as fast as seeking in the YouTube web UI (1 second).

Actual behavior

On my slow Ivy Bridge laptop with or without hardware decoding, loading URLs takes around 4.5 seconds for a window to appear and the play point to appear in the terminal. Around 2.6 seconds is spent waiting for yt-dlp, around 700ms before it trying and failing to play the HTML file, a second or two to buffer the URL, plus overhead.

save-position-on-quit adds around 2.5 seconds (probably variable) to the startup time, spent seeking. Manually seeking after playback begins takes a variable amount of time depending on where you seek to, I've observed 3 and 6 seconds.

Using https://www.youtube.com/watch?v=9ghnfSWKOgY as a seek time benchmark:

  • mpv (h.264) takes 3.0 seconds to seek to 19:04 bookmark (Page Up). I'm guessing it's not using range-based URLs.
  • firefox (supplied with the VP9 non-range-based URL obtained from yt-dlp -j 'https://www.youtube.com/watch?v=9ghnfSWKOgY'|gron|less) takes ~2.1 seconds to seek to 19:04 (eyeballed).
    • mpv on the same URL takes 2 seconds to seek to 19:02, and 3 seconds to seek to 19:05. It could probably be improved, but not by much.
  • youtube web takes 1 second to seek to 19:04 bookmark (Ctrl+Right), with cache cleared!!!
    • firefox dev tools's network tab shows youtube web is using range-based URLs (URLs ending with &range=333999644-335298975&rn=5&rbuf=0 or &range=335298976-336678963&rn=7&rbuf=1813 etc.), eliminating the need to parse video metadata to seek quickly.

Ideas

  • mpv [open_demux_thread] takes 700ms failing to play a html page before invoking yt-dlp. Hopefully this can be fixed; you could send certain urls straight to yt-dlp instead of trying to download them, or race direct playback and yt-dlp for URLs that don't end in a recognized media file extension, or when you see a text/html file you cancel demuxing it and invoke yt-dlp instead. I've applied this patch locally and it seems to help, but I don't know if it breaks compatibility with text/html pages which succeed in demuxing (I don't know if they exist). (Returning an error from stream_create_instance prints a loud red error message to terminal, which I felt was inappropriate in normal operation.)
diff --git a/demux/demux.c b/demux/demux.c
index fdec805d15..3934302fcf 100644
--- a/demux/demux.c
+++ b/demux/demux.c
@@ -3401,6 +3401,19 @@ static struct demuxer *demux_open(struct stream *stream,
         }
     }
 
+    // `text/html; charset=utf-8` is never a playable audio or video format.
+    // Erroring early allows us to call yt-dlp and begin video playback faster.
+    {
+        const char *mime_type = stream->mime_type;
+        if (mime_type
+            && strncmp(mime_type, "text/html", 9) == 0
+            && (mime_type[9] == '\0' || mime_type[9] == ';'))
+        {
+            MP_VERBOSE(stream, "Refusing to play HTML file, falling back to ytdl\n");
+            goto done;
+        }
+    }
+
     struct parent_stream_info sinfo = {
         .seekable = stream->seekable,
         .is_network = stream->is_network,
  • yt-dlp isn't fast. i haven't looked into making it fast. it's less slow (1 second) on my Ryzen with wired Ethernet, than on my 10 year old laptop over Wi-Fi (2.6 seconds).
  • mpv hardware decoding is not much slower to start than software decoding. i wouldn't spend time here, but I may be wrong.

The biggest improvement in video resume and seek time would require either modifying yt-dlp or mpv to use range-based URLs to seek YouTube streams efficiently. I don't know if this is a standard practice in the metadata files specifying the URLs to download (what's it called, m3u8? HLS? maybe DASH? see https://www.google.com/search?q=range+rn+rbuf), or specific to YouTube. Is implementing URL-based fast seeking practical?

Log file

mpv-no-seek-no-hwdec.cast.txt
mpv-no-seek.cast.txt
mpv-seek.cast.txt

Sample files

https://www.youtube.com/watch?v=9ghnfSWKOgY

@guidocella
Copy link
Contributor

You can use yt-dlp on the first try with --script-opts-append=ytdl_hook-try_ytdl_first=yes. To increase the download speed see #8655 (comment)

@nyanpasu64
Copy link
Author

nyanpasu64 commented Sep 2, 2022

I think I'm going to try out try_ytdl_first=yes. I still believe my text/html change (or a similar change), or racing yt-dlp and video playback, is a useful alternative to ensure fast video and extractor playback (if it could be made to definitely not break video playback).

#4793 is, unfortunate. Unfortunately Google is effectively a malicious or hostile actor, trying to fingerprint clients and harm the experience of alternative ones. Nonetheless, is downloading a different URL every few seconds (mirroring the YouTube web client, which I suspect changes video quality on file boundaries), rather than sending range HTTP headers, viable to implement (perhaps https://satadalsengupta.github.io/docs/papers/2017_nossdav_youtubedash.pdf)?

@guidocella
Copy link
Contributor

That seems harder as you would have to instruct ffmpeg on which URLs to download. mpv already gets the 10M http-chunk-size from yt-dlp -J, and ffmpeg already implements -offset and -end_offset (if you try something like ffmpeg -offset 10M -end_offset 20M -i 'https://rr1---sn-uxaxpu5ap5-u5ge.googlevideo.com/videoplayback... the download isn't throttled), someone just needs to implement continously updating the offset while playing a single URL. I tried but I couldn't figure it out.

@guidocella
Copy link
Contributor

Also you can use #9042 (comment) to ensure fast yt-dlp playback without breaking media URLs.

@nyanpasu64
Copy link
Author

Well, yt-dlp does not even support vp9 live streams!

I used yt-dlp to get the video (not livestream) information, and it returned a VP9 URL. But YouTube Web uses range-based URLs even for non-livestream videos.

How much is try_ytdl_first=yes affecting the speed for you?

If my calculations are accurate it'll reduce startup time by 700ms or so. I haven't measured, but I'm happy with that part of the startup time.

Ideally something like script-opts=ytdl_hook-try_ytdl_first=yes,ytdl_hook-exclude="%.webm$|%.ts$|%.mp3$|%.m3u8$|%.m3u$|%.mkv$|%.mp4$|%.VOB$" would be added to the default mpv configuration (easy but picking the right command is difficult and bikesheddy), and range-based seeking would be supported (difficult, I don't want to try implementing now).

@guidocella
Copy link
Contributor

Does it throttle if you download from the start?

It doesn't get throttled as long as -end_offset is <= 10M.

@stumpedatwork
Copy link

I find I have seeking issue when ytdl_hook uses EDL to mix multiple streams from youtube so I try to select a format with a single stream when using ytdl.

dexeonify added a commit to dexeonify/mpv-config that referenced this issue Sep 6, 2022
@nyanpasu64
Copy link
Author

I suspect fast seeking in YouTube videos is related to DASH and #7033. It appears nontrivial to implement though.

@sun95n
Copy link

sun95n commented Dec 30, 2022

(if you try something like ffmpeg -offset 10M -end_offset 20M -i 'https://rr1---sn-uxaxpu5ap5-u5ge.googlevideo.com/videoplayback... the download isn't throttled)

Is it possible to pass these options to mpv's libavformat demuxer using --demuxer-lavf-o=?

@guidocella
Copy link
Contributor

Is it possible to pass these options to mpv's libavformat demuxer using --demuxer-lavf-o=?

Yes, but playing a single chunk isn't useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants
@nyanpasu64 @stumpedatwork @guidocella @sun95n and others