Aggiungiamo i sottotitoli a un video registrato dall'utente, ma l'esportazione dal nostro oggetto AVAssetExportSession non riesce in maniera non deterministica: a volte funziona e talvolta no. Non è ancora chiaro come riprodurre l'errore.L'esportazione di AVAssetExportSession non riesce in modo non deterministico con errore: "Operazione interrotta, NSLocalizedFailureReason = Il video non può essere composto."
Abbiamo notato che le tracce delle risorse sembrano andare perse durante l'esportazione.
Prima dell'esportazione, ci sono due tracce (una per audio, una per video) come previsto. Tuttavia, il controllo del numero di tracce per lo stesso URL file in exportDidFinish
mostra 0 tracce. Quindi qualcosa sembra sbagliato con il processo di esportazione.
Aggiornamento: Commentando exporter.videoComposition = mutableComposition
si corregge l'errore, ma ovviamente nessuna trasformazione viene applicata al video. Quindi il problema sembra essere nella creazione di AVMutableVideoComposition
, che causa problemi a valle durante l'esportazione. La documentazione e le esercitazioni su AVMutableVideoComposition
sono sparse, quindi anche se non si dispone di una soluzione, ma si potrebbero consigliare fonti di riferimento oltre a Apple, sarebbe utile.
Errore:
Error Domain=AVFoundationErrorDomain Code=-11841 "Operation Stopped" UserInfo=0x170676e80 {NSLocalizedDescription=Operation Stopped, NSLocalizedFailureReason=The video could not be composed.}
Codice:
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)")
}
Domande:
1) Che cosa sta causando il problema, e che cosa è la soluzione?
2) Suggerimenti su come riprodurre l'errore in modo coerente, che sperabilmente aiuta a risolvere il problema?
Considerare di aggiungere la gestione degli errori alle chiamate 'insertTimeRange' e alle chiamate NSFileManager; impostare un errore restituito a 'nil' non è mai una buona idea, poiché se qualcosa va storto, non ne sentirai più. Personalmente, non mi preoccuperei nemmeno di lavorare su questo codice fino a che non sia stato fatto: chiunque getta allegramente il controllo degli errori è semplicemente ostinato. Quindi, considera di aggiungere più logging al processo in modo da sapere se tutto è andato bene al punto in cui inizia l'esportazione. - Inoltre mi chiedo del tuo approccio alla randomizzazione ai nomi dei file, ma dubito che questo sia il problema .. – matt
@matt sei al 100% giusto sui messaggi di errore. Non sono sicuro, però, quale altro tipo di registrazione includere oltre a verificare che esistano più tracce: cosa consiglia? – Crashalot
@matt ha aggiunto i messaggi di errore, ma nulla viene attivato con "insertTimeRange". L'errore NSFileManager non è rilevante perché in realtà commentiamo quella riga poiché stiamo utilizzando uno schema di nomi casuali durante il test e il monitoraggio dei log per garantire che i conflitti tra i file non costituiscano un problema (ad esempio, "Eliminazione di file esistenti" non viene mai stampato). – Crashalot