已合并
新增视频编码开发指南,包括编码前处理、一入二出 #140858
zhanghongran创建于 4月21日
新增视频编码开发指南,包括编码前处理、一入二出 #140858
已合并
共 4 个文件变更+682-0
| @@ -21,6 +21,8 @@ | |||
| 21 | - [B帧视频编码](video-encoding-b-frame.md) | 21 | - [B帧视频编码](video-encoding-b-frame.md) |
| 22 | - [典型场景的视频编码配置](video-encoding-configuration-typical-scenarios.md) | 22 | - [典型场景的视频编码配置](video-encoding-configuration-typical-scenarios.md) |
| 23 | - [ROI视频编码](video-encoding-ROI.md) | 23 | - [ROI视频编码](video-encoding-ROI.md) |
| 24 | + - [编码支持一入二出](video-encoding-preproc-one-in-dual-out.md) | ||
| 25 | + - [编码支持前处理](video-encoding-preproc.md) | ||
| 24 | - [视频解码](video-decoding.md) | 26 | - [视频解码](video-decoding.md) |
| 25 | - [视频解码同步模式](synchronous-video-decoding.md) | 27 | - [视频解码同步模式](synchronous-video-decoding.md) |
| 26 | - [视频可变帧率](video-variable-refreshrate.md)<!--RP1--><!--RP1End--><!--RP3--><!--RP3End--> | 28 | - [视频可变帧率](video-variable-refreshrate.md)<!--RP1--><!--RP1End--><!--RP3--><!--RP3End--> |
| @@ -0,0 +1,397 @@ | |||||||
| 1 | +# 编码支持一入二出 | ||||||
7 | |||||||
| 2 | + | ||||||
| 3 | +<!--Kit: AVCodec Kit--> | ||||||
readme文件中需补充链接 ![]() ![]() | |||||||
| 4 | +<!--Subsystem: Multimedia--> | ||||||
| 5 | +<!--Owner: @zhanghongran--> | ||||||
| 6 | +<!--Designer: @dpy2650--> | ||||||
| 7 | +<!--Tester: @cyakee--> | ||||||
| 8 | +<!--Adviser: @w_Machine_cc--> | ||||||
| 9 | + | ||||||
| 10 | +从API版本26.0.0开始,对于视频编码场景,支持一入二出编码,即通过同一份视频输入数据,同时驱动**两个独立编码器**产生两路不同编码码流的能力。 | ||||||
| 11 | + | ||||||
| 12 | +## 功能简介 | ||||||
| 13 | + | ||||||
| 14 | +**一入二出(One Input Dual Outputs)** 是指通过同一份视频输入数据,同时驱动 **两个独立编码器** 产生两路不同编码码流的能力。 | ||||||
A ❌疑似检测出英文拼写问题 错误原因:英文术语"Outputs"应为单数形式"Output","One Input Dual Output"是更标准的英语表达。 建议修改为: 改动建议
![]() ![]() | |||||||
| 15 | + | ||||||
| 16 | +| 编码器角色 | 创建方式 | 说明 | | ||||||
| 17 | +|-----------|----------|------| | ||||||
| 18 | +| 主编码器(Primary) | [OH_VideoEncoder_CreatePrimaryWithPreproc](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_createprimarywithpreproc)创建 | 管理共享输入Surface,负责前处理管线调度,可配置独立的编码参数和前处理参数。 | | ||||||
| 19 | +| 副编码器(Secondary) | [OH_VideoEncoder_CreateSecondaryFromPrimary](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_createsecondaryfromprimary)从主编码器创建 | 共享主编码器的输入源,可配置独立的编码参数和前处理参数。 | | ||||||
| 20 | + | ||||||
| 21 | +### 架构图 | ||||||
| 22 | + | ||||||
| 23 | +以下为一入二出架构图: | ||||||
| 24 | + | ||||||
| 25 | + | ||||||
| 26 | + | ||||||
| 27 | +### 使用场景 | ||||||
| 28 | +应用可依据自己的场景选择使用,场景使用举例见下表: | ||||||
| 29 | +| 场景 | 主编码器(Primary) | 副编码器(Secondary) | 用途说明 | | ||||||
| 30 | +|------|---------------------|----------------------|----------| | ||||||
| 31 | +| 多码率直播(ABR) | 高码率主码流 | 降采样 + 丢帧的低码流/低码率 | 无需云端进行多码率转码。在主播端编码两种码流,就可以适配不同看播端设备和带宽,减少云端转码时延和转码成本。 | | ||||||
A ❌疑似检测出中英文错别字问题 错误原因:"看播端"不是标准的技术术语,建议使用"观看端"或"播放端" 建议修改为: 改动建议
![]() ![]() | |||||||
| 32 | +| ROI 区域关注 | 全帧编码归档 | 裁剪感兴趣区域编码 | 监控全帧存储 + 局部区域分析 | | ||||||
| 33 | +| 多人视频通话 | 高分辨率/高帧率/高码率码流 | 降采样/丢帧/低码率码流 | 无需云端进行多码率转码。在发送端编码两种码流,就可以适配不同接收端设备和带宽,减少云端转码时延和转码成本。 | | ||||||
| 34 | + | ||||||
| 35 | +### 约束与限制 | ||||||
| 36 | + | ||||||
| 37 | +**创建与生命周期约束** | ||||||
| 38 | + | ||||||
| 39 | +| 序号 | 约束规则 | | ||||||
| 40 | +|------|----------| | ||||||
| 41 | +| 1 | Secondary数量:每个Primary同时最多挂载**1个**Secondary。 | | ||||||
| 42 | +| 2 | 创建顺序:必须先创建Primary,再从Primary派生Secondary。 | | ||||||
| 43 | +| 3 | 生命周期关系:Primary是Secondary的所有者(Owner),Secondary不得脱离Primary独立存在。<br>- **推荐销毁顺序**:先`Destroy(Secondary)` → 再`Destroy(Primary)`,销毁后立即将对应指针赋值`nullptr`。<br>- **容错机制**:若违反顺序先 Destroy Primary,系统会级联释放关联的Secondary,但仍应显式遵循正确顺序。 | | ||||||
| 44 | +| 4 | 重建能力:Secondary销毁后,可以从同一个Primary重新创建新的Secondary。 | | ||||||
| 45 | +| 5 | 仅支持surface模式。 | | ||||||
| 46 | + | ||||||
| 47 | +**接口可用性约束** | ||||||
| 48 | + | ||||||
补充下具体的状态机转移参考Surface模式的普通编码器 ![]() ![]() | |||||||
| 49 | +| 接口 | 主编码器 | 副编码器 | 备注 | | ||||||
| 50 | +|------|:--------:|:--------:|------| | ||||||
| 51 | +| [OH_VideoEncoder_CreatePrimaryWithPreproc](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_createprimarywithpreproc) | √ | N/A | 创建主编码器入口。 | | ||||||
| 52 | +| [OH_VideoEncoder_CreateSecondaryFromPrimary](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_createsecondaryfromprimary) | √ | N/A | 创建副编码器入口,仅可以通过主编码器句柄创建。 | | ||||||
| 53 | +| [OH_VideoEncoder_RegisterCallback](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_registercallback) | √ | √ | 各自独立注册。 | | ||||||
| 54 | +| [OH_VideoEncoder_RegisterParameterCallback](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_registerparametercallback) | × | × | 不支持随帧参数。 | | ||||||
| 55 | +| [OH_VideoEncoder_PushInputParameter](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_pushinputparameter) | × | × | 不支持随帧参数。 | | ||||||
| 56 | +| [OH_VideoEncoder_Configure](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_configure) | √ | √ | 各自独立配置(分辨率、码率、前处理等均可不同)。 | | ||||||
| 57 | +| [OH_VideoEncoder_GetSurface](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_getsurface) | √ | × | **仅限主编码器调用者**,副编码器调用返回错误。 | | ||||||
| 58 | +| [OH_VideoEncoder_Prepare](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_prepare) | √ | √ | 各自准备资源,参考普通编码器。 | | ||||||
| 59 | +| [OH_VideoEncoder_Start](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_start) | √ | √ | 各自独立控制,参考普通编码器。 | | ||||||
| 60 | +| [OH_VideoEncoder_Stop](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_stop) | √ | √ | 各自独立控制,参考普通编码器。 | | ||||||
| 61 | +| [OH_VideoEncoder_Flush](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_flush) | √ | √ | 各自独立控制,参考普通编码器。 | | ||||||
| 62 | +| [OH_VideoEncoder_Reset](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_reset) | √ | √ | 各自独立控制,参考普通编码器。 | | ||||||
| 63 | +| [OH_VideoEncoder_SetParameter](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_setparameter) | √ | √ | 运行时动态调整。 | | ||||||
| 64 | +| [OH_VideoEncoder_NotifyEndOfStream](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_notifyendofstream) | √ | √ | Surface模式专用。 | | ||||||
| 65 | +| [OH_VideoEncoder_FreeOutputBuffer](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_freeoutputbuffer) | √ | √ | 各自释放各自的 output buffer。 | | ||||||
| 66 | +| [OH_VideoEncoder_PushInputData](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_pushinputdata) | × | × | 不支持 Buffer模式。 | | ||||||
| 67 | +| [OH_VideoEncoder_PushInputBuffer](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_pushinputbuffer) | × | × | 不支持Buffer模式。 | | ||||||
| 68 | +| [OH_VideoEncoder_QueryInputBuffer](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_queryinputbuffer) | × | × | 不支持同步模式。 | | ||||||
| 69 | +| [OH_VideoEncoder_QueryOutputBuffer](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_queryoutputbuffer) | × | × | 不支持同步模式。 | | ||||||
| 70 | +| [OH_VideoEncoder_GetInputDescription](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_getinputdescription) | √ | √ | 含前处理元数据信息。 | | ||||||
| 71 | +| [OH_VideoEncoder_GetOutputDescription](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_getoutputdescription) | √ | √ | 各自信息查询,参考普通编码器。 | | ||||||
| 72 | +| [OH_VideoEncoder_IsValid](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_isvalid) | √ | √ | 各自有效性判断,参考普通编码器。 | | ||||||
| 73 | +| [OH_VideoEncoder_Destroy](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_destroy) | √ | √ | 先销毁Secondary,再销毁Primary。 | | ||||||
| 74 | + | ||||||
| 75 | +**配置约束** | ||||||
| 76 | + | ||||||
| 77 | +| 约束项 | 说明 | | ||||||
| 78 | +|--------|------| | ||||||
| 79 | +| Surface 共享 | 主/副编码器共享同一个 Consumer Surface,仅需从 `GetSurface` 获取一次并绑定到数据源(Camera/XComponent)。 | | ||||||
| 80 | +| Window 生命周期 | `OH_VideoEncoder_GetSurface` 获取的window实例需由开发者负责释放,在所有编码器Destroy之后调用`OH_NativeWindow_DestroyNativeWindow(window)` 销毁。 | | ||||||
A ❌疑似检测出中英文错别字问题 错误原因:英文术语大小写不规范,"window"应为"Window" 建议修改为: 改动建议
![]() ![]() | |||||||
| 81 | +| 前处理独立性 | 每个编码器可分别配置不同的降采样/裁剪/丢帧策略;但每个编码器内部降采样与裁剪仍然互斥。 | | ||||||
| 82 | +| 回调独立性 | 两路的`onNewOutputBuffer`回调在不同线程中触发,需各自释放FreeOutputBuffer。 | | ||||||
| 83 | + | ||||||
| 84 | + | ||||||
| 85 | +## 开发步骤 | ||||||
| 86 | + | ||||||
| 87 | +### 创建主编码器(Primary) | ||||||
| 88 | + | ||||||
| 89 | +在创建前处理编码器之前,建议先通过能力查询接口确认当前编码器是否支持所需的前处理特性: | ||||||
| 90 | + | ||||||
| 91 | +```cpp | ||||||
| 92 | +const char *mime = OH_AVCODEC_MIMETYPE_VIDEO_AVC; | ||||||
| 93 | + | ||||||
| 94 | +OH_AVCapability *capability = OH_AVCodec_GetCapability(mime, true); | ||||||
| 95 | +if (capability == nullptr) { | ||||||
| 96 | + return -1; // 获取能力失败,可能是不支持的MIME类型。 | ||||||
| 97 | +} | ||||||
| 98 | + | ||||||
| 99 | +// 查询是否支持降采样前处理特性。 | ||||||
| 100 | +bool supportDownsampling = OH_AVCapability_IsFeatureSupported(capability, | ||||||
| 101 | + VIDEO_ENCODER_PREPROC_DOWNSAMPLING); | ||||||
| 102 | + | ||||||
| 103 | +// 查询是否支持裁剪前处理特性。 | ||||||
| 104 | +bool supportCrop = OH_AVCapability_IsFeatureSupported(capability, | ||||||
| 105 | + VIDEO_ENCODER_PREPROC_CROP); | ||||||
| 106 | + | ||||||
| 107 | +static OH_AVCodec *g_primary = nullptr; | ||||||
| 108 | +OH_AVErrCode ret = OH_VideoEncoder_CreatePrimaryWithPreproc(mime, &g_primary); | ||||||
| 109 | +if (ret != AV_ERR_OK || g_primary == nullptr) { | ||||||
| 110 | + // 异常处理。 | ||||||
| 111 | + return -1; | ||||||
| 112 | +} | ||||||
| 113 | +``` | ||||||
| 114 | + | ||||||
| 115 | +### 注册主编码器回调 | ||||||
| 116 | + | ||||||
| 117 | +和普通编码器实现一致,参考视频编码[Surface模式](video-encoding.md#surface模式)的“步骤3-调用OH_VideoEncoder_RegisterCallback()设置回调函数”。 | ||||||
| 118 | + | ||||||
| 119 | +### 配置主编码器 | ||||||
| 120 | + | ||||||
| 121 | +编码器参数配置参考视频编码[Surface模式](video-encoding.md#surface模式)的“步骤5-调用OH_VideoEncoder_Configure()配置编码器”。以下内容重点说明基础参数与前处理参数的配置。 | ||||||
| 122 | + | ||||||
| 123 | +```c++ | ||||||
| 124 | +OH_AVFormat *format = OH_AVFormat_Create(); | ||||||
| 125 | + | ||||||
| 126 | +// 基础编码参数(必填)。 | ||||||
| 127 | +OH_AVFormat_SetIntValue(format, OH_MD_KEY_WIDTH, 1920); // 输入图像的宽度。 | ||||||
| 128 | +OH_AVFormat_SetIntValue(format, OH_MD_KEY_HEIGHT, 1080); // 输入图像的高度。 | ||||||
| 129 | +OH_AVFormat_SetDoubleValue(format, OH_MD_KEY_FRAME_RATE, 30.0); // 原始帧率(丢帧功能的前置依赖)。 | ||||||
| 130 | + | ||||||
| 131 | +// 前处理参数(按需选用,先通过IsFeatureSupported确认特性支持后再配置)。 | ||||||
| 132 | +// 方案 A:降采样示例。 | ||||||
| 133 | +// 以下示例为将 1920x1080 缩放到 640x360 后编码。 | ||||||
| 134 | +// 注意:width 和 height 必须成对出现。 | ||||||
| 135 | +if (supportDownsampling) { | ||||||
| 136 | + OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODER_PREPROC_DOWNSAMPLING_WIDTH, 640); // 实际编码图像的宽度。 | ||||||
| 137 | + OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODER_PREPROC_DOWNSAMPLING_HEIGHT, 360); // 实际编码图像的高度。 | ||||||
| 138 | +} | ||||||
| 139 | + | ||||||
| 140 | +// 方案 B:裁剪示例。 | ||||||
| 141 | +// 以下示例为从 1920x1080 中裁剪中心 1280x720 区域。 | ||||||
| 142 | +// 注意:left/top/right/bottom 必须全部同时出现。 | ||||||
| 143 | +// 降采样与裁剪互斥,不能同时使用。 | ||||||
| 144 | +// 举例:left = 320, top = 180, right = 1599, bottom = 899; 对应:宽=1599-320+1=1280, 高=899-180+1=720。 | ||||||
| 145 | +// if (supportCrop) { | ||||||
| 146 | +// OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODER_PREPROC_CROP_LEFT, 320); | ||||||
| 147 | +// OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODER_PREPROC_CROP_TOP, 180); | ||||||
| 148 | +// OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODER_PREPROC_CROP_RIGHT, 1599); | ||||||
| 149 | +// OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODER_PREPROC_CROP_BOTTOM, 899); | ||||||
| 150 | +// } | ||||||
| 151 | + | ||||||
| 152 | +// 方案 C:丢帧示例。 | ||||||
| 153 | +// 示例从 30fps 降到 15fps(可单独使用或与降采样/裁剪组合)。 | ||||||
| 154 | +// OH_AVFormat_SetDoubleValue(format, OH_MD_KEY_VIDEO_ENCODER_PREPROC_DROP_TO_FRAME_RATE, 15.0); | ||||||
| 155 | + | ||||||
| 156 | +// 执行配置。 | ||||||
| 157 | +OH_AVErrCode ret = OH_VideoEncoder_Configure(g_primary, format); | ||||||
| 158 | +if (ret != AV_ERR_OK) { | ||||||
| 159 | + // 错误处理。 | ||||||
| 160 | + OH_AVFormat_Destroy(format); | ||||||
| 161 | + return -1; | ||||||
| 162 | +} | ||||||
| 163 | +OH_AVFormat_Destroy(format); | ||||||
| 164 | +``` | ||||||
| 165 | + | ||||||
| 166 | + | ||||||
| 167 | +### 从主编码器派生创建副编码器(Secondary) | ||||||
| 168 | + | ||||||
| 169 | +```c++ | ||||||
| 170 | +static OH_AVCodec *g_secondary = nullptr; | ||||||
| 171 | + | ||||||
| 172 | +// 必须在Primary成功创建之后才能创建Secondary。 | ||||||
| 173 | +OH_AVErrCode ret = OH_VideoEncoder_CreateSecondaryFromPrimary(g_primary, &g_secondary); | ||||||
| 174 | +if (ret != AV_ERR_OK || g_secondary == nullptr) { | ||||||
| 175 | + // 异常处理。 | ||||||
| 176 | + return -1; | ||||||
| 177 | +} | ||||||
| 178 | +``` | ||||||
| 179 | + | ||||||
| 180 | +### 注册副编码器回调 | ||||||
| 181 | + | ||||||
| 182 | +和普通编码器实现一致,参考视频编码[Surface模式](video-encoding.md#surface模式)的“步骤3-调用OH_VideoEncoder_RegisterCallback()设置回调函数”。 | ||||||
| 183 | + | ||||||
| 184 | +> **注意:** | ||||||
| 185 | +> | ||||||
| 186 | +> Primary 和 Secondary 的回调在**不同线程**中执行。两路都必须各自调用 `FreeOutputBuffer`,否则可能导致阻塞或饥饿。 | ||||||
| 187 | + | ||||||
| 188 | +### 配置副编码器(含差异化前处理) | ||||||
| 189 | + | ||||||
| 190 | +```c++ | ||||||
| 191 | +OH_AVFormat *secFmt = OH_AVFormat_Create(); | ||||||
| 192 | + | ||||||
| 193 | +// 输入尺寸与 Primary 一致(共享同一输入源)。 | ||||||
| 194 | +OH_AVFormat_SetIntValue(secFmt, OH_MD_KEY_WIDTH, 1920); // 同 Primary。 | ||||||
| 195 | +OH_AVFormat_SetIntValue(secFmt, OH_MD_KEY_HEIGHT, 1080); // 同 Primary。 | ||||||
| 196 | +OH_AVFormat_SetDoubleValue(secFmt, OH_MD_KEY_FRAME_RATE, 30.0); // 同 Primary。 | ||||||
| 197 | +OH_AVFormat_SetLongValue(secFmt, OH_MD_KEY_BITRATE, 1500000); // 1.5Mbps(较低码率)。 | ||||||
| 198 | + | ||||||
| 199 | +// 差异化前处理配置(Secondary 独立配置)。 | ||||||
| 200 | +// 模式 A:降采样(最常用)。 | ||||||
| 201 | +// 以下示例为将1080p缩放到480p用于预览。 | ||||||
| 202 | +// 注意:width和height必须成对出现。 | ||||||
| 203 | +OH_AVFormat_SetIntValue(secFmt, | ||||||
| 204 | + OH_MD_KEY_VIDEO_ENCODER_PREPROC_DOWNSAMPLING_WIDTH, 854); | ||||||
| 205 | +OH_AVFormat_SetIntValue(secFmt, | ||||||
| 206 | + OH_MD_KEY_VIDEO_ENCODER_PREPROC_DOWNSAMPLING_HEIGHT, 480); | ||||||
| 207 | + | ||||||
| 208 | +// 组合:降采样 + 丢帧,进一步降低预览路帧率。 | ||||||
| 209 | +OH_AVFormat_SetDoubleValue(secFmt, | ||||||
| 210 | + OH_MD_KEY_VIDEO_ENCODER_PREPROC_DROP_TO_FRAME_RATE, 15.0); | ||||||
| 211 | + | ||||||
| 212 | +// 模式 B:ROI 裁剪(替代方案)。 | ||||||
| 213 | +// 以下示例为编码画面中心区域。 | ||||||
| 214 | +// 注意:left/top/right/bottom 必须全部同时出现。 | ||||||
| 215 | +// OH_AVFormat_SetIntValue(secFmt, OH_MD_KEY_VIDEO_ENCODER_PREPROC_CROP_LEFT, 480); | ||||||
| 216 | +// OH_AVFormat_SetIntValue(secFmt, OH_MD_KEY_VIDEO_ENCODER_PREPROC_CROP_TOP, 270); | ||||||
| 217 | +// OH_AVFormat_SetIntValue(secFmt, OH_MD_KEY_VIDEO_ENCODER_PREPROC_CROP_RIGHT, 1439); | ||||||
| 218 | +// OH_AVFormat_SetIntValue(secFmt, OH_MD_KEY_VIDEO_ENCODER_PREPROC_CROP_BOTTOM, 809); | ||||||
| 219 | + | ||||||
| 220 | +OH_AVErrCode ret = OH_VideoEncoder_Configure(g_secondary, secFmt); | ||||||
| 221 | +OH_AVFormat_Destroy(secFmt); | ||||||
| 222 | + | ||||||
| 223 | +if (ret != AV_ERR_OK) { | ||||||
| 224 | + // 异常处理。 | ||||||
| 225 | + return -1; | ||||||
| 226 | +} | ||||||
| 227 | +``` | ||||||
| 228 | + | ||||||
| 229 | +### 获取共享Surface并绑定数据源 | ||||||
| 230 | + | ||||||
| 231 | +```c++ | ||||||
| 232 | +// 关键规则:只能通过主编码器获取 Surface。 | ||||||
| 233 | +OHNativeWindow *window = nullptr; | ||||||
| 234 | +OH_AVErrCode ret = OH_VideoEncoder_GetSurface(g_primary, &window); | ||||||
| 235 | +if (ret != AV_ERR_OK || window == nullptr) { | ||||||
| 236 | + // 异常处理。 | ||||||
| 237 | + return -1; | ||||||
| 238 | +} | ||||||
| 239 | + | ||||||
| 240 | +// 将 window 绑定到 Camera / XComponent 数据源。 | ||||||
| 241 | +// cameraManager->SetPreviewSurface(window)。 | ||||||
| 242 | +// nativeXComponent->SetSurface(window)。 | ||||||
| 243 | +``` | ||||||
| 244 | + | ||||||
| 245 | +> **核心规则**: | ||||||
| 246 | +> - **仅主编码器**可以合法调用`GetSurface`。 | ||||||
| 247 | +> - 副编码器调用将直接返回 `AV_ERR_OPERATE_NOT_PERMIT`。 | ||||||
| 248 | +> - 主/副共享同一个 Consumer Surface,只需获取和绑定一次。 | ||||||
| 249 | + | ||||||
| 250 | +### 完成编码器准备并启动两个编码器 | ||||||
| 251 | + | ||||||
| 252 | +```c++ | ||||||
| 253 | +OH_AVErrCode ret = OH_VideoEncoder_Prepare(g_primary); | ||||||
| 254 | +if (ret != AV_ERR_OK) { | ||||||
| 255 | + // 异常处理。 | ||||||
| 256 | +} | ||||||
| 257 | +ret = OH_VideoEncoder_Prepare(g_secondary); | ||||||
| 258 | +if (ret != AV_ERR_OK) { | ||||||
| 259 | + // 异常处理。 | ||||||
| 260 | +} | ||||||
| 261 | +ret = OH_VideoEncoder_Start(g_primary); | ||||||
| 262 | +if (ret != AV_ERR_OK) { | ||||||
| 263 | + // 异常处理。 | ||||||
| 264 | +} | ||||||
| 265 | + | ||||||
| 266 | +ret = OH_VideoEncoder_Start(g_secondary); | ||||||
| 267 | +if (ret != AV_ERR_OK) { | ||||||
| 268 | + // 异常处理。 | ||||||
| 269 | +} | ||||||
| 270 | +``` | ||||||
| 271 | + | ||||||
少了代码段结束标记``` ![]() ![]() | |||||||
| 272 | +### 运行时动态调整(可选) | ||||||
| 273 | + | ||||||
| 274 | +可在运行时通过 `SetParameter` 动态修改Secondary的前处理参数: | ||||||
| 275 | + | ||||||
| 276 | +```c++ | ||||||
| 277 | +// 动态调整副编码器的降采样目标分辨率。 | ||||||
| 278 | +void ChangeSecondaryResolution(int newWidth, int newHeight) | ||||||
| 279 | +{ | ||||||
| 280 | + OH_AVFormat *param = OH_AVFormat_Create(); | ||||||
| 281 | + OH_AVFormat_SetIntValue(param, | ||||||
| 282 | + OH_MD_KEY_VIDEO_ENCODER_PREPROC_DOWNSAMPLING_WIDTH, newWidth); | ||||||
| 283 | + OH_AVFormat_SetIntValue(param, | ||||||
| 284 | + OH_MD_KEY_VIDEO_ENCODER_PREPROC_DOWNSAMPLING_HEIGHT, newHeight); | ||||||
| 285 | + OH_VideoEncoder_SetParameter(g_secondary, param); | ||||||
| 286 | + OH_AVFormat_Destroy(param); | ||||||
| 287 | +} | ||||||
| 288 | + | ||||||
| 289 | +// 根据网络状态动态调整丢帧力度。 | ||||||
| 290 | +void AdjustSecondaryDropRate(double targetFps) | ||||||
| 291 | +{ | ||||||
| 292 | + OH_AVFormat *param = OH_AVFormat_Create(); | ||||||
| 293 | + if (targetFps > 0 && targetFps < 30.0) { | ||||||
| 294 | + OH_AVFormat_SetDoubleValue(param, | ||||||
| 295 | + OH_MD_KEY_VIDEO_ENCODER_PREPROC_DROP_TO_FRAME_RATE, targetFps); | ||||||
| 296 | + } else { | ||||||
| 297 | + // 设为 0.0 取消丢帧。 | ||||||
| 298 | + OH_AVFormat_SetDoubleValue(param, | ||||||
| 299 | + OH_MD_KEY_VIDEO_ENCODER_PREPROC_DROP_TO_FRAME_RATE, 0.0); | ||||||
| 300 | + } | ||||||
| 301 | + OH_VideoEncoder_SetParameter(g_secondary, param); | ||||||
| 302 | + OH_AVFormat_Destroy(param); | ||||||
| 303 | +} | ||||||
| 304 | +``` | ||||||
| 305 | + | ||||||
| 306 | +### 停止与销毁 | ||||||
| 307 | + | ||||||
| 308 | +```c++ | ||||||
| 309 | +// 停止编码器。 | ||||||
| 310 | +OH_VideoEncoder_Stop(g_secondary); | ||||||
| 311 | +OH_VideoEncoder_Stop(g_primary); | ||||||
| 312 | + | ||||||
| 313 | +// 发送结束标记(各发各的)。 | ||||||
| 314 | +OH_VideoEncoder_NotifyEndOfStream(g_primary); | ||||||
| 315 | +OH_VideoEncoder_NotifyEndOfStream(g_secondary); | ||||||
| 316 | + | ||||||
| 317 | +// 销毁:先Secondary后Primary。 | ||||||
| 318 | +if (g_secondary != nullptr) | ||||||
| 319 | +{ | ||||||
| 320 | + OH_VideoEncoder_Destroy(g_secondary); | ||||||
| 321 | + g_secondary = nullptr; | ||||||
| 322 | +} | ||||||
| 323 | + | ||||||
| 324 | +if (g_primary != nullptr) | ||||||
| 325 | +{ | ||||||
| 326 | + OH_VideoEncoder_Destroy(g_primary); | ||||||
| 327 | + g_primary = nullptr; | ||||||
| 328 | +} | ||||||
| 329 | + | ||||||
| 330 | +// 释放window实例。 | ||||||
| 331 | +if (window != nullptr){ | ||||||
| 332 | + OH_NativeWindow_DestroyNativeWindow(window); | ||||||
| 333 | + window = nullptr; | ||||||
| 334 | +} | ||||||
| 335 | +``` | ||||||
| 336 | + | ||||||
| 337 | +> **销毁规则**: | ||||||
| 338 | +> - **推荐顺序**:先 Destroy Secondary → 再 Destroy Primary。 | ||||||
| 339 | +> - 若违反顺序先 Destroy Primary,系统会**自动**先销毁关联的 Secondary 再释放 Primary。 | ||||||
| 340 | +> - 但仍建议显式遵循推荐顺序以确保代码清晰可控。 | ||||||
| 341 | + | ||||||
| 342 | + | ||||||
| 343 | +## 推荐配置模式 | ||||||
| 344 | + | ||||||
| 345 | +### 模式 A:纯双分辨率(最常用) | ||||||
| 346 | + | ||||||
| 347 | +- **Primary**:原始分辨率,无前处理或轻度丢帧 → 录制/归档 | ||||||
| 348 | +- **Secondary**:降采样到低分辨率 + 丢帧 → 预览/推流 | ||||||
| 349 | + | ||||||
| 350 | +### 模式 B:全帧 + ROI 裁剪 | ||||||
| 351 | + | ||||||
| 352 | +- **Primary**:全帧编码 → 完整画面归档 | ||||||
| 353 | +- **Secondary**:裁剪指定区域 → 局部分析/特写 | ||||||
| 354 | + | ||||||
| 355 | +### 模式 C:多码率自适应(ABR) | ||||||
| 356 | + | ||||||
| 357 | +- **Primary**:全帧 + 适度丢帧 → 主码流 | ||||||
| 358 | +- **Secondary**:大幅降采样 + 大幅丢帧 → 弱网备用码流 | ||||||
| 359 | + | ||||||
| 360 | +## 常见问题排查 | ||||||
| 361 | + | ||||||
| 362 | +### CreateSecondaryFromPrimary返回`AV_ERR_INVALID_VAL` | ||||||
| 363 | + | ||||||
| 364 | +**可能原因**:传入的不是有效的Primary句柄。 | ||||||
| 365 | + | ||||||
| 366 | +**解决措施**:确保Primary由`CreatePrimaryWithPreproc`创建且已成功Configure。 | ||||||
| 367 | + | ||||||
| 368 | +### CreateSecondaryFromPrimary返回`AV_ERR_OPERATE_NOT_PERMIT` | ||||||
| 369 | + | ||||||
| 370 | +**可能原因**:该Primary已挂载了Secondary。 | ||||||
| 371 | + | ||||||
| 372 | +**解决措施**:先销毁旧的Secondary,再创建新Secondary;或检查是否有遗漏的Destroy调用。 | ||||||
| 373 | + | ||||||
| 374 | +### Secondary Configure报错 | ||||||
| 375 | + | ||||||
| 376 | +**可能原因**:前处理参数不合法。 | ||||||
| 377 | + | ||||||
| 378 | +**解决措施**:检查降采样/裁剪参数完整性、范围合法性、Crop 与 Downsample 是否互斥设置。 | ||||||
| 379 | + | ||||||
| 380 | +### Secondary调用GetSurface报错 | ||||||
| 381 | + | ||||||
| 382 | +**可能原因**:使用了副编码器句柄调用。 | ||||||
| 383 | + | ||||||
| 384 | +**解决措施**:**只能通过主编码器句柄**调用GetSurface,两者返回的是同一个共享Surface。 | ||||||
| 385 | + | ||||||
| 386 | +### Secondary无输出 | ||||||
| 387 | + | ||||||
| 388 | +**可能原因**:未调用Start/Surface未绑定数据源/回调未正确注册。 | ||||||
| 389 | + | ||||||
| 390 | +**解决措施**:检查Secondary是否已Start;确认Primary的Surface已绑定到Camera/XComponent;确认回调函数非null且包含FreeOutputBuffer。 | ||||||
| 391 | + | ||||||
| 392 | +### Primary回调阻塞导致Secondary饥饿 | ||||||
| 393 | + | ||||||
| 394 | +**可能原因**:Primary的onNewOutputBuffer中耗时过长。 | ||||||
| 395 | + | ||||||
| 396 | +**解决措施**:确保Primary回调中尽快FreeOutputBuffer,避免长时间阻塞。 | ||||||
| 397 | + | ||||||
| @@ -0,0 +1,283 @@ | |||||||||||
| 1 | +# 编码支持前处理 | ||||||||||
readme文件中需补充链接 ![]() ![]() | |||||||||||
| 2 | + | ||||||||||
| 3 | +<!--Kit: AVCodec Kit--> | ||||||||||
| 4 | +<!--Subsystem: Multimedia--> | ||||||||||
| 5 | +<!--Owner: @zhanghongran--> | ||||||||||
| 6 | +<!--Designer: @dpy2650--> | ||||||||||
| 7 | +<!--Tester: @cyakee--> | ||||||||||
| 8 | +<!--Adviser: @w_Machine_cc--> | ||||||||||
| 9 | + | ||||||||||
| 10 | +从API版本26.0.0开始,支持编码前处理功能,包含降采样、裁剪和丢帧能力。 | ||||||||||
| 11 | + | ||||||||||
| 12 | +## 功能简介 | ||||||||||
| 13 | + | ||||||||||
| 14 | +编码前处理是视频编码器在输入帧进入编码管线之前执行的预处理能力,通过[OH_VideoEncoder_CreatePrimaryWithPreproc](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_createprimarywithpreproc)创建的主编码器支持以下三种前处理功能: | ||||||||||
| 15 | + | ||||||||||
| 16 | +| 功能 | 说明 | 典型场景 | | ||||||||||
| 17 | +|------|------|----------| | ||||||||||
| 18 | +| 降采样(Downsampling) | 将高分辨率帧缩放到低分辨率后送入编码器。 | 低分辨率编码输出。 | | ||||||||||
| 19 | +| 裁剪(Crop) | 从原始画面中提取指定矩形区域进行编码。 | ROI区域关注、局部特写。 | | ||||||||||
| 20 | +| 丢帧(Drop Frame) | 按目标帧率选择性丢弃输入帧。 | 降低带宽占用、自适应码率。 | | ||||||||||
| 21 | + | ||||||||||
| 22 | +> **互斥规则**:降采样参数与裁剪参数**不能同时使用**。丢帧可与降采样或裁剪**组合使用**。 | ||||||||||
| 23 | + | ||||||||||
| 24 | + | ||||||||||
| 25 | +### 使用场景 | ||||||||||
| 26 | + | ||||||||||
| 27 | +**场景一:低分辨率编码输出** | ||||||||||
| 28 | + | ||||||||||
| 29 | +通过降采样缩放到低分辨率编码输出。 | ||||||||||
| 30 | + | ||||||||||
| 31 | +**场景二:ROI 区域关注编码** | ||||||||||
| 32 | + | ||||||||||
| 33 | +- 对大尺寸摄像头画面使用**裁剪**功能,仅提取感兴趣区域进行编码。 | ||||||||||
| 34 | + | ||||||||||
| 35 | +**场景三:自适应码率(ABR)** | ||||||||||
| 36 | + | ||||||||||
| 37 | +- 网络正常时:不丢帧或轻度丢帧,保证画质。 | ||||||||||
| 38 | +- 网络拥堵时:通过`SetParameter`动态加大丢帧力度,降低输出帧率以节省带宽。 | ||||||||||
| 39 | +- 网络恢复后:将丢帧目标设为`0.00`取消丢帧。 | ||||||||||
| 40 | + | ||||||||||
| 41 | +### 约束与限制 | ||||||||||
| 42 | + | ||||||||||
| 43 | +**基本约束** | ||||||||||
| 44 | + | ||||||||||
| 45 | +| 约束项 | 说明 | | ||||||||||
| 46 | +|--------|------| | ||||||||||
| 47 | +| 支持的MIME类型 | 和普通视频编码器支持范围一致,可通过`OH_AVCodec_GetCapability`查询是否支持指定MIME类型编码器。 | | ||||||||||
| 48 | +| 创建方式 | 必须通过`OH_VideoEncoder_CreatePrimaryWithPreproc`创建,不支持普通创建方式。 | | ||||||||||
| 49 | +| 数据通路 | **仅支持 Surface 异步模式**,Buffer模式和同步模式均不支持(返回`AV_ERR_OPERATE_NOT_PERMIT`)。 | | ||||||||||
| 50 | +| 随帧参数 | 不支持`RegisterParameterCallback`接口(返回`AV_ERR_OPERATE_NOT_PERMIT`)。 | | ||||||||||
| 51 | + | ||||||||||
| 52 | +**降采样(Downsampling)约束** | ||||||||||
| 53 | + | ||||||||||
| 54 | +| 序号 | 约束规则 | | ||||||||||
| 55 | +|------|----------| | ||||||||||
| 56 | +| 1 | 必须成对配置:降采样目标宽度和降采样目标高度必须同时配置或同时设置。若仅配置其中一个,Configure/SetParameter 将返回 `AV_ERR_INVALID_VAL`。 | | ||||||||||
| 57 | +| 2 | 关闭条件:当降采样目标宽度与降采样目标高度均配置为合法的零值时,降采样功能被关闭。 | | ||||||||||
| 58 | +| 3 | 有效范围:当降采样目标宽度与降采样目标高度在支持的范围内时,降采样功能被启用。建议通过`OH_AVCapability_IsVideoSizeSupported`接口查询支持的降采样范围。 | | ||||||||||
| 59 | +| 4 | 越界处理:当降采样目标宽度或降采样目标高度不在支持范围内时,Configure/SetParameter返回`AV_ERR_INVALID_VAL`。 | | ||||||||||
| 60 | +| 5 | 与裁剪互斥:不能与裁剪参数(`CROP_LEFT/TOP/RIGHT/BOTTOM`)同时使用。若同时设置了降采样和裁剪参数,返回`AV_ERR_INVALID_VAL`。 | | ||||||||||
| 61 | + | ||||||||||
| 62 | +**裁剪(Crop)约束** | ||||||||||
| 63 | + | ||||||||||
| 64 | +坐标系统说明: | ||||||||||
| 65 | +- `(left, top)` 为裁剪矩形的左上角坐标。 | ||||||||||
| 66 | +- `(right, bottom)` 为裁剪矩形的右下角坐标。 | ||||||||||
| 67 | +- 行/列索引从 **0** 开始。 | ||||||||||
| 68 | +- **裁剪区域宽度 = right - left + 1**。 | ||||||||||
| 69 | +- **裁剪区域高度 = bottom - top + 1**。 | ||||||||||
| 70 | + | ||||||||||
| 71 | +| 序号 | 约束规则 | | ||||||||||
| 72 | +|------|----------| | ||||||||||
| 73 | +| 1 | 必须完整配置:left、top、right、bottom四个参数必须同时配置。若仅配置其中部分参数,返回`AV_ERR_INVALID_VAL`。 | | ||||||||||
| 74 | +| 2 | 关闭条件:当 left、top、right、bottom **全部为0** 时,裁剪功能被关闭。 | | ||||||||||
| 75 | +| 3 | 有效范围:当裁剪区域宽度、高度在支持的范围内时,裁剪功能被启用。建议通过`OH_AVCapability_IsVideoSizeSupported`查询支持的裁剪范围。 | | ||||||||||
| 76 | +| 4 | 越界处理:当裁剪区域宽度、高度不在支持范围内时,返回`AV_ERR_INVALID_VAL`。 | | ||||||||||
| 77 | +| 5 | 与降采样互斥:不能与降采样参数(`DOWNSAMPLING_WIDTH/HEIGHT`)同时使用。若同时设置,返回`AV_ERR_INVALID_VAL`。 | | ||||||||||
| 78 | +| 6 | 行为效果:裁剪启用时,编码器仅对输入帧的裁剪区域进行编码,裁剪矩形之外的内容将被丢弃。 | | ||||||||||
| 79 | + | ||||||||||
| 80 | +**丢帧(Drop Frame)约束** | ||||||||||
| 81 | + | ||||||||||
| 82 | +| 序号 | 约束规则 | | ||||||||||
| 83 | +|------|----------| | ||||||||||
| 84 | +| 1 | 前置条件:调用方必须已设置原始帧率(`OH_MD_KEY_FRAME_RATE`)。 | | ||||||||||
| 85 | +| 2 | 精度要求:数值精度保留到小数点后 **2 位**(采用**四舍五入**方式)。 | | ||||||||||
| 86 | +| 3 | 值为 0.00:丢帧功能被关闭。 | | ||||||||||
| 87 | +| 4 | 合法正值:设置为大于 0 且小于原始帧率的正数时,将按设定帧率进行丢帧。 | | ||||||||||
| 88 | +| 5 | 非法值:设置为负数或大于等于原始帧率的值时,返回 `AV_ERR_INVALID_VAL`。 | | ||||||||||
| 89 | +| 6 | 可组合性:可与降采样参数**同时使用**。 | | ||||||||||
| 90 | +| 7 | 可组合性:可与裁剪参数**同时使用** 。| | ||||||||||
AA ❌疑似检测出标点符号格式问题 错误原因:句号前有空格,不符合中文标点规范。 建议修改为: 改动建议
![]() ![]() ❌疑似检测出标点符号问题 错误原因:行末有多余的空格 建议修改为: 改动建议
![]() ![]() | |||||||||||
| 91 | + | ||||||||||
| 92 | +**接口可用性约束** | ||||||||||
| 93 | + | ||||||||||
| 94 | +对于通过OH_VideoEncoder_CreatePrimaryWithPreproc创建的编码器,以下接口的可用性如下: | ||||||||||
| 95 | + | ||||||||||
| 96 | +| 接口 | 是否可用 | 备注 | | ||||||||||
| 97 | +|------|:--------:|------| | ||||||||||
| 98 | +| [OH_VideoEncoder_RegisterCallback](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_registercallback) | √ | 支持。 | | ||||||||||
| 99 | +| [OH_VideoEncoder_RegisterParameterCallback](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_registerparametercallback) | × | 不支持随帧参数配置,返回AV_ERR_OPERATE_NOT_PERMIT。 | | ||||||||||
| 100 | +| [OH_VideoEncoder_PushInputParameter](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_pushinputparameter) | × | 不支持随帧参数配置,返回AV_ERR_OPERATE_NOT_PERMIT。 | | ||||||||||
| 101 | +| [OH_VideoEncoder_Configure](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_configure) | √ | 支持,可配置含前处理参数。 | | ||||||||||
| 102 | +| [OH_VideoEncoder_GetSurface](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_getsurface) | √ | 支持。 | | ||||||||||
| 103 | +| [OH_VideoEncoder_Prepare](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_prepare) | √ | 支持,准备内部资源。 | | ||||||||||
| 104 | +| [OH_VideoEncoder_Start](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_start) | √ | 支持。 | | ||||||||||
| 105 | +| [OH_VideoEncoder_Stop](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_stop) | √ | 支持。 | | ||||||||||
| 106 | +| [OH_VideoEncoder_Flush](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_flush) | √ | 支持。 | | ||||||||||
| 107 | +| [OH_VideoEncoder_Reset](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_reset) | √ | 支持,重置到 Initialized状态。 | | ||||||||||
| 108 | +| [OH_VideoEncoder_SetParameter](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_setparameter) | √ | 支持,运行时动态调整前处理等参数。 | | ||||||||||
| 109 | +| [OH_VideoEncoder_NotifyEndOfStream](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_notifyendofstream) | √ | 支持,通知编码器EOS信息。 | | ||||||||||
| 110 | +| [OH_VideoEncoder_FreeOutputBuffer](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_freeoutputbuffer) | √ | 支持。 | | ||||||||||
| 111 | +| [OH_VideoEncoder_GetInputDescription](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_getinputdescription) | √ | 支持,包含前处理元数据。 | | ||||||||||
| 112 | +| [OH_VideoEncoder_GetOutputDescription](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_getoutputdescription) | √ | 支持。 | | ||||||||||
| 113 | +| [OH_VideoEncoder_IsValid](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_isvalid) | √ | 支持。 | | ||||||||||
| 114 | +| [OH_VideoEncoder_Destroy](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_destroy) | √ | 支持,销毁编码器实例。 | | ||||||||||
| 115 | +| [OH_VideoEncoder_PushInputData](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_pushinputdata) | × | Buffer模式不支持。 | | ||||||||||
| 116 | +| [OH_VideoEncoder_PushInputBuffer](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_pushinputbuffer) | × | Buffer模式不支持。 | | ||||||||||
| 117 | +| [OH_VideoEncoder_QueryInputBuffer](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_queryinputbuffer) | × | 同步模式不支持。 | | ||||||||||
| 118 | +| [OH_VideoEncoder_QueryOutputBuffer](../../reference/apis-avcodec-kit/capi-native-avcodec-videoencoder-h.md#oh_videoencoder_queryoutputbuffer) | × | 同步模式不支持。 | | ||||||||||
| 119 | + | ||||||||||
| 120 | +## 开发步骤 | ||||||||||
| 121 | + | ||||||||||
| 122 | +支持前处理编码和普通编码器的使用流程一致,主要差异点在于创建方式、支持前处理参数配置以及动态更新。本文主要对差异点进行详细说明。完整编码器开发流程参考视频编码[Surface模式](video-encoding.md#surface模式)。 | ||||||||||
| 123 | + | ||||||||||
| 124 | +### 创建支持前处理的编码器 | ||||||||||
每个标题下面补充简述 ![]() ![]() | |||||||||||
| 125 | + | ||||||||||
| 126 | +在创建前处理编码器之前,建议先通过能力查询接口确认当前编码器是否支持所需的前处理特性: | ||||||||||
| 127 | +```c++ | ||||||||||
| 128 | +const char *mime = OH_AVCODEC_MIMETYPE_VIDEO_AVC; | ||||||||||
| 129 | + | ||||||||||
| 130 | +OH_AVCapability *capability = OH_AVCodec_GetCapability(mime, true); | ||||||||||
| 131 | +if (capability == nullptr) { | ||||||||||
| 132 | + return -1; // 获取能力失败,可能是不支持的MIME类型。 | ||||||||||
| 133 | +} | ||||||||||
| 134 | + | ||||||||||
| 135 | +// 查询是否支持降采样前处理特性。 | ||||||||||
| 136 | +bool supportDownsampling = OH_AVCapability_IsFeatureSupported(capability, | ||||||||||
| 137 | + VIDEO_ENCODER_PREPROC_DOWNSAMPLING); | ||||||||||
| 138 | + | ||||||||||
| 139 | +// 查询是否支持裁剪前处理特性。 | ||||||||||
| 140 | +bool supportCrop = OH_AVCapability_IsFeatureSupported(capability, | ||||||||||
| 141 | + VIDEO_ENCODER_PREPROC_CROP); | ||||||||||
| 142 | + | ||||||||||
| 143 | +OH_AVCodec *encoder = nullptr; | ||||||||||
| 144 | +OH_AVErrCode ret = OH_VideoEncoder_CreatePrimaryWithPreproc(mime, &encoder); | ||||||||||
| 145 | +if (ret != AV_ERR_OK || encoder == nullptr) { | ||||||||||
| 146 | + // 异常处理。 | ||||||||||
| 147 | + return -1; | ||||||||||
| 148 | +} | ||||||||||
| 149 | +``` | ||||||||||
| 150 | + | ||||||||||
| 151 | +### 注册回调 | ||||||||||
| 152 | + | ||||||||||
| 153 | +参考视频编码[Surface模式](video-encoding.md#surface模式)的“步骤3-调用OH_VideoEncoder_RegisterCallback()设置回调函数”。 | ||||||||||
| 154 | + | ||||||||||
| 155 | +### 配置编码参数与前处理参数 | ||||||||||
| 156 | + | ||||||||||
| 157 | +编码器参数配置参考视频编码[Surface模式](video-encoding.md#surface模式)的“步骤5-调用OH_VideoEncoder_Configure()配置编码器”。以下内容重点说明基础参数与前处理参数的配置。 | ||||||||||
| 158 | + | ||||||||||
| 159 | +```c++ | ||||||||||
| 160 | +OH_AVFormat *format = OH_AVFormat_Create(); | ||||||||||
| 161 | + | ||||||||||
| 162 | +// 基础编码参数(必填)。 | ||||||||||
| 163 | +OH_AVFormat_SetIntValue(format, OH_MD_KEY_WIDTH, 1920); // 输入图像的宽度。 | ||||||||||
| 164 | +OH_AVFormat_SetIntValue(format, OH_MD_KEY_HEIGHT, 1080); // 输入图像的高度。 | ||||||||||
| 165 | +OH_AVFormat_SetDoubleValue(format, OH_MD_KEY_FRAME_RATE, 30.0); // 原始帧率(丢帧功能的前置依赖)。 | ||||||||||
| 166 | + | ||||||||||
| 167 | +// 前处理参数(按需选用,先通过IsFeatureSupported确认特性支持后再配置)。 | ||||||||||
| 168 | +// 方案 A:降采样示例。 | ||||||||||
| 169 | +// 以下示例为将 1920x1080 缩放到 640x360 后编码。 | ||||||||||
| 170 | +// 注意:width 和 height 必须成对出现。 | ||||||||||
| 171 | +if (supportDownsampling) { | ||||||||||
| 172 | + OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODER_PREPROC_DOWNSAMPLING_WIDTH, 640); // 降采样目标宽度。 | ||||||||||
| 173 | + OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODER_PREPROC_DOWNSAMPLING_HEIGHT, 360); // 降采样目标高度。 | ||||||||||
| 174 | +} | ||||||||||
| 175 | + | ||||||||||
| 176 | +// 方案 B:裁剪示例。 | ||||||||||
| 177 | +// 以下示例为从 1920x1080 中裁剪中心 1280x720 区域。 | ||||||||||
| 178 | +// 注意:left/top/right/bottom 必须全部同时出现。 | ||||||||||
| 179 | +// 降采样与裁剪互斥,不能同时使用。 | ||||||||||
| 180 | +// 举例:left = 320, top = 180, right = 1599, bottom = 899; 对应:宽=1599-320+1=1280, 高=899-180+1=720。 | ||||||||||
| 181 | +// if (supportCrop) { | ||||||||||
| 182 | +// OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODER_PREPROC_CROP_LEFT, 320); | ||||||||||
| 183 | +// OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODER_PREPROC_CROP_TOP, 180); | ||||||||||
| 184 | +// OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODER_PREPROC_CROP_RIGHT, 1599); | ||||||||||
| 185 | +// OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODER_PREPROC_CROP_BOTTOM, 899); | ||||||||||
| 186 | +// } | ||||||||||
| 187 | + | ||||||||||
| 188 | +// 方案 C:丢帧示例。 | ||||||||||
| 189 | +// 以下示例为从 30fps 降到 15fps(可单独使用或与降采样/裁剪组合)。 | ||||||||||
| 190 | +// OH_AVFormat_SetDoubleValue(format, OH_MD_KEY_VIDEO_ENCODER_PREPROC_DROP_TO_FRAME_RATE, 15.0); | ||||||||||
| 191 | + | ||||||||||
| 192 | +// 执行配置。 | ||||||||||
| 193 | +OH_AVErrCode ret = OH_VideoEncoder_Configure(encoder, format); | ||||||||||
| 194 | +if (ret != AV_ERR_OK) { | ||||||||||
| 195 | + // 错误处理。 | ||||||||||
| 196 | + OH_AVFormat_Destroy(format); | ||||||||||
| 197 | + return -1; | ||||||||||
| 198 | +} | ||||||||||
| 199 | +OH_AVFormat_Destroy(format); | ||||||||||
| 200 | +``` | ||||||||||
| 201 | + | ||||||||||
| 202 | +### 获取 Surface | ||||||||||
| 203 | + | ||||||||||
| 204 | +```c++ | ||||||||||
| 205 | +// 关键:只能通过主编码器句柄获取 Surface。 | ||||||||||
| 206 | +OHNativeWindow *window = nullptr; | ||||||||||
| 207 | +OH_AVErrCode ret = OH_VideoEncoder_GetSurface(encoder, &window); | ||||||||||
| 208 | +if (ret != AV_ERR_OK || window == nullptr) { | ||||||||||
| 209 | + // 异常处理 | ||||||||||
| 210 | + return -1; | ||||||||||
| 211 | +} | ||||||||||
| 212 | + | ||||||||||
| 213 | +// 将window绑定到Camera/XComponent等数据源。 | ||||||||||
| 214 | +// 例如:cameraManager->SetPreviewSurface(window)。 | ||||||||||
| 215 | +// nativeXComponent->SetSurface(window)。 | ||||||||||
| 216 | +``` | ||||||||||
| 217 | + | ||||||||||
| 218 | +> **重要规则**: | ||||||||||
| 219 | +> - **仅主编码器**可以调用 `GetSurface`,副编码器调用将返回`AV_ERR_OPERATE_NOT_PERMIT`。 | ||||||||||
| 220 | +> - 对于一入二出场景,只需获取一次Surface即可(主/副共享同一输入源)。 | ||||||||||
| 221 | + | ||||||||||
| 222 | +### 准备、启动、写入编码图像 | ||||||||||
| 223 | +参考视频编码[Surface模式](video-encoding.md#surface模式)的步骤7、8、10。 | ||||||||||
| 224 | + | ||||||||||
| 225 | +### 运行时动态调整(可选) | ||||||||||
| 226 | + | ||||||||||
| 227 | +可通过 `SetParameter` 在运行时动态修改前处理参数: | ||||||||||
| 228 | + | ||||||||||
| 229 | +```c++ | ||||||||||
| 230 | +// 动态调整丢帧目标帧率(例如响应网络状态变化)。 | ||||||||||
| 231 | +// 网络拥堵可以选择大幅丢帧,如:AdjustDropFrameRate(encoder, 10.0); | ||||||||||
| 232 | +// 网络恢复可取消丢帧:AdjustDropFrameRate(encoder, 0.0); | ||||||||||
| 233 | +void AdjustDropFrameRate(OH_AVCodec *enc, double targetFps) | ||||||||||
| 234 | +{ | ||||||||||
| 235 | + OH_AVFormat *param = OH_AVFormat_Create(); | ||||||||||
| 236 | + if (targetFps > 0) { | ||||||||||
| 237 | + OH_AVFormat_SetDoubleValue(param, | ||||||||||
| 238 | + OH_MD_KEY_VIDEO_ENCODER_PREPROC_DROP_TO_FRAME_RATE, targetFps); | ||||||||||
| 239 | + } else { | ||||||||||
| 240 | + // 设置为 0.00关闭丢帧功能。 | ||||||||||
| 241 | + OH_AVFormat_SetDoubleValue(param, | ||||||||||
| 242 | + OH_MD_KEY_VIDEO_ENCODER_PREPROC_DROP_TO_FRAME_RATE, 0.0); | ||||||||||
| 243 | + } | ||||||||||
| 244 | + OH_VideoEncoder_SetParameter(enc, param); | ||||||||||
| 245 | + OH_AVFormat_Destroy(param); | ||||||||||
| 246 | +} | ||||||||||
| 247 | + | ||||||||||
| 248 | +// 动态调整降采样目标尺寸。 | ||||||||||
| 249 | +void AdjustDownsampling(OH_AVCodec *enc, int newWidth, int newHeight) | ||||||||||
| 250 | +{ | ||||||||||
| 251 | + OH_AVFormat *param = OH_AVFormat_Create(); | ||||||||||
| 252 | + OH_AVFormat_SetIntValue(param, OH_MD_KEY_VIDEO_ENCODER_PREPROC_DOWNSAMPLING_WIDTH, newWidth); | ||||||||||
| 253 | + OH_AVFormat_SetIntValue(param, OH_MD_KEY_VIDEO_ENCODER_PREPROC_DOWNSAMPLING_HEIGHT, newHeight); | ||||||||||
| 254 | + OH_VideoEncoder_SetParameter(enc, param); | ||||||||||
| 255 | + OH_AVFormat_Destroy(param); | ||||||||||
| 256 | +} | ||||||||||
| 257 | +``` | ||||||||||
| 258 | +其他编码器仍可动态配置,参考视频编码[Surface模式](video-encoding.md#surface模式)的步骤9。 | ||||||||||
| 259 | + | ||||||||||
| 260 | +### 通知编码结束、释放编码帧、销毁编码器 | ||||||||||
| 261 | +和普通编码器一致,参考视频编码[Surface模式](video-encoding.md#surface模式)的步骤12、13、17。 | ||||||||||
| 262 | + | ||||||||||
| 263 | +## GetInputDescription 查询 | ||||||||||
| 264 | + | ||||||||||
| 265 | +启用前处理后,可通过 `GetInputDescription`查询预处理后的实际输入信息及配置参数: | ||||||||||
| 266 | + | ||||||||||
| 267 | +```c++ | ||||||||||
| 268 | +OH_AVFormat *inputDesc = OH_VideoEncoder_GetInputDescription(encoder); | ||||||||||
| 269 | +if (inputDesc != nullptr) { | ||||||||||
| 270 | + int32_t originWidth = 0, originHeight = 0; | ||||||||||
| 271 | + OH_AVFormat_GetIntValue(inputDesc, OH_MD_KEY_WIDTH, &originWidth); | ||||||||||
| 272 | + OH_AVFormat_GetIntValue(inputDesc, OH_MD_KEY_HEIGHT, &originHeight); | ||||||||||
| 273 | + | ||||||||||
| 274 | + // 可查询当前生效的前处理配置参数。 | ||||||||||
| 275 | + int32_t dsWidth = 0; | ||||||||||
| 276 | + if (OH_AVFormat_GetIntValue(inputDesc, | ||||||||||
| 277 | + OH_MD_KEY_VIDEO_ENCODER_PREPROC_DOWNSAMPLING_WIDTH, &dsWidth)) { | ||||||||||
| 278 | + } | ||||||||||
| 279 | +} | ||||||||||
| 280 | +``` | ||||||||||
| 281 | + | ||||||||||
| 282 | + | ||||||||||
| 283 | + | ||||||||||


图片请检查是否使用鸿蒙字体,然后箭头要对齐。第一个框也需要以中英文同时体现,分发这里可以拉长一点。 这些问题改完,可以发给金学荣 60081993审核一下UX效果。