繁体   English   中英

AVAssetExportSession 导出不确定性地失败并显示错误:“操作已停止,NSLocalizedFailureReason=无法制作视频。”

[英]AVAssetExportSession export fails non-deterministically with error: "Operation Stopped, NSLocalizedFailureReason=The video could not be composed."

我们为用户录制的视频添加了字幕,但我们的 AVAssetExportSession 对象的导出不确定地失败:有时它有效,有时它不起作用。 甚至不清楚如何重现错误。

我们注意到资产轨道似乎在导出过程中丢失了。

在导出之前,如预期的那样有两个轨道(一个用于音频,一个用于视频)。 但是在exportDidFinish中检查相同文件 URL 的轨道数显示 0 个轨道。 所以出口过程似乎有些问题。

更新:注释掉exporter.videoComposition = mutableComposition修复了错误,但当然不会对视频应用任何转换。 所以问题似乎在于创建AVMutableVideoComposition ,这会在导出过程中导致下游出现问题。 AVMutableVideoComposition文档和教程很少,因此即使您没有解决方案但可以推荐 Apple 以外的参考来源,这也会有所帮助。

错误:

错误域=AVFoundationErrorDomain 代码=-11841“操作停止” UserInfo=0x170676e80 {NSLocalizedDescription=操作停止,NSLocalizedFailureReason=视频无法合成。}

代码:

    let videoAsset = AVURLAsset(URL: fileUrl, options: nil)
    let mixComposition = AVMutableComposition()
    let videoTrack = mixComposition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID(kCMPersistentTrackID_Invalid))
    let audioTrack = mixComposition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID(kCMPersistentTrackID_Invalid))

    let sourceVideoTrack = videoAsset.tracksWithMediaType(AVMediaTypeVideo)[0] as! AVAssetTrack
    let sourceAudioTrack = videoAsset.tracksWithMediaType(AVMediaTypeAudio)[0] as! AVAssetTrack
    videoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), ofTrack: sourceVideoTrack, atTime: kCMTimeZero, error: nil)
    audioTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), ofTrack: sourceAudioTrack, atTime: kCMTimeZero, error: nil)

    // Create something mutable???
    // -- Create instruction
    let instruction = AVMutableVideoCompositionInstruction()
    instruction.timeRange = CMTimeRangeMake(kCMTimeZero, videoAsset.duration)
    let videoLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: sourceVideoTrack)
    instruction.layerInstructions = [videoLayerInstruction]

    let mutableComposition = AVMutableVideoComposition()
    //mutableComposition.renderSize = videoTrack.naturalSize
    mutableComposition.renderSize = CGSize(width: 320, height: 320)
    mutableComposition.frameDuration = CMTimeMake(1, 60)
    mutableComposition.instructions = [instruction]

    // Animate
    mutableComposition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, inLayer: parentLayer)

    // -- Get path
    let fileName = "/editedVideo-\(arc4random() % 10000).mp4"
    let allPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
    let docsPath = allPaths[0] as! NSString
    let exportPath = docsPath.stringByAppendingFormat(fileName)
    let exportUrl = NSURL.fileURLWithPath(exportPath as String)!

    println("Tracks before export: \(mixComposition.tracks.count). File URL: \(exportUrl)")

    // -- Remove old video?
    if NSFileManager.defaultManager().fileExistsAtPath(exportPath as String) {
        println("Deleting existing file\n")
        NSFileManager.defaultManager().removeItemAtPath(exportPath as String, error: nil)
    }

    // -- Create exporter
    let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)
    exporter.videoComposition = mutableComposition
    exporter.outputFileType = AVFileTypeMPEG4
    exporter.outputURL = exportUrl
    exporter.shouldOptimizeForNetworkUse = true

    // -- Export video
    exporter.exportAsynchronouslyWithCompletionHandler({
        self.exportDidFinish(exporter)
    })


func exportDidFinish(exporter: AVAssetExportSession) {
    println("Exported video with status: \(getExportStatus(exporter))")

    // Save video to photo album
    let assetLibrary = ALAssetsLibrary()
    assetLibrary.writeVideoAtPathToSavedPhotosAlbum(exporter.outputURL, completionBlock: {(url: NSURL!, error: NSError!) in
        println("Saved video to album \(exporter.outputURL)")
        if (error != nil) {
            println("Error saving video")
        }
    })

    // Check asset tracks
    let asset = AVAsset.assetWithURL(exporter.outputURL) as? AVAsset
    println("Tracks after export: \(asset!.tracks.count). File URL: \(exporter.outputURL)")
}

问题:

1)是什么导致了问题,解决方案是什么?

2)关于如何一致地重现错误的建议,希望有助于调试问题?

什么似乎是治愈是确保在assetTrack参数AVMutableVideoCompositionLayerInstruction是不是从AVURLAsset对象,但返回的视频对象addMutableTrackWithMediaType

换句话说,这一行:

let videoLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: sourceVideoTrack)

应该:

let videoLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videoTrack)

啊。 数小时无休止的挫败感,因为有时第一行有效,有时则无效。

仍然想将赏金奖励给某人。

如果您能解释为什么第一行非确定性地失败,而不是每次都失败,或者提供有关 AVMutableComposition 及其相关类的更深入的教程——为了向用户录制的视频添加文本覆盖——赏金全归你. :)

我猜你的一些视频的sourceVideoTrack是:

  • 不连续的轨道
  • 时间范围短于视频整个时间范围的曲目

另一方面,可变轨道videoTrack保证正确的时间范围(按照AVMutableVideoCompositionInstruction指示),因此它始终有效。

我通过使用AVAssetExportPresetPassthrough导出预设而不是使用特定分辨率或AVAssetExportHighestQuality解决了这个问题……

let exportSession = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetPassthrough)

这应该使用导出文件中导入视频的分辨率。

聚会迟到了,但这对我有用。 导出将“随机”失败。 那么接下来我调试视频轨道的长度和音频轨道的长度。

我注意到当音轨长于视频轨时,导出会失败。

所以我做了这个改变:

let assetVideoTrack = asset.tracks(withMediaType: .video).first!
let assetAudioTrack = asset.tracks(withMediaType: .audio).first!


var validTimeRange:CMTimeRange
if assetVideoTrack.timeRange.duration.value > assetAudioTrack.timeRange.duration.value {
    validTimeRange = assetVideoTrack.timeRange
} else {
    validTimeRange = assetAudioTrack.timeRange
}

那么我将在这里使用该值:

let instruction = AVMutableVideoCompositionInstruction()
instruction.layerInstructions = [layerInstructions]
instruction.timeRange = validTimeRange

这为我解决了这个问题。 现在 100% 的时间都有效。

导出的视频看起来不错,录制的音频听起来很棒。

问题的答案:

1)是什么导致了问题,解决方案是什么?

2)关于如何一致地重现错误的建议,希望有助于调试问题?

对我来说有以下几点:

  1. 视频和音频轨道之间的持续时间略有不同。 instruction.timeRange使用较短的时间会使导出失败。

  2. instruction.timeRange设置为两条轨道中较短的时间,导出失败。

如果您将宽度或高度设置为零可能导致操作停止时崩溃,NSLocalizedFailureReason=视频无法合成

self.mutableVideoComposition.renderSize = CGSizeMake(assetVideoTrack.naturalSize.height,assetVideoTrack.naturalSize.width);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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