HLS Stream Using AVAssetResourceLoaderDelegate TS Fragment Requests Missing Cookie Header

2.3k views Asked by At

I am using the AVAssetResourceLoadingDelegate to intercept all manifest requests for an HLS Manifest

let str = "examplehttp://example.com/path/to/master.m3u8?token=SOMETOKEN"
guard let url = URL(string: str) else { return }

let asset = AVURLAsset(url: url)
let loaderQueue = DispatchQueue(label: "com.example.LoaderQueue")
asset.resourceLoader.setDelegate(delegate, queue: loaderQueue)

let item = AVPlayerItem(asset: asset)

player = AVPlayer(playerItem: item)
player?.playImmediately(atRate: 1.0)

In the delegate I perform all the manifest requests myself using URLSession and return the responses back to the AVAssetResourceLoadingRequest

// NOTE: dataRequest: AVAssetResourceLoadingDataRequest
dataRequest.respond(with: data)
loadingRequest.response = response
loadingRequest.finishLoading()

This stream is protected using a fairly standard auth process:

Request to master manifest is made with an appended token query param. The response to the master manifest includes a set-cookie header. Each subsequent request that has a domain specified on the set-cookie response header includes the cookie on its request headers.

What I am finding is that all requests made through the delegate do have the cookie added to the header but since the delegate cannot be used for TS segments the cookie is not being added.

Does anyone know of a way to force the AVURLAsset to always use the cookie header provided by the response to the master manifest for requests that are made outside of the AVAssetResourceLoaderDelegate?

Since I do provide the URLResponse back to AVAssetResourceLoadingRequest and I know you can add cookies to a URLSession using the URLSessionConfiguration's httpShouldAccpetCookies, httpCookieAcceptPolicy, and httpCookieStorage properties. I don't think this is outside the realm of possibilities.

I am also aware of the AVURLAssetHTTPCookiesKey that can be added to the instantiation of the AVURLAasset but I do not have the cookie until a master manifest request is made.

1

There are 1 answers

0
John Gainfort On

Response From Apple:

It is not possible to use the AVAssetResourceLoaderDelegate to capture and modify the master/child manifests for an HLS playlist to set a cookie header for the domain of the manifest URL. This is not currently supported in AVFoundation.

[The] only current support for cookies is the ability to use the AVURLAsset AVURLAssetHTTPCookiesKey initialization option to allow the AVURLAsset to use additional HTTP cookies for HTTP(S) requests. See https://developer.apple.com/reference/avfoundation/avurlassethttpcookieskey for more information.

Simply get your cookies and create a dictionary with the key value pairs as shown below ( @{AVURLAssetHTTPCookiesKey : cookies} ), then specify this dictionary in the AVURLAsset URLAssetWithURL:linkUrl options:

NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];

AVURLAsset * asset = [AVURLAsset URLAssetWithURL:yourURL options:@{AVURLAssetHTTPCookiesKey : cookies}];

AVPlayerItem * item = [AVPlayerItem playerItemWithAsset:asset];

AVPlayer *player = [[AVPlayer alloc] initWithPlayerItem:item];

Please note this only allows you to set cookies when the AVAsset is created, and you can't then subsequently mutate these at a later time.

Also, as discussed in the documentation, in HLS, many HTTP requests (e.g., media, crypt key, variant index) might be issued to different paths or hosts. In both of these cases, HTTP requests will be missing any cookies that do not apply to the AVURLAsset URL.

One "unofficial" solution that might work is to pass the playback request through a reverse proxy which will allow you to intercept the request, add headers, send it to the real server, and then extract the headers from the response before returning it to the AVPlayer.