5
votes

i'm trying to play an AES encrypted stream in AVPlayer.. typically a link of the key is delivered to the player inside the M3U8 playlist.. in my scenario the key is divided in half.. the first half is delivered by the server and i should append the other half inside the app to decrypt when playing

i've already done this on Android, is there a way to do it also on iOS?

This is the playlist:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:BANDWIDTH=200000,RESOLUTION=284x160
chunklist_w670540365_b200000.m3u8?t=57b5b16d3824d
#EXT-X-STREAM-INF:BANDWIDTH=850000,RESOLUTION=640x360
chunklist_w670540365_b850000.m3u8?t=57b5b16d3824d

And this is the chunk list:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-ALLOW-CACHE:NO
#EXT-X-TARGETDURATION:11
#EXT-X-MEDIA-SEQUENCE:13544
#EXT-X-KEY:METHOD=AES-128,URI="http://example.com/api/getEncryptionKey?t=57b5b16d3824d"
#EXTINF:9.6,
media_w670540365_b200000_13544.ts?t=57b5b16d3824d
#EXTINF:9.6,
media_w670540365_b200000_13545.ts?t=57b5b16d3824d
#EXTINF:10.56,
media_w670540365_b200000_13546.ts?t=57b5b16d3824d

This is what AVPlayer does:

1- the playlist gets downloaded and a chunk list is selected 2- the player downloads the chunk list 3- the decryption key to decrypt the chunks is downloaded 4- the player begins downloading the chunks sequentially to play them 5- every chunk is decrypted and played

What i need to do is: after the 3rd step when the player calls the api to get the encryption key using this link: 'http://example.com/api/getEncryptionKey?t=57b5b16d3824d', i want to intercept the response and append the other half of the key

Is it possible?

1
How did you do it in Android? Please share some sample code of Android. I have done in iOS but stuck in AndroidVashum
@Vashum yes i've done it on android, what video player are you using on android?Hadi Najem
EXO player, can you help? Any sample code will be great 😀Vashum
can you please help?Vashum
Hi @HadiNajem, could you share your Android custom Data Source code as I am stuck to implement this on Android.saDashiv sinha

1 Answers

4
votes

yes, it is very much possible! I recently did it in one of my projects.

Whenever AVPlayer loads the encrypted video, it tries to load decryption key from the URL mentioned in prog_index.m3u8. If AVPlayer is not able to play video with the fetched key or if it didn't get the key at all on the specified url, it calls a delegate method from AVAssetResourceLoaderDelegate that is

 public func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForRenewalOfRequestedResource renewalRequest: AVAssetResourceRenewalRequest) -> Bool {
    return shouldLoadOrRenewRequestedResource(resourceLoadingRequest: renewalRequest)
}

and,

public func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
    return shouldLoadOrRenewRequestedResource(resourceLoadingRequest: loadingRequest)
}

which of course differ in the cases they are being called. Prior one is called when the player should wait for loading resource and later one is called when the player needs to renew the resource.

func shouldLoadOrRenewRequestedResource(resourceLoadingRequest: AVAssetResourceLoadingRequest) -> Bool {

    guard var url = resourceLoadingRequest.request.url else {
        return false
    }

   //FETCH THE KEY FROM NETWORK CALL/KEYSTORE, CONVERT IT TO DATA AND FINISH LOADING OF RESOURCE WITH THAT DATA, IN YOUR CASE JOIN THE OTHER HALF OF THE KEY TO ACTUAL KEY (you can get the first half from the url above)
   resourceLoadingRequest.dataRequest?.respond(with: keyData)
   resourceLoadingRequest.finishLoading()

    return true;
}}

Once you'll return true with the actual key, the video will be played instantly.