Skip to content

fix(youtube): restore SABR initialization cold start#86

Open
Priveetee wants to merge 1 commit into
InfinityLoop1308:mainfrom
Priveetee:fix/youtube-sabr-init-cold-start
Open

fix(youtube): restore SABR initialization cold start#86
Priveetee wants to merge 1 commit into
InfinityLoop1308:mainfrom
Priveetee:fix/youtube-sabr-init-cold-start

Conversation

@Priveetee

Copy link
Copy Markdown
Contributor

Summary

While fixing a SABR playback regression in TypeType, I found that initialization requests could receive several valid SABR responses without the requested init segment.

The session eventually failed with:

Requested SABR segment was not returned: itag=140:init
Requested SABR segment was not returned: itag=299:init

PipePipe normally gets initialization data directly from the format metadata, but both the player fallback and the downloader still use fetchSegment(SabrSegmentRequest.initialization(...)) when that data is unavailable.

This restores the cold-start request shape needed for those initialization requests without changing the normal media request flow.

Problem

An initialization request needs to identify the requested audio or video track without carrying the buffered ranges and playback state of a normal follow-up request.

With the current request shape, YouTube can return policy and media responses while skipping the requested init segment entirely.

Changes

  • Use an initialization-specific request state while fetching an init segment.
  • Enable only the requested audio or video track for that request.
  • Keep audio and video format preferences separate from the selected track state.
  • Omit buffered ranges and top-level player time from the initialization request.
  • Restore the normal session state after the request completes.

Validation

Build:

  • ./gradlew :extractor:compileJava
  • git diff --check

Runtime validation through TypeType with the patched extractor:

  • audio initialization returned successfully
  • video initialization returned successfully
  • audio and video media segments continued loading
  • playback reached 45 seconds without interruption or player errors

Test videos:

  • https://www.youtube.com/watch?v=X4FeyMPB6SY
  • https://www.youtube.com/watch?v=J_T7EFHEcpc

Notes

This is extractor-only. PipePipe already uses the affected initialization API, so no client change is required.

This does not address WebView, BotGuard, or DNS failures. It only fixes SABR init-segment retrieval.

@InfinityLoop1308

Copy link
Copy Markdown
Owner

Thanks. I was able to reproduce the missing init segment issue, but in my local testing, changing the request shape as done here did not appear to fix it. Can you share some logs showing the difference in your test environment?

@InfinityLoop1308

InfinityLoop1308 commented Jul 19, 2026

Copy link
Copy Markdown
Owner

btw, thanks for your replies and doc updates this week :) I'm busy with some other things this week and your works really helps a lot. (and I am not good at writing docs lol
do you mind making a small pr in the main repo to edit the readme: remove the User in User wiki maintained by [@Priveetee](https://github.com/Priveetee) ? this also makes you shown as Contributor in the main repo

@Priveetee

Copy link
Copy Markdown
Contributor Author

Hey, I found why our results were different.

My first test called fetchSegment(init) on a fresh session, and request 0 works on both sides. To reproduce it, the session needs to consume request 0 first, then lose its cached init before requesting it again.

The concrete PipePipe caller is the SABR downloader recovery path, not the normal player bootstrap. If pumpOnceStreaming() returns media before an init was written, SabrSegmentWriter keeps that media pending and fetchMissingInitializations() calls session.fetchSegment(initialization(...)) after the session has already advanced. Without the init, the pending media cannot be written or muxed.

This is the minimal sequence I used with the normal test Downloader / NewPipe.init(...) setup:

final YoutubeSabrInfo info = YoutubeSabrProbe.fetchSabrInfo(
        "X4FeyMPB6SY",
        YoutubeSabrClientProfile.WEB,
        localization,
        country);
final YoutubeSabrFormat audio = info.findFormatByItag(140);
final YoutubeSabrFormat video = info.findFormatByItag(271);
final YoutubeSabrSession session = new YoutubeSabrSession(
        info, audio, video, null, null);

session.setTraceEnabled(true);
session.pumpOnce(localization);
session.discardCachedSegment(SabrSegmentRequest.initialization(audio));
session.discardCachedSegment(SabrSegmentRequest.initialization(video));

try {
    final SabrMediaSegment init = session.fetchSegment(
            SabrSegmentRequest.initialization(video), localization);
    System.out.println("video init bytes=" + init.getLength());
} finally {
    System.out.println(session.getDiagnosticTrace());
}

On base b974fde4, request 1 still contains the audio/video buffered ranges, the response has initialization=[], and the video init is never returned.

On this PR at ec9ba0dd, request 1 has ranges=, the response contains initialization=[140, 271], and the call returns 3153 bytes.

Using a fresh session and changing the final request to initialization(audio) gives the same difference: base fails, this PR returns 1811 bytes. I also reproduced it with J_T7EFHEcpc using video 299/audio 140, and with MWEB.

I applied the same diff temporarily on current main 68e834b3 too, and both init calls passed there.

So without this change the downloader recovery asks for init but keeps receiving normal follow-up media, then retries the whole cold start. With this change it receives the init, writes it, and can flush the media that was already waiting.

@Priveetee

Copy link
Copy Markdown
Contributor Author

Yeah of course, np at all! I’ll take care of the small README PR :)

And honestly, thx a lot for the recognition and for giving me this opportunity! I really appreciate it, and I’m glad the docs and replies helped :p

@Priveetee
Priveetee force-pushed the fix/youtube-sabr-init-cold-start branch from ec9ba0d to cbd63dd Compare July 19, 2026 20:30
@InfinityLoop1308

InfinityLoop1308 commented Jul 19, 2026

Copy link
Copy Markdown
Owner

btw, can you check the webview doc under the latest codebase? It shouldn't require update now but I haven't make tests.

@Priveetee

Priveetee commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Yep, I checked it on the latest codebase :)

I used fresh PipePipeClient and PipePipeExtractor clones, then tested the
published 5.2.4-beta on clean Android 6, 7 and 8 AOSP images with their original
WebView 44, 52 and 61. Playback worked on all three without Chrome or Google
Play Services.

I updated the documentation to explain that 5.2.4-beta no longer requires
WebView 80+, added the new screenshots, kept the old 5.2.3 instructions in a
collapsed legacy section, and fixed the diagram readability too:

Webview

And like we said before, if u ever need me for the documentation, the extractor,
or if u have any question, just assign me or ping me, I am here, no worries.

It genuinely makes me very happy to contribute to PipePipe, it is honestly my
favorite project, so never hesitate :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants