AVAssetExportSession videoComposition не показывает все кадры в видео

Я делаю видео из массива UIImages. Я сделал это успешно, и все изображения отображаются на видео. Я использую AVAssetExportSession для экспорта видео, которое также работает, за исключением случаев, когда я использую свойство videoComposition AVAssetExportSession, видео показывает только первое изображение. Вот мой код:

func mergeAudioVideoFiles(videoUrl:NSURL, audioUrl:NSURL)->NSURL
{
    let mixComposition : AVMutableComposition = AVMutableComposition()
    var mutableCompositionVideoTrack : [AVMutableCompositionTrack] = []
    var mutableCompositionAudioTrack : [AVMutableCompositionTrack] = []
    let totalVideoCompositionInstruction : AVMutableVideoCompositionInstruction = AVMutableVideoCompositionInstruction()


    //start merge

    let aVideoAsset : AVAsset = AVAsset(URL: videoUrl)
    let aAudioAsset : AVAsset = AVAsset(URL: audioUrl)

    mutableCompositionVideoTrack.append(mixComposition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid))
    mutableCompositionAudioTrack.append( mixComposition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid))

    let aVideoAssetTrack : AVAssetTrack = aVideoAsset.tracksWithMediaType(AVMediaTypeVideo)[0]
    let aAudioAssetTrack : AVAssetTrack = aAudioAsset.tracksWithMediaType(AVMediaTypeAudio)[0]
    do{
        try mutableCompositionVideoTrack[0].insertTimeRange(CMTimeRangeMake(kCMTimeZero, aVideoAssetTrack.timeRange.duration), ofTrack: aVideoAssetTrack, atTime: kCMTimeZero)

        try mutableCompositionAudioTrack[0].insertTimeRange(CMTimeRangeMake(kCMTimeZero, aVideoAssetTrack.timeRange.duration), ofTrack: aAudioAssetTrack, atTime: kCMTimeZero)    
    }catch{

    }
    print("\nslide duraition:\(CMTimeGetSeconds(aVideoAssetTrack.timeRange.duration))\n")
    totalVideoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero,aVideoAssetTrack.timeRange.duration )

    let mutableVideoComposition : AVMutableVideoComposition = AVMutableVideoComposition(propertiesOfAsset: aVideoAsset)
    mutableVideoComposition.frameDuration = aVideoAssetTrack.timeRange.duration
    mutableVideoComposition.renderSize = CGSizeMake(1280,720)

    //find your video on this URl
    let savePathUrl : NSURL = NSURL(fileURLWithPath: documentsPath.stringByAppendingPathComponent("pandorarofinalist.mov"))

    // 4. Add subtitles (we call it theme)
    let insertTime = kCMTimeZero
    //let endTime = aVideoAssetTrack.timeRange.duration
    //let range = self.totalFrameDuration
    //let themeVideoComposition : AVMutableVideoComposition = AVMutableVideoComposition(propertiesOfAsset: aVideoAsset)
    // 4.2 - Create an AVMutableVideoCompositionLayerInstruction for the video track and fix the orientation.

    let videolayerInstruction : AVMutableVideoCompositionLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: aVideoAssetTrack)
    totalVideoCompositionInstruction.layerInstructions = NSArray(array: [videolayerInstruction]) as! [AVVideoCompositionLayerInstruction]
    mutableVideoComposition.instructions = NSArray(array: [totalVideoCompositionInstruction]) as! [AVVideoCompositionInstructionProtocol]

    //mutableCompositionAudioTrack[0].preferredTransform
    videolayerInstruction.setTransform(mutableCompositionVideoTrack[0].preferredTransform, atTime: insertTime)
    //videolayerInstruction.setOpacity(0.0, atTime: endTime)

    // 4.3 - Add instructions


   // mutableVideoComposition.renderScale = 1.0
    //themeVideoComposition.renderSize = CGSizeMake(aVideoAssetTrack.naturalSize.width, aVideoAssetTrack.naturalSize.height)
   //themeVideoComposition.frameDuration = self.totalFrameDuration

    // add text

    let title = String("my video")

    let titleLayer = CATextLayer()
    titleLayer.string = title
    titleLayer.frame =  CGRect(x: 0, y: 0, width: aVideoAssetTrack.naturalSize.width, height: 100)
    let fontName: CFStringRef = "Helvetica-Bold"
    let fontSize = CGFloat(50)
    titleLayer.font = CTFontCreateWithName(fontName, fontSize, nil)
    titleLayer.alignmentMode = kCAAlignmentCenter
    titleLayer.foregroundColor = UIColor.orangeColor().CGColor

    let backgroundLayer = CALayer()
    backgroundLayer.frame = CGRect(x: 0, y: 0, width: aVideoAssetTrack.naturalSize.width, height: aVideoAssetTrack.naturalSize.height)
    backgroundLayer.masksToBounds = true
    backgroundLayer.addSublayer(titleLayer)

    // 2. set parent layer and video layer

    let parentLayer = CALayer()
    let videoLayer = CALayer()
    parentLayer.frame =  CGRect(x: 0, y: 0, width: aVideoAssetTrack.naturalSize.width, height: aVideoAssetTrack.naturalSize.height)
    videoLayer.frame =  CGRect(x: 0, y: 0, width: aVideoAssetTrack.naturalSize.width, height: aVideoAssetTrack.naturalSize.height)
    parentLayer.addSublayer(videoLayer)
    parentLayer.addSublayer(backgroundLayer)

    //backgroundLayer.opacity = 1.0

    // 3. make animation

    mutableVideoComposition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, inLayer: parentLayer)

    // Remove the file if it already exists (merger does not overwrite)

    do{
        let fileManager = NSFileManager.defaultManager()
        try fileManager.removeItemAtURL(savePathUrl)
    }catch{
    }

    let assetExport: AVAssetExportSession = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)!
    assetExport.outputFileType = AVFileTypeMPEG4
    assetExport.outputURL = savePathUrl
    assetExport.shouldOptimizeForNetworkUse = true
    assetExport.videoComposition = mutableVideoComposition

    assetExport.exportAsynchronouslyWithCompletionHandler { () -> Void in
        switch assetExport.status {

        case AVAssetExportSessionStatus.Completed:

            PHPhotoLibrary.sharedPhotoLibrary().performChanges({
                PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(savePathUrl)
            }) { success, error in
                if !success {
                    print("Error saving video: \(error)")
                }
            }

            //Uncomment this if u want to store your video in asset

            //let assetsLib = ALAssetsLibrary()
            //assetsLib.writeVideoAtPathToSavedPhotosAlbum(savePathUrl, completionBlock: nil)

            print("success")
        case  AVAssetExportSessionStatus.Failed:
            print("failed \(assetExport.error)")
        case AVAssetExportSessionStatus.Cancelled:
            print("cancelled \(assetExport.error)")
        default:
            print("complete")
        }
    }

    return savePathUrl
}

