Based on the answer of Marc Palmer I came up with the following solution, which is a bit „swiftier“.
I extended NSDataAsset
to conform to AVAssetResourceLoaderDelegate
protocol and I extended AVURLAsset
to init using a NSDataAsset
.
The last line in the code uses I little bit of Objective-C magic to retain the delegate, because AVURLAsset's ResourceLoader does not do this and Swift does not let you define vars
in extensions. Because AVURLAsset
is a Objective-C class this should not be a problem.
You can read more about objc_setAssociatedObject
in this post.
import Foundation
import AVFoundation
#if os(macOS)
import AppKit
#else
import UIKit
#endif
extension NSDataAsset:AVAssetResourceLoaderDelegate{
@objc public func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
if let infoRequest = loadingRequest.contentInformationRequest{
infoRequest.contentType = typeIdentifier
infoRequest.contentLength = Int64(data.count)
infoRequest.isByteRangeAccessSupported = true
}
if let dataRequest = loadingRequest.dataRequest{
dataRequest.respond(with: data.subdata(in:Int(dataRequest.requestedOffset) ..< Int(dataRequest.requestedOffset) + dataRequest.requestedLength))
loadingRequest.finishLoading()
return true
}
return false
}
}
extension AVURLAsset{
public convenience init?(_ dataAsset:NSDataAsset){
guard let name = dataAsset.name.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed),
let url = URL(string:"NSDataAsset://\(name))")
else {return nil}
self.init(url:url) // not really used!
self.resourceLoader.setDelegate(dataAsset, queue: .main)
// Retain the weak delegate for the lifetime of AVURLAsset
objc_setAssociatedObject(self, "AVURLAsset+NSDataAsset", dataAsset, .OBJC_ASSOCIATION_RETAIN)
}