ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

视频不卡的关键:video-compress-cj 多线程编解码管线与 MutexManager 帧队列设计

视频不卡的关键:video-compress-cj 多线程编解码管线与 MutexManager 帧队列设计 视频不卡的关键video-compress-cj 多线程编解码管线与 MutexManager 帧队列设计【免费下载链接】video-compress-cj一个高性能的视频压缩器使用硬件解码和编码API(MediaCodec)。项目地址: https://gitcode.com/Cangjie-TPC/video-compress-cjvideo-compress-cj是一款基于硬件编解码MediaCodec的高性能视频压缩器它用多线程流水线 MutexManager 帧队列的设计让解封装 → 解码 → 编码 → 封装全程并行运转压缩大视频时界面不卡顿、主流程不阻塞。 为什么视频压缩会卡先想一个反面场景如果把压缩写成一条串行链路——读一帧 → 解码 → 编码 → 写盘 → 读下一帧 → ……问题立刻出现串行等待解码器在忙时编码器和写盘只能干等软解太慢CPU 软件解码 1080P 视频速度往往远低于视频本身帧率阻塞主线程压缩循环一旦占住调用线程界面直接假死。video-compress-cj的答案是三个字拆、并行、硬件跑。 核心设计一条多线程流水线整个压缩流程由 6 个组件构成编排逻辑集中在 cjcodec.cpp 的CompressVideo入口中┌─────────┐ ┌─────────┐ Surface零拷贝 ┌─────────┐ ┌─────────┐ │ DeMuxer │ → │ VideoDec │ ──────────────→ │ VideoEnc │ → │ Muxer │ │ 解封装 │ │ 硬件解码 │ (GPU 不落地) │ 硬件编码 │ │ 封装写盘 │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ (同上音视频两路并行) AudioDec │ → AudioEnc │ → │(合并写入)│ └─────────┘ └─────────┘ └─────────┘各组件的分工源码见 videocompress_ffi/src/main/cpp/组件职责源码位置DeMuxer解析文件、拿到音/视频轨道信息DeMuxer.cppVideoDec硬件视频解码H.264 / H.265VideoDec.cppVideoEnc硬件视频编码按质量降码率VideoEnc.cppAudioDec / AudioEncAAC 音频解码 / 编码audio/Muxer把音视频码流封装进 mp4muxer.cpp关键点视频解码器的输出不是内存像素而是直接渲染到编码器的输入 SurfaceOH_VideoDecoder_SetSurface见 VideoDec.cpp 的SetOutputSurface。解码后的画面在 GPU 内直通编码器全程不经过 CPU 内存拷贝这是不卡的第一个大前提。 线程模型回调只负责投递工作线程负责消费这是本设计最值得新手学习的部分。每个编解码组件都遵循同一套生产者—消费者模式硬件回调生产者OH_AVCodec的异步回调onNeedInputData/onNeedOutputData被触发时只把 buffer 索引、属性压入队列并notify_all()绝不在这条短命回调里做重活工作线程消费者常驻线程在条件变量上等待队列有数据就醒来取帧处理没数据就睡眠零 CPU 空转。以视频解码器为例信号队列定义在 VideoDec.h 的VDecSignal中——mutex condition_variable queue三件套各备一套输入、输出各一个// VDecSignal回调与工作线程之间的信物交换所 std::mutex inMutex_; std::condition_variable inCond_; std::queueuint32_t inIdxQueue; // 待推送的输入 buffer 索引 std::queueOH_AVMemory * inBufferQueue_; // outMutex_ / outCond_ / outIdxQueue_ 同理用于解码输出侧启动时解码器会拉起2 条独立线程VideoDec.cpp 的StartVideoDecoderinputLoop_→InputFunc从解封装器取包、推给硬件解码器outputLoop_→OutputFunc把解码输出渲染到编码器 Surface遇到 EOS 就通知编码器收尾。编码器侧同理VideoEnc.cpp 的OutputFunc线程负责把编码产物OH_AVMuxer_WriteSample写入封装器VideoEnc.cpp。于是运行时音、视频两路 × 解、编两级 × 输入输出两条线程并行流水——当解码器在解第 N 帧时编码器可能正在压第 N-2 帧封装器正在写第 N-3 帧三者互相不等待。这就是管线Pipeline一词的全部含义。 MutexManager帧队列与锁的中央调度台各组件之间需要共享什么锁、帧队列、轨道 ID、那块关键的 Surface。这些全被收拢进一个类——MutexManager定义见 tools.hclass MutexManager { public: std::mutex videoMutex_; // 视频队列互斥锁 std::dequestd::shared_ptrDemuxElement VMem; // 视频帧队列 std::mutex videoEncMutex; // 视频编码队列锁 std::dequeEncodeElement VRawQueue; // 视频编码码流队列 std::mutex audioMutex_; // 音频队列互斥锁 std::dequestd::shared_ptrDemuxElement AMem; // 音频帧队列 std::mutex AudioEncMutex; std::dequeEncodeElement ARawQueue; // 音频编码码流队列 OHNativeWindow *nativeWindow; // 解码→编码 共享 Surface int32_t vTrackId; // 视频轨道 ID int32_t aTrackId; // 音频轨道 ID };设计上它解决了三个问题锁与队列成对出现VMem永远和videoMutex_配套、VRawQueue永远和videoEncMutex配套多线程读写帧队列时拿锁再碰队列成为肌肉记忆杜绝数据竞争信息只登记一次DeMuxer 解析出的宽高、帧率、码率、轨道 IDDeMuxer.cpp 的GetTrackInfo统一挂到 MutexManager 上下游组件直接复用不重复解析Surface 全局唯一nativeWindow由编码器GetSurface()创建VideoEnc.cpp再交给解码器SetSurface使用两块硬件因此能零拷贝对接。每个组件通过RegisterMuxerManager(...)拿到这同一个 MutexManager 指针编排代码见 cjcodec.cpp相当于所有人领到同一本值班手册。 收尾也讲究EOS 传播与线程有序回收压缩结束靠一条 EOSEnd Of Stream信号链解封装读到末尾 → 解码器输入 EOS → 解码输出 EOS →VideoEnc-SendEncEos()通知编码器 → 编码器输出 EOS → 停止 Muxer。调用侧用WaitForEos依次 join 各工作线程cjcodec.cpp 的VideoCompressorWaitEos配合各组件StopInLoop / StopOutLoop中的清队列 唤醒 join三步曲如 VideoDec.cpp保证线程一个不漏、资源一个不泄。 如何上手体验克隆仓库git clone -b 具体分支名 --single-branch --recursive https://gitcode.com/Cangjie-TPC/video-compress-cj用 DevEco StudioCangjie 插件 5.0.13打开选中videocompressor模块执行 Make Module 完成编译在示例工程 entry/ 中调用压缩接口核心 API 文档见 doc/feature_api.mdCangjie 层封装见 video_compressor.cj。 相关文件导航内容路径流水线编排入口videocompress_ffi/src/main/cpp/cjcodec.cppMutexManager 帧队列定义videocompress_ffi/src/main/cpp/tools.h视频解码线程模型videocompress_ffi/src/main/cpp/video/decoder/VideoDec.cpp视频编码与 Surface 获取videocompress_ffi/src/main/cpp/video/encoder/VideoEnc.cpp解封装与轨道解析videocompress_ffi/src/main/cpp/demuxer/DeMuxer.cppAPI 接口文档doc/feature_api.md项目总览README.md✅ 小结视频不卡的三个秘密流水线并行解封装、解码、编码、封装各占独立线程帧在队列里接力谁也不用等谁硬件跑零拷贝MediaCodec 硬件编解码 Surface 直连像素数据不上 CPU锁队列成对管理MutexManager 把锁 帧队列 共享资源集中托管并发安全一目了然。下次再写压缩/转码功能把这套回调投递 条件变量消费 中央调度台的范式抄走卡顿自然就没了。【免费下载链接】video-compress-cj一个高性能的视频压缩器使用硬件解码和编码API(MediaCodec)。项目地址: https://gitcode.com/Cangjie-TPC/video-compress-cj创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表