news 2026/4/16 23:47:18

鸿蒙开发入门指南:前端开发者快速理解视频编码概念——输入模式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
鸿蒙开发入门指南:前端开发者快速理解视频编码概念——输入模式

# 鸿蒙开发入门指南:前端开发者快速掌握视频编码

    • 一、视频编码到底是干啥的?
    • 二、先看一张图:编码器的状态机
    • 三、Surface模式:从摄像头到视频文件
      • 第1步:创建编码器实例
      • 第2步:注册回调函数
      • 第3步:配置编码参数
      • 第4步:获取 Surface 并启动
      • 第5步:通知编码结束
      • 第6步:取编码结果
      • 第7步:清理
    • 四、Buffer模式:从文件到文件
      • 区别1:需要自己准备输入数据
      • 区别2:需要处理跨距
    • 五、运行时动态调整参数
    • 六、总结一下两种模式怎么选
    • 总结

大家好,我是木斯佳。今天聊点不一样的——鸿蒙的视频编码。说实话,第一次看到这玩意儿我有点懵,这跟前端有啥关系?但后来想想,现在哪个 App 不上传视频?做音视频编辑、视频上传、直播推流,都绕不开编码。鸿蒙这套 API 是 C++ 的,但设计思路其实挺有意思,咱们用前端的思维来理解一下。

一、视频编码到底是干啥的?

先说说我的理解:你手机拍视频,录出来的是原始数据,一秒钟几十兆,根本存不下也传不动。编码就是把这些原始数据压缩成 H.264、H.265 这种格式,体积能小几十倍。

鸿蒙提供的这套 Native API,就是让你在 C++ 层干这个活。支持 H.264、H.265,还支持 HDR Vivid(就是高动态范围标准)。

官网文档里区分了两种输入模式:

  • Surface模式:数据来自摄像头预览画面、屏幕录制这些地方,直接对接 GPU 纹理,效率最高
  • Buffer模式:数据来自文件,你自己往内存里塞,适合读本地 YUV 文件转码

打个比方:Surface模式像「流水线直接对接」,Buffer模式像「自己搬砖」。

二、先看一张图:编码器的状态机

官网给了个状态机图,比较难理解,我用自己的语言给大家翻译一下,这其实就和我们常用的生命周期是类似原理:

状态前端类比
Initializednew 了一个实例,还没配置
Configured参数配好了,宽高、码率都设置完
Prepared准备工作做完,等着启动
Executing正在编码,处理每一帧
Flushed清空了缓冲区,但没停
EOS文件末尾,最后一帧处理完了
Error出错了,比如输入数据格式不对
Released实例销毁,资源释放

流程大概是:创建 → 配置 → 准备 → 启动 → 编码 → 结束 → 销毁。

如果中途出错或者想重置,可以走 Flush 或者 Reset 回到之前的状态。

前端视角:这有点像 Video 元素的生命周期,或者 WebCodecs API 的状态管理。

三、Surface模式:从摄像头到视频文件

官网给了完整的示例代码,我把核心步骤串一遍。

第1步:创建编码器实例

有两种方式,按名字创建或者按 MIME 类型创建:

// 按名字创建(可以指定硬件编码器)OH_AVCapability*capability=OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_VIDEO_AVC,true);constchar*codecName=OH_AVCapability_GetName(capability);OH_AVCodec*videoEnc=OH_VideoEncoder_CreateByName(codecName);// 按 MIME 创建(更简单)OH_AVCodec*videoEnc=OH_VideoEncoder_CreateByMime(OH_AVCODEC_MIMETYPE_VIDEO_AVC);

前端视角:类似new VideoEncoder(),但这里需要指定用软解还是硬解。

第2步:注册回调函数

编码器是异步的,数据准备好了会回调你:

// 错误回调staticvoidOnError(OH_AVCodec*codec,int32_terrorCode,void*userData){// 编码出错了}// 数据流变化回调(比如分辨率变了)staticvoidOnStreamChanged(OH_AVCodec*codec,OH_AVFormat*format,void*userData){// 从 format 里取出新的宽高OH_AVFormat_GetIntValue(format,OH_MD_KEY_VIDEO_PIC_WIDTH,&width);OH_AVFormat_GetIntValue(format,OH_MD_KEY_VIDEO_PIC_HEIGHT,&height);}// 输入回调(Surface模式下没啥用,数据从 surface 来)staticvoidOnNeedInputBuffer(OH_AVCodec*codec,uint32_tindex,OH_AVBuffer*buffer,void*userData){// Surface 模式不处理这个}// 输出回调(编码完了一帧,来这里拿数据)staticvoidOnNewOutputBuffer(OH_AVCodec*codec,uint32_tindex,OH_AVBuffer*buffer,void*userData){outQueue.Enqueue(...);// 把编码好的数据存起来}OH_AVCodecCallback cb={&OnError,&OnStreamChanged,&OnNeedInputBuffer,&OnNewOutputBuffer};OH_VideoEncoder_RegisterCallback(videoEnc,cb,nullptr);

