Использование AVAssetReader для чтения (потока) из удаленного ресурса

Моя главная цель - потоковое видео с сервера и обрезать его кадр за кадром во время потоковой передачи (чтобы оно могло использоваться OpenGL). Для этого я использовал этот код, который я нашел повсюду в Интернете (насколько я помню, он был из примера кода Apple GLVideoFrame):

NSArray * tracks = [asset tracks];
NSLog(@"%d", tracks.count);

for(AVAssetTrack* track in tracks) {

    NSLog(@"type: %@", [track mediaType]);

    initialFPS = track.nominalFrameRate;
    width = (GLuint)track.naturalSize.width;
    height = (GLuint)track.naturalSize.height;


    NSError * error = nil;

    // _movieReader is a member variable
    @try {
        self._movieReader = [[[AVAssetReader alloc] initWithAsset:asset error:&error] autorelease];
    }
    @catch (NSException *exception) {
        NSLog(@"%@ -- %@", [exception name], [exception reason]);
        NSLog(@"skipping track");

        continue;
    }


    if (error)
    {
        NSLog(@"CODE:%d\nDOMAIN:%@\nDESCRIPTION:%@\nFAILURE_REASON:%@", [error code], [error domain], error.localizedDescription, [error localizedFailureReason]);                                          
        continue;
    }

    NSString* key = (NSString*)kCVPixelBufferPixelFormatTypeKey;
    NSNumber* value = [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA];
    NSDictionary* videoSettings = [NSDictionary dictionaryWithObject:value forKey:key]; 
    [_movieReader addOutput:[AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:track
                                                                       outputSettings:videoSettings]];
    [_movieReader startReading];
    [self performSelectorOnMainThread:@selector(frameStarter) withObject:nil waitUntilDone:NO];
}

Но я всегда получаю это исключение в [[AVAssetReader alloc] initWithAsset:error:],

NSInvalidArgumentException -- *** -[AVAssetReader initWithAsset:error:] Cannot initialize an instance of AVAssetReader with an asset at non-local URL 'http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8'

Итак, мои два вопроса:

  1. Действительно ли исключение говорит мне, что AVAssetReader должен иметь локальный URL? Может ли он использоваться для потоковой передачи (так же, как и остальные AVFoundation классы)?
  2. Если AVFoundation Подход не сработает. Какие есть другие предложения для потоковой передачи видео и разделения его кадров одновременно?

Большое спасибо за вашу помощь.

3 ответа

Решение

@Cocoanetics, я не могу комментировать из-за низкой репутации. Но то, что вы сказали, не совсем верно. AVFoundation, похоже, не делает различий между локальными и нелокальными файлами, как между типом используемых файлов или протоколов. Существует ОЧЕНЬ четкое различие между использованием mp4/mov по сравнению с использованием потокового протокола HTTP Live через m3u8, но различия, использующие локальный или удаленный mp4, немного более размыты.

Чтобы расширить вышесказанное:

a) Если ваш "удаленный" актив - это M3U8 (то есть вы используете потоковую передачу HTTP), то никаких шансов нет. Неважно, находится ли M3U8 в вашей локальной файловой системе или на удаленном сервере, по множеству причин AVAssetReader и все функции, связанные с AVAsset, просто НЕ работают. However, AVPlayer, AVPlayerItem etc would work just fine.

б) Если речь идет о MP4/MOV, требуется дальнейшее расследование. Local MP4/MOV's work flawlessly. Хотя в случае с удаленными MP4 / MOV я могу создать (или извлечь из AVPlayerItem, AVPlayer или AVAssetTracks) AVURLAsset, с помощью которого я иногда могу успешно инициализировать AVAssetReader (я подробно остановлюсь на "иногда" ну и в скором времени). ТЕМ НЕ МЕНИЕ, copyNextSampleBuffer always returns nil in case of remote MP4's, Поскольку несколько вещей до момента вызова copyNextSampleBuffer работают, я не уверен на 100%, если:

i) copyNextSampleBuffer не работает для удаленных mp4, после того, как все остальные шаги были успешными, это предполагаемая / ожидаемая функциональность.

II) То, что "другие шаги", похоже, работают вообще для удаленных MP4, является случайностью реализации Apple, и эта несовместимость просто выходит на первый план, когда мы нажимаем copyNextSampleBuffer.............. что это за "другие шаги", я подробно опишу в ближайшее время.

III) я делаю что-то не так, когда пытаюсь вызвать copyNextSampleBuffer для удаленных MP4.

@Paula, вы можете попытаться исследовать немного дальше с удаленными MOV/MP4.

Для справки, вот те подходы, которые я пробовал для захвата кадра из видео:

а)

Создайте AVURLAsset прямо из видео URL.

Получить видео дорожку, используя [asset trackWithMediaType:AVMediaTypeVideo]

Подготовьте AVAssetReaderTrackOutput, используя видеодорожку в качестве источника.

Создайте AVAssetReader, используя AVURLAsset.

