You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 06:35:43