• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 视频编码
2
3<!--Kit: AVCodec Kit-->
4<!--Subsystem: Multimedia-->
5<!--Owner: @zhanghongran-->
6<!--Designer: @dpy2650--->
7<!--Tester: @cyakee-->
8<!--Adviser: @zengyawen-->
9
10开发者可以调用本模块的Native API接口,完成视频编码,即将未压缩的视频数据压缩成视频码流。
11
12<!--RP3--><!--RP3End-->
13
14当前支持的编码能力请参考[AVCodec支持的格式](avcodec-support-formats.md#视频编码)。
15
16<!--RP1--><!--RP1End-->
17
18视频编码支持以下能力:
19
20<!--RP4-->
21|          支持的能力                       |                              使用简述                                            |
22| --------------------------------------- | ---------------------------------------------------------------------------------- |
23| 分层编码 <br> 设置LTR帧、参考帧                      | 具体可参考:[时域可分层视频编码](video-encoding-temporal-scalability.md)        |
24| 支持历史帧repeat编码                    | 具体可参考:[OH_MD_KEY_VIDEO_ENCODER_REPEAT_PREVIOUS_FRAME_AFTER](../../reference/apis-avcodec-kit/_codec_base.md#oh_md_key_video_encoder_repeat_previous_frame_after)、<br> [OH_MD_KEY_VIDEO_ENCODER_REPEAT_PREVIOUS_MAX_COUNT](../../reference/apis-avcodec-kit/_codec_base.md#oh_md_key_video_encoder_repeat_previous_max_count)    |
25<!--RP4End-->
26
27## 限制约束
28
291. Buffer模式不支持10bit的图像数据。
302. 由于硬件编码器资源有限,每个编码器在使用完毕后都必须调用OH_VideoEncoder_Destroy接口来销毁实例并释放资源。
313. Flush,Reset,Stop,Destroy在非回调线程中执行时,会等待所有回调执行完成后,将执行结果返回给开发者。
324. 一旦调用Flush,Reset,Stop接口,会触发系统回收OH_AVBuffer,开发者不应对之前回调函数获取到的OH_AVBuffer继续进行操作。
335. Buffer模式和Surface模式使用方式一致的接口,所以只提供了Surface模式的示例。
346. 在Buffer模式下,开发者通过输入回调函数OH_AVCodecOnNeedInputBuffer获取到OH_AVBuffer的指针实例后,必须通过调用OH_VideoEncoder_PushInputBuffer接口来通知系统该实例已被使用完毕。这样系统才能够将该实例里面的数据进行编码。如果开发者在调用OH_AVBuffer_GetNativeBuffer接口时获取到OH_NativeBuffer指针实例,并且该实例的生命周期超过了当前的OH_AVBuffer指针实例,那么需要进行一次数据的拷贝操作。在这种情况下,开发者需要自行管理新生成的OH_NativeBuffer实例的生命周期,确保其正确使用和释放。
35<!--RP14--><!--RP14End-->
36
37## surface输入与buffer输入
38
391. 两者的数据来源不同。
40
412. 两者的适用场景不同:
42    - surface输入是指用OHNativeWindow来传递输入数据,可以与其他模块对接,例如相机模块。
43    - buffer输入是指有一块预先分配好的内存区域,开发者需要将原始数据拷贝进这块内存区域中。更适用于从文件中读取视频数据等场景。
44
453. 在接口调用的过程中,两种方式的接口调用方式基本一致,但存在以下差异点:
46    - Buffer模式下,开发者通过OH_VideoEncoder_PushInputBuffer接口输入数据;Surface模式下,开发者应在编码器就绪前调用OH_VideoEncoder_GetSurface接口,获取OHNativeWindow用于传递视频数据。
47    - Buffer模式下,开发者通过OH_AVBuffer中的attr传入结束flag,编码器读取到尾帧后,停止编码;Surface模式下,需要调用OH_VideoEncoder_NotifyEndOfStream接口通知编码器输入流结束。
48
494. Surface模式的数据流转性能优于Buffer模式。
50
51两种模式的开发步骤详细说明请参考:[Surface模式](#surface模式)和[Buffer模式](#buffer模式)。
52
53## 状态机调用关系
54
55如下为状态机调用关系图:
56
57![Invoking relationship of state](figures/state-invocation.png)
58
591. 有两种方式可以使编码器进入Initialized状态:
60   - 初始创建编码器实例时,编码器处于Initialized状态。
61   - 任何状态下,调用OH_VideoEncoder_Reset接口,编码器将会移回Initialized状态。
62
632. Initialized状态下,调用OH_VideoEncoder_Configure接口配置编码器,配置成功后编码器进入Configured状态。
643. Configured状态下,调用OH_VideoEncoder_Prepare()进入Prepared状态。
654. Prepared状态下,调用OH_VideoEncoder_Start接口使编码器进入Executing状态:
66   - 处于Executing状态时,调用OH_VideoEncoder_Stop接口可以使编码器返回到Prepared状态。
67
685. 在极少数情况下,编码器可能会遇到错误并进入Error状态。编码器的错误传递,可以通过队列操作返回无效值或者抛出异常:
69   - Error状态下,可以调用OH_VideoEncoder_Reset接口将编码器移到Initialized状态;或者调用OH_VideoEncoder_Destroy接口移动到最后的Released状态。
70
716. Executing 状态具有三个子状态:Flushed、Running和End-of-Stream:
72   - 在调用了OH_VideoEncoder_Start接口之后,编码器立即进入Running子状态。
73   - 对于处于Executing状态的编码器,可以调用OH_VideoEncoder_Flush接口返回到Flushed子状态。
74   - 当待处理数据全部传递给编码器后,可以在input buffers队列中为最后一个入队的input buffer中添加[AVCODEC_BUFFER_FLAGS_EOS](../../reference/apis-avcodec-kit/_core.md#oh_avcodecbufferflags-1)标记,遇到这个标记时,编码器会转换为End-of-Stream子状态。在此状态下,编码器不再接受新的输入,但是仍然会继续生成输出,直到输出到达尾帧。
75
767. 使用完编码器后,必须调用OH_VideoEncoder_Destroy接口销毁编码器实例,使编码器进入Released状态。
77
78## 开发指导
79
80详细的API说明请参考[API文档](../../reference/apis-avcodec-kit/_video_encoder.md)。
81如下为视频编码调用关系图:
82
83- 虚线表示可选。
84
85- 实线表示必选。
86
87![Invoking relationship of video encode stream](figures/video-encode.png)
88
89### 在 CMake 脚本中链接动态库
90
91```cmake
92target_link_libraries(sample PUBLIC libnative_media_codecbase.so)
93target_link_libraries(sample PUBLIC libnative_media_core.so)
94target_link_libraries(sample PUBLIC libnative_media_venc.so)
95```
96
97> **说明:**
98>
99> 上述'sample'字样仅为示例,此处由开发者根据实际工程目录自定义。
100>
101
102### 定义基础结构
103
104本部分示例代码按照C++17标准编写,仅作参考。开发者可以参考此部分,定义自己的buffer对象。
105
1061. 添加头文件。
107
108    ```c++
109    #include <condition_variable>
110    #include <memory>
111    #include <mutex>
112    #include <queue>
113    #include <shared_mutex>
114    ```
115
1162. 编码器回调buffer的信息。
117
118    ```c++
119    struct CodecBufferInfo {
120        CodecBufferInfo(uint32_t index, OH_AVBuffer *buffer): index(index), buffer(buffer), isValid(true) {}
121        CodecBufferInfo(uint32_t index, OH_AVFormat *parameter): index(index), parameter(parameter), isValid(true) {}
122        // 回调buffer。
123        OH_AVBuffer *buffer = nullptr;
124        // Surface模式下,输入回调的随帧参数,需要注册随帧通路后使用。
125        OH_AVFormat *parameter = nullptr;
126        // 回调buffer对应的index。
127        uint32_t index = 0;
128        // 判断当前buffer信息是否有效。
129        bool isValid = true;
130    };
131    ```
132
1333. 编码输入输出队列。
134
135    ```c++
136    class CodecBufferQueue {
137    public:
138        // 将回调buffer的信息传入队列。
139        void Enqueue(const std::shared_ptr<CodecBufferInfo> bufferInfo)
140        {
141            std::unique_lock<std::mutex> lock(mutex_);
142            bufferQueue_.push(bufferInfo);
143            cond_.notify_all();
144        }
145
146        // 获取回调buffer的信息。
147        std::shared_ptr<CodecBufferInfo> Dequeue(int32_t timeoutMs = 1000)
148        {
149            std::unique_lock<std::mutex> lock(mutex_);
150            (void)cond_.wait_for(lock, std::chrono::milliseconds(timeoutMs), [this]() { return !bufferQueue_.empty(); });
151            if (bufferQueue_.empty()) {
152                return nullptr;
153            }
154            std::shared_ptr<CodecBufferInfo> bufferInfo = bufferQueue_.front();
155            bufferQueue_.pop();
156            return bufferInfo;
157        }
158
159        // 清空队列,之前的回调buffer设置为不可用。
160        void Flush()
161        {
162            std::unique_lock<std::mutex> lock(mutex_);
163            while (!bufferQueue_.empty()) {
164                std::shared_ptr<CodecBufferInfo> bufferInfo = bufferQueue_.front();
165                // Flush、Stop、Reset、Destroy操作之后,之前回调的buffer信息设置为无效。
166                bufferInfo->isValid = false;
167                bufferQueue_.pop();
168            }
169        }
170
171    private:
172        std::mutex mutex_;
173        std::condition_variable cond_;
174        std::queue<std::shared_ptr<CodecBufferInfo>> bufferQueue_;
175    };
176    ```
177
1784. 全局变量。
179
180    仅作参考,可以根据实际情况将其封装到对象中。
181
182    ```c++
183    // 视频帧宽度。
184    int32_t width = 320;
185    // 视频帧高度。
186    int32_t height = 240;
187    // 视频像素格式。
188     OH_AVPixelFormat pixelFormat = AV_PIXEL_FORMAT_NV12;
189    // 视频宽跨距。
190    int32_t widthStride = 0;
191    // 视频高跨距。
192    int32_t heightStride = 0;
193    // 编码器实例指针。
194    OH_AVCodec *videoEnc = nullptr;
195    // 编码器同步锁。
196    std::shared_mutex codecMutex;
197    // 编码器输入队列。
198    CodecBufferQueue inQueue;
199    // 编码器输出队列。
200    CodecBufferQueue outQueue;
201    ```
202
203### Surface模式
204
205参考以下示例代码,可以完成Surface模式下视频编码的全流程,实现异步模式的数据轮转。此处以输入surface数据,编码成H.264格式为例。
206
2071. 添加头文件。
208
209    ```c++
210    #include <multimedia/player_framework/native_avcodec_videoencoder.h>
211    #include <multimedia/player_framework/native_avcapability.h>
212    #include <multimedia/player_framework/native_avcodec_base.h>
213    #include <multimedia/player_framework/native_avformat.h>
214    #include <multimedia/player_framework/native_avbuffer.h>
215    #include <fstream>
216    ```
217
2182. 创建编码器实例。
219
220    开发者可以通过名称或媒体类型创建编码器。示例中的变量说明如下:
221
222    - videoEnc:视频编码器实例的指针;
223    - capability:编解码器能力查询实例的指针;
224    - OH_AVCODEC_MIMETYPE_VIDEO_AVC:AVC格式视频编解码器。
225
226    创建方式示例如下:
227
228    ```c++
229    // 通过codec name创建编码器,应用有特殊需求,比如选择支持某种分辨率规格的编码器,可先查询capability,再根据codec name创建编码器。
230    OH_AVCapability *capability = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_VIDEO_AVC, true);
231    // 创建硬件编码器实例。
232    OH_AVCapability *capability= OH_AVCodec_GetCapabilityByCategory(OH_AVCODEC_MIMETYPE_VIDEO_AVC, true, HARDWARE);
233    const char *codecName = OH_AVCapability_GetName(capability);
234    OH_AVCodec *videoEnc = OH_VideoEncoder_CreateByName(codecName);
235    ```
236
237    ```c++
238    // 通过MIME TYPE创建编码器,只能创建系统推荐的特定编解码器。
239    // 只能创建硬件编码器。
240    OH_AVCodec *videoEnc = OH_VideoEncoder_CreateByMime(OH_AVCODEC_MIMETYPE_VIDEO_AVC);
241    ```
242
2433. 调用OH_VideoEncoder_RegisterCallback()设置回调函数。
244
245    注册回调函数指针集合OH_AVCodecCallback,包括:
246
247    - OH_AVCodecOnError 编码器运行错误,返回的错误码详情请参见[OH_AVCodecOnError](../../reference/apis-avcodec-kit/_codec_base.md#oh_avcodeconerror);
248    - OH_AVCodecOnStreamChanged  码流信息变化,如格式变化等;
249    - OH_AVCodecOnNeedInputBuffer 输入回调无作用,开发者通过获取的surface输入数据;
250    - OH_AVCodecOnNewOutputBuffer 运行过程中产生了新的输出数据,即编码完成。
251
252    <!--RP2--><!--RP2End-->
253
254    示例如下所示:
255
256    ```c++
257    // 设置OH_AVCodecOnError 回调函数,编码异常。
258    static void OnError(OH_AVCodec *codec, int32_t errorCode, void *userData)
259    {
260        // 回调的错误码由开发者判断处理。
261        (void)codec;
262        (void)errorCode;
263        (void)userData;
264    }
265    ```
266
267    <!--RP12-->
268    ```c++
269    // 设置OH_AVCodecOnStreamChanged 回调函数,编码数据流变化。
270    static void OnStreamChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData)
271    {
272        // Surface模式下,该回调函数在surface分辨率变化时触发。
273        (void)codec;
274        (void)userData;
275        bool ret = OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_WIDTH, &width) &&
276                   OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_HEIGHT, &height);
277        if (!ret) {
278         	// 异常处理。
279        }
280    }
281    ```
282    <!--RP12End-->
283
284    ```c++
285    // 设置 OH_AVCodecOnNeedInputBuffer 回调函数,编码输入帧送入数据队列。
286    static void OnNeedInputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
287    {
288        // Surface模式下,该回调函数无作用,开发者通过获取的surface输入数据。
289        (void)userData;
290        (void)index;
291        (void)buffer;
292    }
293    ```
294
295    ```c++
296    // 设置 OH_AVCodecOnNewOutputBuffer 回调函数,编码完成帧送入输出队列。
297    static void OnNewOutputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
298    {
299        // 完成帧的数据buffer和对应的index送入outQueue队列。
300        (void)codec;
301        (void)userData;
302        outQueue.Enqueue(std::make_shared<CodecBufferInfo>(index, buffer));
303    }
304    ```
305
306    ```c++
307    // 配置异步回调,调用 OH_VideoEncoder_RegisterCallback()接口。
308    OH_AVCodecCallback cb = {&OnError, &OnStreamChanged, &OnNeedInputBuffer, &OnNewOutputBuffer};
309    OH_AVErrCode ret = OH_VideoEncoder_RegisterCallback(videoEnc, cb, nullptr); // nullptr:开发者执行回调所依赖的数据userData为空。
310    if (ret != AV_ERR_OK) {
311        // 异常处理。
312    }
313    ```
314
315    > **说明:**
316    > 在回调函数中,对数据队列进行操作时,需要注意多线程同步的问题。
317    >
318
3194. (可选)调用OH_VideoEncoder_RegisterParameterCallback()在Configure接口之前注册随帧通路回调。
320
321    详情请参考[时域可分层视频编码](video-encoding-temporal-scalability.md)。
322
323    <!--RP7-->
324    ```c++
325    // 4.1 编码输入参数回调OH_VideoEncoder_OnNeedInputParameter实现。
326    static void OnNeedInputParameter(OH_AVCodec *codec, uint32_t index, OH_AVFormat *parameter, void *userData)
327    {
328        // 输入帧的数据parameter和对应的index送入inQueue队列。
329        inQueue.Enqueue(std::make_shared<CodecBufferInfo>(index, parameter));
330    }
331
332    // 4.2 注册随帧参数回调。
333    OH_VideoEncoder_OnNeedInputParameter inParaCb = OnNeedInputParameter;
334    OH_VideoEncoder_RegisterParameterCallback(videoEnc, inParaCb, nullptr); // nullptr:开发者执行回调所依赖的数据userData为空。
335    ```
336    <!--RP7End-->
337
3385. 调用OH_VideoEncoder_Configure()配置编码器。
339
340    详细可配置选项的说明请参考[视频专有键值对](../../reference/apis-avcodec-kit/_codec_base.md#媒体数据键值对)。
341
342    参数校验规则请参考[OH_VideoEncoder_Configure()参考文档](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_configure)。
343
344    参数取值范围可以通过能力查询接口获取,具体示例请参考[获取支持的编解码能力文档](obtain-supported-codecs.md)。
345
346    目前支持的所有格式都必须配置以下选项:视频帧宽度、视频帧高度、视频像素格式。
347
348    ```c++
349    // 配置视频帧速率。
350    double frameRate = 30.0;
351    // 配置视频YUV值范围标志。
352    bool rangeFlag = false;
353    // 配置视频原色。
354    int32_t primary = static_cast<int32_t>(OH_ColorPrimary::COLOR_PRIMARY_BT709);
355    // 配置传输特性。
356    int32_t transfer = static_cast<int32_t>(OH_TransferCharacteristic::TRANSFER_CHARACTERISTIC_BT709);
357    // 配置最大矩阵系数。
358    int32_t matrix = static_cast<int32_t>(OH_MatrixCoefficient::MATRIX_COEFFICIENT_IDENTITY);
359    // 配置编码Profile。
360    int32_t profile = static_cast<int32_t>(OH_AVCProfile::AVC_PROFILE_HIGH);
361    // 配置编码比特率模式。
362    int32_t rateMode = static_cast<int32_t>(OH_BitrateMode::BITRATE_MODE_VBR);
363    // 配置关键帧的间隔,单位为毫秒。
364    int32_t iFrameInterval = 1000;
365    // 配置质量稳定码率因子。
366    int32_t sqrFactor = 30;
367    // 配置最大比特率,单位为bps。
368    int64_t maxBitRate = 20000000;
369    // 配置比特率,单位为bps。
370    int64_t bitRate = 5000000;
371    // 配置编码质量。
372    int64_t quality = 90;
373
374    auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
375    if (format == nullptr) {
376        // 异常处理。
377    }
378    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_WIDTH, width); // 必须配置。
379    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_HEIGHT, height); // 必须配置。
380    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_PIXEL_FORMAT, pixelFormat); // 必须配置,
381
382    OH_AVFormat_SetDoubleValue(format.get(), OH_MD_KEY_FRAME_RATE, frameRate);
383    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_RANGE_FLAG, rangeFlag);
384    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_COLOR_PRIMARIES, primary);
385    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_TRANSFER_CHARACTERISTICS, transfer);
386    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_MATRIX_COEFFICIENTS, matrix);
387    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_I_FRAME_INTERVAL, iFrameInterval);
388    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_PROFILE, profile);
389    //只有当OH_BitrateMode = BITRATE_MODE_CQ时,才需要配置OH_MD_KEY_QUALITY。
390    if (rateMode == static_cast<int32_t>(OH_BitrateMode::BITRATE_MODE_CQ)) {
391        OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_QUALITY, quality);
392    } else if (rateMode == static_cast<int32_t>(OH_BitrateMode::BITRATE_MODE_SQR)) {
393        // 只有当OH_BitrateMode = BITRATE_MODE_SQR时,才需要配置OH_MD_KEY_MAX_BITRATE和OH_MD_KEY_SQR_FACTOR。
394        OH_AVFormat_SetLongValue(format.get(), OH_MD_KEY_MAX_BITRATE, maxBitRate);
395        OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_SQR_FACTOR, sqrFactor);
396    } else if (rateMode == static_cast<int32_t>(OH_BitrateMode::BITRATE_MODE_CBR) ||
397               rateMode == static_cast<int32_t>(OH_BitrateMode::BITRATE_MODE_VBR)){
398        OH_AVFormat_SetLongValue(format.get(), OH_MD_KEY_BITRATE, bitRate);
399    }
400    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_VIDEO_ENCODE_BITRATE_MODE, rateMode);
401    OH_AVErrCode ret = OH_VideoEncoder_Configure(videoEnc, format.get());
402    if (ret != AV_ERR_OK) {
403        // 异常处理。
404    }
405    ```
406
407    > **注意:**
408    > 配置非必须参数错误时,会返回AV_ERR_INVALID_VAL错误码。但OH_VideoEncoder_Configure()不会失败,而是使用默认值继续执行。
409    >
410
4116. 获取surface。
412
413    获取编码器Surface模式的OHNativeWindow输入,获取surface需要在调用OH_VideoEncoder_Prepare接口之前完成。
414
415    ```c++
416    // 获取需要输入的surface,以进行编码。
417    OHNativeWindow *nativeWindow;
418    OH_AVErrCode ret = OH_VideoEncoder_GetSurface(videoEnc, &nativeWindow);
419    if (ret != AV_ERR_OK) {
420        // 异常处理。
421    }
422    // 通过OHNativeWindow*变量类型,可通过生产者接口获取待填充数据地址。
423    ```
424
425    OHNativeWindow*变量类型的使用方法请参考图形子系统 [OHNativeWindow](../../reference/apis-arkgraphics2d/capi-nativewindow.md)。
426
4277. 调用OH_VideoEncoder_Prepare()编码器就绪。
428
429    该接口将在编码器运行前进行一些数据的准备工作。
430
431    ```c++
432    OH_AVErrCode ret = OH_VideoEncoder_Prepare(videoEnc);
433    if (ret != AV_ERR_OK) {
434        // 异常处理。
435    }
436    ```
437
4388. 调用OH_VideoEncoder_Start()启动编码器。
439
440    ```c++
441    // 配置待编码文件路径。
442    std::string_view outputFilePath = "/*yourpath*.h264";
443    std::unique_ptr<std::ofstream> outputFile = std::make_unique<std::ofstream>();
444    if (outputFile != nullptr) {
445        outputFile->open(outputFilePath.data(), std::ios::out | std::ios::binary | std::ios::ate);
446    }
447    // 启动编码器,开始编码。
448    OH_AVErrCode ret = OH_VideoEncoder_Start(videoEnc);
449    if (ret != AV_ERR_OK) {
450        // 异常处理。
451    }
452    ```
453
4549. (可选)OH_VideoEncoder_SetParameter()在运行过程中动态配置编码器参数。
455
456    <!--RP8-->
457    ```c++
458    auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
459    if (format == nullptr) {
460        // 异常处理。
461    }
462    // 支持动态请求IDR帧。
463    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_REQUEST_I_FRAME, true);
464    // SQR码控支持动态配置最大码率参数和质量稳定码率因子参数。
465    if (rateMode == static_cast<int32_t>(OH_BitrateMode::BITRATE_MODE_SQR)) {
466        int32_t sqrFactor = 25; // 质量稳定码率因子。
467        int64_t maxBitRate = 10000000; // 最大码率参数,单位为bps。
468        OH_AVFormat_SetLongValue(format.get(), OH_MD_KEY_MAX_BITRATE, maxBitRate);
469        OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_SQR_FACTOR, sqrFactor);
470    }
471    OH_AVErrCode ret = OH_VideoEncoder_SetParameter(videoEnc, format.get());
472    if (ret != AV_ERR_OK) {
473        // 异常处理。
474    }
475    ```
476    <!--RP8End-->
477
47810. 写入编码图像。
479    在之前的第6步中,开发者已经对OH_VideoEncoder_GetSurface接口返回的OHNativeWindow*类型变量进行配置。因为编码所需的数据,由配置的surface进行持续地输入,所以开发者无需对OnNeedInputBuffer回调函数进行处理,也无需使用OH_VideoEncoder_PushInputBuffer接口输入数据。
480    <!--RP13--><!--RP13End-->
481
48211. (可选)调用OH_VideoEncoder_PushInputParameter()通知编码器随帧参数配置输入完成。
483    在之前的第4步中,开发者已经注册随帧通路回调。
484
485    以下示例中:
486
487    - index:回调函数OnNeedInputParameter传入的参数,与buffer唯一对应的标识。
488
489    ```c++
490    std::shared_ptr<CodecBufferInfo> bufferInfo = inQueue.Dequeue();
491    std::shared_lock<std::shared_mutex> lock(codecMutex);
492    if (bufferInfo == nullptr || !bufferInfo->isValid) {
493        // 异常处理。
494    }
495    // 值由开发者决定。
496    int32_t isIFrame;
497    OH_AVFormat_SetIntValue(bufferInfo->parameter, OH_MD_KEY_REQUEST_I_FRAME, isIFrame);
498    OH_AVErrCode ret = OH_VideoEncoder_PushInputParameter(videoEnc, bufferInfo->index);
499    if (ret != AV_ERR_OK) {
500        // 异常处理。
501    }
502    ```
503
50412. 调用OH_VideoEncoder_NotifyEndOfStream()通知编码器结束。
505
506    ```c++
507    // Surface模式:通知视频编码器输入流已结束,只能使用此接口进行通知。
508    // 不能像Buffer模式中将flag设为AVCODEC_BUFFER_FLAGS_EOS,再调用OH_VideoEncoder_PushInputBuffer接口通知编码器输入结束。
509    OH_AVErrCode ret = OH_VideoEncoder_NotifyEndOfStream(videoEnc);
510    if (ret != AV_ERR_OK) {
511        // 异常处理。
512    }
513    ```
514
51513. 调用OH_VideoEncoder_FreeOutputBuffer()释放编码帧。
516
517    以下示例中,bufferInfo的成员变量:
518
519    - index:回调函数OnNewOutputBuffer传入的参数,与buffer唯一对应的标识;
520    - buffer:回调函数OnNewOutputBuffer传入的参数,可以通过[OH_AVBuffer_GetAddr](../../reference/apis-avcodec-kit/_core.md#oh_avbuffer_getaddr)接口得到共享内存地址的指针;
521    - isValid:bufferInfo中存储的buffer实例是否有效。
522
523    <!--RP6-->
524    ```c++
525    std::shared_ptr<CodecBufferInfo> bufferInfo = outQueue.Dequeue();
526    std::shared_lock<std::shared_mutex> lock(codecMutex);
527    if (bufferInfo == nullptr || !bufferInfo->isValid) {
528        // 异常处理。
529    }
530    // 获取编码后信息。
531    OH_AVCodecBufferAttr info;
532    OH_AVErrCode getBufferRet = OH_AVBuffer_GetBufferAttr(bufferInfo->buffer, &info);
533    if (getBufferRet != AV_ERR_OK) {
534        // 异常处理。
535    }
536    // 将编码完成帧数据buffer写入到对应输出文件中。
537    uint8_t *addr = OH_AVBuffer_GetAddr(bufferInfo->buffer);
538    if (addr == nullptr) {
539       // 异常处理。
540    }
541    if (outputFile != nullptr && outputFile->is_open()) {
542        outputFile->write(reinterpret_cast<char *>(addr), info.size);
543    }
544    // 释放已完成写入的数据,index为对应输出队列下标
545    OH_AVErrCode freeOutputRet = OH_VideoEncoder_FreeOutputBuffer(videoEnc, bufferInfo->index);
546    if (freeOutputRet != AV_ERR_OK) {
547        // 异常处理。
548    }
549    ```
550    <!--RP6End-->
551
55214. (可选)调用OH_VideoEncoder_Flush()刷新编码器。
553
554    调用OH_VideoEncoder_Flush接口后,编码器仍处于运行态,但会清除编码器中缓存的输入和输出数据及参数集如H.264格式的PPS/SPS555
556    此时需要调用OH_VideoEncoder_Start接口重新开始编码。
557
558    ```c++
559    std::unique_lock<std::shared_mutex> lock(codecMutex);
560    // 刷新编码器videoEnc。
561    OH_AVErrCode flushRet = OH_VideoEncoder_Flush(videoEnc);
562    if (flushRet != AV_ERR_OK) {
563        // 异常处理。
564    }
565    inQueue.Flush();
566    outQueue.Flush();
567    // 重新开始编码。
568    OH_AVErrCode startRet = OH_VideoEncoder_Start(videoEnc);
569    if (startRet != AV_ERR_OK) {
570        // 异常处理。
571    }
572    ```
573
57415. (可选)调用OH_VideoEncoder_Reset()重置编码器。
575
576    调用OH_VideoEncoder_Reset接口后,编码器将回到初始化的状态,需要调用OH_VideoEncoder_Configure接口和OH_VideoEncoder_Prepare接口重新配置。
577
578    ```c++
579    std::unique_lock<std::shared_mutex> lock(codecMutex);
580    // 重置编码器videoEnc。
581    OH_AVErrCode resetRet = OH_VideoEncoder_Reset(videoEnc);
582    if (resetRet != AV_ERR_OK) {
583        // 异常处理。
584    }
585    inQueue.Flush();
586    outQueue.Flush();
587    // 重新配置编码器参数。
588    auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
589    if (format == nullptr) {
590        // 异常处理。
591    }
592    OH_AVErrCode configRet = OH_VideoEncoder_Configure(videoEnc, format.get());
593    if (configRet != AV_ERR_OK) {
594        // 异常处理。
595    }
596    // 编码器重新就绪。
597    OH_AVErrCode prepareRet = OH_VideoEncoder_Prepare(videoEnc);
598    if (prepareRet != AV_ERR_OK) {
599        // 异常处理。
600    }
601    ```
602
60316. (可选)调用OH_VideoEncoder_Stop()停止编码器。
604
605    调用OH_VideoEncoder_Stop接口后,编码器保留了编码实例,释放输入输出buffer。开发者可以直接调用OH_VideoEncoder_Start接口继续编码,输入的第一个buffer需要携带参数集,从IDR帧开始送入。
606
607    ```c++
608    std::unique_lock<std::shared_mutex> lock(codecMutex);
609    // 终止编码器videoEnc。
610    OH_AVErrCode ret = OH_VideoEncoder_Stop(videoEnc);
611    if (ret != AV_ERR_OK) {
612        // 异常处理。
613    }
614    inQueue.Flush();
615    outQueue.Flush();
616    ```
617
61817. 调用OH_VideoEncoder_Destroy()销毁编码器实例,释放资源。
619
620    > **说明:**
621    >
622    > 1. 不能在回调函数中调用;
623    > 2. 执行该步骤之后,需要开发者将videoEnc指向nullptr,防止野指针导致程序错误。
624    >
625
626    ```c++
627    std::unique_lock<std::shared_mutex> lock(codecMutex);
628    // 释放nativeWindow实例。
629    if(nativeWindow != nullptr){
630        OH_NativeWindow_DestroyNativeWindow(nativeWindow);
631        nativeWindow = nullptr;
632    }
633    // 调用OH_VideoEncoder_Destroy,注销编码器。
634    OH_AVErrCode ret = AV_ERR_OK;
635    if (videoEnc != nullptr) {
636        ret = OH_VideoEncoder_Destroy(videoEnc);
637        videoEnc = nullptr;
638    }
639    if (ret != AV_ERR_OK) {
640        // 异常处理。
641    }
642    inQueue.Flush();
643    outQueue.Flush();
644    ```
645
646### Buffer模式
647
648参考以下示例代码,可以完成Buffer模式下视频编码的全流程,实现异步模式的数据轮转。此处以输入YUV文件,编码成H.264格式为例。
649
6501. 添加头文件。
651
652    ```c++
653    #include <multimedia/player_framework/native_avcodec_videoencoder.h>
654    #include <multimedia/player_framework/native_avcapability.h>
655    #include <multimedia/player_framework/native_avcodec_base.h>
656    #include <multimedia/player_framework/native_avformat.h>
657    #include <multimedia/player_framework/native_avbuffer.h>
658    #include <fstream>
659    ```
660
6612. 创建编码器实例。
662
663    与Surface模式相同,此处不再赘述。
664
665    ```c++
666    // 通过codec name创建编码器,应用有特殊需求,比如选择支持某种分辨率规格的编码器,可先查询capability,再根据codec name创建编码器。
667    OH_AVCapability *capability = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_VIDEO_AVC, true);
668    const char *codecName = OH_AVCapability_GetName(capability);
669    OH_AVCodec *videoEnc = OH_VideoEncoder_CreateByName(codecName);
670    ```
671
672    ```c++
673    // 通过MIME TYPE创建编码器,只能创建系统推荐的特定编解码器。
674    // 涉及创建多路编解码器时,优先创建硬件编码器实例,硬件资源不够时再创建软件编码器实例。
675    OH_AVCodec *videoEnc = OH_VideoEncoder_CreateByMime(OH_AVCODEC_MIMETYPE_VIDEO_AVC);
676    ```
677
6783. 调用OH_VideoEncoder_RegisterCallback()设置回调函数。
679
680    注册回调函数指针集合OH_AVCodecCallback,包括:
681    - OH_AVCodecOnError 编码器运行错误,返回的错误码详情请参见[OH_AVCodecOnError](../../reference/apis-avcodec-kit/_codec_base.md#oh_avcodeconerror);
682    - OH_AVCodecOnStreamChanged 码流信息变化,如格式变化等;
683    - OH_AVCodecOnNeedInputBuffer 运行过程中需要新的输入数据,即编码器已准备好,可以输入YUV/RGB数据;
684    - OH_AVCodecOnNewOutputBuffer 运行过程中产生了新的输出数据,即编码完成。
685
686    开发者可以通过处理该回调报告的信息,确保编码器正常运转。
687
688    <!--RP2--><!--RP2End-->
689
690    <!--RP9-->
691    ```c++
692    bool isFirstFrame = true;
693    ```
694    <!--RP9End-->
695
696    ```c++
697    // 编码异常回调OH_AVCodecOnError实现。
698    static void OnError(OH_AVCodec *codec, int32_t errorCode, void *userData)
699    {
700        // 回调的错误码由开发者判断处理。
701        (void)codec;
702        (void)errorCode;
703        (void)userData;
704    }
705    ```
706
707    ```c++
708    // 编码数据流变化回调OH_AVCodecOnStreamChanged实现。
709    static void OnStreamChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData)
710    {
711        // Buffer模式下,该回调函数无作用。
712        (void)codec;
713        (void)format;
714        (void)userData;
715    }
716    ```
717
718    ```c++
719    // 编码输入回调OH_AVCodecOnNeedInputBuffer实现。
720    static void OnNeedInputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
721    {
722        // 获取视频宽跨距、高跨距。
723        if (isFirstFrame) {
724            auto format = std::shared_ptr<OH_AVFormat>(OH_VideoEncoder_GetInputDescription(codec), OH_AVFormat_Destroy);
725            if (format == nullptr) {
726                // 异常处理。
727            }
728            bool ret = OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_STRIDE, &widthStride) &&
729                       OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_SLICE_HEIGHT, &heightStride);
730            if (!ret) {
731             	// 异常处理。
732            }
733            isFirstFrame = false;
734        }
735        // 输入帧的数据buffer和对应的index送入inQueue队列。
736        (void)codec;
737        (void)userData;
738        inQueue.Enqueue(std::make_shared<CodecBufferInfo>(index, buffer));
739    }
740    ```
741
742    <!--RP10-->
743    ```c++
744    // 编码输出回调OH_AVCodecOnNewOutputBuffer实现。
745    static void OnNewOutputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
746    {
747        // 完成帧的数据buffer和对应的index送入outQueue队列。
748        (void)userData;
749        outQueue.Enqueue(std::make_shared<CodecBufferInfo>(index, buffer));
750    }
751    ```
752    <!--RP10End-->
753
754    ```c++
755    // 配置异步回调,调用OH_VideoEncoder_RegisterCallback接口。
756    OH_AVCodecCallback cb = {&OnError, &OnStreamChanged, &OnNeedInputBuffer, &OnNewOutputBuffer};
757    OH_AVErrCode ret = OH_VideoEncoder_RegisterCallback(videoEnc, cb, nullptr);
758    if (ret != AV_ERR_OK) {
759        // 异常处理。
760    }
761    ```
762
763    > **说明:**
764    >
765    > 在回调函数中,对数据队列进行操作时,需要注意多线程同步的问题。
766    >
767
7684. 调用OH_VideoEncoder_Configure()配置编码器。
769
770    与Surface模式相同,此处不再赘述。
771
772    ```c++
773    auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
774    if (format == nullptr) {
775        // 异常处理。
776    }
777    // 写入format。
778    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_WIDTH, width); // 必须配置。
779    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_HEIGHT, height); // 必须配置。
780    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_PIXEL_FORMAT, pixelFormat); // 必须配置。
781    // 配置编码器。
782    OH_AVErrCode ret = OH_VideoEncoder_Configure(videoEnc, format.get());
783    if (ret != AV_ERR_OK) {
784        // 异常处理。
785    }
786    ```
787
7885. 调用OH_VideoEncoder_Prepare()编码器就绪。
789
790    该接口将在编码器运行前进行一些数据的准备工作。
791
792    ```c++
793    OH_AVErrCode ret = OH_VideoEncoder_Prepare(videoEnc);
794    if (ret != AV_ERR_OK) {
795        // 异常处理。
796    }
797    ```
798
7996. 调用OH_VideoEncoder_Start()启动编码器,进入运行态。
800
801    启动编码器后,回调函数将开始响应事件。所以,需要先配置输入文件、输出文件。
802
803    ```c++
804    // 配置待编码文件路径。
805    std::string_view inputFilePath = "/*yourpath*.yuv";
806    std::string_view outputFilePath = "/*yourpath*.h264";
807    std::unique_ptr<std::ifstream> inputFile = std::make_unique<std::ifstream>();
808    std::unique_ptr<std::ofstream> outputFile = std::make_unique<std::ofstream>();
809    if (inputFile != nullptr) {
810        inputFile->open(inputFilePath.data(), std::ios::in | std::ios::binary);
811    }
812    if (outputFile != nullptr) {
813        outputFile->open(outputFilePath.data(), std::ios::out | std::ios::binary | std::ios::ate);
814    }
815    // 启动编码器,开始编码。
816    OH_AVErrCode ret = OH_VideoEncoder_Start(videoEnc);
817    if (ret != AV_ERR_OK) {
818        // 异常处理。
819    }
820    ```
821
8227. (可选)在运行过程中动态配置编码器参数。
823
824   <!--RP11-->
825    ```c++
826    auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
827    if (format == nullptr) {
828        // 异常处理。
829    }
830    // 支持动态请求IDR帧。
831    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_REQUEST_I_FRAME, true);
832    OH_AVErrCode ret = OH_VideoEncoder_SetParameter(videoEnc, format.get());
833    if (ret != AV_ERR_OK) {
834        // 异常处理。
835    }
836    ```
837    <!--RP11End-->
838
8398. 调用OH_VideoEncoder_PushInputBuffer()写入编码图像。
840
841    送入输入队列进行编码,以下示例中:
842    - widthStride: 获取到的buffer数据的宽跨距。
843    - heightStride:获取到的buffer数据的高跨距。
844
845    bufferInfo的成员变量:
846    - buffer:回调函数OnNeedInputBuffer传入的参数,可以通过[OH_AVBuffer_GetAddr](../../reference/apis-avcodec-kit/_core.md#oh_avbuffer_getaddr)接口得到共享内存地址的指针;
847    - index:回调函数OnNeedInputBuffer传入的参数,与buffer唯一对应的标识;
848    - isValid:bufferInfo中存储的buffer实例是否有效。
849
850
851    ```c++
852    std::shared_ptr<CodecBufferInfo> bufferInfo = inQueue.Dequeue();
853    std::shared_lock<std::shared_mutex> lock(codecMutex);
854    if (bufferInfo == nullptr || !bufferInfo->isValid) {
855        // 异常处理。
856    }
857    // 写入图像数据。
858    int32_t frameSize = 0;
859    if (widthStride == width && heightStride == height) {
860        frameSize = width * height * 3 / 2; // NV12像素格式下,每帧数据大小的计算公式
861        int32_t capacity = OH_AVBuffer_GetCapacity(bufferInfo->buffer);
862        if (frameSize > capacity) {
863            // 异常处理。
864        }
865        // 处理文件流得到帧的长度,再将需要编码的数据写入到对应index的buffer中。
866        uint8_t *addr = OH_AVBuffer_GetAddr(bufferInfo->buffer);
867        if (addr == nullptr) {
868           // 异常处理。
869        }
870        if (inputFile != nullptr && inputFile->is_open()) {
871            inputFile->read(reinterpret_cast<char *>(addr), frameSize);
872        }
873    } else {
874        // 如果跨距不等于宽,需要开发者按照跨距进行偏移,具体可参考以下示例。
875    }
876    // 配置buffer info信息。
877    OH_AVCodecBufferAttr info;
878    info.size = frameSize;
879    info.offset = 0;
880    info.pts = 0;
881    OH_AVErrCode setBufferRet = OH_AVBuffer_SetBufferAttr(bufferInfo->buffer, &info);
882    if (setBufferRet != AV_ERR_OK) {
883        // 异常处理。
884    }
885    // 配置buffer 随帧信息。
886    // 值由开发者决定。
887    int32_t isIFrame;
888    auto parameter = std::shared_ptr<OH_AVFormat>(OH_AVBuffer_GetParameter(bufferInfo->buffer), OH_AVFormat_Destroy);
889    if (parameter == nullptr) {
890        // 异常处理。
891    }
892    OH_AVFormat_SetIntValue(parameter.get(), OH_MD_KEY_REQUEST_I_FRAME, isIFrame);
893    OH_AVErrCode parameterRet = OH_AVBuffer_SetParameter(bufferInfo->buffer, parameter.get());
894    if (parameterRet != AV_ERR_OK) {
895        // 异常处理。
896    }
897    // 送入编码输入队列进行编码,index为对应输入队列的下标。
898    OH_AVErrCode pushInputRet = OH_VideoEncoder_PushInputBuffer(videoEnc, bufferInfo->index);
899    if (pushInputRet != AV_ERR_OK) {
900        // 异常处理。
901    }
902    ```
903
904    对跨距进行偏移,以NV12图像为例,示例如下:
905
906    以NV12图像为例,width、height、wStride、hStride图像排布参考下图:
907
908    - OH_MD_KEY_WIDTH表示width;
909    - OH_MD_KEY_HEIGHT表示height;
910    - OH_MD_KEY_VIDEO_STRIDE表示wStride;
911    - OH_MD_KEY_VIDEO_SLICE_HEIGHT表示hStride。
912
913    ![copy by line](figures/copy-by-line-encoder.png)
914
915    添加头文件。
916
917    ```c++
918    #include <string.h>
919    ```
920
921    使用示例:
922
923    ```c++
924    struct Rect   // 源内存区域的宽、高,由开发者自行设置。
925    {
926        int32_t width;
927        int32_t height;
928    };
929
930    struct DstRect // 目标内存区域的宽跨距、高跨距,通过接口OH_VideoEncoder_GetInputDescription获取。
931    {
932        int32_t wStride;
933        int32_t hStride;
934    };
935
936    struct SrcRect // 源内存区域的宽跨距、高跨距,由开发者自行设置。
937    {
938        int32_t wStride;
939        int32_t hStride;
940    };
941
942    Rect rect = {320, 240};
943    DstRect dstRect = {320, 256};
944    SrcRect srcRect = {320, 250};
945    uint8_t* dst = new uint8_t[dstRect.hStride * dstRect.wStride * 3 / 2]; // 目标内存区域的指针。
946    uint8_t* src = new uint8_t[srcRect.hStride * srcRect.wStride * 3 / 2]; // 源内存区域的指针。
947    uint8_t* dstTemp = dst;
948    uint8_t* srcTemp = src;
949    rect.height = ((rect.height + 1) / 2)  * 2 // 避免height为奇数;
950    rect.width = ((rect.width + 1) / 2)  * 2 // 避免width为奇数;
951
952    // Y 将Y区域的源数据复制到另一个区域的目标数据中。
953    for (int32_t i = 0; i < rect.height; ++i) {
954        //将源数据的一行数据复制到目标数据的一行中。
955        memcpy(dstTemp, srcTemp, rect.width);
956        // 更新源数据和目标数据的指针,进行下一行的复制。每更新一次源数据和目标数据的指针都向下移动一个wStride。
957        dstTemp += dstRect.wStride;
958        srcTemp += srcRect.wStride;
959    }
960    // padding。
961    // 更新源数据和目标数据的指针,指针都向下移动一个padding。
962    dstTemp += (dstRect.hStride - rect.height) * dstRect.wStride;
963    srcTemp += (srcRect.hStride - rect.height) * srcRect.wStride;
964    rect.height >>= 1;
965    // UV 将UV区域的源数据复制到另一个区域的目标数据中。
966    for (int32_t i = 0; i < rect.height; ++i) {
967        memcpy(dstTemp, srcTemp, rect.width);
968        dstTemp += dstRect.wStride;
969        srcTemp += srcRect.wStride;
970    }
971
972    delete[] dst;
973    dst = nullptr;
974    delete[] src;
975    src = nullptr;
976    ```
977
978    硬件编码在处理buffer数据时(推送数据前),需要开发者拷贝宽、高对齐后的图像数据到输入回调的AVbuffer中。
979    一般需要获取数据的宽、高、跨距、像素格式来保证编码输入数据被正确的处理。
980
981    具体实现请参考:[Buffer模式](#buffer模式)的步骤3-调用OH_VideoEncoder_RegisterCallback接口设置回调函数来获取数据的宽、高、跨距、像素格式。
982
9839. 通知编码器结束。
984
985    以下示例中,bufferInfo的成员变量:
986    - index:回调函数OnNeedInputBuffer传入的参数,与buffer唯一对应的标识;
987    - buffer:回调函数OnNeedInputBuffer传入的参数,可以通过[OH_AVBuffer_GetAddr](../../reference/apis-avcodec-kit/_core.md#oh_avbuffer_getaddr)接口得到共享内存地址的指针;
988    - isValid:bufferInfo中存储的buffer实例是否有效。
989
990    与“步骤-8. 写入编码图像”一样,使用同一个接口OH_VideoEncoder_PushInputBuffer,通知编码器输入结束,需要将flag标识成AVCODEC_BUFFER_FLAGS_EOS。
991
992    ```c++
993    std::shared_ptr<CodecBufferInfo> bufferInfo = inQueue.Dequeue();
994    std::shared_lock<std::shared_mutex> lock(codecMutex);
995    if (bufferInfo == nullptr || !bufferInfo->isValid) {
996        // 异常处理。
997    }
998    OH_AVCodecBufferAttr info;
999    info.size = 0;
1000    info.offset = 0;
1001    info.pts = 0;
1002    info.flags = AVCODEC_BUFFER_FLAGS_EOS;
1003    OH_AVErrCode setBufferRet = OH_AVBuffer_SetBufferAttr(bufferInfo->buffer, &info);
1004    if (setBufferRet != AV_ERR_OK) {
1005        // 异常处理。
1006    }
1007    OH_AVErrCode pushInputRet = OH_VideoEncoder_PushInputBuffer(videoEnc, bufferInfo->index);
1008    if (pushInputRet != AV_ERR_OK) {
1009        // 异常处理。
1010    }
1011    ```
1012
101310. 调用OH_VideoEncoder_FreeOutputBuffer()释放编码帧。
1014    与Surface模式相同,此处不再赘述。
1015
1016    ```c++
1017    std::shared_ptr<CodecBufferInfo> bufferInfo = outQueue.Dequeue();
1018    std::shared_lock<std::shared_mutex> lock(codecMutex);
1019    if (bufferInfo == nullptr || !bufferInfo->isValid) {
1020        // 异常处理。
1021    }
1022    // 获取编码后信息。
1023    OH_AVCodecBufferAttr info;
1024    OH_AVErrCode getBufferRet = OH_AVBuffer_GetBufferAttr(bufferInfo->buffer, &info);
1025    if (getBufferRet != AV_ERR_OK) {
1026        // 异常处理。
1027    }
1028    // 将编码完成帧数据buffer写入到对应输出文件中。
1029    uint8_t *addr = OH_AVBuffer_GetAddr(bufferInfo->buffer);
1030    if (addr == nullptr) {
1031       // 异常处理。
1032    }
1033    if (outputFile != nullptr && outputFile->is_open()) {
1034        outputFile->write(reinterpret_cast<char *>(addr), info.size);
1035    }
1036    // 释放已完成写入的数据,index为对应输出队列的下标。
1037    OH_AVErrCode freeOutputRet = OH_VideoEncoder_FreeOutputBuffer(videoEnc, bufferInfo->index);
1038    if (freeOutputRet != AV_ERR_OK) {
1039        // 异常处理。
1040    }
1041    ```
1042
1043后续流程(包括刷新、重置、停止和销毁编码器)与Surface模式一致,请参考[Surface模式](#surface模式)的步骤14-17。
1044