AVFoundation视频变速设置短时长时实际时长不符问题求助
问题分析与修复方案
核心问题
你的代码中速度倍数的计算逻辑完全颠倒,导致短目标时长时视频被错误放慢而非加速:
- 原代码里
speedMultiplier = desiredDuration / currentDuration,当目标时长远小于原时长时,这个值小于1,会让视频变慢,最终导出的视频显示时长和实际播放时长严重不符(播放器读取的元数据时长和实际播放速度不匹配)。 - 正确的速度倍数应该是原时长 ÷ 目标时长,即
let speedMultiplier = currentDuration / desiredDuration,目标时长越短,速度倍数越大,视频才能被加速到指定时长。
另外,CMTimeMultiplyByFloat64的浮点精度可能导致时长计算偏差,建议改用CMTimeMakeWithSeconds生成更精确的目标时长。
修复后的完整代码
private func speedUpVideo(url: URL, desiredDuration: Double, completion: @escaping (_ url: URL) -> Void) { do { let asset = AVAsset(url: url) guard let videoTrack = asset.tracks(withMediaType: .video).first else { return } let currentDuration = asset.duration.seconds // 修正速度倍数计算逻辑 let speedMultiplier = currentDuration / desiredDuration print("Speed multiplier: \(speedMultiplier)") let speedComposition = AVMutableComposition() guard let speedVideoTrack = speedComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid) else { return } speedVideoTrack.preferredTransform = videoTrack.preferredTransform let assetTimeRange = CMTimeRange(start: .zero, duration: asset.duration) try speedVideoTrack.insertTimeRange(assetTimeRange, of: videoTrack, at: .zero) // 用CMTimeMakeWithSeconds生成精确目标时长,避免浮点误差 let newDuration = CMTimeMakeWithSeconds(desiredDuration, preferredTimescale: asset.duration.timescale) speedVideoTrack.scaleTimeRange(assetTimeRange, toDuration: newDuration) let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] let dateFormatter = DateFormatter() dateFormatter.dateStyle = .long dateFormatter.timeStyle = .long let date = dateFormatter.string(from: Date()) let savePath = (documentDirectory as NSString).appendingPathComponent("storySocial-\(date).mp4") let outputURL = URL(fileURLWithPath: savePath) guard let exporter = AVAssetExportSession(asset: speedComposition, presetName: AVAssetExportPreset1920x1080) else { return } exporter.outputURL = outputURL exporter.outputFileType = .mp4 exporter.shouldOptimizeForNetworkUse = true exporter.exportAsynchronously { switch exporter.status { case .completed: guard let url = exporter.outputURL else { print("No Output URL") return } completion(url) case .failed, .cancelled: print("Export failed: \(exporter.error?.localizedDescription ?? "unknown error")") default: print("Export status: \(exporter.status.rawValue)") } } } catch { print("Error: \(error.localizedDescription)") } }
额外优化点
- 增加
speedVideoTrack非空判断,避免可选值调用崩溃 - 替换
NSDate()为原生Date(),符合Swift语法规范 - 错误打印输出具体信息,便于调试
- 重命名变量避免和入参混淆
内容的提问来源于stack exchange,提问作者Madar
相关产品推荐
相关产品推荐

