ios swift avfoundation avassetexportsession

ios - La exportación de AVAssetExportSession falla de manera no determinista con el error: "Operación detenida, NSLocalizedFailureReason=El video no se pudo componer".



swift avfoundation (4)

Añadimos subtítulos a un video grabado por el usuario, pero la exportación de nuestro objeto AVAssetExportSession falla de manera no determinista: a veces funciona, y otras no. No está claro incluso cómo reproducir el error.

Notamos que las pistas de activos parecen perderse durante la exportación.

Antes de exportar, hay dos pistas (una para audio, una para video) como se esperaba. Pero al verificar el número de pistas para la misma URL del archivo en exportDidFinish muestra 0 pistas. Así que algo parece mal con el proceso de exportación.

Actualización: al comentar exporter.videoComposition = mutableComposition corrige el error, pero, por supuesto, no se aplican transformaciones al video. Por lo tanto, el problema parece estar en la creación de AVMutableVideoComposition , que causa problemas descendentes durante la exportación. La documentación y los tutoriales en AVMutableVideoComposition son escasos, por lo que incluso si no tiene una solución pero podría recomendar fuentes de referencia más allá de Apple, eso sería útil.

Error:

Error de dominio = AVFoundationErrorDomain Código = -11841 "Operación detenida" UserInfo = 0x170676e80 {NSLocalizedDescription = Operación detenida, NSLocalizedFailureReason = El video no se pudo componer.}

Código:

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

Preguntas:

1) ¿Qué está causando el problema y cuál es la solución?

2) ¿Sugerencias sobre cómo reproducir el error de forma coherente, lo que esperamos ayude a solucionar el problema?


Lo que parece ser la assetTrack es asegurarse de que el parámetro assetTrack en AVMutableVideoCompositionLayerInstruction no sea del objeto AVURLAsset , sino del objeto de video devuelto por addMutableTrackWithMediaType .

En otras palabras, esta línea:

let videoLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: sourceVideoTrack)

Debiera ser:

let videoLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videoTrack)

Argh. Horas de frustración interminable porque a veces la primera línea funcionaba y otras no.

Todavía me gustaría otorgar la recompensa a alguien.

Si puede explicar por qué la primera línea falló de manera no determinista, en lugar de hacerlo cada vez, o puede proporcionar un tutorial más profundo sobre AVMutableComposition y sus clases relacionadas, con el fin de agregar superposiciones de texto a los videos grabados por el usuario, la recompensa es suya. . :)


Si U establece el ancho o la altura a cero, se puede producir un bloqueo con la Operación detenida, NSLocalizedFailureReason = El video no se pudo componer

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


Supongo que algunos de los videos de sourceVideoTrack de tus videos son:

  • pistas que no son contiguas
  • Pistas con un rango de tiempo más corto que el rango de tiempo completo del video

Por otro lado, la pista de videoTrack mutable, pista, tiene garantizado el rango de tiempo correcto (como lo indica AVMutableVideoCompositionInstruction ) por lo que siempre funciona.


AVAssetExportPresetPassthrough este problema usando el AVAssetExportPresetPassthrough exportación AVAssetExportPresetPassthrough lugar de usar una resolución específica o AVAssetExportHighestQuality ...

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

Esto debería usar la resolución del video importado en el archivo exportado.