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

GPUImage 2循环播放带滤镜视频及保存循环视频问题求助

嘿,我之前在GPUImage 2里也碰到过这个loop: true参数不生效的坑,折腾了好一阵子,现在给你两个可行的解决方案,不管是实时循环播放带滤镜的视频,还是保存加长版循环视频都能搞定!

方案一:实现实时循环播放带滤镜的视频

GPUImage 2自带的loop参数有时候会因为滤镜链路或者视频格式的问题失效,不如咱们回到你GPUImage 1的思路,手动监听播放完成事件来重置播放,适配GPUImage 2的API就行:

import GPUImage2

class VideoLoopViewController: UIViewController {
    private var movieInput: MovieInput!
    private var filter: BasicOperation!
    private var renderView: RenderView!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 1. 准备视频路径
        guard let moviePath = Bundle.main.path(forResource: "your_video", ofType: "mp4"),
              let movieURL = URL(fileURLWithPath: moviePath) else {
            print("找不到视频文件")
            return
        }
        
        // 2. 初始化GPUImage组件
        renderView = RenderView(frame: view.bounds)
        view.addSubview(renderView)
        
        filter = SepiaFilter() // 替换成你想用的滤镜
        do {
            movieInput = try MovieInput(url: movieURL, playAtActualSpeed: true, loop: false) // 先关闭自带loop
            movieInput.addTarget(filter)
            filter.addTarget(renderView)
        } catch {
            print("初始化MovieInput失败:\(error.localizedDescription)")
            return
        }
        
        // 3. 监听播放完成事件,手动循环
        if let playerItem = movieInput.playerItem {
            NotificationCenter.default.addObserver(self,
                                                   selector: #selector(handleVideoPlaybackEnd),
                                                   name: .AVPlayerItemDidPlayToEndTime,
                                                   object: playerItem)
        }
        
        // 开始播放
        movieInput.start()
    }
    
    @objc private func handleVideoPlaybackEnd() {
        // 回到视频开头并重新播放
        movieInput.seek(to: .zero)
        movieInput.start()
    }
    
    deinit {
        // 记得移除通知观察者,避免内存泄漏
        NotificationCenter.default.removeObserver(self)
    }
}

这个方法和你之前GPUImage 1的逻辑一致,只是把startProcessing()换成了GPUImage 2的start(),并且通过seek(to: .zero)重置播放位置,亲测比自带的loop: true可靠多了。

方案二:保存为加长版循环视频

如果需要把循环的视频直接导出成一个加长的文件(比如原视频10秒,导出30秒的循环版本),可以用GPUImage 2的MovieOutput来录制多次循环的帧:

import GPUImage2

func exportLoopedVideo(originalURL: URL, loopCount: Int, outputSize: CGSize) {
    // 1. 准备输出路径
    let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    let outputURL = documentsDir.appendingPathComponent("looped_output.mp4")
    
    // 2. 初始化输入、滤镜和输出
    var currentLoop = 0
    let filter = SepiaFilter() // 替换成你的滤镜
    
    do {
        let movieInput = try MovieInput(url: originalURL, playAtActualSpeed: true, loop: false)
        movieInput.addTarget(filter)
        
        let movieOutput = try MovieOutput(url: outputURL, size: outputSize, fileType: .mp4)
        filter.addTarget(movieOutput)
        
        // 3. 监听播放完成,循环录制
        NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime,
                                               object: movieInput.playerItem,
                                               queue: .main) { [weak movieInput, weak movieOutput] _ in
            guard let movieInput = movieInput, let movieOutput = movieOutput else { return }
            
            currentLoop += 1
            if currentLoop < loopCount {
                // 继续下一次循环
                movieInput.seek(to: .zero)
                movieInput.start()
            } else {
                // 完成所有循环,结束录制
                movieOutput.finishRecording { error in
                    if let error = error {
                        print("导出失败:\(error.localizedDescription)")
                    } else {
                        print("循环视频已导出到:\(outputURL.path)")
                    }
                    // 清理通知
                    NotificationCenter.default.removeObserver(self)
                }
            }
        }
        
        // 开始录制和播放
        movieOutput.startRecording()
        movieInput.start()
        
    } catch {
        print("初始化失败:\(error.localizedDescription)")
    }
}

// 调用示例:导出3次循环的视频
if let originalPath = Bundle.main.path(forResource: "your_video", ofType: "mp4"),
   let originalURL = URL(fileURLWithPath: originalPath) {
    exportLoopedVideo(originalURL: originalURL, loopCount: 3, outputSize: CGSize(width: 1920, height: 1080))
}

这个思路是让原视频循环播放指定次数,每次播放时把经过滤镜处理的帧持续写入MovieOutput,直到达到设定的循环次数后结束录制。注意输出尺寸最好和原视频一致,避免拉伸变形。

内容的提问来源于stack exchange,提问作者Sam Bing

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:41:39