title: Why is my AVPlayer not working?
body: <p>I am having trouble with connecting to a live stream which is connected through HLS with 128-bit encryption, it is an Azure Media stream. The server also requires an authorization token, which is given through the HTTP header <code>Authorization</code> and is of the type <code>Bearer &lt;insert token here&gt;</code>.</p>
  
  <p>The code I have is this:</p>
  
  <pre><code>let url = "http://&lt;redacted&gt;"
  
  let headers = ["Authorization": "Bearer &lt;token redacted&gt;"]
  let options = ["AVURLAssetHTTPHeaderFieldsKey": headers]
  
  let avAsset = AVURLAsset(url: URL(string: url)!, options: options)
  let avItem = AVPlayerItem(asset: avAsset)
  player = AVPlayer(playerItem: avItem)
  
  player.play()
  </code></pre>
  
  <p>I have debugged the network calls and the <code>AVPlayer</code> sends out 2 calls, one to see if there is a new version (I guess from the 304 I receive on that call) and one to get the stream. These calls return with 304 Not Modified and then 206 Partial Content, respectively.</p>
  
  <p>These calls:</p>
  
  <p><a href="https://i.stack.imgur.com/omr5R.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/omr5R.png" alt="304 call"></a></p>
  
  <p><a href="https://i.stack.imgur.com/FWfi3.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/FWfi3.png" alt="206 call"></a></p>
  
  <p>I don't hear any sound, but any other sounds on the device are muted (as it should). This tells me that there is a problem with the playing of the received data. My colleague can load the stream on Android by doing nothing more than what I am doing here, and I can load it on <a href="http://ampdemo.azureedge.net/azuremediaplayer.html" rel="nofollow noreferrer">http://ampdemo.azureedge.net/azuremediaplayer.html</a>.</p>
  
  <p>I check the status of the player by fetching the <code>player.currentItem?.status</code>, this is <code>failed</code>. The <code>player.currentItem?.error</code> is </p>
  
  <blockquote>
    <p>Error Domain=AVFoundationErrorDomain Code=-11828 "Cannot Open" UserInfo={NSUnderlyingError=0x60800005a610 {Error Domain=NSOSStatusErrorDomain Code=-12847 "(null)"}, NSLocalizedFailureReason=This media format is not supported., NSLocalizedDescription=Cannot Open}</p>
  </blockquote>
  
  <p><em>Edit:</em>  I tried debugging this further. I implemented <code>AVAssetResourceLoaderDelegate</code> and in <code>resourceLoader:shouldWaitForLoadingOfRequestedResource:</code> I added the following code:</p>
  
  <pre><code>func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -&gt; Bool {
      DispatchQueue.main.async {
          var request = loadingRequest.request
  
          var headers = request.allHTTPHeaderFields!
          if headers["Authorization"] == nil {
              headers["Authorization"] = "Bearer &lt;token&gt;"
          } 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
  }
  </code></pre>
  
  <p>I only add the <code>Authorization</code> header when it doesn't exist yet. I thought that overwriting the <code>Authorization</code> header on all calls might break the encryption.</p>
  
  <p>Because <code>resourceLoader:shouldWaitForLoadingOfRequestedResource:</code> only gets called when the request is using a scheme other than <code>http</code> and <code>https</code>, so I replaced <code>http://</code> with <code>mycustomscheme://</code>. That is also why I set the url here, because the original url in the request does not have a valid url scheme.</p>
  
  <p>This way is better for me, as I can view the request and response as they are happening. No more Burp Suite to debug the calls.</p>
  
  <p>The issue I am having now is that I receive the data I expect (the player does perform the same request twice), but when I check the <code>player.currentItem?.status</code>, this is <code>failed</code>. The <code>player.currentItem?.error</code> is</p>
  
  <blockquote>
    <p>Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo={NSLocalizedDescription=The request timed out., NSUnderlyingError=0x60000005f320 {Error Domain=NSOSStatusErrorDomain Code=-1001 "(null)"}}</p>
  </blockquote>
  
  <p>It seems weird to me that I get a timeout, as the Android and web versions work without problems.</p>
  
  <p>I have looked into the Android code and he uses <code>&lt;url&gt;.ism/manifest(format=m3u8-aapl-v3)</code> instead of just <code>&lt;url&gt;.ism/manifest</code>. If I use that url, I get the same <code>failed</code> status, but with an error of</p>
  
  <blockquote>
    <p>Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo={NSUnderlyingError=0x608000059e60 {Error Domain=NSOSStatusErrorDomain Code=-12936 "(null)"}, NSLocalizedFailureReason=An unknown error occurred (-12936), NSLocalizedDescription=The operation could not be completed}</p>
  </blockquote>
  
  <p>Okay I got it to work, but I have no idea why it works and why it didn't work. I entered the URL that Android performs as a second call to get the stream, this one has the quality info embedded. It is of the form <code>&lt;url&gt;.ism/QualityLevels(3782)/Manifest(video,format=m3u8-aapl-v3,audiotrack=aac_UND_2_192)</code>.</p>
  
  <p>Can anyone explain what is going on here?</p>