2022-05-19 07:29:25 -06:00
|
|
|
import AVFoundation
|
|
|
|
import DVAssetLoaderDelegate
|
2023-12-07 00:47:40 -07:00
|
|
|
import Foundation
|
2022-05-19 07:29:25 -06:00
|
|
|
|
|
|
|
class RCTVideoCachingHandler: NSObject, DVAssetLoaderDelegatesDelegate {
|
2023-12-07 00:47:40 -07:00
|
|
|
private var _videoCache: RCTVideoCache! = RCTVideoCache.sharedInstance()
|
2024-04-04 05:23:44 -06:00
|
|
|
var playerItemPrepareText: ((AVAsset?, NSDictionary?, String) async -> AVPlayerItem)?
|
2023-12-07 00:47:40 -07:00
|
|
|
|
2022-05-19 07:29:25 -06:00
|
|
|
override init() {
|
|
|
|
super.init()
|
|
|
|
}
|
2023-12-07 00:47:40 -07:00
|
|
|
|
|
|
|
func shouldCache(source: VideoSource, textTracks: [TextTrack]?) -> Bool {
|
|
|
|
if source.isNetwork && source.shouldCache && ((textTracks == nil) || (textTracks!.isEmpty)) {
|
2022-05-19 07:29:25 -06:00
|
|
|
/* 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.
|
|
|
|
*/
|
2023-12-07 00:47:40 -07:00
|
|
|
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
|
|
|
|
""")
|
2022-05-19 07:29:25 -06:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2023-12-07 00:47:40 -07:00
|
|
|
|
2024-04-04 05:23:44 -06:00
|
|
|
func playerItemForSourceUsingCache(uri: String!, assetOptions options: NSDictionary!) async throws -> AVPlayerItem {
|
2022-05-19 07:29:25 -06:00
|
|
|
let url = URL(string: uri)
|
2024-04-04 05:23:44 -06:00
|
|
|
let (videoCacheStatus, cachedAsset) = await getItemForUri(uri)
|
2023-12-07 00:47:40 -07:00
|
|
|
|
2024-04-04 05:23:44 -06:00
|
|
|
guard let playerItemPrepareText else {
|
|
|
|
throw NSError(domain: "", code: 0, userInfo: nil)
|
|
|
|
}
|
2023-12-07 00:47:40 -07:00
|
|
|
|
2024-04-04 05:23:44 -06:00
|
|
|
switch videoCacheStatus {
|
|
|
|
case .missingFileExtension:
|
|
|
|
DebugLog("""
|
|
|
|
Could not generate cache key for uri '\(uri ?? "NO_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])
|
|
|
|
return await playerItemPrepareText(asset, options, "")
|
2023-12-07 00:47:40 -07:00
|
|
|
|
2024-04-04 05:23:44 -06:00
|
|
|
case .unsupportedFileExtension:
|
|
|
|
DebugLog("""
|
|
|
|
Could not generate cache key for uri '\(uri ?? "NO_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])
|
|
|
|
return await playerItemPrepareText(asset, options, "")
|
2023-12-07 00:47:40 -07:00
|
|
|
|
2024-04-04 05:23:44 -06:00
|
|
|
default:
|
|
|
|
if let cachedAsset {
|
|
|
|
DebugLog("Playing back uri '\(uri ?? "NO_URI")' from cache")
|
|
|
|
// See note in playerItemForSource about not being able to support text tracks & caching
|
|
|
|
return AVPlayerItem(asset: cachedAsset)
|
2022-05-19 07:29:25 -06:00
|
|
|
}
|
2024-04-04 05:23:44 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
let asset: DVURLAsset! = DVURLAsset(url: url, options: options as! [String: Any], networkTimeout: 10000)
|
|
|
|
asset.loaderDelegate = self
|
|
|
|
|
|
|
|
/* More granular code to have control over the DVURLAsset
|
|
|
|
let resourceLoaderDelegate = DVAssetLoaderDelegate(url: url)
|
|
|
|
resourceLoaderDelegate.delegate = self
|
|
|
|
let components = NSURLComponents(url: url, resolvingAgainstBaseURL: false)
|
|
|
|
components?.scheme = DVAssetLoaderDelegate.scheme()
|
|
|
|
var asset: AVURLAsset? = nil
|
|
|
|
if let url = components?.url {
|
|
|
|
asset = AVURLAsset(url: url, options: options)
|
|
|
|
}
|
|
|
|
asset?.resourceLoader.setDelegate(resourceLoaderDelegate, queue: DispatchQueue.main)
|
|
|
|
*/
|
|
|
|
|
|
|
|
return AVPlayerItem(asset: asset)
|
2022-05-19 07:29:25 -06:00
|
|
|
}
|
|
|
|
|
2024-04-04 05:23:44 -06:00
|
|
|
func getItemForUri(_ uri: String) async -> (videoCacheStatus: RCTVideoCacheStatus, cachedAsset: AVAsset?) {
|
|
|
|
await withCheckedContinuation { continuation in
|
2023-12-07 00:47:40 -07:00
|
|
|
self._videoCache.getItemForUri(uri, withCallback: { (videoCacheStatus: RCTVideoCacheStatus, cachedAsset: AVAsset?) in
|
2024-04-04 05:23:44 -06:00
|
|
|
continuation.resume(returning: (videoCacheStatus, cachedAsset))
|
2022-05-19 07:29:25 -06:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2023-12-07 00:47:40 -07:00
|
|
|
|
2022-05-19 07:29:25 -06:00
|
|
|
// MARK: - DVAssetLoaderDelegate
|
2023-12-07 00:47:40 -07:00
|
|
|
|
|
|
|
func dvAssetLoaderDelegate(_: DVAssetLoaderDelegate!, didLoad data: Data!, for url: URL!) {
|
|
|
|
_videoCache.storeItem(data as Data?, forUri: url.absoluteString, withCallback: { (_: Bool) in
|
2022-05-19 07:29:25 -06:00
|
|
|
DebugLog("Cache data stored successfully 🎉")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|