Добавьте AVAssetReaderTrackOutput к AVAssetReader и начните чтение.

Получить изображения с помощью copyNextSampleBuffer.

б)

Создайте AVPlayerItem из видео URL, а затем AVPlayer из него (или создайте AVPlayer непосредственно из URL).

Получите свойство 'asset' AVPlayer и загрузите его 'дорожки', используя "loadValuesAsynchronouslyForKeys:".

Разделите дорожки типа AVMediaTypeVideo (или просто вызовите trackWithMediaType: для актива после загрузки дорожек) и создайте AVAssetReaderTrackOutput с использованием видеодорожки.

Создайте AVAssetReader с помощью "актива", "startReading" AVPlayer, а затем извлекайте изображения с помощью copyNextSampleBuffer.

с)

Создайте AVPlayerItem+AVPlayer или AVPlayer непосредственно из URL-адреса видео.

KVO свойство AVPlayerItem'дорожки' и после загрузки дорожек отделяйте AVAssetTracks типа AVMediaTypeVideo.

Получите AVAsset из свойства 'asset' объекта AVPlayerItem/AVPlayer/AVAssetTrack.

Остальные шаги аналогичны подходу (б).

г)

Создайте AVPlayerItem+AVPlayer или AVPlayer непосредственно из URL-адреса видео.

KVO свойство AVPlayerItem "дорожки", и после загрузки дорожек отделяйте их от типа AVMediaTypeVideo.

Создайте AVMutableComposition и инициализируйте связанный AVMutableCompositionTrack типа AVMediaTypeVideo.

Вставьте соответствующий CMTimeRange из видео дорожки, полученной ранее, в этот AVMutableCompositionTrack.

Аналогично (b) и (c), теперь создайте ваши AVAssetReader и AVAssetReaderTrackOutput, но с той разницей, что вы используете AVMutableComposition в качестве базового AVAsset для инициализации AVAssetReader и AVMutableCompositionTrack в качестве базового AVAssetTrack для вашего AVAssetReaderTrackOutput.

'startReading' и использовать copyNextSampleBuffer, чтобы получить кадры из AVAssetReader.

PS: я попробовал подход (d) здесь, чтобы обойти тот факт, что AVAsset, полученный непосредственно из AVPlayerItem или AVPlayer, не вел себя. Поэтому я хотел создать новый AVAsset из уже имеющихся у меня AVAssetTracks. По общему признанию хакерский, и, возможно, бессмысленный (где еще будет в конечном итоге информация о треке, если не исходный AVAsset!), Но это все равно стоило отчаянной попытки.

Вот краткое изложение результатов для разных типов файлов:

1) Местные MOV/MP4 - все 4 подхода работают безупречно.

2) Удаленные MOV/MP4 - актив и дорожки корректно извлекаются в подходах (b) - (d), и AVAssetReader также инициализируется, но copyNextSampleBuffer всегда возвращает nil. В случае (a) создание самого AVAssetReader завершается неудачно с NSOSStatusErrorDomain "Неизвестная ошибка" -12407.

3) Локальные M3U8 (доступ через встроенный / локальный HTTP-сервер) - Подходы (a), (b) и (c) с треском проваливаются, поскольку попытка получить AVURLAsset/AVAsset в любой форме или форме для файлов, передаваемых через M3U8, поручение дураков.

В случае (a) ресурс вообще не создается, и вызов initWithURL: на AVURLAsset завершается неудачно с "Неизвестной ошибкой" AVFoundationErrorDomain -11800.

В случае (b) и (c), получение AVURLAsset из AVPlayer/AVPlayerItem или AVAssetTracks возвращает объект SOME, но доступ к свойству "дорожек" на нем всегда возвращает пустой массив.

В случае (d) я могу успешно извлекать и изолировать видеодорожки, но при попытке создать AVMutableCompositionTrack происходит сбой при попытке вставить CMTimeRange из исходной дорожки в AVMutableCompositionTrack с "неизвестной ошибкой" NSOSStatusErrorDomain -12780.

4) Удаленные M3U8 ведут себя точно так же, как и локальные M3U8.

Я не совсем осведомлен о том, почему эти различия существуют или не могут быть смягчены Apple. Но вот, пожалуйста.

@Cocanetics, это не так. Вы можете получить удаленный файл на AVMutableCompositionTrack

AVURLAsset* soundTrackAsset = [[AVURLAsset alloc]initWithURL:[NSURL URLWithString:@"http://www.yoururl.com/yourfile.mp3"] options:nil];

AVMutableCompositionTrack *compositionAudioSoundTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
[compositionAudioSoundTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioAsset.duration) 
                               ofTrack:[[soundTrackAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] 
                                atTime:kCMTimeZero error:nil];

Однако этот подход не очень хорошо работает с файлами с более высокой степенью сжатия, такими как MP4.

Использование нелокальных URL-адресов в настоящее время невозможно с AVFoundation, по крайней мере, не напрямую.

Другие вопросы по тегам