AVAssetExportSession не соблюдает инструкции по компоновке видео.

Я пытаюсь применить AVMutableVideoCompositionLayerInstruction к AVMutableComposition для видео. Проблема в том, что инструкция не соблюдается при сохранении видео с помощью AVAssetExportSession. Странно то, что та же самая композиция работает и с AVPlayer (AVPlayer соблюдает инструкцию).

Вот код:

        let path = Bundle.main.path(forResource: "flame", ofType: "mp4")
        let url = NSURL(fileURLWithPath: path!)
        let asset = AVAsset(url: url as URL)

        let mutableComposition = AVMutableComposition()

        let type = AVMediaTypeVideo
        let prefTrackID = kCMPersistentTrackID_Invalid

        let sourceVideoAssetTrack: AVAssetTrack = asset.tracks(withMediaType: type).first!
        let sourceAudioAssetTrack: AVAssetTrack = asset.tracks(withMediaType: AVMediaTypeAudio).first!

        let videoCompositionTrack1 = mutableComposition.addMutableTrack(withMediaType: type, preferredTrackID: prefTrackID)


        do {
            let range = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(60,600))
            try videoCompositionTrack1.insertTimeRange(range, of: sourceVideoAssetTrack, at: kCMTimeZero)
        }catch { print(error) }

        let firstTransform = videoCompositionTrack1.preferredTransform;

        let fromLayer = AVMutableVideoCompositionLayerInstruction(assetTrack: videoCompositionTrack1)
        fromLayer.setTransform(firstTransform, at: kCMTimeZero)
        fromLayer.setCropRectangle(CGRect.init(x: 5, y: 5, width: 200, height: 200), at: kCMTimeZero)

        let instruction = AVMutableVideoCompositionInstruction()
        instruction.layerInstructions = [fromLayer]
        instruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(60,600))

        videoComposition = AVMutableVideoComposition()
        videoComposition!.instructions = [instruction]
        videoComposition!.renderSize = CGSize.init(width: 300, height: 300)
        videoComposition!.frameDuration = CMTimeMake(1, 30)


        if(true){ // just to switch between the saving and playing modes
            var exportPath: NSString = NSTemporaryDirectory().appendingFormat("/video.mov")
            var exportUrl: NSURL = NSURL.fileURL(withPath: exportPath as String) as NSURL

            var exporter = AVAssetExportSession(asset: mutableComposition, presetName: AVAssetExportPresetMediumQuality)!
            exporter.outputURL = exportUrl as URL
            exporter.videoComposition = videoComposition!

            exporter.outputFileType = AVFileTypeMPEG4
            exporter.shouldOptimizeForNetworkUse = true
            exporter.canPerformMultiplePassesOverSourceMediaData = true

            exporter.exportAsynchronously(completionHandler: {
                PHPhotoLibrary.shared().performChanges({
                    PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: exportUrl as URL)
                }) { completed, error in
                    if completed {
                        print("Video is saved!")
                    }
                }
            })
        }
        else{
            let playerItem = AVPlayerItem(asset: mutableComposition)
            playerItem.videoComposition = videoComposition!
            player = AVPlayer(playerItem: playerItem)
            playerLayer = AVPlayerLayer(player: player)
            playerLayer.frame = self.view.frame
            self.view.layer.addSublayer(playerLayer)
            player.play()
        }

AVPlayer выполняет инструкцию урожая прямоугольника

AVPlayer соблюдает инструкцию cropRectangle, как показано выше.

Сохраненное видео совпадает с исходным видео

Сохраненное видео такое же, как исходное видео.

Я строю это на iOS 9. Что я делаю неправильно?


