Mac Catalyst导出无Core Animation输出的框架问题求助
Mac Catalyst下AVVideoCompositionCoreAnimationTool导出无Core Animation输出问题
问题概述
仅在Mac Catalyst平台存在视频导出会话中无Core Animation输出的问题,iOS与iPadOS平台表现正常,疑似由AVVideoCompositionCoreAnimationTool导致。
可复现代码
/// Exports a video with the target animating. func exportVideo() { let destinationURL = createExportFileURL(from: Date()) guard let videoURL = Bundle.main.url(forResource: "black_video", withExtension: "mp4") else { delegate?.exporterDidFailExporting(exporter: self) print("Can't find video") return } // Initialize the video asset let asset = AVURLAsset(url: videoURL, options: [AVURLAssetPreferPreciseDurationAndTimingKey: true]) guard let assetVideoTrack: AVAssetTrack = asset.tracks(withMediaType: AVMediaType.video).first, let assetAudioTrack: AVAssetTrack = asset.tracks(withMediaType: AVMediaType.audio).first else { return } let composition = AVMutableComposition() guard let videoCompTrack = composition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)), let audioCompTrack = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else { return } videoCompTrack.preferredTransform = assetVideoTrack.preferredTransform // Get the duration let videoDuration = asset.duration.seconds // Get the video rect let videoSize = assetVideoTrack.naturalSize.applying(assetVideoTrack.preferredTransform) let videoRect = CGRect(origin: .zero, size: videoSize) // Initialize the target layers and animations animationLayers = TargetView.initTargetViewAndAnimations(atPoint: CGPoint(x: videoRect.midX, y: videoRect.midY), atSecondsIntoVideo: 2, videoRect: videoRect) // Set the playback speed let duration = CMTime(seconds: videoDuration, preferredTimescale: CMTimeScale(600)) let appliedRange = CMTimeRange(start: .zero, end: duration) videoCompTrack.scaleTimeRange(appliedRange, toDuration: duration) audioCompTrack.scaleTimeRange(appliedRange, toDuration: duration) // Create the video layer. let videolayer = CALayer() videolayer.frame = CGRect(origin: .zero, size: videoSize) // Create the parent layer. let parentlayer = CALayer() parentlayer.frame = CGRect(origin: .zero, size: videoSize) parentlayer.addSublayer(videolayer) let times = timesForEvent(startTime: 0.1, endTime: duration.seconds - 0.01) let timeRangeForCurrentSlice = times.timeRange // Insert the relevant video track segment do { try videoCompTrack.insertTimeRange(timeRangeForCurrentSlice, of: assetVideoTrack, at: .zero) try audioCompTrack.insertTimeRange(timeRangeForCurrentSlice, of: assetAudioTrack, at: .zero) } catch let compError { print("TrimVideo: error during composition: \(compError)") delegate?.exporterDidFailExporting(exporter: self) return } // Add all the non-nil animation layers to be exported. for layer in animationLayers.compactMap({ $0 }) { parentlayer.addSublayer(layer) } // Configure the layer composition. let layerComposition = AVMutableVideoComposition() layerComposition.frameDuration = CMTimeMake(value: 1, timescale: 30) layerComposition.renderSize = videoSize layerComposition.animationTool = AVVideoCompositionCoreAnimationTool( postProcessingAsVideoLayer: videolayer, in: parentlayer) let instructions = initVideoCompositionInstructions( videoCompositionTrack: videoCompTrack, assetVideoTrack: assetVideoTrack) layerComposition.instructions = instructions // Creates the export session and exports the video asynchronously. guard let exportSession = initExportSession( composition: composition, destinationURL: destinationURL, layerComposition: layerComposition) else { delegate?.exporterDidFailExporting(exporter: self) return } // Execute the exporting exportSession.exportAsynchronously(completionHandler: { if let error = exportSession.error { print("Export error: \(error), \(error.localizedDescription)") } self.delegate?.exporterDidFinishExporting(exporter: self, with: destinationURL) }) }
动画实现代码
class AnimationCreator: NSObject { // MARK: - Target Animations /// Creates the target animations. static func addAnimationsToTargetView(_ targetView: TargetView, startTime: Double) { // Add the appearance animation AnimationCreator.addAppearanceAnimation(on: targetView, defaultBeginTime: AVCoreAnimationBeginTimeAtZero, startTime: startTime) // Add the pulse animation. AnimationCreator.addTargetPulseAnimation(on: targetView, defaultBeginTime: AVCoreAnimationBeginTimeAtZero, startTime: startTime) // Add the rotation animation AnimationCreator.addRotationAnimation(to: targetView, beginTime: AVCoreAnimationBeginTimeAtZero, startTime: startTime) } /// Adds the appearance animation to the target private static func addAppearanceAnimation(on targetView: TargetView, defaultBeginTime: Double = 0, startTime: Double = 0) { // Starts the target transparent and then turns it opaque at the specified time targetView.targetImageView.layer.opacity = 0 let appear = CABasicAnimation(keyPath: "opacity") appear.duration = .greatestFiniteMagnitude // stay on screen forever appear.fromValue = 1.0 // Opaque appear.toValue = 1.0 // Opaque appear.beginTime = defaultBeginTime + startTime targetView.targetImageView.layer.add(appear, forKey: "appear") } /// Adds a pulsing animation to the target. private static func addTargetPulseAnimation(on targetView: TargetView, defaultBeginTime: Double = 0, startTime: Double = 0) { let targetPulse = CABasicAnimation(keyPath: "transform.scale") targetPulse.fromValue = 1 // Regular size targetPulse.toValue = 1.1 // Slightly larger size targetPulse.duration = 0.4 targetPulse.beginTime = defaultBeginTime + startTime targetPulse.autoreverses = true targetPulse.repeatCount = .greatestFiniteMagnitude targetView.targetImageView.layer.add(targetPulse, forKey: "pulse_animation") } /// Adds a spinning animation to the target. private static func addRotationAnimation(to targetView: TargetView, beginTime: Double = 0, startTime: Double = 0) { let rotation: CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z") rotation.toValue = Double.pi * 2 // rotate in a complete circle rotation.duration = 1.0 rotation.isCumulative = true rotation.repeatCount = .greatestFiniteMagnitude rotation.beginTime = beginTime targetView.targetImageView.layer.add(rotation, forKey: "rotation_animation") } }
已尝试的解决方案
将分数变化的CABasicAnimation替换为CAKeyframeAnimation,但Mac Catalyst导出的视频中仍无动画显示,且已注释其他CABasicAnimation实例:
原代码
/// Creates the score change animation for a new point. func initScoreChangeStartAnimation(scoreViewLayer: CALayer, startTime: Double, duration: Double) { let scoreAnimation = CABasicAnimation(keyPath: "opacity") scoreAnimation.duration = duration - VideoHighlightReelOverlayUIConstants.pointChangeAnimationDuration scoreAnimation.fromValue = 1 scoreAnimation.toValue = 1 scoreAnimation.beginTime = startTime // Add the animation to the score view layer. scoreViewLayer.add(scoreAnimation, forKey: "scoreChangeStart") } /// Creates the score change animation for the score of the next point. func initScoreChangeEndAnimation(scoreViewLayer: CALayer, startTime: Double, duration: Double) { let scoreAnimation = CABasicAnimation(keyPath: "opacity") scoreAnimation.duration = VideoHighlightReelOverlayUIConstants.pointChangeAnimationDuration scoreAnimation.fromValue = 1 scoreAnimation.toValue = 1 scoreAnimation.beginTime = startTime + (duration - VideoHighlightReelOverlayUIConstants.pointChangeAnimationDuration) // Add the animation to the score view layer. scoreViewLayer.add(scoreAnimation, forKey: "scoreChangeEnd") }
修改后的代码
/// Creates the score change animation for a new point. func initScoreChangeStartAnimation(scoreViewLayer: CALayer, startTime: Double, duration: Double) { let scoreAnimation = CAKeyframeAnimation(keyPath: "opacity") let mainDuration = duration - VideoHighlightReelOverlayUIConstants.pointChangeAnimationDuration scoreAnimation.values = [1.0, 1.0] scoreAnimation.keyTimes = [0, NSNumber(value: mainDuration/duration)] scoreAnimation.duration = duration scoreAnimation.beginTime = startTime // Add the animation to the score view layer. scoreViewLayer.add(scoreAnimation, forKey: "scoreChangeStart") } /// Creates the score change animation for the score of the next point. func initScoreChangeEndAnimation(scoreViewLayer: CALayer, startTime: Double, duration: Double) { let scoreAnimation = CAKeyframeAnimation(keyPath: "opacity") let mainDuration = VideoHighlightReelOverlayUIConstants.pointChangeAnimationDuration scoreAnimation.values = [1.0, 1.0] scoreAnimation.keyTimes = [NSNumber(value: (duration - mainDuration)/duration), 1] scoreAnimation.duration = duration scoreAnimation.beginTime = startTime // Add the animation to the score view layer. scoreViewLayer.add(scoreAnimation, forKey: "scoreChangeEnd") }
请求协助
请提供该问题的解决办法,或指导如何实现对应的自定义合成器以完成动画导出。
内容的提问来源于stack exchange,提问作者Curiosity
相关产品推荐
相关产品推荐

