Skip to content

UMP et décodage

Partie de SABR dans l'extracteur. Ici : l'enveloppe UMP au niveau octet, chaque type de part que le décodeur connaît, et la réponse décodée.

Le cadrage UMP

Le corps de la réponse est de l'UMP (Ultra-Minimal Playback), le conteneur propre à YouTube, pas du protobuf au niveau de l'enveloppe (les payloads à l'intérieur des parts sont du protobuf). UmpReader.readAll(byte[]) parcourt un seul tableau en mémoire comme une suite de parts :

Layout d'une part UMP

Chaque part est [type : varint-UMP][size : varint-UMP][payload : size octets], répété jusqu'à EOF. Il n'y a aucun réassemblage cross-buffer à cette couche, le body entier doit déjà être un seul tableau ; un buffer tronqué throw simplement SabrProtocolException.

Le varint UMP

Le varint d'UMP n'est pas celui de protobuf. Le nombre total d'octets est décidé par les bits de poids fort du premier octet :

Premier octetTotal octetsValeur
0x00–0x7F (< 128)1l'octet lui-même
0x80–0xBF2(b0 & 0x3f) + 64·b1
0xC0–0xDF3(b0 & 0x1f) + 32·(b1 + 256·b2)
0xE0–0xEF4(b0 & 0x0f) + 16·(b1 + 256·(b2 + 256·b3))
0xF0–0xFF5les 4 octets suivants, little-endian (les bits de valeur du premier octet sont jetés)

Le résultat est un int signé 32-bit ; le cas 5 octets peut overflow en négatif, ce que le garde type < 0 || size < 0 rejette, c'est le plafond pratique de taille de part.

UmpPart tient type, size, et data. getData() renvoie un clone défensif ; un getRawData() interne renvoie le tableau backing (utilisé sur les hot paths décodage/stitch pour éviter de copier des segments de plusieurs Mo).

Les types de part

SabrResponseDecoder dispatch sur l'id de type de part. L'ensemble complet qu'il reconnaît :

IDConstanteDécodé en
10ONESIE_HEADERSabrOnesieHeader (porté aux parts onesie suivantes)
11ONESIE_DATASabrOnesieData
12ONESIE_ENCRYPTED_MEDIASabrOnesieData (chiffré)
20MEDIA_HEADERSabrMediaHeader
21MEDIAoctets média (1er octet du payload = header id)
22MEDIA_ENDferme un header id
31LIVE_METADATASabrLiveMetadata
35NEXT_REQUEST_POLICYSabrNextRequestPolicy (pose le backoff)
37FORMAT_SELECTION_CONFIGSabrFormatSelectionConfig
42FORMAT_INITIALIZATION_METADATASabrFormatInitializationMetadata
43SABR_REDIRECTSabrRedirect
44SABR_ERRORSabrError
45SABR_SEEKSabrSeek
46RELOAD_PLAYER_RESPONSESabrReloadPlayerResponse (pose reloadRequested)
47PLAYBACK_START_POLICYSabrPlaybackStartPolicy
51SELECTABLE_FORMATSSabrSelectableFormats
52REQUEST_IDENTIFIERSabrRequestIdentifier
53REQUEST_CANCELLATION_POLICYSabrRequestCancellationPolicy
57SABR_CONTEXT_UPDATESabrContextUpdate
58STREAM_PROTECTION_STATUSSabrStreamProtectionStatus (pose status + maxRetries)
59SABR_CONTEXT_SENDING_POLICYSabrContextSendingPolicy
65PREWARM_CONNECTIONSabrPrewarmConnection
67SNACKBAR_MESSAGESabrSnackbarMessage

Plus des ids connus-mais-non-parsés (30 CONFIG, 32–34 hints live-metadata, 36/38 metadata ustreamer, 48–50 hints cache/bande passante, 54–56, 60–64, 66) seulement résumés, et tout id inconnu enregistré via addUnknownPartType. Voir la référence des control parts pour ce que font les riches.

La boucle de décodage

Pipeline de décodage

SabrResponseDecoder.decode lit toutes les parts d'abord, puis itère. Le seul état porté entre parts est le header onesie courant (un ONESIE_HEADER s'applique aux parts de données onesie qui le suivent). Chaque part est enregistrée dans l'ordre, puis dispatchée :

  • MEDIA_HEADER (20) → décode et enregistre le header.
  • MEDIA (21) → le 1er octet du payload est le header id ; la longueur restante est accumulée comme compte d'octets média de cet id.
  • MEDIA_END (22) → 1er octet = header id, marque l'id complet.
  • control parts riches → décode dans le champ typé de SabrDecodedResponse + un résumé humain.
  • NEXT_REQUEST_POLICY a un traitement spécial : après décodage, le backoff est relu directement du champ 4 proto (varint) comme backoffTimeMs faisant autorité.

La réponse décodée

SabrDecodedResponse tient chaque part dans l'ordre plus des accesseurs typés : headers média, comptes d'octets média par header-id (LinkedHashMap, sommés), ids media-end, metadata format-init, live metadata, données onesie, et les control parts uniques (redirect, seek, error, reload, next-request policy, status de protection + max retries, backoff ms, …).

Les helpers d'analyse sont ce sur quoi la session branche :

  • hasMedia() — des headers ou octets média.
  • isNoMediaResponse() — l'inverse.
  • isPolicyOnlyResponse() — pas de média mais une next-request policy (un round de pur rythme).
  • isProtectedNoMediaResponse() — pas de média et streamProtectionStatus >= 3 : la frontière jeton PO (status 3 + pas de média = mint un jeton).
  • getIntegrityIssues() — contrôles croisés : duplicate-media-header, missing-media, length-mismatch:expected=…:actual=…, missing-media-end, media-without-header, media-end-without-header. La session traite tout résultat non-vide comme fatal.

Suite : Média, segments et l'index.