Проблема в том, что строка assetExport.videoComposition = mutableVideoComposition. Если я пропущу эту строку, выходное видео будет в порядке. Но если я добавлю эту строку, выходное видео покажет только первое изображение, которое я добавил для видео. Мне нужно установить videoComposition, потому что я добавляю текст заголовка к видео, которое я добавил как CALayer. Я использую swift 2.2 для своего проекта. Любая помощь, пожалуйста? Заранее спасибо.


person Linkon Sid    schedule 02.11.2016    source источник
comment
Вы нашли решение?   -  person Sam    schedule 09.11.2016
comment
Это ошибка в AVVideoCompositionCoreAnimationTool. Пожалуйста, проверьте этот пост и все связанные с ним посты stackoverflow.com/search?tab=newest&q=swift% 20AVAssetWriter   -  person Sam    schedule 09.11.2016
comment
Однако мне не удалось реализовать решение   -  person Sam    schedule 09.11.2016
comment
Нет, я не нашел решения для этого. В качестве альтернативы я сделал следующее: добавил текст заголовка с выбранными изображениями, которые генерируют новое изображение, содержащее текст, а затем с выходными изображениями было создано желаемое видео. В процессе я пропустил строку, которая вызывает проблему.   -  person Linkon Sid    schedule 09.11.2016


Ответы (1)


Я считаю, что проблема в этой строке:

mutableVideoComposition.frameDuration = aVideoAssetTrack.timeRange.duration

Предполагается, что frameDuration представляет продолжительность одного кадра в видео, а не общую продолжительность видео. Строка выше делает так, что один кадр видео длится столько же, сколько исходная видеодорожка, поэтому вы увидите только один кадр, как если бы это было неподвижное изображение.

Для видео со скоростью 30 кадров в секунду вы должны установить для параметра frameDuration значение 1/30 секунды, например:

mutableVideoComposition.frameDuration = CMTime(value: 1, timescale: 30)


Предупреждение: Будьте осторожны, чтобы не использовать другой метод инициализации CMTime(seconds: 1.0, preferredTimescale: 30), потому что это сделает ваш frameDuration равным 1 секунде.

person richkzad    schedule 11.10.2017