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
相关产品推荐
相关产品推荐

