Using AVURLAsset on a custom NSURLProtocol

I have written a custom NSURLProtocol (called "memory:") that allows me to fetch stored NSData items from a NSDictionary based on a name. For example, this code registers the NSURLProtocol class and adds some data:

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

This allows me to refer to the NSData via a url like "memory://video".

Below is my custom NSURLProtocol implementation:

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 whether this code works or not but that is not what I have a problem with. Despite registering the custom protocol, canInitWithRequest: is never 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);

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

Thanks


Certainly the underpinnings used to only support particular URL schemes— as an eBook developer I've seen this happen for any media type loaded through a URL such as epub:// or zip:// . In those cases, on iOS 5.x and earlier, tracing through the relevant code would wind up in a QuickTime method which compared the URL scheme against a small number of supported ones: file , http , https , ftp and whatever it is that iTunes uses-- I forget what it's called.

In iOS 6+ there is a new API in AVFoundation, however, which is designed to help here. While I've 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);

With this in place, you need only implement the AVAssetResourceLoader protocol somewhere, which is very simple as it contains only one method. Since you already have an NSURLProtocol implementation, all your real work is done and you can simply hand off the real work to the Cocoa loading system or your protocol class directly.

Again, I'll point out that I've yet to actually make use of this, so the above is entirely theoretical.

链接地址: http://www.djcxy.com/p/34832.html

上一篇: 为什么我的自定义委托方法不被调用?

下一篇: 在自定义NSURLProtocol上使用AVURLAsset