繁体   English   中英

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

[英]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'

所以我的两个问题是:

  1. 异常真的告诉我AVAssetReader必须有一个本地 URL 吗? 它可以用于流式传输(就像AVFoundation类的 rest 一样)?
  2. 如果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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM