Использование AVURLAsset в пользовательском протоколе NSURLProtocol

Я написал собственный NSURLProtocol (называемый «memory:»), который позволяет мне извлекать сохраненные элементы NSData из NSDictionary на основе имени. Например, этот код регистрирует класс NSURLProtocol и добавляет некоторые данные:

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

Это позволяет мне обращаться к NSData через URL-адрес типа «memory://video».

Ниже приведена моя собственная реализация 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
{

}

Я не уверен, работает ли этот код или нет, но проблема не в этом. Несмотря на регистрацию пользовательского протокола, canInitWithRequest: никогда не вызывается, когда я пытаюсь использовать URL-адрес в этом коде:

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, а не пользовательские?

Спасибо


person Cthutu    schedule 18.10.2012    source источник
comment
Возможно, стоит проверить, возвращает ли registerClass YES, хотя из документов это выглядит так.   -  person Robert Diamond    schedule 03.01.2013
comment
Кроме того, вы должны @throw или что-то еще, если регистр не был вызван - сделайте его иваром, а не статическим   -  person Robert Diamond    schedule 03.01.2013


Ответы (1)


Конечно, основы использовались для поддержки только определенных схем URL-адресов — как разработчик электронных книг я видел, как это происходит для любого типа мультимедиа, загружаемого через URL-адрес, такой как epub:// или zip://. В этих случаях в iOS 5.x и более ранних версиях отслеживание соответствующего кода завершалось бы в методе QuickTime, который сравнивал схему URL с небольшим числом поддерживаемых: file, http, https, ftp и тем, что iTunes использует-- Я забыл, как это называется.

Однако в iOS 6+ есть новый API в AVFoundation, который призван помочь здесь. Хотя я не использовал его лично, вот как это должно работать:

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 или вашему классу протокола напрямую.

Опять же, я укажу, что я еще не использовал это, поэтому вышеизложенное является полностью теоретическим.

person Jim Dovey    schedule 13.02.2013
comment
Я попробовал то, что вы сделали, но столкнулся с препятствием. В Google или SO нет ничего, что могло бы мне помочь. Возможно, вы знаете что-то об этом. Я задал новый вопрос: stackoverflow.com/questions/15005474/ - person Cthutu; 21.02.2013