前语

最近想做个简单的带滤镜功用的录制器,于是乎就想到GPUImage这个远古且强大的滤镜库,其间GPUImageGPUImageMovieWriter能够完结录制功用,用起来比较简单,关于我这种OpenGL小白非常友爱。

可是,开发过程中,发现这个录制器的暂停功用是有问题的。

这种情况,最好是换一个库,毕竟GPUImage现已好久没有更新了,今后问题估计会越来越多,可是此刻录制相关的逻辑都写得差不多了,这沉没本钱有点大。。。

终究决议,专门针对这个录制器进行修复吧。

终究效果

【iOS】JPMovieWriter 基于GPUImageMovieWriter改造的录制器

Demo地址:JPMovieWriter_Demo,可暂停、续录、守时、带滤镜美颜的录制器,另附GIF制造。

暂停问题

GPUImageMovieWriter目前存在的问题是:暂停又重录会黑屏或者有画面没声响

经过调试发现:

  1. 屡次暂停恢复后,GPUImageVideoCamera- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection这个方法会收集不了音频。

  2. 经百度谷歌发现这篇文章也是发现这个问题,可是注释了文章说到的那段代码后,虽然音频能持续收集,可是屡次暂停恢复,误差会越来越大,终究导致音画不同步(有时候声响卡了,有时候是画面卡了,终究连声响也没了)。

// 文中提及注释的当地
if (offsetTime.value > 0) {
     CFRelease(audioBuffer);
     audioBuffer = [self adjustTime:audioBuffer by:offsetTime];
     CFRetain(audioBuffer);
}

解决方案

GPUImageMovieWriter内部是经过AVAssetWriter完结的录制功用,但AVAssetWriter本来就没有暂停的功用,而GPUImageMovieWriter是经过一些延时算法来完结,可是并不靠谱,屡次暂停后误差会越来越离谱。

持续百度谷歌还有ChatGPT的问询后,最好办法便是每次暂停后,换一个新的AVAssetWriter从头录制,这是最稳妥的解决方案。

由于GPUImageMovieWriter除了能录制,内部还有对视频帧进行滤镜烘托,为了不影响这部分的逻辑,我的做法便是彻底复制一个新的GPUImageMovieWriter文件,然后只针对AVAssetWriter的部分进行改造:移除【暂停】这个功用,只能【完结】录制,不同的是,【完结】录制后,能够【再次】录制

这便是JPMovieWriter的由来,一个能够重复录制,而且带滤镜烘托功用的录制器。

新的特性

  • 可重复、从头录制:每次录制都是一个全新的AVAssetWriter,因此每次”暂停“都是新的一个视频,也能够清空已录制部分从头开始录制,且在内部完结,无需从头设置滤镜烘托链(从头设置页面会明显卡顿)。
  • 可设置最大录制时长:录制过程中会不断监听录制的时间,到达最大值会主动停止录制。

JPMovieWriter能够彻底替代GPUImageMovieWriter,由于除了AVAssetWriter的部分,其余当地几乎没有改动,而且也做了大量的代码结构性优化,愈加面向对象化,便利阅读和调试。

留意点

虽然说能重录,但每次录制都是新的一个视频,所以终究完毕录制时,会回来一个存放在Temp文件夹的视频URL数组,鉴于以便应付不同的需求,内部并没有将多段视频组成一个视频的完结,数据将原汁原味保留(组成的逻辑Demo中有完结)。

遵守JPMovieWriterDelegate协议,监听录制过程,录制完毕后组成并导出:

// MARK: - <JPMovieWriterDelegate>
extension RecordViewController: JPMovieWriterDelegate {
    // AVAssetWriter重置过错的回调(例如目标途径已存在文件)
    func movieWriter(_ writer: JPMovieWriter, resetFailed recordedURLs: [URL], error: Error) {
        JPProgressHUD.showError(withStatus: (error as NSError).localizedDescription, userInteractionEnabled: true)
    }
    // 录制进度的回调
    func movieWriter(_ writer: JPMovieWriter, recording recordDuration: TimeInterval, totalDuration: TimeInterval) {
        let progress = recordDuration / totalDuration
        controlBtn.setProgress(progress, animated: false)
    }
    // 录制行将完毕的回调
    func movieWriter(_ writer: JPMovieWriter, recordWillDone error: Error?) {
        JPProgressHUD.show(withStatus: error == nil ? "正在完毕录制" : "录制发生过错")
        controlBar.isEnabled = false
    }
    // 录制现已完毕的回调
    func movieWriter(_ writer: JPMovieWriter, recordDone recordedURLs: [URL], error: Error?) {
        if let error = error as? NSError {
            // 录制发送过错
            return
        }
        // 录制完结,将视频碎片组成并导出
        VideoTool.mergeVideos(recordedURLs, videoSize: UIConfig.videoSize, contentMode: .scaleAspectFit, maxDuration: CMTime(value: Int64(RecordConfig.videoMaxDuration), timescale: 1)) { mergedFilePath in
            // mergedFilePath:组成好的途径 -> 缓存
        } faild: { kError in
            // 组成或导出失利
        }
    }
}

Demo介绍

JPMovieWriter_Demo:可暂停、续录、守时、带滤镜美颜的录制器,另附GIF制造。没什么技术含量,不过能够玩一下。

Feature:
    ✅ 可暂停、守时、续录;
    ✅ 多种滤镜选择;
    ✅ 基础美颜功用;
    ✅ 支持相册视频的导入和拼接;
    ✅ GIF制造;

TODO:
     更多的滤镜和特效;
     愈加定制化的GIF制造;
     视频编辑。

美颜滤镜

GIF制造

【iOS】JPMovieWriter 基于GPUImageMovieWriter改造的录制器

生成图:

【iOS】JPMovieWriter 基于GPUImageMovieWriter改造的录制器

其他

  • 首页的瀑布流布局:WaterfallLayoutDemo
  • 封面编辑裁剪:JPCrop
  • 视频GIF制造:JPImageresizerView

终究

本来想做一个GIF制造(用于微信斗图)、同时也能随时随地记录心境的App,可是最近有更重要的工作要先去做,还有很多页面和功用都没写完,这个项目只能放置了。