Does anyone know how to properly implement the AVAssetResourceLoaderDelegate methods?

I am trying to get AVFoundation to read from a custom URL. A custom url is running. The code below creates an NSData with a movie file:

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

I set the 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 send queue is parallel.

Then I will 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 from the 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)} 

Implementation of the delegate method:

 - (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 this method correctly? Does anyone know what I'm doing right?

Thanks.

EDIT: The movie I collect in its entirety is a single-frame movie.

+6
source share
4 answers

I have implemented a working version of this method. It took me a while to understand. But the application that appears now works. This indicates that the code is in order.

My application has a multimedia file that I did not want to send in an unencrypted package. I would like to decrypt the file dynamically. (block at a time).

The method should respond both to a content request (tell the player what it is loading) and to data requests (give the player some data). When you first call a method, there is always a request for content. Then there will be a sequence of data requests.

The player is greedy. It always requests the whole file. You are not required to provide this. He asks for the whole cake. You can give him one piece.

I am transmitting media player data blocks. Usually 1 MB at a time. With a special case for processing a smaller final block. Blocks are usually requested sequentially. But you should also be able to handle requests that are not in the sequence.

 - (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; } 
+5
source

I just spent 2 hours trying to do something very similar.

It turns out that it only works on the device and does not work on the iOS simulator!

I assume that the AVFoundation in the simulator is somehow β€œtied” to the Mac host AVFoundation. Unfortunately, this API is not available in OS X 10.8 (according to some data on WebCore it will be available in OS X 10.9), so for now it does not work in the simulator.

+1
source

You need to create an NSURLResponse object to return. You go back nil . Without it, AVAssetResourceLoader has no idea what to do with the data that you pass to them (i.e., it has no idea what type of data it is - an error message, jpeg, etc.). You should also use -[NSData dataWithContentsOfURL:options:error:] and check for an error before proceeding with success.

0
source

Custom NSURLComponent along with the = = enc scheme to invoke the AVAssetResourceLoaderDelegate method.

 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")) 
0
source

All Articles