前端视角:就是 Promise 或者事件监听,数据到了告诉你。

第3步:配置编码参数

这一步最繁琐,要设置一堆参数:

autoformat=OH_AVFormat_Create();OH_AVFormat_SetIntValue(format,OH_MD_KEY_WIDTH,320);// 宽度OH_AVFormat_SetIntValue(format,OH_MD_KEY_HEIGHT,240);// 高度OH_AVFormat_SetIntValue(format,OH_MD_KEY_PIXEL_FORMAT,AV_PIXEL_FORMAT_NV12);// 像素格式OH_AVFormat_SetDoubleValue(format,OH_MD_KEY_FRAME_RATE,30);// 帧率OH_AVFormat_SetLongValue(format,OH_MD_KEY_BITRATE,5000000);// 码率 5MbpsOH_AVFormat_SetIntValue(format,OH_MD_KEY_I_FRAME_INTERVAL,1000);// 关键帧间隔 1 秒OH_VideoEncoder_Configure(videoEnc,format);

前端视角:类似new VideoEncoder({output, error})之后调用configure(),参数差不多。

第4步:获取 Surface 并启动

这是 Surface 模式的关键——从编码器拿一个 Surface,交给相机或者其他生产者:

// 获取 SurfaceOHNativeWindow*nativeWindow;OH_VideoEncoder_GetSurface(videoEnc,&nativeWindow);// 准备就绪OH_VideoEncoder_Prepare(videoEnc);// 开始编码OH_VideoEncoder_Start(videoEnc);

拿到nativeWindow之后,可以传给相机模块,相机的预览数据就直接送进编码器了,你不需要手动往编码器塞数据。

第5步:通知编码结束

数据送完了,通知编码器收工:

OH_VideoEncoder_NotifyEndOfStream(videoEnc);

第6步:取编码结果

OnNewOutputBuffer回调里,编码好的数据会送过来,你需要把它写进文件:

