5

I have been working on a simple AVPlayer to play encrypted HLS media.

I am using the AVAssetResourceLoaderDelegate to handle the key retrieving process so the encrypted media can be played with a valid key.

The program works perfectly on simulator, but it doesn't work at all on device.

Here are the codes:

- (void) playUrlByAVPlayer:(NSString *) videoUrl
{
    NSURL *streamURL = [NSURL URLWithString:videoUrl];

    AVURLAsset *asset = [AVURLAsset URLAssetWithURL:streamURL options:nil];

    [asset.resourceLoader setDelegate:self queue:dispatch_get_main_queue()];

    self.playerItem = [AVPlayerItem playerItemWithAsset:asset];                           
    self.player = [AVPlayer playerWithPlayerItem:self.playerItem];

    self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
    [self.playerLayer setFrame:self.view.frame];
    [self.view.layer addSublayer:self.playerLayer];

    [self.player play];
}

After some debugging, I realized that the delegate method shouldWaitForLoadingOfRequestedResource was never called on device.

I have read other relevant questions:

AVAssetResourceLoaderDelegate not being called

AVAssetResourceLoaderDelegate - Only requests first two bytes?

and I tried enclosing all codes within a dispatch_async, dispatch_get_main_queue block but there's no luck on solving mine.

Currently my codes above are not enclosed by any dispatch queue blocks.

Any thoughts on the problem?

Community
  • 1
  • 1
onsankawai
  • 91
  • 1
  • 7
  • did you get to solve this problem? The answer below is true but it only explains how to trigger the delegate. How do you continue with the request? – nhenrique Nov 05 '15 at 10:53

4 Answers4

8

If you take a look on Apple example code where they show bipbop.m3u8 HLS playback you will see that they are using masks for real http requests: "http:/host/bipbop.m3u8" => "custom_scheme:/host/bipbop.m3u8" Same trick should be made with playlist subresources.

Otherwise avplayer ignores AVAssetResourceLoaderDelegate and load data directly.

You need to implement some kind of mapping:

NSString* videoUrl = @"fake_scheme://host/video.m3u8";
NSURL *streamURL = [NSURL URLWithString:videoUrl];
Chugaister
  • 364
  • 4
  • 12
  • 2
    indeed AVAssetResourceLoaderDelegate gets called but the url is pretty much useless. Is there any way to change this url again, during the delegate methods, so it actually works? – nhenrique Oct 26 '15 at 15:49
  • 1
    @nhenrique have you got any success in order to change the custom scheme with http? As the loadingRequest is a let constant in delegate and its request property is a get only property. – Rahul Verma Aug 03 '17 at 09:26
  • @nhenrique Did you find how to do this in swift? – Ankit Kumar Gupta Nov 23 '21 at 05:47
3

As I mentioned in the other thread as well, AVAssetResourceLoaderDelegate works only when we use a "Non Standard/Non Reserved" url scheme. HTTP, HTTPS etc are considered reserved URL schemes and iOS will not make a delegate call if the URL has one of those schemes. What I ended up doing was using my_own_http for the http urls and my_own_https for the https urls. It works well after I made that change. As you know this makes your playlist unusable on other deices.

Dvyz
  • 479
  • 4
  • 9
  • how can this work if such a scheme like my_own_http is not valid? Do you remove this scheme when AVAssetResourceLoaderDelegate is called? If so, what do you do to continue with the request? – nhenrique Oct 27 '15 at 10:59
  • @nhenrique your code should remove the custom scheme and make the request to the server. Your delegate code acts as a proxy. – Dvyz Jun 26 '17 at 18:09
2

In your delegate shouldWaitForLoadingOfRequestedResource change the URL scheme back to http:

NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:loadingRequest.request.URL resolvingAgainstBaseURL:NO];
    urlComponents.scheme = @"http";
NSMutableURLRequest *mutableLoadingRequest = [loadingRequest.request mutableCopy];
[mutableLoadingRequest setURL:urlComponents.URL];
Alex
  • 995
  • 12
  • 25
  • 2
    Am not sure in objective C but in Swift 3, loadingRequest is a let constant and loadingRequest.request is a get only property. So the change will be done on copy version not on main request. leads to crap. – Rahul Verma Aug 03 '17 at 09:25
  • 1
    @Rahul Did you find the solution to change URL scheme back to http in swift ? – Raxit Pandya Nov 14 '19 at 07:29
  • @RaxitPandya Did you find how to do this in swift? – Ankit Kumar Gupta Nov 22 '21 at 13:21
  • @RahulVerma Did you find how to do this in swift? – – Ankit Kumar Gupta Nov 22 '21 at 13:21
  • @AnkitKumarGupta In shouldWait var url = loadingRequest.request.url?.absoluteString var comps = URLComponents(url: loadingRequest.request.url!, resolvingAgainstBaseURL: false)! let originalScheme = comps.scheme if originalScheme != nil {url = url?.replacingOccurrences(of: "\(originalScheme!)", with: "https")} let redirect = URLRequest(url: URL(string: sourceURL)!) loadingRequest.redirect = redirect let res = HTTPURLResponse(url: URL(string: url!)!, statusCode: 302, httpVersion: nil, headerFields: nil) loadingRequest.response = res return loadingRequest – Rahul Verma Nov 25 '21 at 06:56
  • @RaxitPandya You can modify the m3u8 and key or even ts here if needed. using if (url?.contains(".m3u8"))! || (url?.contains(".key"))! { In our case we are using an extra layer of encryption on m3u8 and key. So we were validating it here and for ts we are just redirecting it to http one. – Rahul Verma Nov 25 '21 at 07:00
  • Hi @RahulVerma. Things worked for me without replacing the scheme. I just had to run my code on a physical device. Please find my repo here. You just need to update your credentials and URL and test this on any physical device. https://github.com/ankit0812/FairplayTestProj – Ankit Kumar Gupta Nov 25 '21 at 09:25
  • Can we achieve NTLM Authentication? – iOS Lifee Apr 20 '22 at 10:45
0

Please find a demo for this working along with a ViewController Implementation

Run this on a Real device only as Simulator doesn't allows https/http on it.

https://github.com/ankit0812/FairplayTestProj

Ankit Kumar Gupta
  • 3,994
  • 4
  • 31
  • 54