【问题标题】:Using AVAssetReader to read (stream) from a remote asset使用 AVAssetReader 从远程资产读取(流)
【发布时间】:2019-05-17 19:56:36
【问题描述】:

我的主要目标是从服务器流式传输视频,并在流式传输时逐帧剪切(以便 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 的其他类一样)?
  2. 如果 AVFoundation 方法不起作用,还有哪些其他建议可以同时流式传输视频并拆分其帧?

非常感谢您的帮助。

【问题讨论】:

  • 在这里遇到了类似的问题。然而,在我的情况下,AVAssetReader 没有得到异常,而是简单地拒绝初始化,并抛出一个“未知错误”(AVFoundationErrorDomain -11800)。我还尝试获取视频轨道,将其提供给新的 AVMutableComposition 和关联的 AVMutableCompositionTrack,然后尝试从中创建 AVAssetReader。那也行不通。编辑:顺便说一句,这种方法确实适用于本地 mp4/mov 文件。
  • iOS 中有一些新的 API 可以帮助您实现最终目标。见stackoverflow.com/questions/12500408/…

标签: iphone objective-c ios video-streaming avfoundation


【解决方案1】:

AVFoundation 似乎没有区分本地文件和非本地文件,因为它区分文件的种类或使用的协议。使用 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。由于在调用 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 的轨道(或在加载轨道后简单地调用资源上的 trackingWithMediaType:),并使用视频轨道创建您的 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 获取帧。

P.S:我在这里尝试了方法 (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) 在尝试为流式传输的文件获取任何形状或形式的 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 无法缓解这些差异。但是你去吧。

【讨论】:

    【解决方案2】:

    您可以在 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 等具有更高压缩率的文件

    【讨论】:

    • 我需要访问live m3u8 url的音视频轨道数据。你能帮我么?我们可以获取实时 m3u8 url 的 AVAssets 吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-14
    • 2015-05-04
    • 2012-03-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多