12

I am trying to coax AVFoundation to read from a custom URL. The custom URL stuff works. The code below creates a NSData with a movie file:

NSData* movieData = [NSData dataWithContentsOfURL:@"memory://video"];

I've set up a AVAssetResourceLoader object using the following code:

NSURL* url = [NSURL URLWithString:@"memory://video"];
AVURLAsset* asset = [[AVURLAsset alloc] initWithURL:url options:nil];
AVAssetResourceLoader* loader = [asset resourceLoader];
[loader setDelegate:self queue:mDispatchQueue];

The dispatch queue is concurrent.

I then try to extract the first frame from the movie:

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);

But when I run this but of code I get:

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)}

The implementation of the delegate method is:

- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
{
    NSData* data = [NSData dataWithContentsOfURL:loadingRequest.request.URL];
    [loadingRequest finishLoadingWithResponse:nil data:data redirect:nil];
    return YES;
}

Now, my question is, am I implementing the method correctly? Does anyone know if what I am doing correct?

Thanks.

EDIT: The movie I am fetching in its entirety is a single frame movie.

4

4 回答 4

8

我已经实现了这个方法的一个工作版本。我花了一段时间才弄清楚。但生成的应用程序现在可以工作了。这表明代码没问题。

我的应用程序包含一个我不想在未加密的包中发送的媒体文件。我想动态解密文件。(一次一个块)。

该方法必须同时响应内容请求(告诉播放器正在加载什么)和数据请求(给播放器一些数据)。第一次调用该方法时,总是有一个内容请求。然后会有一系列的数据请求。

玩家很贪心。它总是要求整个文件。您没有义务提供。它要求整个蛋糕。你可以给它一片。

我把数据块交给媒体播放器。通常一次 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;
}
于 2014-05-30T15:52:50.920 回答
1

我只是浪费了 2 个小时试图做一些非常相似的事情。

原来它只适用于设备,不适用于 iOS 模拟器!

我猜模拟器中的 AVFoundation 以某种方式“桥接”到主机 Mac 的 AVFoundation。不幸的是,这个 API 在 OS X 10.8 上不可用(根据 WebCore 上的一些提交,它将在 OS X 10.9 中可用),所以现在它在模拟器中不起作用。

于 2013-03-27T15:03:25.180 回答
1

用户 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"))
于 2017-08-02T09:57:40.040 回答
0

您需要创建一个 NSURLResponse 对象来传回。你要回去了nil。没有它,AVAssetResourceLoader 不知道如何处理你交给它的数据(也就是说,它不知道它是什么类型的数据 - 错误消息、jpeg 等)。在假设成功之前,您还应该真正使用-[NSData dataWithContentsOfURL:options:error:]并检查错误。

于 2013-02-21T15:35:22.930 回答