SReverseby Simpa Labs

Player network flows

Reverse engineering an ExoPlayer network flow

A playback session produces several kinds of network traffic, and they do not share one code path. Reconstructing the flow means separating them before deciding which parts a client has to reproduce.

The four request families

A capture from a protected playback session contains requests that look unrelated until you group them.

  • Manifest and metadata requests, which tell the player what to fetch. For live content these repeat as the manifest is refreshed.
  • The licence request, which stands out as a small binary POST to a different host, usually right before playback begins.
  • Media segment requests, which are the bulk of the traffic and often carry byte ranges and CDN query strings.
  • Application control calls for playback start, progress, heartbeat and entitlement, which belong to the app rather than to the player.

Each family has its own authorization and its own lifetime. Treating them as one flow produces a client that authenticates the segments with the licence token or refreshes a manifest URL that never expires.

Which HTTP client actually sends the bytes

Media3 loads media through a data source, and the choice of data source decides where you can observe the request. The default data source uses the platform HTTP stack. Apps that want their existing networking behaviour add the OkHttp data source, which runs media traffic through the same client and interceptor chain as the rest of the app. Apps that already use Cronet add the Cronet data source for the same reason.

That single decision changes the trace. With the OkHttp data source, one interceptor hook captures control calls, manifests, licence requests and segments together, and the app's signing code runs on all of them. With the default data source, the licence request still goes through the DRM callback while segments bypass the app's client, so a hook in the interceptor chain sees half the traffic.

Headers arrive from three places. The data source factory can set default request properties that apply to every request. Individual requests can add their own. A resolving data source can rewrite a URI at request time, which is a documented Media3 component and the usual home for short-lived CDN tokens and host rewriting. When segment URLs change shape between captures, look there before assuming the manifest changed.

Reading a trace in order

The useful habit is to number the requests and note what each one depends on. The playback start call returns values the later requests use. The manifest returns the protection metadata that determines which key requests appear. The first segment request follows the first successful licence, because the decoder cannot be configured until a session holds keys. Live playback adds a repeating manifest fetch that carries no new credentials.

Two patterns are easy to misread. Byte range requests mean one logical segment is being fetched in parts, so several requests in the trace may belong to one unit of media. A repeated request with the same URL and a different token usually indicates a retry after an expired credential rather than a new resource.

Where to observe the flow

  • The app's HTTP interceptor or data source, which shows full URLs, headers and bodies for anything the app sends through that client.
  • The DRM callback, which shows the licence request and response at the boundary where the app hands bytes to the platform.
  • The platform DRM methods that produce and install keys, which show when a licence was accepted, renewed or refused.
  • Session callbacks, which show expirations and key status changes that no HTTP trace records.

Running two of these at once is useful. A licence POST in the HTTP log tells you what was sent; the DRM boundary tells you whether the response installed keys. Without both, a 200 response with a policy refusal looks like success.

Why the flow is stateful

A rebuilt client cannot treat any of these calls as independent. The licence request depends on protection metadata from a manifest that the app fetched with credentials from an earlier call. Segment URLs often expire within minutes. The licence itself is bound to a device and a session, and a renewal may be required before the content finishes. The app's control calls keep a playback session alive on the server, and skipping them can end the session even while the media requests keep working.

Model the flow as a small state machine with named steps and explicit inputs. Each step records the value it produces and the value it consumes, which makes a failure point at the step that returned something unexpected. That structure is also what makes the result maintainable, since the app's own flow changes when the backend does.

Building the deliverable

The useful output is a client that performs the app's side of the flow on demand: it requests authorization, obtains a manifest, presents the right credentials to the licence endpoint, and fetches media with fresh URLs. Key handling stays with the platform DRM component, so the client works for a legitimate account on an ordinary device rather than extracting anything from the CDM.

Verify it the way the app behaves rather than with one successful run. Replay the flow after the first token expires, pause long enough for a licence to come up for renewal, and confirm that a manifest refresh during live playback still works. Those cases are where a hand built client fails first, and they are also the cases that prove the reconstruction matches the app.

Reviewed 28 September 2026 · SReverse research desk

Start your full APK reconstruction

Send the full APK

Projects start at $120. Choose WhatsApp or email, then attach the APK in the app that opens. We reply within one hour with the next step and send the fixed quote after review.

Want us to contact you?