AVAssetReaderTrackOutput 在 copyNextSampleBuffer 上挂起

Posted

技术标签:

【中文标题】AVAssetReaderTrackOutput 在 copyNextSampleBuffer 上挂起【英文标题】:AVAssetReaderTrackOutput hangs on copyNextSampleBuffer 【发布时间】:2017-01-16 09:19:00 【问题描述】:

我遇到了AVAssetWritter 的问题。有时会发生我的视频写作会话只是挂起。在检查了当前在我的设备上运行的线程后,我发现整个视频处理都在等待copyNextSampleBuffer 返回。我不知道是什么导致了这个问题。有谁成功克服了这个问题?

以下是从仪器捕获的线程转储。它以mach_msg_trap 结束。

视频处理循环

while ([self.assetWriterVideoInput isReadyForMoreMediaData] && !(*completedOrFailed) && !self.cancelled)
    
        @autoreleasepool 
            
            
            CMSampleBufferRef sampleBuffer = [self.assetReaderVideoOutput copyNextSampleBuffer];
            
                        
            
            CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
            CVPixelBufferRef croppedBuffer = NULL;
            NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                                     [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
                                     [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey, nil];
            CVPixelBufferCreate(kCFAllocatorDefault, self.outputSize.width, self.outputSize.height, CVPixelBufferGetPixelFormatType(pixelBuffer), (__bridge CFDictionaryRef) options, &croppedBuffer);
            CIImage *img = [[CIImage alloc] initWithCVPixelBuffer:pixelBuffer];
            
            // img processing
           
            
            [self.context render:img toCVPixelBuffer:croppedBuffer];
            
            
            if (sampleBuffer != NULL)
            
                
                BOOL success = [self.avPixelAdaptor appendPixelBuffer:croppedBuffer withPresentationTime:sampleTime];
                
                CFRelease(sampleBuffer);
                sampleBuffer = NULL;
                *completedOrFailed = !success;
            
            else
            
                *completedOrFailed = YES;
            
            CVPixelBufferRelease(croppedBuffer);
        
    

更新

资产阅读器的源资产是AVMutableComposition,它由几个指向照片库的AVURLAsset组成(即url = "assets-library://asset/asset.MOV?id=4CA9A2C6-F2D4- 4FDF-AAEC-6335B9BD840A&ext=MOV")。每个源资产需要 2 秒,在源资产 0.6 秒后开始。如果所有源资产都从 0 开始,则视频处理永远不会挂起。

总结

主要问题是:什么情况会导致copyNextSampleBuffer 永远等待退出。文档没有提到这种情况。

【问题讨论】:

你在 github 上有一个可以重现问题的小项目吗? 不,我没有这样的项目 :( 如果你能做一个就好了 你有没有在这里取得进步?我有同样的问题,只是读取样本缓冲区以显示在 AVSampleBufferDisplayLayer 中(无写入) 【参考方案1】:

这个错误也可能出现在AVPlayerAVAssetExportSession 上。我的解决方案可用于AVPlayerItemAVAssetExportSession

我花了一段时间才明白发生了什么。我仍然不能 100% 确定,但这就是我想出来的:

问题在于视频的源和目标时间映射。我可以猜到如果您检查videoTrack.segments(例如第一个)segment.timeMapping.source.start 将不等于segment.timeMapping.target.start。默认情况下,在内部,AVVideoComposition 将继承 sourceTrackIDForFrameTiming 从您的原始资产合成,以及有关时间、帧率等的所有内容,这将导致错误。如果您不设置用于导出的 videoComposition,它将执行相同的操作。我可以猜测资产读取器的一些内部逻辑会破坏这个特定案例的时间计算。

因此,对于修复,您可以将 sourceTrackIDForFrameTiming 设置为 AVVideoComposition

作为assetReaderVideoOutput,您应该使用AVAssetReaderVideoCompositionOutput,并为其分配videoComposition

你可以像这样创建videoComposition,例如:

    let videoComposition = AVMutableVideoComposition(propertiesOf: mutableComposition)

    //MARK: - The most important thing
    videoComposition.sourceTrackIDForFrameTiming = kCMPersistentTrackID_Invalid

    videoComposition.frameDuration = CMTimeMake(value: 1, timescale: 30)
    videoComposition.renderScale  = 1.0
    videoComposition.renderSize = CGSize(width: 720.0, height: 1280.0)
    
    let layerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: compositionVideoTrack)
    
    let mainInstruction = AVMutableVideoCompositionInstruction()
    mainInstruction.timeRange = CMTimeRange(start: .zero, duration: composition.duration)
    mainInstruction.layerInstructions = [layerInstruction]
    
    videoComposition.instructions = [mainInstruction]

在此处根据您的需要更改硬编码值。您也可以在不设置 sourceTrackIDForFrameTiming 的情况下使用 let videoComposition = AVMutableVideoComposition() 初始化程序,在我的测试中,这也有效。将videoComposition 分配给assetReaderVideoOutput。并导出。

就是这样!

【讨论】:

【参考方案2】:

我没有看到您在代码中将输入标记为已完成并结束了会话,如下所示。您是否调用了这些终结器?

//Finish the session:
[writerInput markAsFinished];
[videoWriter endSessionAtSourceTime:totalDuration];

[videoWriter finishWritingWithCompletionHandler:^

// Handle videoWriter.status here

];

【讨论】:

以上是关于AVAssetReaderTrackOutput 在 copyNextSampleBuffer 上挂起的主要内容,如果未能解决你的问题,请参考以下文章

iOS - 视频循环播放

使用 AVAssetReader 读取多个轨道

如何使用 AVFoundation 从视频流中获取原始格式的图像缓冲区?