4

名前に基づいて NSDictionary から格納された NSData アイテムをフェッチできるカスタム NSURLProtocol (「memory:」と呼ばれる) を作成しました。たとえば、次のコードは 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」を使用すると画像は常に nil ですが、「file:///...」を使用すると正常に動作します。私は何が欠けていますか?canInitWithRequest が呼び出されないのはなぜですか? AVFoundation は特定の URL プロトコルのみをサポートし、カスタム プロトコルはサポートしませんか?

ありがとう

4

1 に答える 1

2

確かに、基盤は特定の URL スキームのみをサポートするために使用されていました。電子書籍の開発者として、epub://やなどの URL を介してロードされるすべてのメディア タイプでこれが発生するのを見てきましたzip://。そのような場合file、 iOS 5.x 以前ではhttp、関連するコードをトレースするとhttps、URL スキームをサポートされている少数のスキームと比較する QuickTime メソッドで終了します。ftpそれが何と呼ばれているか忘れてください。

ただし、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どこかにプロトコルを実装するだけで済みます。これは、メソッドが 1 つしか含まれていないため非常に簡単です。すでにNSURLProtocol実装があるので、実際の作業はすべて完了しており、実際の作業を Cocoa ローディング システムまたはプロトコル クラスに直接渡すことができます。

繰り返しますが、私はまだこれを実際に利用していないことを指摘しておきます。したがって、上記は完全に理論的なものです。

于 2013-02-13T22:58:50.643 に答える