VEX-5938: Update resource loader to handle encrypted local files (#12)

Adds offline decryption key and uses it to decrypt content during offline playback

Jira: VEX-5938
https://jira.tenkasu.net/browse/VEX-5938

- Update to accept scheme for key required to play offline playback
- Uses provided scheme to intercept call from player and return the key
- Fixes player item observer removal pattern

### Reviews
- Major reviewer (domain expert): @armadilio3
This commit is contained in:
Nick Fujita
2021-10-28 10:34:05 +09:00
committed by GitHub
parent 8b75438148
commit e27baeb065
12 changed files with 573 additions and 379 deletions

View File

@@ -1,52 +1,50 @@
import Foundation
import AVFoundation
import DVAssetLoaderDelegate
import Promises
class RCTVideoCachingHandler: NSObject, DVAssetLoaderDelegatesDelegate {
private var _videoCache:RCTVideoCache! = RCTVideoCache.sharedInstance()
private var _playerItemPrepareText: (AVAsset?, NSDictionary?, (AVPlayerItem?)->Void) -> Void
var playerItemPrepareText: ((AVAsset?, NSDictionary?) -> AVPlayerItem)?
init(_ playerItemPrepareText: @escaping (AVAsset?, NSDictionary?, (AVPlayerItem?)->Void) -> Void) {
_playerItemPrepareText = playerItemPrepareText
override init() {
super.init()
}
func playerItemForSourceUsingCache(shouldCache:Bool, textTracks:[AnyObject]?, uri:String, assetOptions:NSMutableDictionary, handler:@escaping (AVPlayerItem?)->Void) -> Bool {
if shouldCache && ((textTracks == nil) || (textTracks!.count == 0)) {
func shouldCache(source: VideoSource, textTracks:[TextTrack]?) -> Bool {
if source.isNetwork && source.shouldCache && ((textTracks == nil) || (textTracks!.count == 0)) {
/* The DVURLAsset created by cache doesn't have a tracksWithMediaType property, so trying
* to bring in the text track code will crash. I suspect this is because the asset hasn't fully loaded.
* Until this is fixed, we need to bypass caching when text tracks are specified.
*/
DebugLog("Caching is not supported for uri '\(uri)' because text tracks are not compatible with the cache. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md")
playerItemForSourceUsingCache(uri: uri, assetOptions:assetOptions, withCallback:handler)
DebugLog("Caching is not supported for uri '\(source.uri)' because text tracks are not compatible with the cache. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md")
return true
}
return false
}
func playerItemForSourceUsingCache(uri:String!, assetOptions options:NSDictionary!, withCallback handler: @escaping (AVPlayerItem?)->Void) {
func playerItemForSourceUsingCache(uri:String!, assetOptions options:NSDictionary!) -> Promise<AVPlayerItem?> {
let url = URL(string: uri)
_videoCache.getItemForUri(uri, withCallback:{ [weak self] (videoCacheStatus:RCTVideoCacheStatus,cachedAsset:AVAsset?) in
guard let self = self else { return }
return getItemForUri(uri)
.then{ [weak self] (videoCacheStatus:RCTVideoCacheStatus,cachedAsset:AVAsset?) -> AVPlayerItem in
guard let self = self, let playerItemPrepareText = self.playerItemPrepareText else {throw NSError(domain: "", code: 0, userInfo: nil)}
switch (videoCacheStatus) {
case .missingFileExtension:
DebugLog("Could not generate cache key for uri '\(uri)'. It is currently not supported to cache urls that do not include a file extension. The video file will not be cached. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md")
let asset:AVURLAsset! = AVURLAsset(url: url!, options:options as! [String : Any])
self._playerItemPrepareText(asset, options, handler)
return
return playerItemPrepareText(asset, options)
case .unsupportedFileExtension:
DebugLog("Could not generate cache key for uri '\(uri)'. The file extension of that uri is currently not supported. The video file will not be cached. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md")
let asset:AVURLAsset! = AVURLAsset(url: url!, options:options as! [String : Any])
self._playerItemPrepareText(asset, options, handler)
return
return playerItemPrepareText(asset, options)
default:
if let cachedAsset = cachedAsset {
DebugLog("Playing back uri '\(uri)' from cache")
// See note in playerItemForSource about not being able to support text tracks & caching
handler(AVPlayerItem(asset: cachedAsset))
return
return AVPlayerItem(asset: cachedAsset)
}
}
@@ -65,8 +63,16 @@ class RCTVideoCachingHandler: NSObject, DVAssetLoaderDelegatesDelegate {
asset?.resourceLoader.setDelegate(resourceLoaderDelegate, queue: DispatchQueue.main)
*/
handler(AVPlayerItem(asset: asset))
})
return AVPlayerItem(asset: asset)
}
}
func getItemForUri(_ uri:String) -> Promise<(videoCacheStatus:RCTVideoCacheStatus,cachedAsset:AVAsset?)> {
return Promise<(videoCacheStatus:RCTVideoCacheStatus,cachedAsset:AVAsset?)> { fulfill, reject in
self._videoCache.getItemForUri(uri, withCallback:{ (videoCacheStatus:RCTVideoCacheStatus,cachedAsset:AVAsset?) in
fulfill((videoCacheStatus, cachedAsset))
})
}
}
// MARK: - DVAssetLoaderDelegate