I'm having trouble connecting to a real-time stream that is connected via HLS with 128-bit encryption, this is an Azure Media stream. The server also requires an authorization token, which is passed through the Authorization HTTP header and is of type Bearer <insert token here> .
The code I have is the following:
let url = "http://<redacted>" let headers = ["Authorization": "Bearer <token redacted>"] let options = ["AVURLAssetHTTPHeaderFieldsKey": headers] let avAsset = AVURLAsset(url: URL(string: url)!, options: options) let avItem = AVPlayerItem(asset: avAsset) player = AVPlayer(playerItem: avItem) player.play()
I debugged network calls, and AVPlayer sends 2 calls, one to see if there is a new version (I think of 304 that I get on this call), and one to receive the stream. These calls are returned with 304 Not Modified, and then 206 Partial Content, respectively.
These challenges are:


No sound is heard, but any other sounds on the device are muted (as it should be). This suggests that there is a problem with the reproduction of the received data. My colleague can download the stream on Android without doing anything other than what I'm doing here, and can download it at http://ampdemo.azureedge.net/azuremediaplayer.html .
I check the status of the player by retrieving player.currentItem?.status , this is failed . player.currentItem?.error -
Domain error = AVFoundationErrorDomain Code = -11828 "Unable to open" UserInfo = {NSUnderlyingError = 0x60800005a610 {Domain error = NSOSStatusErrorDomain Code = -12847 "(null)"}, NSLocalizedFailureReason = This media format is not supported. NSLocal not
Edit: I tried to debug this further. I implemented AVAssetResourceLoaderDelegate and in resourceLoader:shouldWaitForLoadingOfRequestedResource: added the following code:
func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool { DispatchQueue.main.async { var request = loadingRequest.request var headers = request.allHTTPHeaderFields! if headers["Authorization"] == nil { headers["Authorization"] = "Bearer <token>" } else { print("Auth header exists: \(headers)") } request.url = URL(string: self.url)! request.allHTTPHeaderFields = headers URLSession(configuration: URLSessionConfiguration.default).dataTask(with: request) { data, response, error in if let error = error { print(error) loadingRequest.finishLoading(with: error) } else { loadingRequest.dataRequest?.respond(with: data!) loadingRequest.finishLoading() } }.resume() } return true }
I add the Authorization header when it does not exist yet. I thought that rewriting the Authorization header for all calls could break the encryption.
Because resourceLoader:shouldWaitForLoadingOfRequestedResource: is only called when the request uses a scheme other than http and https , so I replaced http:// with mycustomscheme:// . This is why I am setting the URL here because the source url in the request does not have a valid URL scheme.
This method is best for me, as I can view the request and response when they occur. No more Burp Suite for debugging calls.
The problem that I am currently facing is that I get the data that I expect (the player executes the same request twice), but when I check player.currentItem?.status , it failed . player.currentItem?.error -
Domain Error = NSURLErrorDomain Code = -1001 "Request timed out." UserInfo = {NSLocalizedDescription = Request timed out. NSUnderlyingError = 0x60000005f320 {Domain Error = NSOSStatusErrorDomain Code = -1001 "(null)"}}
It seems strange to me that I get a timeout, since Android and web versions work without problems.
I looked at the Android code, and instead of <url>.ism/manifest it uses <url>.ism/manifest(format=m3u8-aapl-v3) . If I use this url, I get the same failed status, but with an error
Domain Error = AVFoundationErrorDomain Code = -11800 "Operation cannot be completed" UserInfo = {NSUnderlyingError = 0x608000059e60 {Error Domain = NSOSStatusErrorDomain Code = -12936 "(null)"}, NSLocalizedFailureReason = An unknown error occurred (NS12936) Operation -129 cannot be completed}
Well, I got this to work, but I have no idea why this works and why it didn't work. I entered the URL that Android executes as the second call to get the stream, it has built-in quality information. It has the form <url>.ism/QualityLevels(3782)/Manifest(video,format=m3u8-aapl-v3,audiotrack=aac_UND_2_192) .
Can anyone explain what is going on here?