swift – 无法使用AVVideoCompositionCoreAnimationTool在视频中显示动画CALayer

swift – 无法使用AVVideoCompositionCoreAnimationTool在视频中显示动画CALayer,第1张

概述更新6: 我已经设法完全解决了我的问题,但我仍然想要一个更好的解释,而不是我猜的是如果我不正确它是不起作用的原因 我一直试图在视频上制作精灵表,但每次导出视频时,最终结果都是我开始的示例视频. 这是我的代码: 首先我的自定义CALayer来处理我自己的精灵表 class SpriteLayer: CALayer { var frameIndex: Int override ini 更新6:
我已经设法完全解决了我的问题,但我仍然想要一个更好的解释,而不是我猜的是如果我不正确它是不起作用的原因

我一直试图在视频上制作精灵表,但每次导出视频时,最终结果都是我开始的示例视频.

这是我的代码:

首先我的自定义CALayer来处理我自己的精灵表

class SpriteLayer: CALayer {    var frameIndex: Int    overrIDe init() {        // Using 0 as a default state        self.frameIndex = 0        super.init()    }    required init?(coder aDecoder: NSCoder) {        self.frameIndex = 0        super.init(coder: aDecoder)    }    overrIDe func display() {        let currentFrameIndex = self.frameIndex        if currentFrameIndex == 0 {            return        }        let frameSize = self.contentsRect.size        self.contentsRect = CGRect(x: 0,y: CGfloat(currentFrameIndex - 1) * frameSize.height,wIDth: frameSize.wIDth,height: frameSize.height)    }    overrIDe func action(forKey event: String) -> CAAction? {        if event == "contentsRect" {            return nil        }        return super.action(forKey: event)    }    overrIDe class func needsdisplay(forKey key: String) -> Bool {        return key == "frameIndex"    }}

Gif是一个没有花哨的基本课程,工作得很好. gif.Strip是表示gif的垂直精灵表的UIImage.

现在出现了应该导出新视频的方法(它是用于导出的更大类的一部分.

func convertAndExport(to url :URL,completion: @escaPing () -> VoID ) {        // Get Initial info and make sure our destination is available        self.outputURL = url        let stripCgImage = self.gif.strip!.cgImage!        // This is used to time how long the export took        let start = dispatchTime.Now()        do {            try fileManager.default.removeItem(at: outputURL)        } catch {            print("Remove Error: \(error.localizedDescription)")            print(error)        }        // Find and load "sample.mp4" as a AVAsset        let vIDeoPath = Bundle.main.path(forResource: "sample",ofType: "mp4")!        let vIDeoUrl = URL(fileURLWithPath: vIDeoPath)        let vIDeoAsset = AVAsset(url: vIDeoUrl)        // Start a new mutable Composition with the same base vIDeo track        let mixComposition = AVMutableComposition()        let compositionVIDeoTrack = mixComposition.addMutableTrack(withMediaType: .vIDeo,preferredTrackID: kCMPersistentTrackID_InvalID)!        let clipVIDeoTrack = vIDeoAsset.tracks(withMediaType: .vIDeo).first!        do {            try compositionVIDeoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero,vIDeoAsset.duration),of: clipVIDeoTrack,at: kCMTimeZero)        } catch {            print("Insert Error: \(error.localizedDescription)")            print(error)            return        }        compositionVIDeoTrack.preferredtransform = clipVIDeoTrack.preferredtransform        // Quick access to the vIDeo size        let vIDeoSize = clipVIDeoTrack.naturalSize        // Setup CALayer and it's animation        let aLayer = SpriteLayer()        aLayer.contents = stripCgImage        aLayer.frame = CGRect(x: 0,y: 0,wIDth: vIDeoSize.wIDth,height: vIDeoSize.height)        aLayer.opacity = 1.0        aLayer.masksToBounds = true        aLayer.bounds = CGRect(x: 0,height: vIDeoSize.height)        aLayer.contentsRect = CGRect(x: 0,wIDth: 1,height: 1.0 / 3.0)        let spriteAnimation = CABasicAnimation(keyPath: "frameIndex")        spriteAnimation.fromValue = 1        spriteAnimation.tovalue = 4        spriteAnimation.duration = 2.25        spriteAnimation.repeatCount = .infinity        spriteAnimation.autoreverses = false        spriteAnimation.beginTime = AVCoreAnimationBeginTimeatZero        aLayer.add(spriteAnimation,forKey: nil)        // Setup Layers for AVVIDeoCompositionCoreAnimationTool        let parentLayer = CALayer()        let vIDeolayer = CALayer()        parentLayer.frame = CGRect(x: 0,height: vIDeoSize.height)        vIDeolayer.frame = CGRect(x: 0,height: vIDeoSize.height)        parentLayer.addSublayer(vIDeolayer)        parentLayer.addSublayer(aLayer)        // Create the mutable vIDeo composition        let vIDeoComp = AVMutableVIDeoComposition()        vIDeoComp.renderSize = vIDeoSize        vIDeoComp.frameDuration = CMTimeMake(1,30)        vIDeoComp.animationTool = AVVIDeoCompositionCoreAnimationTool(postProcessingAsVIDeolayer: vIDeolayer,in: parentLayer)        // Set the vIDeo composition to apply to the composition's vIDeo track        let instruction = AVMutableVIDeoCompositionInstruction()        instruction.timeRange = CMTimeRangeMake(kCMTimeZero,mixComposition.duration)        let vIDeoTrack = mixComposition.tracks(withMediaType: .vIDeo).first!        let layerInstruction = AVMutableVIDeoCompositionLayerInstruction(assetTrack: vIDeoTrack)        instruction.layerInstructions = [layerInstruction]        vIDeoComp.instructions = [instruction]        // Initialize export session        let assetExport = AVAssetExportSession(asset: mixComposition,presetname: AVAssetExportPresetPassthrough)!        assetExport.vIDeoComposition = vIDeoComp        assetExport.outputfileType = AVfileType.mp4        assetExport.outputURL = self.outputURL        assetExport.shouldOptimizeforNetworkUse = true        // Export        assetExport.exportAsynchronously {            let status = assetExport.status            switch status {            case .Failed:                print("Export Failed")                print("Export Error: \(assetExport.error!.localizedDescription)")                print(assetExport.error!)            case .unkNown:                print("Export UnkNown")            case .exporting:                print("Export Exporting")            case .waiting:                print("Export Waiting")            case .cancelled:                print("Export Cancelled")            case .completed:                let end = dispatchTime.Now()                let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds                let timeInterval = Double(nanoTime) / 1_000_000_000                // Function is Now over,we can print how long it took                print("Time to generate vIDeo: \(timeInterval) seconds")                completion()            }        }}

编辑:
我将我的代码基于以下链接

> SpriteLayer and how to use it
> CABasicAnimation on a video
> Using AVVideoCompositionCoreAnimationTool and AVAssetExportSession to save the new video

更新1:
我已经尝试删除我的代码中的CABasicAnimation部分并使用我的CALayer,但无济于事.我甚至无法让图像显示出来.
为了测试一下,我尝试使用Xcode Playground中的contentsRect上的CAKeyframeAnimation动画这个精灵表,它运行正常,所以我认为问题不在于CABasicAnimation,甚至可能与CALayer本身无关.我真的可以在这方面使用一些帮助,因为我不明白为什么我甚至无法在导出的示例视频上显示图像.

更新2:
为了回应matt的评论我已经尝试忘记了一点精灵表并将其改成CATextLayer但仍然没有在我的视频上看到任何东西(它有深色图像,所以白色文字应该是完全可见的)

let aLayer = CATextLayer()aLayer.string = "This is a test"aLayer.FontSize = vIDeoSize.height / 6aLayer.alignmentMode = kCAAlignmentCenteraLayer.foregroundcolor = UIcolor.white.cgcoloraLayer.bounds = CGRect(x: 0,height: vIDeoSize.height / 6)

更新3:
根据Matt的要求,我尝试将parentLayer.addSublayer(aLayer)更改为vIDeolayer.addSublayer(aLayer),但仍然没有改变,但我想尽可能多,因为AVVIDeoCompositionCoreAnimationTool的documentation如下

convenIEnce init(postProcessingAsVIDeolayer vIDeolayer: CALayer,in animationLayer: CALayer)

意思是我的parentLayer是它的animationLayer,可能意味着任何动画应该在这一层完成.

更新4:
我开始疯狂了,我现在已经放弃了显示文字或动画图像的想法,我想以任何可能的方式影响我的视频,所以我将aLayer更改为:

let aLayer = CALayer()aLayer.frame = CGRect(x: 0,height: vIDeoSize.height)aLayer.backgroundcolor = UIcolor.white.cgcolor

好吧,这绝对没有,我仍然在我的outputUrl上获取我的示例视频(如果你想“玩”,我开始在 *** 场上用以下代码测试它)

import PlaygroundSupportimport UIKitimport Foundationimport AVFoundationfunc convertAndExport(to url :URL,completion: @escaPing () -> VoID ) {    let start = dispatchTime.Now()    do {        try fileManager.default.removeItem(at: url)    } catch {        print("Remove Error: \(error.localizedDescription)")        print(error)    }    let vIDeoPath = Bundle.main.path(forResource: "sample",ofType: "mp4")!    let vIDeoUrl = URL(fileURLWithPath: vIDeoPath)    let vIDeoAsset = AVURLAsset(url: vIDeoUrl)    let mixComposition = AVMutableComposition()    let compositionVIDeoTrack = mixComposition.addMutableTrack(withMediaType: .vIDeo,preferredTrackID: kCMPersistentTrackID_InvalID)!    let clipVIDeoTrack = vIDeoAsset.tracks(withMediaType: .vIDeo).first!    do {        try compositionVIDeoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero,at: kCMTimeZero)    } catch {        print("Insert Error: \(error.localizedDescription)")        print(error)        return    }    compositionVIDeoTrack.preferredtransform = clipVIDeoTrack.preferredtransform    let vIDeoSize = clipVIDeoTrack.naturalSize    print("VIDeo Size Detected: \(vIDeoSize.wIDth) x \(vIDeoSize.height)")    let aLayer = CALayer()    aLayer.frame = CGRect(x: 0,height: vIDeoSize.height)    aLayer.backgroundcolor = UIcolor.white.cgcolor    let parentLayer = CALayer()    let vIDeolayer = CALayer()    parentLayer.frame = CGRect(x: 0,height: vIDeoSize.height)    vIDeolayer.frame = CGRect(x: 0,height: vIDeoSize.height)    parentLayer.addSublayer(vIDeolayer)    parentLayer.addSublayer(aLayer)    aLayer.setNeedsdisplay()    let vIDeoComp = AVMutableVIDeoComposition()    vIDeoComp.renderSize = vIDeoSize    vIDeoComp.frameDuration = CMTimeMake(1,30)    vIDeoComp.animationTool = AVVIDeoCompositionCoreAnimationTool(postProcessingAsVIDeolayer: vIDeolayer,in: parentLayer)    let instruction = AVMutableVIDeoCompositionInstruction()    instruction.timeRange = CMTimeRangeMake(kCMTimeZero,mixComposition.duration)    let vIDeoTrack = mixComposition.tracks(withMediaType: .vIDeo).first!    let layerInstruction = AVMutableVIDeoCompositionLayerInstruction(assetTrack: vIDeoTrack)    instruction.layerInstructions = [layerInstruction]    vIDeoComp.instructions = [instruction]    let assetExport = AVAssetExportSession(asset: mixComposition,presetname: AVAssetExportPresetPassthrough)!    assetExport.vIDeoComposition = vIDeoComp    assetExport.outputfileType = AVfileType.mp4    assetExport.outputURL = url    assetExport.shouldOptimizeforNetworkUse = true    assetExport.exportAsynchronously {        let status = assetExport.status        switch status {        case .Failed:            print("Export Failed")            print("Export Error: \(assetExport.error!.localizedDescription)")            print(assetExport.error!)        case .unkNown:            print("Export UnkNown")        case .exporting:            print("Export Exporting")        case .waiting:            print("Export Waiting")        case .cancelled:            print("Export Cancelled")        case .completed:            let end = dispatchTime.Now()            let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds            let timeInterval = Double(nanoTime) / 1_000_000_000            print("Time to generate vIDeo: \(timeInterval) seconds")            completion()        }    }}let outputUrl = fileManager.default.temporaryDirectory.appendingPathComponent("test.mp4")convertAndExport(to: outputUrl) {    print(outputUrl)}

请有人帮我理解我做错了什么……

更新5:
我正在运行iPad Air 2以外的所有游乐场测试(所以没有模拟器),因为我使用相机拍照,然后将它们拼接成精灵表,然后我计划通过电子邮件发送动画.我开始做Playground测试,因为来自iPad的每个测试都要求我经历整个应用程序周期(倒计时,照片,表单,电子邮件发送/接收)

解决方法 好的,终于让它按照我一直想要的方式工作.

首先,即使他删除了他的评论,感谢Matt链接到一个工作示例,帮助我将我的代码错误拼凑在一起.

>首先

let assetExport = AVAssetExportSession(asset: mixComposition,presetname: AVAssetExportPresetPassthrough)!

我需要使用AVAssetExportPresetHighestQuality而不是AVAssetExportPresetPassthrough.我的猜测是,直通预设意味着你不进行任何重新编码,因此将其设置为最高(因为我导出的视频超过400×400而不是中等)使得我可以实际重新编码我的视频.我猜这是阻止导出的视频包含我尝试的任何CALayer(甚至用白色覆盖视频)的原因.

>其次(不确定这是否真的影响但我会稍后再试)

parentLayer.addSublayer(aLayer)

我将其替换为:

vIDeolayer.addSublayer(aLayer)

不确定这是否真的很重要,但我的理解是,这实际上是AVVIDeoCompositionCoreAnimationTool的动画层,而parentLayer只是一个容器,并不意味着包含更多,但我可能错了.

>我做了第三次改变

let spriteAnimation = CABasicAnimation(keyPath: "frameIndex")spriteAnimation.fromValue = 1spriteAnimation.tovalue = 4spriteAnimation.duration = 2.25spriteAnimation.repeatCount = .infinityspriteAnimation.autoreverses = falsespriteAnimation.beginTime = AVCoreAnimationBeginTimeatZeroaLayer.add(spriteAnimation,forKey: nil)

我改成了这个:

let animation = CAKeyframeAnimation(keyPath: #keyPath(CALayer.contentsRect))animation.duration = 2.25animation.calculationMode = kCAAnimationdiscreteanimation.repeatCount = .infinityanimation.values = [    CGRect(x: 0,height: 1/3.0),CGRect(x: 0,y: 1/3.0,y: 2/3.0,height: 1/3.0)    ] as [CGRect]animation.beginTime = AVCoreAnimationBeginTimeatZeroanimation.fillMode = kCAFillModeBackwardsanimation.isRemovedOnCompletion = falseaLayer.add(animation,forKey: nil)

这个改变主要是删除我的精灵表的自定义动画(因为它总是一样的,我首先想要一个工作的例子,然后我将它概括,并可能将它添加到我的私人UI Pod).但最重要的是animation.isRemovedOnCompletion = false我注意到删除它会使得动画根本无法在导出的视频上播放.因此,对于CABasicAnimation导出后未对视频进行动画处理的任何人,请尝试查看您的动画上是否正确设置了isRemovedOnCompletion.

我认为这几乎都是我所做的改变.

虽然我在技术上回答了我的问题,但我的理解仍然是了解AVVIDeoCompositionCoreAnimationTool和AVAssetExport是如何工作的,以及为什么我必须做的更改,如果有人有兴趣解释,我最终会让它工作.

再次感谢Matt,你通过向我展示你是如何做到的来帮助我的.

总结

以上是内存溢出为你收集整理的swift – 无法使用AVVideoCompositionCoreAnimationTool在视频中显示动画CALayer全部内容,希望文章能够帮你解决swift – 无法使用AVVideoCompositionCoreAnimationTool在视频中显示动画CALayer所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/1017784.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-23
下一篇 2022-05-23

发表评论

登录后才能评论

评论列表(0条)

保存