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

我的主要目标是从服务器stream式传输video,并在stream式传输时逐帧剪切(以便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:]得到这个exception。

 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. 这个exception真的告诉我, AVAssetReader必须有一个本地URL? 它可以用于stream(就像AVFoundation类的其余部分)?
  2. 如果AVFoundation方法不起作用,还有什么其他的build议可以同时stream式传输video和分割帧?

非常感谢你的帮助。

@Cocoanetics,我无法评论由于低代表。 但是你所说的话似乎并不完全正确。 AVFoundation在本地和非本地文件之间似乎没有区别,就像它在所使用的一些文件或协议之间所做的那样。 使用mp4 / mov与使用通过m3u8的HTTP Livestream协议之间有一个非常明显的区别,但是使用本地或远程mp4的差别有点模糊。

展开上面的内容:

a)如果您的“远程”资产是M3U8(也就是说,您正在使用HTTP“实时”stream式传输),那么没有任何机会。 无论M3U8是在您的本地文件系统还是在远程服务器上,由于AVAssetReader和所有与AVAsset相关的function都不起作用。 However, AVPlayer, AVPlayerItem etc would work just fine.

b)如果是MP4 / MOV,则需要进一步调查。 Local MP4/MOV's work flawlessly. 而在远程MP4 / MOV的情况下,我能够创build(或从AVPlayerItem或AVPlayer或AVAssetTracks检索)AVURLAsset,有时我可以成功地初始化一个AVAssetReader(我将展开'有时'以及,很快)。 但是, copyNextSampleBuffer always returns nil in case of remote MP4'scopyNextSampleBuffer always returns nil in case of remote MP4's 。 由于几件事情调用copyNextSampleBuffer的工作点,我不是100%确定如果:

i)在所有其他步骤已经成功之后,copyNextSampleBuffer不起作用于远程mp4的function。

ii)“其他步骤”对于远程MP4似乎都起作用,这是苹果公司实施的一个偶然事件,当我们碰到copyNextSampleBuffer时,这种不兼容性就显得很突出了…………..这些“其他的步骤”是什么,我马上就会详细介绍。

iii)我试图调用copyNextSampleBuffer远程MP4的时候做错了。

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

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

一个)

直接从videourl创build一个AVURLAsset。

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

准备AVAssetReaderTrackOutput使用video轨道作为来源。

使用AVURLAsset创build一个AVAssetReader。

将AVAssetReaderTrackOutput添加到AVAssetReader和startReading。

使用copyNextSampleBuffer检索图像。

b)

从videourl创build一个AVPlayerItem,然后从它创build一个AVPlayer(或直接从URL创buildAVPlayer)。

检索AVPlayer的“资产”属性并使用“loadValuesAsynchronouslyForKeys:”加载其“轨道”。

分开AVMediaTypeVideotypes的轨道(或者只需在轨道加载后在资产上调用tracksWithMediaType:),然后使用video轨道创buildAVAssetReaderTrackOutput。

使用AVPlayer的'asset','startReading'创buildAVAssetReader,然后使用copyNextSampleBuffer检索图像。

C)

直接从videourl创build一个AVPlayerItem + AVPlayer或AVPlayer。

KVO AVPlayerItem的“音轨”属性,一旦加载音轨,分开AVMediaTypeVideotypes的AVAssetTracks。

从AVPlayerItem / AVPlayer / AVAssetTrack的“资产”属性中检索AVAsset。

其余步骤与(b)类似。

d)

直接从videourl创build一个AVPlayerItem + AVPlayer或AVPlayer。

KVO AVPlayerItem的“轨道”属性,一旦加载轨道,分开AVMediaTypeVideotypes。

创buildAVMutableComposition,并初始化AVMediaTypeVideotypes的关联AVMutableCompositionTrack。

从先前检索的video轨道插入适当的CMTimeRange到这个AVMutableCompositionTrack。

与(b)和(c)类似,现在创build您的AVAssetReader和AVAssetReaderTrackOutput,但不同之处在于您使用AVMutableComposition作为初始化AVAssetReader的基础AVAsset,并使用AVMutableCompositionTrack作为AVAssetReaderTrackOutput的基础AVAssetTrack。

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

PS:我在这里尝试了方法(d),以避免直接从AVPlayerItem或AVPlayer检索AVAsset不起作用的事实。 所以我想从我手头已经有的AVAssetTrack创build一个新的AVAsset。 无可否认,也许毫无意义(如果不是最初的AVAsset,最终将从哪里获得曲目信息?),但无论如何,这是值得一试的尝试。

以下是不同types文件的结果摘要:

1)本地MOV / MP4的 – 所有4个方法完美地工作。

2)远程MOV / MP4的 – 在方法(b)到(d)中资产和轨道被正确地检索,并且AVAssetReader也被初始化,但copyNextSampleBuffer总是返回nil。 在(a)的情况下,创buildAVAssetReader本身失败,出现“未知错误”NSOSStatusErrorDomain -12407。

3)本地M3U8(通过应用内/本地HTTP服务器访问) – 方法(a),(b)和(c)失败,试图获取任何形状或forms的AVURLAsset / AVAsset通过M3U8stream传输的文件一个傻瓜差事。

在(a)的情况下,根本不创build资产,并且AVURLAsset上的initWithURL:调用失败,并出现“未知错误”AVFoundationErrorDomain -11800。

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

在(d)的情况下,我能够成功地检索和隔离video轨道,但是在尝试创buildAVMutableCompositionTrack时,尝试从源轨道插入AVMutableCompositionTrack中插入CMTimeRange失败,出现“未知错误” NSOSStatusErrorDomain -12780。

4)遥控M3U8,与当地的M3U8完全一样。

我没有完全了解为什么这些差异存在,或不能被苹果减轻。 但是,你去了。

@科学,那不是真的。 你可以在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等压缩比较高的文件并不适用

目前AVFoundation不可能使用非本地URL,至less不是直接的。