web-dev-qa-db-ja.com

AVAssetExportSessionのエクスポートは、「操作が停止しました、NSLocalizedFailureReason =ビデオを作成できませんでした」というエラーで非決定的に失敗します。

ユーザーが録画したビデオに字幕を追加しますが、AVAssetExportSessionオブジェクトによるエクスポートは非​​決定論的に失敗します。機能する場合と機能しない場合があります。エラーを再現する方法すら不明です。

エクスポート中にアセットトラックが失われたように見えることに気づきました。

エクスポートする前に、予想どおり2つのトラック(1つはオーディオ用、もう1つはビデオ用)があります。ただし、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)エラーを一貫して再現する方法に関する提案。これは、問題のデバッグに役立つことを願っていますか?

19
Crashalot

解決策のように見えるのは、assetTrackAVMutableVideoCompositionLayerInstructionパラメータがAVURLAssetオブジェクトからではなく、addMutableTrackWithMediaTypeによって返されるビデオオブジェクトからのものであることを確認することです。

言い換えれば、この行:

let videoLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: sourceVideoTrack)

する必要があります:

let videoLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videoTrack)

ああ。最初の行が機能することもあれば、機能しないこともあるため、何時間にもわたる欲求不満。

それでも誰かに賞金を授与したいと思います。

最初の行が毎回ではなく非決定論的に失敗した理由を説明できる場合、またはユーザーが録画したビデオにテキストオーバーレイを追加する目的で、AVMutableCompositionとその関連クラスのより深いチュートリアルを提供できる場合は、その恩恵はすべてあなたのものです。 :)

19
Crashalot

あなたの動画のsourceVideoTracksのいくつかは次のいずれかだと思います。

  • 隣接していないトラック
  • ビデオの全時間範囲よりも短い時間範囲のトラック

一方、可変トラックvideoTrackは、(AVMutableVideoCompositionInstructionの指示に従って)正しい時間範囲が保証されているため、常に機能します。

6
John Estropia

特定の解像度やAVAssetExportPresetPassthrough…を使用するのではなく、AVAssetExportHighestQualityエクスポートプリセットを使用してこの問題を解決しました。

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

これは、エクスポートされたファイルにインポートされたビデオの解像度を使用する必要があります。

3
Ashley Mills

uが幅または高さをゼロに設定すると、クラッシュが発生する可能性があります操作が停止しました、NSLocalizedFailureReason =ビデオを作成できませんでした

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