voidOnNewOutputBuffer(OH_AVCodec*codec,uint32_tindex,OH_AVBuffer*buffer,void*userData){// 获取编码后的数据OH_AVCodecBufferAttr info;OH_AVBuffer_GetBufferAttr(buffer,&info);// 写入文件uint8_t*data=OH_AVBuffer_GetAddr(buffer);outputFile->write(data,info.size);// 释放这个 bufferOH_VideoEncoder_FreeOutputBuffer(videoEnc,index);}

第7步:清理

用完了记得销毁,不然会内存泄漏:

OH_NativeWindow_DestroyNativeWindow(nativeWindow);OH_VideoEncoder_Stop(videoEnc);OH_VideoEncoder_Destroy(videoEnc);videoEnc=nullptr;

四、Buffer模式:从文件到文件

Buffer 模式和 Surface 模式的区别在于:数据来源不是 Surface,而是你自己往 buffer 里塞。

大部分步骤一样,主要区别在这几处:

区别1:需要自己准备输入数据

Buffer 模式下,OnNeedInputBuffer回调是有用的:

voidOnNeedInputBuffer(OH_AVCodec*codec,uint32_tindex,OH_AVBuffer*buffer,void*userData){// 从文件读取一帧 YUV 数据uint8_t*addr=OH_AVBuffer_GetAddr(buffer);inputFile->read(addr,frameSize);// 配置这一帧的信息(大小、时间戳、是不是关键帧)OH_AVCodecBufferAttr info;info.size=frameSize;info.pts=frameIndex*1000000/frameRate;// 时间戳info.flags=0;// 普通帧,最后一帧设为 AVCODEC_BUFFER_FLAGS_EOSOH_AVBuffer_SetBufferAttr(buffer,&info);// 推给编码器OH_VideoEncoder_PushInputBuffer(videoEnc,index);}

区别2:需要处理跨距

YUV 数据在内存里不一定连续存放,有跨距(stride)的概念——每行数据之间可能有 padding。

官网给了个示例代码,把源数据按跨距复制到目标 buffer:

for(int32_ti=0;i<rect.height;++i){memcpy(dstTemp,srcTemp,rect.width);dstTemp+=dstRect.wStride;// 跳过 paddingsrcTemp+=srcRect.wStride;}

前端视角:类似处理 Canvas 的 ImageData,每行可能有额外的字节对齐。

五、运行时动态调整参数

编码过程中可以动态调整参数,比如突然要切一个关键帧,或者码率要动态变化:

OH_AVFormat*format=OH_AVFormat_Create();// 强制下一帧是关键帧OH_AVFormat_SetIntValue(format,OH_MD_KEY_REQUEST_I_FRAME,true);// 动态调整码率(VBR 模式)OH_AVFormat_SetLongValue(format,OH_MD_KEY_BITRATE,2000000);// 动态调整帧率OH_AVFormat_SetDoubleValue(format,OH_MD_KEY_FRAME_RATE,60.0);OH_VideoEncoder_SetParameter(videoEnc,format);OH_AVFormat_Destroy(format);

这个能力挺实用的——网络不好的时候降码率,用户滑动进度条的时候切关键帧。

六、总结一下两种模式怎么选

场景推荐模式原因
相机拍摄Surface直接对接相机输出,零拷贝
屏幕录制Surface直接从 GPU 拿数据
文件转码Buffer自己控制数据来源
视频编辑Buffer需要逐帧处理
实时滤镜Buffer需要拿到 YUV 数据做滤镜处理

一句话:能走 Surface 就走 Surface,性能好;只有需要逐帧操作数据时才用 Buffer。

总结

虽然这套 API 是 C++ 的,但设计思路和 WebCodecs 有相似之处:

概念WebCodecs鸿蒙
编码器VideoEncoderOH_VideoEncoder
配置参数configure()OH_VideoEncoder_Configure()
编码帧encode()OH_VideoEncoder_PushInputBuffer()
输出帧output 回调OnNewOutputBuffer 回调
结束flush()NotifyEndOfStream() / EOS flag

如果用过 WebCodecs,上手鸿蒙这套 API 会快很多。没接触过也没关系,核心就是:配置参数 → 塞数据 → 拿结果 → 清理,所有编解码器都是这个套路。

最后提醒一下:硬件编码器资源有限,用完必须调用OH_VideoEncoder_Destroy释放,否则可能影响其他应用甚至被系统杀掉。另外不能在回调函数里销毁编码器,会死锁。

有问题评论区聊。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 23:44:48

CAN1.2,CAN2.0,CANFD的关系

CAN FD&#xff08;很多人把 CAN FD 叫成 高速CAN &#xff09;&#xff0c;按CAN 1.2 → CAN 2.0 → CAN FD 一路演进线。一、三者核心关系总结CAN 1.2&#xff1a;最早基础版&#xff0c;只有11 位标准 ID、一帧最多8 字节、速率最高1Mbps。CAN 2.0&#xff1a;完全兼容 CAN …

作者头像 李华
网站建设 2026/4/16 23:42:52

终极LlamaParse API开发指南:Python和TypeScript实战教程

终极LlamaParse API开发指南&#xff1a;Python和TypeScript实战教程 【免费下载链接】llama_parse Knowledge Agents and Management in the Cloud 项目地址: https://gitcode.com/gh_mirrors/ll/llama_parse LlamaParse API是一款强大的文档解析工具&#xff0c;能够帮…

作者头像 李华
网站建设 2026/4/16 23:42:29

Windows字体渲染终极优化指南:用MacType让文字如Mac般清晰

Windows字体渲染终极优化指南&#xff1a;用MacType让文字如Mac般清晰 【免费下载链接】mactype Better font rendering for Windows. 项目地址: https://gitcode.com/gh_mirrors/ma/mactype 你是否曾羡慕Mac电脑上清晰锐利的字体显示效果&#xff1f;Windows系统默认的…

作者头像 李华
网站建设 2026/4/16 23:37:28

9款爱毕业aibiye精选查重神器,免费无限次使用,AI智能降重优化文本,学术写作更高效,重复率一键达标。

核心工具对比速览 工具名称 查重速度 降重效果 特色功能 适用场景 aicheck 极快 重复率可降30% 专业术语保留 高重复率紧急处理 aibiye 中等 逻辑优化明显 学术表达增强 提升论文质量 askpaper 快 结构保持完整 多语言支持 外文论文降重 秒篇 极快 上下文…

作者头像 李华
网站建设 2026/4/16 23:36:30

RK3588寄存器调试实战:从IO命令到GPIO配置

1. RK3588寄存器操作基础入门 第一次接触RK3588寄存器操作时&#xff0c;我也被各种专业术语搞得一头雾水。后来发现&#xff0c;寄存器就像是硬件设备的控制面板&#xff0c;每个开关和旋钮都对应着特定的功能。RK3588作为一款高性能处理器&#xff0c;它的寄存器控制着从GPIO…

作者头像 李华