使用AVAssetReader从远程资产中读取(流)

Pau*_*ouh 30 iphone objective-c avfoundation video-streaming ios

我的主要目标是从服务器流式传输视频,并在流式传输时逐帧剪切(以便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];
}
Run Code Online (Sandbox Code Playgroud)

但我总是得到这个例外[[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'
Run Code Online (Sandbox Code Playgroud)

所以我的两个问题是:

  1. 异常是否真的告诉我AVAssetReader必须有本地URL?它可以用于流式传输(就像其他AVFoundation类一样)吗?
  2. 如果该AVFoundation方法不起作用,那么流式传输视频并同时拆分帧的其他建议是什么?

非常感谢你的帮助.

Dev*_*hen 36

@Cocoanetics,由于低代表我无法发表评论.但你所说的似乎并不完全正确.AVFoundation似乎没有区分本地和非本地文件,因为它在使用的KIND文件或协议之间.使用mp4/mov与通过m3u8使用HTTP Live流协议之间有一个非常明显的区别,但使用本地或远程mp4的差异有点模糊.

为了扩展上述内容:

a)如果你的"远程"资产是M3U8(也就是说,你正在使用HTTP'直播'流媒体),那么就没有任何机会了.无论M3U8是在本地文件系统中还是在远程服务器上,由于多种原因,AVAssetReader和所有与AVAsset相关的功能都不起作用.However, AVPlayer, AVPlayerItem etc would work just fine.

b)如果是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.由于UPTO调用copyNextSampleBuffer的工作有几点,我不是100%肯定:

i)copyNextSampleBuffer不能用于远程mp4,在所有其他步骤成功之后,是预期/预期的功能.

ii)"其他步骤"似乎对远程MP4起作用是苹果公司实施的一个意外,当我们点击copyNextSampleBuffer .............时,这种不兼容性就会突然出现.这些"其他步骤"是什么,我将很快详述.

iii)尝试为远程MP4调用copyNextSampleBuffer时,我做错了什么.

所以@Paula你可以尝试用远程MOV/MP4进一步调查.

作为参考,以下是我尝试从视频中捕获帧的方法:

一个)

直接从视频URL创建AVURLAsset.

使用[asset tracksWithMediaType:AVMediaTypeVideo]检索视频轨道

使用视频轨道作为源准备AVAssetReaderTrackOutput.

使用AVURLAsset创建AVAssetReader.

将AVAssetReaderTrackOutput添加到AVAssetReader和startReading.

使用copyNextSampleBuffer检索图像.

b)

从视频URL创建AVPlayerItem,然后从中创建AVPlayer(或直接从URL创建AVPlayer).

检索AVPlayer的'asset'属性并使用"loadValuesAsynchronouslyForKeys:"加载其'track'.

将AVMediaTypeVideo类型的轨道分开(或者在加载轨道后简单地在资产上调用tracksWithMediaType:),并使用视频轨道创建AVAssetReaderTrackOutput.

使用AVPlayer的"资产","startReading"创建AVAssetReader,然后使用copyNextSampleBuffer检索图像.

C)

直接从视频URL创建AVPlayerItem + AVPlayer或AVPlayer.

KVO AVPlayerItem的'tracks'属性,加载曲目后,将AVMediaTypeVideo类型的AVAssetTracks分开.

从AVPlayerItem/AVPlayer/AVAssetTrack的'asset'属性中检索AVAsset.

其余步骤与方法(b)类似.

d)

直接从视频URL创建AVPlayerItem + AVPlayer或AVPlayer.

KVO AVPlayerItem的'tracks'属性,加载曲目后,将AVMediaTypeVideo类型分开.

创建AVMutableComposition,并初始化AVMediaTypeVideo类型的关联AVMutableCompositionTrack.

将之前检索到的视频轨道中的相应CMTimeRange插入此AVMutableCompositionTrack.

与(b)和(c)类似,现在创建AVAssetReader和AVAssetReaderTrackOutput,但区别在于您使用AVMutableComposition作为初始化AVAssetReader的基本AVAsset,并使用AVMutableCompositionTrack作为AVAssetReaderTrackOutput的基本AVAssetTrack.

'startReading'并使用copyNextSampleBuffer从AVAssetReader获取帧.

PS:我尝试在这里接近(d)来解决直接从AVPlayerItem或AVPlayer检索到的AVAsset没有表现的事实.所以我想从我手头的AVAssetTracks创建一个新的AVAsset.不可否认,也许是没有意义的(如果不是最初的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)的情况下,根本不创建资产,并且AVURLAsset上的initWithURL:调用失败,并显示"未知错误"AVFoundationErrorDomain -11800.

在(b)和(c)的情况下,从AVPlayer/AVPlayerItem或AVAssetTracks中检索AVURLAsset会返回SOME对象,但访问它上面的'tracks'属性总是返回一个空数组.

在(d)的情况下,我能够成功检索和隔离视频轨道,但在尝试创建AVMutableCompositionTrack时,尝试将CMTimeRange从源轨道插入到AVMutableCompositionTrack时失败,并显示"未知错误" NSOSStatusErrorDomain -12780.

4)远程M3U8的行为与本地M3U8完全相同.

我没有完全了解为什么这些差异存在,或者不能被Apple减轻.但是你去吧.