我试图哄骗AVFoundation从自定义URL读取。自定义网址的工作原理。下面的代码创建一个带有电影文件的NSData:
NSData* movieData = [NSData dataWithContentsOfURL:@"memory://video"];
我使用以下代码设置了AVAssetResourceLoader对象:
NSURL* url = [NSURL URLWithString:@"memory://video"];
AVURLAsset* asset = [[AVURLAsset alloc] initWithURL:url options:nil];
AVAssetResourceLoader* loader = [asset resourceLoader];
[loader setDelegate:self queue:mDispatchQueue];
调度队列是并发的。
然后我尝试从电影中提取第一帧:
AVAssetImageGenerator* imageGen = [AVAssetImageGenerator assetImageGeneratorWithAsset:asset];
CMTime time = CMTimeMakeWithSeconds(0, 600);
NSError* error = nil;
CMTime actualTime;
CGImageRef image = [imageGen copyCGImageAtTime:time
actualTime:&actualTime
error:&error];
if (error) NSLog(@"%@", error);
但是当我运行此代码时,我得到了:
2013-02-21 10:02:22.197 VideoPlayer[501:907] Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo=0x1f863090 {NSLocalizedDescription=The operation could not be completed, NSUnderlyingError=0x1e575a90 "The operation couldn’t be completed. (OSStatus error 268451843.)", NSLocalizedFailureReason=An unknown error occurred (268451843)}
委托方法的实现是:
- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
{
NSData* data = [NSData dataWithContentsOfURL:loadingRequest.request.URL];
[loadingRequest finishLoadingWithResponse:nil data:data redirect:nil];
return YES;
}
现在,我的问题是,我是否正确实施了该方法?有谁知道我做的正确吗?
感谢。
编辑:我正在拍摄的电影是一部单帧电影。
答案 0 :(得分:7)
我已经实现了此方法的工作版本。我花了一段时间才弄明白。但现在的应用程序正常运行。这表明代码没问题。
我的应用包含一个媒体文件,我不想在未加密的软件包中发送。我想动态解密文件。 (一次一块)。
该方法必须同时响应内容请求(告诉玩家它正在加载什么) 并向数据请求(给玩家一些数据)。第一次调用该方法时,始终存在内容请求。然后会有一系列数据请求。
玩家很贪心。它总是要求整个文件。您没有义务提供。它要求整个蛋糕。你可以给它一片。
我交出媒体播放器的数据块。通常一次1 MB。用特殊情况处理较小的最终块。通常会按顺序询问块。但是你也需要能够处理无序请求。
- (BOOL) resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
{
NSURLRequest* request = loadingRequest.request;
AVAssetResourceLoadingDataRequest* dataRequest = loadingRequest.dataRequest;
AVAssetResourceLoadingContentInformationRequest* contentRequest = loadingRequest.contentInformationRequest;
//handle content request
if (contentRequest)
{
NSError* attributesError;
NSString* path = request.URL.path;
_fileURL = request.URL;
if (_fileHandle == nil)
{
_fileHandle = [NSFileHandle fileHandleForReadingAtPath:path];
}
// fire up the decryption here..
// for example ...
if (_decryptedData == nil)
{
_cacheStart = 1000000000;
_decryptedData = [NSMutableData dataWithLength:BUFFER_LENGTH+16];
CCCryptorCreate(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, [sharedKey cStringUsingEncoding:NSISOLatin1StringEncoding], kCCKeySizeAES256, NULL, &cryptoRef);
}
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&attributesError];
NSNumber *fileSizeNumber = [fileAttributes objectForKey:NSFileSize];
_fileSize = [fileSizeNumber longLongValue];
//provide information about the content
_mimeType = @"mp3";
contentRequest.contentType = _mimeType;
contentRequest.contentLength = _fileSize;
contentRequest.byteRangeAccessSupported = YES;
}
//handle data request
if (dataRequest)
{
//decrypt a block of data (can be any size you want)
//code omitted
NSData* decodedData = [NSData dataWithBytes:outBuffer length:reducedLen];
[dataRequest respondWithData:decodedData];
[loadingRequest finishLoading];
}
return YES;
}
答案 1 :(得分:1)
我浪费了2个小时试图做一些非常相似的事情。
原来它只能在设备上运行,并且在iOS模拟器上不起作用!
我猜模拟器中的AVFoundation以某种方式“桥接”到主机Mac的AVFoundation。不幸的是,这个API在OS X 10.8上不可用(根据WebCore上的一些提交,它将在OS X 10.9中提供),所以现在它在模拟器中不起作用。
答案 2 :(得分:0)
您需要create an NSURLResponse object才能传回来。你回来了nil
。没有它,AVAssetResourceLoader不知道如何处理你提供的数据(也就是说,它不知道它是什么类型的数据 - 错误消息,jpeg等)。在假设成功之前,您还应该使用-[NSData dataWithContentsOfURL:options:error:]
并检查错误。
答案 3 :(得分:0)
用户NSURLComponent以及scheme =“enc”以便调用AVAssetResourceLoaderDelegate方法。
let urlComponents = NSURLComponents(url: video_url, resolvingAgainstBaseURL: false)
urlComponents?.scheme = "enc"
let avAsset = AVURLAsset(url: (urlComponents?.url)!, options: ["AVURLAssetHTTPHeaderFieldsKey": headers])
avAsset.resourceLoader.setDelegate(self, queue: DispatchQueue(label: "AVARLDelegateDemo loader"))