[英]Using AVAssetReader to read (stream) from a remote asset
我的主要目标是 stream 来自服务器的视频,并在流式传输时逐帧剪切(以便 OpenGL 可以使用它)。 为此,我使用了在 Internet 上随处可见的代码(我记得它来自 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'
所以我的两个问题是:
AVAssetReader
必须有一个本地 URL 吗? 它可以用于流式传输(就像AVFoundation
类的 rest 一样)?AVFoundation
方法不起作用,那么对 ZF7B44CFFAFD5C52223D5498196C8A2E7BZ 视频并同时拆分其帧有什么其他建议?非常感谢你的帮助。
AVFoundation 似乎并没有区分本地和非本地文件,因为它区分了所使用的文件或协议的种类。 使用 mp4/mov 与通过 m3u8 使用 HTTP 实时流协议之间存在非常明显的区别,但使用本地或远程 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
。 由于在调用 copyNextSampleBuffer 之前有几件事起作用,我不能 100% 确定:
i) copyNextSampleBuffer 在所有其他步骤都成功之后不适用于远程 mp4,这是预期/预期的功能。
ii)“其他步骤”似乎对远程 MP4 完全有效,这是 Apple 实施的一个意外,当我们点击 copyNextSampleBuffer 时,这种不兼容性就会浮出水面......这些“其他步骤”是什么,我稍后会详细介绍。
iii) 我在尝试为远程 MP4 调用 copyNextSampleBuffer 时做错了。
所以@Paula 您可以尝试使用远程 MOV/MP4 进一步调查。
作为参考,以下是我尝试从视频中捕获帧的方法:
一个)
直接从视频 URL 创建一个 AVURLAsset。
使用 [asset trackingWithMediaType:AVMediaTypeVideo] 检索视频轨道
使用视频轨道作为源准备 AVAssetReaderTrackOutput。
使用 AVURLAsset 创建一个 AVAssetReader。
将 AVAssetReaderTrackOutput 添加到 AVAssetReader 并 startReading。
使用 copyNextSampleBuffer 检索图像。
b)
从视频 URL 创建一个 AVPlayerItem,然后从中创建一个 AVPlayer(或直接从 URL 创建 AVPlayer)。
检索 AVPlayer 的“资产”属性并使用“loadValuesAsynchronouslyForKeys:”加载其“轨道”。
分离类型为 AVMediaTypeVideo 的轨道(或者在加载轨道后在资产上调用 trackWithMediaType:),并使用视频轨道创建 AVAssetReaderTrackOutput。
使用 AVPlayer 的 'asset'、'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 作为基础 AVAsset 来初始化 AVAssetReader,而 AVMutableCompositionTrack 作为基础 AVAssetTrack 用于 AVAssetReaderTrackOutput。
'startReading' 并使用 copyNextSampleBuffer 从 AVAssetReader 获取帧。
PS:我在这里尝试了方法(d)来解决直接从 AVPlayerItem 或 AVPlayer 检索到的 AVAsset 没有表现的事实。 所以我想从我手头的 AVAssetTracks 中创建一个新的 AVAsset。 诚然,hacky,也许毫无意义(如果不是原始的 AVAsset,最终还能从哪里检索到轨道信息。)但无论如何还是值得一试。
以下是不同类型文件的结果摘要:
1) 本地 MOV/MP4 - 所有 4 种方法都能完美运行。
2) 远程 MOV/MP4 - 在方法 (b) 到 (d) 中正确检索资产和轨道,并且 AVAssetReader 也被初始化,但 copyNextSampleBuffer 始终返回 nil。 在 (a) 的情况下,AVAssetReader 本身的创建失败并出现“未知错误”NSOSStatusErrorDomain -12407。
3) 本地 M3U8(通过应用内/本地 HTTP 服务器访问)- 方法 (a)、(b) 和 (c) 惨遭失败,因为尝试为通过 M3U8 流式传输的文件获取任何形状或形式的 AVURLAsset/AVAsset 是一个傻瓜的差事。
在 (a) 的情况下,根本不会创建资产,并且对 AVURLAsset 的 initWithURL: 调用失败,并出现“未知错误”AVFoundationErrorDomain -11800。
在 (b) 和 (c) 的情况下,从 AVPlayer/AVPlayerItem 或 AVAssetTracks 检索 AVURLAsset 会返回一些 object,但访问其上的 'tracks' 属性总是返回一个空数组。
在 (d) 的情况下,我能够成功检索和隔离视频轨道,但在尝试创建 AVMutableCompositionTrack 时,尝试将源轨道中的 CMTimeRange 插入 AVMutableCompositionTrack 时失败,并出现“未知错误” NSOSStatusErrorDomain -12780。
4) 远程 M3U8 的行为与本地 M3U8 完全相同。
我没有完全了解为什么存在这些差异,或者苹果无法缓解这些差异。 但是你有 go。
您可以在 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 等具有更高压缩率的文件
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.