以下为对 HarmonyOS 视频编码 Native API 的核心内容进行的结构化总结,包括API 设计与使用流程的Mermaid 图示,便于开发者快速理解与落地。
✅ 一、能力概览
能力 简述 运行时参数配置 动态设置帧率、码率、QP 等 随帧 QP 设置 每帧可设置 QPMin/QPMax 分层编码(LTR) 支持时域可分层视频编码 获取编码信息 每帧可获取 QPAverage 和 MSE 变分辨率 Surface 模式支持输入分辨率变化 HDR Vivid 编码 API 11+,需使用 H265(HEVC)
✅ 二、输入模式对比
模式 数据来源 适用场景 性能 接口差异 Surface OHNativeWindow 相机等实时流 高 使用 OH_VideoEncoder_GetSurface
Buffer 预分配内存 文件读取等 中 使用 OH_VideoEncoder_PushInputBuffer
✅ 三、状态机设计(Mermaid)
✅ 四、开发流程(Surface 模式)
✅ 五、Buffer 模式差异点
步骤 差异说明 输入数据 使用 OH_VideoEncoder_PushInputBuffer
结束标志 设置 AVCODEC_BUFFER_FLAGS_EOS
跨距处理 需手动处理 stride 与 width 不一致的情况
✅ 六、CMake 链接库
cmake
target_link_libraries(sample PUBLIC libnative_media_codecbase.so)
target_link_libraries(sample PUBLIC libnative_media_core.so)
target_link_libraries(sample PUBLIC libnative_media_venc.so)
✅ 七、关键 API 一览
功能 API 创建编码器 OH_VideoEncoder_CreateByMime
/ CreateByName
注册回调 OH_VideoEncoder_RegisterCallback
配置参数 OH_VideoEncoder_Configure
获取 Surface OH_VideoEncoder_GetSurface
启动编码 OH_VideoEncoder_Start
动态设置 OH_VideoEncoder_SetParameter
通知结束 OH_VideoEncoder_NotifyEndOfStream
释放输出 OH_VideoEncoder_FreeOutputBuffer
销毁实例 OH_VideoEncoder_Destroy
✅ 八、注意事项
- Buffer 模式不支持 10bit 图像
- 编码器资源有限,必须调用 Destroy 释放
- 不能在回调中调用 Destroy
- Flush/Reset/Stop/Destroy 会等待所有回调完成
- 跨距不一致时需手动拷贝数据
✅ 九、建议封装结构
- 使用
CodecBufferQueue
管理输入输出队列 - 使用
shared_mutex
保证线程安全 - 使用
OH_AVFormat
配置编解码参数 - 使用
OH_AVBuffer
操作内存与属性
如需进一步落地开发,建议直接参考官方提供的示例工程与API 文档。
完整的api使用案例
下面给出一个可直接编译运行的最小完整示例(Surface 模式,H.264 硬编码)。
代码仅保留主干逻辑,省略了异常处理的细节,方便你先把流程跑通;真正工程化时再加上日志、异常检查、线程同步等即可。
────────────────────────────
- 目录结构
css
encode_demo/
├─ CMakeLists.txt
└─ main.cpp
────────────────────────────
- CMakeLists.txt
cmake
cmake_minimum_required(VERSION 3.16)
project(encode_demo)
set(CMAKE_CXX_STANDARD 17)
# HarmonyOS 标准 NDK 环境变量已配置好
add_executable(encode_demo main.cpp)
target_link_libraries(encode_demo
libnative_media_codecbase.so
libnative_media_core.so
libnative_media_venc.so
libnative_window.so # OHNativeWindow
)
────────────────────────────
- main.cpp(Surface 模式完整示例)
cpp
#include <chrono>
#include <fstream>
#include <iostream>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <shared_mutex>
#include <multimedia/player_framework/native_avcodec_videoencoder.h>
#include <multimedia/player_framework/native_avcapability.h>
#include <multimedia/player_framework/native_avcodec_base.h>
#include <multimedia/player_framework/native_avformat.h>
#include <multimedia/player_framework/native_avbuffer.h>
#include <native_window/external_window.h>
using namespace std;
/* ---------- 1. 公共数据结构 ---------- */
struct CodecBufferInfo {
uint32_t index;
OH_AVBuffer *buffer;
bool isValid;
CodecBufferInfo(uint32_t i, OH_AVBuffer *b) : index(i), buffer(b), isValid(true) {}
};
class CodecBufferQueue {
public:
void Enqueue(shared_ptr<CodecBufferInfo> info) {
unique_lock<mutex> lock(mtx_);
q_.push(info);
cv_.notify_all();
}
shared_ptr<CodecBufferInfo> Dequeue(int ms = 1000) {
unique_lock<mutex> lock(mtx_);
cv_.wait_for(lock, chrono::milliseconds(ms), [this] { return !q_.empty(); });
if (q_.empty()) return nullptr;
auto ret = q_.front();
q_.pop();
return ret;
}
void Flush() {
unique_lock<mutex> lock(mtx_);
while (!q_.empty()) {
q_.front()->isValid = false;
q_.pop();
}
}
private:
mutex mtx_;
condition_variable cv_;
queue<shared_ptr<CodecBufferInfo>> q_;
};
/* ---------- 2. 全局变量 ---------- */
OH_AVCodec *g_enc = nullptr;
OHNativeWindow *g_window = nullptr;
shared_mutex g_mtx;
CodecBufferQueue g_outQ;
bool g_run = true;
/* ---------- 3. 回调函数 ---------- */
void OnError(OH_AVCodec *, int32_t, void *) { cout << "Encoder error\n"; }
void OnStreamChanged(OH_AVCodec *, OH_AVFormat *, void *) {}
void OnNeedInputBuffer(OH_AVCodec *, uint32_t, OH_AVBuffer *, void *) {}
void OnNewOutputBuffer(OH_AVCodec *, uint32_t index, OH_AVBuffer *buf, void *) {
g_outQ.Enqueue(make_shared<CodecBufferInfo>(index, buf));
}
/* ---------- 4. 工具函数 ---------- */
void Release() {
unique_lock<shared_mutex> lock(g_mtx);
if (g_enc) OH_VideoEncoder_Destroy(g_enc);
g_enc = nullptr;
if (g_window) OH_NativeWindow_DestroyNativeWindow(g_window);
g_window = nullptr;
}
/* ---------- 5. 主流程 ---------- */
int main() {
/* 5.1 创建编码器 */
OH_AVCapability *cap =
OH_AVCodec_GetCapabilityByCategory(OH_AVCODEC_MIMETYPE_VIDEO_AVC, true, HARDWARE);
const char *codecName = OH_AVCapability_GetName(cap);
g_enc = OH_VideoEncoder_CreateByName(codecName);
/* 5.2 注册回调 */
OH_AVCodecCallback cb{OnError, OnStreamChanged, OnNeedInputBuffer, OnNewOutputBuffer};
OH_VideoEncoder_RegisterCallback(g_enc, cb, nullptr);
/* 5.3 配置编码参数 */
OH_AVFormat *fmt = OH_AVFormat_Create();
int w = 1280, h = 720;
OH_AVFormat_SetIntValue(fmt, OH_MD_KEY_WIDTH, w);
OH_AVFormat_SetIntValue(fmt, OH_MD_KEY_HEIGHT, h);
OH_AVFormat_SetIntValue(fmt, OH_MD_KEY_PIXEL_FORMAT, AV_PIXEL_FORMAT_NV12);
OH_AVFormat_SetDoubleValue(fmt, OH_MD_KEY_FRAME_RATE, 30.0);
OH_AVFormat_SetLongValue(fmt, OH_MD_KEY_BITRATE, 4'000'000);
OH_AVFormat_SetIntValue(fmt, OH_MD_KEY_I_FRAME_INTERVAL, 1000);
OH_VideoEncoder_Configure(g_enc, fmt);
OH_AVFormat_Destroy(fmt);
/* 5.4 获取 Surface */
OH_VideoEncoder_GetSurface(g_enc, &g_window);
/* 5.5 准备与启动 */
OH_VideoEncoder_Prepare(g_enc);
OH_VideoEncoder_Start(g_enc);
/* 5.6 打开输出文件 */
ofstream out("out.h264", ios::binary);
/* 5.7 模拟相机送帧线程(此处用随机数据填充) */
thread producer([&] {
int bufSize = w * h * 3 / 2;
while (g_run) {
OHNativeWindowBuffer *nwBuf;
int releaseFence;
OH_NativeWindow_NativeWindowRequestBuffer(g_window, &nwBuf, &releaseFence);
void *virAddr;
OH_NativeWindow_NativeWindowBufferHandle handle =
OH_NativeWindow_GetBufferHandleFromNative(nwBuf);
OH_NativeWindow_NativeWindowBufferMap(nwBuf, &virAddr);
memset(virAddr, 0, bufSize); // 实际应填 NV12 数据
OH_NativeWindow_NativeWindowBufferUnmap(nwBuf);
OH_NativeWindow_NativeWindowFlushBuffer(g_window, nwBuf, -1,
OH_NativeWindow_FlushMode::FLUSH_MODE_SYNC);
this_thread::sleep_for(chrono::milliseconds(33));
}
});
/* 5.8 接收编码码流 */
while (g_run) {
auto info = g_outQ.Dequeue();
if (!info || !info->isValid) continue;
OH_AVCodecBufferAttr attr;
OH_AVBuffer_GetBufferAttr(info->buffer, &attr);
uint8_t *data = reinterpret_cast<uint8_t *>(OH_AVBuffer_GetAddr(info->buffer));
out.write(reinterpret_cast<char *>(data), attr.size);
OH_VideoEncoder_FreeOutputBuffer(g_enc, info->index);
}
/* 5.9 结束清理 */
OH_VideoEncoder_Stop(g_enc);
g_run = false;
producer.join();
Release();
return 0;
}
────────────────────────────
- 编译 & 运行(在 DevEco Studio 或 OHOS NDK 命令行均可)
bash
cd encode_demo
mkdir build && cd build
cmake .. -DOHOS_STL=c++_shared -DCMAKE_TOOLCHAIN_FILE=$OHOS_NDK/build/cmake/ohos.toolchain.cmake
make -j
把生成的 encode_demo
推送到设备:
bash
hdc file send encode_demo /data/local/tmp/
hdc shell chmod +x /data/local/tmp/encode_demo
hdc shell /data/local/tmp/encode_demo # 运行
运行结束后,在 /data/local/tmp/out.h264
拿到编码结果,可 hdc file recv
回 PC 用 VLC/ffplay 播放验证。
────────────────────────────
- 如何替换成自己的 YUV 数据
把 producer
线程里的 memset
换成:
cpp
fread(virAddr, 1, bufSize, yuvFp);
即可把本地 input.yuv
逐帧喂给 Surface,实现离线文件编码。
至此,一个可运行的完整 Surface 模式视频编码示例就完成了。