Eksport AVAssetExportSession nie powiódł się bez deterministycznie z błędem: "operacja zatrzymana, NSLocalizedFailureReason=wideo nie może być skomponowane."

Dodajemy napisy do filmu nagranego przez użytkownika, ale eksport przez nasz obiekt AVAssetExportSession zawodzi niedeterministycznie: czasami działa, a czasami nie. nie wiadomo nawet, jak odtworzyć błąd.

Zauważyliśmy, że ślady aktywów wydają się gubić podczas eksportu.

Przed eksportem, są dwie ścieżki (jedna dla audio, jedna dla wideo) zgodnie z oczekiwaniami. Ale sprawdzanie liczby ścieżek dla tego samego adresu URL pliku w exportDidFinish pokazuje 0 ścieżek. Więc coś wydaje się nie tak z procesem eksportu.

Aktualizacja: Komentowanie exporter.videoComposition = mutableComposition naprawia błąd, ale oczywiście do filmu nie są stosowane żadne transformacje. Problem polega więc na stworzeniu AVMutableVideoComposition, co powoduje problemy w dalszej kolejności podczas eksportu. Dokumentacja i samouczki na temat AVMutableVideoComposition są rzadkie, więc nawet jeśli nie masz rozwiązania, ale mógłbyś polecić źródła odniesienia poza Apple, byłoby to pomocne.

Błąd:

Error Domain=AVFoundationErrorDomain Code = -11841 " Operacja Zatrzymana" UserInfo=0x170676e80 {NSLocalizedDescription=operacja zatrzymana, NSLocalizedFailureReason=film nie może być skomponowany.}

Kod:

    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)")
}

Pytania:

1) co jest przyczyną problemu, a jakie jest rozwiązanie?

2) sugestie, jak odtworzyć błąd konsekwentnie, co miejmy nadzieję pomaga debugować problem?

Author: Crashalot, 2015-06-27

5 answers

To, co wydaje się być lekarstwem, to upewnienie się, że parametr assetTrack w AVMutableVideoCompositionLayerInstruction nie pochodzi z obiektu AVURLAsset, ale z obiektu wideo zwróconego przez addMutableTrackWithMediaType.

Innymi słowy, ten wiersz:

let videoLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: sourceVideoTrack)

Powinno być:

let videoLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videoTrack)

Argh. Godziny niekończącej się frustracji, ponieważ czasami pierwsza linia działała, a czasami nie.

Nadal chciałbym komuś przyznać nagrodę.

Jeśli możesz wyjaśnić, dlaczego pierwsza linia nie powiodła się bez deterministycznie, zamiast każdego czas, lub dostarczyć głębszy samouczek do AVMutableComposition i związanych z nim klas -- w celu dodawania nakładek tekstowych do filmów nagranych przez użytkownika -- nagroda jest cała Twoja. :)

 29
Author: Crashalot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-30 19:31:08

Zgaduję, że niektóre z Twoich filmów sourceVideoTrack są albo:

  • utwory nie sąsiadujące ze sobą
  • utwory z zakresem czasowym krótszym niż cały zakres czasowy wideo

Zmienna ścieżka videoTrack, z drugiej strony, ma zagwarantowany prawidłowy zakres czasu (zgodnie z instrukcją AVMutableVideoCompositionInstruction), więc zawsze działa.

 7
Author: John Estropia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-03 05:33:18

Rozwiązałem ten problem, używając presetu eksportu AVAssetExportPresetPassthrough zamiast używać określonej rozdzielczości lub AVAssetExportHighestQuality ...

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

To powinno używać rozdzielczości importowanego wideo w wyeksportowanym pliku.

 7
Author: Ashley Mills,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-28 15:17:11

Jeśli u ustawiona szerokość lub wysokość na zero może doprowadzić do awarii z Operacja zatrzymana, NSLocalizedFailureReason=film nie może być skomponowany

self.mutableVideoComposition.renderSize = CGSizeMake(assetVideoTrack.naturalSize.height,assetVideoTrack.naturalSize.width);
 0
Author: JonphyChen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-24 10:11:18

Spóźniłem się na imprezę, ale oto, co mi się udało. Eksport nie powiedzie się "losowo". Następnie debuguję długość ścieżki wideo i długość ścieżki audio.

Zauważyłem, że gdy ścieżka audio jest dłuższa niż Ścieżka wideo, eksport nie powiedzie się.

Więc dokonałem tej zmiany:

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
}

Więc użyłbym tej wartości tutaj:

let instruction = AVMutableVideoCompositionInstruction()
instruction.layerInstructions = [layerInstructions]
instruction.timeRange = validTimeRange
To rozwiązało problem. Działa w 100% przypadków.

Wyeksportowane wideo wygląda dobrze i nagrane dźwięk z nim brzmi świetnie.

Odpowiedź na pytania:

1) co jest przyczyną problemu, a jakie jest rozwiązanie?

2) sugestie, jak odtworzyć błąd konsekwentnie, co miejmy nadzieję pomaga debugować problem?

Dla mnie są następujące:

  1. Nieco inny czas trwania między ścieżkami wideo i audio. Użycie krótszego czasu w instruction.timeRange zawiedzie eksport.

  2. Ustaw instruction.timeRange na krótszy czas dwóch ścieżek i eksport nie powiódł się.

 0
Author: zumzum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-16 23:15:49