How to let AVPlayer retrieve playlist secured by SSL?

We´re developing a HTTP-streaming iOS app that requires us to receive playlists from a secured site. This site requires us to authenticate using a self signed SSL certificate.

We read the credentials from a .p12 file before we use NSURLConnection with a delegate to react to the authorization challenge.

  • AVPlayerItem replaceCurrentItemWithPlayerItem Blocking
  • Playing video from within app?
  • AVPlayer Dynamic Volume control
  • Swift Radio Streaming AVPlayer
  • AVAudioPlayer rate
  • How can I capture an image when AVPlayer playing m3u8 stream?
  • - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
    {
        [[challenge sender] useCredential:  self.credentials forAuthenticationChallenge:challenge];
    }
    
    - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
    {
        return YES;
    }
    

    By doing this initial connection to the URL where we´re getting the .m3u8 playlist we´re able to play back the playlist using AVPlayer. The problem is that this method only works in the simulator.

    NOTE: We´re able to download the playlist using the NSURLConnection on device. This must mean that the AVPlayer somehow can´t continue using the trust established during this initial connection.

    We have also tried adding the credentials to the [NSURLCredentialStorage sharedCredentialStorage] without any luck.

    Below follows our shotgun approach for that:

    NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc]
                                             initWithHost:host
                                             port:443
                                             protocol:@"https"
                                             realm:nil
                                             authenticationMethod:NSURLAuthenticationMethodClientCertificate];
    
    
    
    [[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:creds
                                                        forProtectionSpace:protectionSpace];
    
        NSURLProtectionSpace *protectionSpace2 = [[NSURLProtectionSpace alloc]
                                             initWithHost:host
                                             port:443
                                             protocol:@"https"
                                             realm:nil
                                             authenticationMethod:NSURLAuthenticationMethodServerTrust];
    
    
    
    [[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:creds
                                                        forProtectionSpace:protectionSpace2];
    

    EDIT: According to this question: the above method doesn´t work with certificates.

    Any hint to why it doesn´t work on device, or an alternate solution is welcome!

    2 Solutions Collect From Internet About “How to let AVPlayer retrieve playlist secured by SSL?”

    From iOS 6 onwards AVAssetResourceLoader can be used for retrieving an HTTPS secured playlist or key file.

    An AVAssetResourceLoader object mediates resource requests from an AVURLAsset object with a delegate object that you provide. When a request arrives, the resource loader asks your delegate if it is able to handle the request and reports the results back to the asset.

    Please find the sample code below.

    // AVURLAsset + Loader
    AVURLAsset      *asset          = [[AVURLAsset alloc] initWithURL:url options:nil];
    AVPlayerItem    *playerItem     = [AVPlayerItem playerItemWithAsset:asset];
    AVAssetResourceLoader *loader   = asset.resourceLoader;
    [loader setDelegate:self queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
    
    // AVPlayer
    AVPlayer        *avPlayer       = [AVPlayer playerWithPlayerItem:playerItem];
    

    You will need to handle the resourceLoader:shouldWaitForLoadingOfRequestedResource:delegate method which will be called when there is an authentication need and you can use NSURLConnection to request for the secured resource.

    (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader    shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
    {
    
     //Handle NSURLConnection to the SSL secured resource here
      return YES;
    }
    

    Hope this helps!

    P.S : The proxy approach using CocoaHTTPServer works well but using an AVAssetResourceLoader is a more elegant solution.

    It seems that until Apple lets us control what NSURLConnections the AVPlayer uses the only answer seems to be to implement a HTTP loopback server.

    To quote the apple representative that answered our support question:

    Another option is to implement a loopback
    HTTP server and point client objects at that. The clients can use
    HTTP (because their requests never make it off the device), while the
    loopback HTTP server itself can use HTTPS to connect to the origin
    server. Again, this gives you access to the underlying
    NSURLConnections, allowing you to do custom server trust evaluation.

    Using this technique with UIWebView is going to be tricky unless you
    completely control the content at the origin server. If the origin
    server can return arbitrary content, you have to grovel through the
    returned HTTP and rewrite all the links, which is not much fun. A
    similar restriction applies to MPMoviePlayerController/AVPlayer, but
    in this case it’s much more common to control all of the content and
    thus be able to avoid non-relative links.

    EDIT:
    I managed to implement a loopback server using custom implemenations of the
    HTTPResponse and HTTPConnection classes found in CocoaHTTPServer

    I can´t disclose the source, but I used NSURLConnection together with a mix of the AsyncHTTPResponse and DataHTTPResponse demonstration responses.

    EDIT:
    Remember to set myHttpServerObject.interface = @"loopback";

    EDIT: WARNING!!! This approach does not seem to work with airplay since the airplay device will ask 127.1.1.1 for encryption keys. The correct approach seems to be defined here:
    https://developer.apple.com/library/content/documentation/AudioVideo/Conceptual/AirPlayGuide/EncryptionandAuthentication/EncryptionandAuthentication.html#//apple_ref/doc/uid/TP40011045-CH5-SW1

    “Specify the keys in the .m3u8 files using an application-defined URL scheme.”

    EDIT:
    An apple TV and iOS update has resolved the issue mentioned in the edit above!