feat(youtube): SABR extractor support#69
Conversation
|
bit of context since this isn't a fresh idea: the PoC #68 (and the client PoC InfinityLoop1308/PipePipeClient#43) were exactly that, throwaway PoCs to prove SABR actually plays end to end. they did their job, so i closed #68 rather than keep pushing onto a messy branch. this is the real integration, rebuilt clean. the SABR extractor stands on its own here, and i reworked the buffering to be reader-driven (pacing + cache eviction follow what the player has actually read, not the play head, so it doesn't deadlock or blow up memory anymore), plus the Safari client-version fix and the live-metadata foundation. on the client side i split it too: the media3 migration is its own PR (InfinityLoop1308/PipePipeClient#45) and the SABR client sits on top. still WIP, not for merge yet (FORCE_SABR_FOR_TESTING is on so every video goes through SABR while i hammer it), but this is where it continues from now :) |
7bab69f to
9364aa7
Compare
9364aa7 to
12ec2e6
Compare
|
I've been working on this whenever I had some free time. I tested it a lot (u know me by now, im a little paranoid 🥲) on a S25, a Z Fold 5 and a Pixel 8, mostly the app/code itself, and found and fixed a shit load of bugs along the way 🫠: forward seeks, rewind after the stream buffered to the end, return from background, a crash on decoder init... u can see it all in the commit history. Honestly I'm starting to reach my limits testing alone, so I think this is getting close to review-ready @InfinityLoop1308. No rush at all, as we say in French: y a pas le feu au lac :) If u want me to split this PR differently (or anything else really), don't hesitate to tell me. One thing I'd really like your take on: how we wire SABR into the app. My take is it shouldn't be SABR-only, I'd rather marry both, keep the current delivery and have SABR live next to it (as an option / fallback) so we don't regress anyone. But u might see it differently, so im genuinely curious what u think. Right now Last thing: it's pushed with Could be handy to hand a debug build to users who hit issues so their reports are actually useful. |
|
Oh and I almost forgot the fun part 😄 I wrote actual docs for the extractor! the whole thing's in there: architecture, extraction flow, the info model, the downloader, paging, adding a service, the YouTube service... and then it dives deep into SABR (UMP framing, part-type decoding, building the had a real blast writing it, curious what u think :) (If u have time of course) |
84997da to
d105adb
Compare
|
the other day i was watching some LaSalle videos in fullscreen (1080p60 vp9) and kept getting this micro-freeze: the picture would hang for a moment every ~6-7s, and it got worse the longer the video played. only happened in fullscreen, and only on some videos. threw some logging on the SABR pipeline to see what was going on, and the webm segment index was never getting built. the parser was bailing with "Invalid WebM element size" on the vp9 inits (itag 303): a SABR/DASH init's Segment element declares its full media size (basically the whole video), way bigger than the few-KB init we actually have, so readElement threw and the whole parse gave up 😅 with no index we were falling back to a uniform segment-duration guess (averageDurationMs), which drifts from the real variable-length segments (~4.8-5.4s here). so the chunk timing slowly desyncs from the actual samples and the picture stutters/freezes, worse as the drift adds up. mp4/avc was fine the whole time (its sidx parsed), this was webm-only, which is why it only showed on vp9. fix is small: when an element overruns the init buffer, clamp it to the buffer instead of failing the parse. the cues are located by the explicit index range anyway, so we only ever read what we have :p tested on my pixel 8: 1080p60 vp9 plays smooth now, no more freeze ! |
|
fixed the 4K OOM crashes (rewind + forward-seek deadlock): on a big seek the cache held two disconnected spans and busted the byte cap, so i collapse it to a window around the seek target now. also stream the SABR response instead of buffering the whole body + byte-sized the back-buffer so 4K keeps its read-ahead. tested pixel 8 (1080p/1440p/4K, seek + relaunch), 0 OOM. |
|
honestly every time i think i'm done i find another damn bug 😂 but for real this time, i think it's ready. @InfinityLoop1308 it's your call now. i'm kinda reaching my limits here, these days i'm spending way more time on niche bugs and performance than on the actual implementation, which is probably a good sign the core is solid. the way i see it there are two paths: either i do the work to wire SABR into the app properly alongside HLS (imo it shouldn't be SABR-only), or you tell me the direction you'd prefer and i roll with that. happy either way. one thing for sure, we'll want testing on more devices now, my poor pixel 8 has really been through it 😅 and one phone only tells you so much (decoders behave so wildly from one device to the next). but honestly i'm pretty excited about where this landed, it feels genuinely close now and the core's holding up really well :) |
|
Awesome work :) I'm going to test it on my devices and think about the next step. |
First real integration of the SABR extractor (supersedes the PoC #68). Pairs with the client SABR PR (coming) and the media3 migration InfinityLoop1308/PipePipeClient#45. Tracking: InfinityLoop1308/PipePipeClient#42.
Docs (how SABR works end to end): https://priveetee.github.io/Docs-PipePipe/developer-guide/introduction
Related:
Summary
Adds the YouTube SABR (Server ABR) extraction path end to end: UMP wire reading, the proto request/response codecs, the typed UMP parts (media, policy, context, onesie...), response decoding + mp4/webm segment index parsing, and the session/state model the client pump drives. A new
DeliveryMethod.SABRexposes it.Changes
DeliveryMethod.SABRdelivery method.YoutubeStreamExtractor.rewindBufferedTo/prepareForRewind): the buffered head was one-way (assumeBufferedUntilonly ever extends), so a rewind onto an already-sent segment left the request claiming we still had it and the server sent nothing back; this shrinks the head (contiguous + observed-timing) so the server re-sends from the target.SabrLiveMetadatagetters (live foundation).YoutubeParsingHelper: Safari player request now uses the live web client version (the hardcoded one started returningpage needs to be reloaded).Why
YouTube serves some videos SABR-only; the old paths return "Content Not Yet Supported". This adds a real extractor path, with the client PR as the runnable counterpart.
Impact / Compatibility
The new
sabr/package andDeliveryMethod.SABRare additive. The one behavioural change is inYoutubeStreamExtractor: aFORCE_SABR_FOR_TESTINGflag (currentlytrue) routes every YouTube video through the SABR pipeline, so HLS/DASH/progressive are bypassed on purpose to stress-test SABR on everything. With the flagfalse(production default), HLS/DASH stay untouched and SABR only fills the SABR-only / no-HLS gap that upstream currently throwsContentNotSupportedExceptionon. The Safari client-version fix also helps the existing logged-in path.Validation
./gradlew :extractor:compileJava -x checkstyleMainNotes
FORCE_SABR_FOR_TESTING = trueon purpose: all YouTube playback goes through SABR with no HLS fallback, so we exercise the SABR path on every video. It flips back tofalsebefore merge (then HLS stays as fallback and SABR only covers the SABR-only gap).checkstyleMainexcluded, fails repo-wide unrelated to this change.