Using AVURLAsset in a Custom NSURLProtocol

I wrote a special NSURLProtocol (called "memory:") that allows me to retrieve saved NSData elements from an NSDictionary based on the name. For example, this code registers the NSURLProtocol class and adds some data:

[VPMemoryURLProtocol register]; [VPMemoryURLProtocol addData:data withName:@"video"]; 

This allows me to reference NSData using a URL such as "memory: // video".

Below is my regular implementation of NSURLProtocol:

 NSMutableDictionary* gMemoryMap = nil; @implementation VPMemoryURLProtocol { } + (void)register { static BOOL inited = NO; if (!inited) { [NSURLProtocol registerClass:[VPMemoryURLProtocol class]]; inited = YES; } } + (void)addData:(NSData *)data withName:(NSString *)name { if (!gMemoryMap) { gMemoryMap = [NSMutableDictionary new]; } gMemoryMap[name] = data; } + (BOOL)canInitWithRequest:(NSURLRequest *)request { NSLog(@"URL: %@, Scheme: %@", [[request URL] absoluteString], [[request URL] scheme]); NSString* theScheme = [[request URL] scheme]; return [theScheme caseInsensitiveCompare:@"memory"] == NSOrderedSame; } + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { return request; } - (void)startLoading { NSString* name = [[self.request URL] path]; NSData* data = gMemoryMap[name]; NSURLResponse* response = [[NSURLResponse alloc] initWithURL:[self.request URL] MIMEType:@"video/mp4" expectedContentLength:-1 textEncodingName:nil]; id<NSURLProtocolClient> client = [self client]; [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; [client URLProtocol:self didLoadData:data]; [client URLProtocolDidFinishLoading:self]; } - (void)stopLoading { } 

I am not sure if this code works or not, but that is not a problem. Despite registering a user protocol, canInitWithRequest: never gets called when I try to use the url in this code:

 NSURL* url = [NSURL URLWithString:@"memory://video"]; AVURLAsset* asset = [[AVURLAsset alloc] initWithURL:url options:nil]; AVAssetImageGenerator* imageGen = [AVAssetImageGenerator assetImageGeneratorWithAsset:asset]; CMTime time = CMTimeMakeWithSeconds(0, 600); NSError* error; CMTime actualTime; CGImageRef image = [imageGen copyCGImageAtTime:time actualTime:&actualTime error:&error]; UIImage* uiImage = [UIImage imageWithCGImage:image]; CGImageRelease(image); 

The image is always nil if I use "memory: // video", but it works fine if I use "file: /// ...". What am I missing? Why is canInitWithRequest not being called? Does AVFoundation support only specific URL protocols, not custom ones?

thanks

+4
source share
1 answer

Of course, the basics used only to support certain URL schemes, as an e-book developer, I saw how this happens for any type of media file downloaded via a URL, such as epub:// or zip:// . In these cases, in iOS 5.x and earlier versions, tracking through the corresponding code will end in the QuickTime method, which compares the URL scheme with a small number of supported ones: file , http , https , ftp and whatever iTunes uses. .. I forgot what he called.

IOS 6+ has a new API in AVFoundation, however, which is intended to help here. Although I have not used it personally, this is how it should work:

 NSURL* url = [NSURL URLWithString:@"memory://video"]; AVURLAsset* asset = [[AVURLAsset alloc] initWithURL:url options:nil]; //////////////////////////////////////////////////////////////// // NEW CODE START AVAssetResourceLoader* loader = [asset resourceLoader]; id<AVAssetResourceLoaderDelegate> delegate = [SomeClass newInstanceWithNSURLProtocolClass: [VPMemoryURLProtocol class]]; [loader setDelegate: delegate queue: some_dispatch_queue]; // NEW CODE END //////////////////////////////////////////////////////////////// AVAssetImageGenerator* imageGen = [AVAssetImageGenerator assetImageGeneratorWithAsset:asset]; CMTime time = CMTimeMakeWithSeconds(0, 600); 

In this case, you only need to implement the AVAssetResourceLoader protocol somewhere, which is very simple, since it contains only one method . Since you already have an implementation of NSURLProtocol , all of your real work is done, and you can simply transfer the real work to the Cocoa boot system or your protocol class directly.

Again, I will point out that I have not yet taken advantage of this, therefore the above is completely theoretical.

+2
source

Source: https://habr.com/ru/post/1440654/


All Articles