4

我编写了一个自定义的 NSURLProtocol(称为“memory:”),它允许我根据名称从 NSDictionary 中获取存储的 NSData 项。例如,这段代码注册了 NSURLProtocol 类并添加了一些数据:

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

这使我可以通过“memory://video”之类的 url 引用 NSData。

下面是我的自定义 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
{

}

我不确定这段代码是否有效,但这不是我遇到的问题。尽管注册了自定义协议,但当我尝试在此代码中使用 URL 时,永远不会调用 canInitWithRequest::

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

如果我使用“memory://video”,图像始终为零,但如果我使用“file:///...”,则效果很好。我错过了什么?为什么不调用 canInitWithRequest ?AVFoundation 是否只支持特定的 URL 协议而不支持自定义的?

谢谢

4

1 回答 1

2

当然,过去只支持特定 URL 方案的基础——作为电子书开发人员,我已经看到通过 URL 加载的任何媒体类型都会发生这种情况,例如epub://zip://. 在这些情况下,在 iOS 5.x 和更早版本上,跟踪相关代码将最终以 QuickTime 方法结束,该方法将 URL 方案与少数支持的方案进行比较:filehttphttps以及ftpiTunes 使用的任何内容——我忘记它叫什么了。

然而,在 iOS 6+ 中,AVFoundation 中有一个新的 API,旨在提供帮助。虽然我没有亲自使用它,但它应该是这样工作的:

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

有了这个,你只需要在AVAssetResourceLoader某个地方实现协议,这非常简单,因为它只包含一个方法。由于您已经有了一个NSURLProtocol实现,所有实际工作都已完成,您可以简单地将实际工作直接交给 Cocoa 加载系统或您的协议类。

再一次,我要指出我还没有真正利用它,所以上面的内容完全是理论上的。

于 2013-02-13T22:58:50.643 回答