person karyboy    schedule 28.04.2017    source источник
comment
может быть так, что AVPlayer будет вращать носитель на основе метаданных, а затем применять ваши инструкции, экспортер этого не делает, вам может потребоваться оценить его преобразование и соответствующим образом настроить   -  person Sean Lintern    schedule 28.04.2017
comment
Спасибо за ваш комментарий, Шон. Я не уверен, какие метаданные вы имеете в виду здесь? Мне жаль, что я нуб в AVFoundation. Вы имеете в виду метаданные AVAsset?   -  person karyboy    schedule 28.04.2017
comment
В AVAsset есть преобразования, которые сообщают игроку о его повороте, когда вы используете экспортер, вам нужно получить и выполнить эти преобразования самостоятельно.   -  person Sean Lintern    schedule 29.04.2017
comment
Но разве AVMutableVideoCompositionLayerInstruction не должен выполнять преобразования, avplayer или экспортер? И это применяется в обоих случаях. Также я не вращаю актив, а просто обрезаю.   -  person karyboy    schedule 29.04.2017
comment
Да, но вы должны сказать это, если хотите, чтобы это что-то изменило   -  person Sean Lintern    schedule 29.04.2017
comment
Так что это похоже на флаг, который нужно установить на экспортере, который говорит ему соблюдать инструкции слоя? Как сообщить об этом экспортеру? Есть ли пример, на который вы можете мне указать? Я в значительной степени следовал способу Apple экспортировать его разработчику .apple.com/library/content/documentation/AudioVideo/   -  person karyboy    schedule 29.04.2017
comment
Нет, это в инструкции AVMutableVideoCompositionLayerInstruction.   -  person Sean Lintern    schedule 30.04.2017
comment
не похоже, что в AVMutableVideoCompositionLayerInstruction есть что-то, что могло бы здесь помочь. developer.apple.com/reference/avfoundation/   -  person karyboy    schedule 30.04.2017
comment
setTransofrm: ....как будто ты даже не пытался...   -  person Sean Lintern    schedule 30.04.2017
comment
Итак, вы говорите, что setTransform на AVMutableVideoCompositionLayerInstruction будет работать, но не setCropRectangle в случае экспортера? Так что это означает, что это ошибка на стороне Apple, потому что оба они являются частью AVMutableVideoCompositionLayerInstruction. Я пробовал fromLayer.setTransform(CGAffineTransform.init().rotated(by: 0.785), at: kCMTimeZero), и это тоже не соблюдается.   -  person karyboy    schedule 01.05.2017
comment
нет, обрезка для обрезки, преобразование для... преобразования (вращение, перемещение, размер). Если вы посмотрите на AVAsset, у него есть предпочтительный вариант Transform, насколько я помню, вы используете fromLayer.setTransform(asset.preferredTransform)   -  person Sean Lintern    schedule 01.05.2017
comment
Как я упоминал в своем последнем комментарии, setTransform также не соблюдается. Кроме того, мы все еще не на одной странице после стольких возвратов и встреч, и если вы думаете, что можете заставить это работать на вашей стороне, сделайте это и добавьте это в качестве ответа. На это есть награда, это будет все твое.   -  person karyboy    schedule 02.05.2017
comment
исходное видео выглядит 16: 9, так почему вы пытаетесь его обрезать, кадрирование уменьшает размер видимой области, все, что вам нужно сделать, это применить преобразование, а renderSize должен быть выходным размером экрана ... если вы дадите мне ссылка на видео сделаю в обед через несколько часов   -  person Sean Lintern    schedule 02.05.2017
comment
Я нашел проблему. Именно с этой строкой `NSTemporaryDirectory().appendingFormat(/video.mov)`. Похоже, что exporter.outputUrl не переопределяет, если файл уже существует по адресу outputUrl. Если я изменю video.mov на что-то другое, я смогу увидеть инструкции слоя, экспортируемые в видео. Все инструкции работают, включая setCropRectangle и преобразования. Так что все это время я смотрел свое первое экспортированное видео, которое, вероятно, не содержало никаких инструкций.   -  person karyboy    schedule 02.05.2017
comment
Ха, я вижу, вы должны были увидеть ошибку, говорящую о том, что она не может быть завершена ?, спасибо, она полностью отсортирована, нужно больше просто ответить здесь, я думаю   -  person Sean Lintern    schedule 02.05.2017
comment
Да, статус экспортера содержит ошибку. Виноват. В любом случае, спасибо за ваши усилия, чтобы подумать об этом со мной. Если хотите, напишите ответ здесь и получите награду. Я не могу вернуть его, и если никто не ответит, он исчезнет.   -  person karyboy    schedule 02.05.2017
comment
Сделал :D спасибо   -  person Sean Lintern    schedule 02.05.2017


Ответы (1)


Применяя преобразование к AVMutableVideoCompositionLayerInstruction, вы можете получить желаемое преобразование из preferredTransform AVAsset.

РЕДАКТИРОВАТЬ: оказалось, что это была ошибка экспорта для существующего файла, либо используйте уникальное имя при попытке написать, например.

String(Date) + ".mov"

Или удалить перед попыткой написать

person Sean Lintern    schedule 02.05.2017
comment
Это неправильный ответ. Можете ли вы добавить правильный ответ, который решил это? В противном случае люди будут отрицать этот ответ в будущем. - person karyboy; 03.05.2017