• 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从API 20开始,支持视频编码同步模式。
11
12开发者可以调用本模块的Native API接口,完成同步模式的视频编码。
13
14当前支持的编码能力,请参考[AVCodec支持的格式](avcodec-support-formats.md#视频编码)。
15
16视频编码的限制约束、支持的能力、状态机调用关系请参考[视频编码](video-encoding.md)。
17
18## 适用场景
19
20通常情况下,推荐使用异步模式。若需要主动请求buffer去送帧,则可以采用同步模式。
21
22
23## 开发指导
24
25详细的API说明请参考[VideoEncoder](../../reference/apis-avcodec-kit/_video_encoder.md)。
26
27- 虚线表示可选。
28
29- 实线表示必选。
30
31![Invoking relationship of video encode stream](figures/synchronous-video-encode.png)
32
33### 在CMake脚本中链接动态库
34
35``` cmake
36target_link_libraries(sample PUBLIC libnative_media_codecbase.so)
37target_link_libraries(sample PUBLIC libnative_media_core.so)
38target_link_libraries(sample PUBLIC libnative_media_venc.so)
39```
40
41> **说明:**
42>
43> 上述'sample'字样仅为示例,此处由开发者根据实际工程目录自定义。
44>
45
46### 定义基础结构
47
48本部分示例代码按照C++17标准编写,仅作参考。
49
501. 添加头文件。
51
52    ```c++
53    #include <multimedia/player_framework/native_avcodec_videoencoder.h>
54    #include <multimedia/player_framework/native_avcapability.h>
55    #include <multimedia/player_framework/native_avcodec_base.h>
56    #include <multimedia/player_framework/native_avformat.h>
57    #include <multimedia/player_framework/native_avbuffer.h>
58    #include <multimedia/player_framework/native_averrors.h>
59    #include <native_buffer/native_buffer.h>
60    #include <memory>
61    #include <fstream>
62    #include <mutex>
63    #include <shared_mutex>
64    ```
65
662. 全局变量(仅作参考,可以根据实际情况将其封装到对象中)。
67
68    ```c++
69    // 视频帧宽度。
70    int32_t width = 320;
71    // 视频帧高度。
72    int32_t height = 240;
73    // 视频宽跨距。
74    int32_t widthStride = 0;
75    // 视频高跨距。
76    int32_t heightStride = 0;
77    // 视频像素格式。
78    OH_AVPixelFormat pixelFormat = AV_PIXEL_FORMAT_NV12;
79    // 编码器同步锁。
80    std::shared_mutex codecMutex;
81    // 编码器实例指针。
82    OH_AVCodec *videoEnc = nullptr;
83    // 编码输出。
84    bool outputDone = false;
85    // 编码输入。
86    bool inputDone = false;
87    std::unique_ptr<std::ifstream> inFile_;
88    ```
89
90### Surface模式
91
92参考以下示例代码,可以完成Surface模式下视频编码的全流程,实现同步模式的数据轮转。此处以输入surface数据,编码成H.264格式为例。
93
94
951. 创建编码器实例。
96
97    通过名称创建编码器。示例中的变量说明如下:
98
99    - videoEnc:视频编码器实例的指针。
100    - capability:编码器能力查询实例的指针。
101    - [OH_AVCODEC_MIMETYPE_VIDEO_AVC](../../reference/apis-avcodec-kit/_codec_base.md#oh_avcodec_mimetype_video_avc):AVC格式视频编解码器。
102
103    ```c++
104    // 创建硬件编码器实例。
105    OH_AVCapability *capability= OH_AVCodec_GetCapabilityByCategory(OH_AVCODEC_MIMETYPE_VIDEO_AVC, true, HARDWARE);
106    const char *name = OH_AVCapability_GetName(capability);
107    OH_AVCodec *videoEnc = OH_VideoEncoder_CreateByName(name);
108    if (videoEnc == nullptr) {
109        printf("create videoEnc failed");
110        return;
111    }
112    ```
113
1142. 调用OH_VideoEncoder_Configure()配置编码器。
115
116    - 详细可配置选项的说明请参考[媒体数据键值对](../../reference/apis-avcodec-kit/_codec_base.md#媒体数据键值对)。
117    - 参数校验规则请参考[OH_VideoEncoder_Configure()](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_configure)。
118    - 参数取值范围可以通过能力查询接口获取,具体示例请参考[获取支持的编解码能力](obtain-supported-codecs.md)。
119
120    目前支持的所有格式都必须配置以下选项:视频帧宽度、视频帧高度、视频像素格式。
121
122    ```c++
123
124    auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
125    if (format == nullptr) {
126        // 异常处理。
127    }
128    // 写入format。
129    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_WIDTH, width); // 必须配置。
130    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_HEIGHT, height); // 必须配置。
131    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_PIXEL_FORMAT, pixelFormat);// 必须配置。
132    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_ENABLE_SYNC_MODE, 1); // 同步模式配置。
133    // 配置编码器。
134    OH_AVErrCode ret = OH_VideoEncoder_Configure(videoEnc, format.get());
135    if (ret != AV_ERR_OK) {
136        // 异常处理。
137    }
138    ```
139
140    > **注意:**
141    >
142    > 1. 要使能视频编码同步模式,必须将OH_MD_KEY_ENABLE_SYNC_MODE配置为1。
143    > 2. 同步模式在调用OH_VideoEncoder_Configure接口前不能调用OH_VideoEncoder_RegisterCallback或OH_VideoEncoder_RegisterParameterCallback接口,否则为异步模式。
144    > 3. 不支持Surface模式的随帧通路的同步模式。
145    >
146
1473. 设置surface。
148
149   示例中的变量说明如下:
150   - nativeWindow:获取方式请参考[视频编码Surface模式](video-encoding.md#surface模式)的“步骤-6:设置surface”。
151
152    ```c++
153    // 获取需要输入的surface,以进行编码。
154    OH_AVErrCode ret = OH_VideoEncoder_GetSurface(videoEnc, &nativeWindow);
155    if (ret != AV_ERR_OK) {
156        // 异常处理。
157    }
158    ```
159
1604. 调用OH_VideoEncoder_Prepare()编码器就绪。
161
162    该接口将在编码器运行前进行一些数据的准备工作。
163
164    ```c++
165    OH_AVErrCode ret = OH_VideoEncoder_Prepare(videoEnc);
166    if (ret != AV_ERR_OK) {
167        // 异常处理。
168    }
169    ```
170
1715. 调用调用OH_VideoEncoder_Start()启动编码器。
172
173    ```c++
174    // 配置待编码文件路径。
175    std::string_view outputFilePath = "/*yourpath*.h264";
176    std::unique_ptr<std::ofstream> outputFile = std::make_unique<std::ofstream>();
177    if (outputFile != nullptr) {
178        outputFile->open(outputFilePath.data(), std::ios::out | std::ios::binary | std::ios::ate);
179    }
180    // 启动编码器,开始编码。
181    OH_AVErrCode ret = OH_VideoEncoder_Start(videoEnc);
182    if (ret != AV_ERR_OK) {
183        // 异常处理。
184    }
185    ```
186
1876. 获取可用buffer并释放编码帧。
188
189   - 调用[OH_VideoEncoder_QueryOutputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_queryoutputbuffer)接口获取下一个可用的输出缓冲区(buffer)的索引(index)。
190   - 根据获取的索引(index),调用[OH_VideoEncoder_GetOutputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_getoutputbuffer)接口获取对应的缓冲区(buffer)实例。
191   - 调用[OH_VideoEncoder_FreeOutputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_freeoutputbuffer)接口释放编码帧。
192
193    ```c++
194    bool EncoderOutput(OH_AVCodec *videoEnc, int64_t timeoutUs)
195    {
196        uint32_t index;
197        std::shared_lock<std::shared_mutex> lock(codecMutex);
198
199        OH_AVErrCode ret = OH_VideoEncoder_QueryOutputBuffer(videoEnc, &index, timeoutUs);
200        switch (ret) {
201            case AV_ERR_OK: {
202                OH_AVBuffer *buffer = OH_VideoEncoder_GetOutputBuffer(videoEnc, index);
203                if (buffer == nullptr) {
204                    // 异常处理。
205                    return false;
206                }
207
208                // 获取编码后信息。
209                OH_AVCodecBufferAttr info;
210                OH_AVErrCode getBufferRet = OH_AVBuffer_GetBufferAttr(buffer, &info);
211                if (getBufferRet != AV_ERR_OK) {
212                    // 异常处理。
213                    return false;
214                }
215                if (info.flags & AVCODEC_BUFFER_FLAGS_EOS) {
216                    outputDone = 1;
217                }
218
219                // 将编码完成帧数据buffer写入到对应输出文件中。
220                uint8_t *addr = OH_AVBuffer_GetAddr(buffer);
221                if (addr == nullptr) {
222                   // 异常处理
223                   return false;
224                }
225                if (outputFile != nullptr && outputFile->is_open()) {
226                    outputFile->write(reinterpret_cast<char *>(addr), info.size);
227                }
228                // 释放已完成写入的数据,index为对应输出队列下标。
229                OH_AVErrCode freeOutputRet = OH_VideoEncoder_FreeOutputBuffer(videoEnc, index);
230                if (freeOutputRet != AV_ERR_OK) {
231                    // 异常处理。
232                    return false;
233                }
234                break;
235            }
236            case AV_ERR_TRY_AGAIN_LATER: {
237                break;
238            }
239            case AV_ERR_STREAM_CHANGED: {
240                auto format = std::shared_ptr<OH_AVFormat>(OH_VideoEncoder_GetOutputDescription(videoEnc), OH_AVFormat_Destroy);
241                if (format == nullptr) {
242                    // 异常处理。
243                }
244                // 获取新宽高。
245                bool getIntRet = OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_WIDTH, &width) &&
246                                 OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_HEIGHT, &height);
247                if (!getIntRet) {
248                 	// 异常处理。
249                }
250                break;
251            }
252            default: {
253                // 异常处理。
254                return false;
255            }
256        }
257        return true;
258    }
259    ```
260
2617. 编码器出帧处理循环。
262
263   ```c++
264    bool result = true;
265    int64_t timeoutUs = 0; // 单位:微秒(us),负值:无限等待;0:立即退出;正值:等待指定时长后退出。
266
267    while (!outputDone && result) {
268        if (!outputDone ) {
269            result = EncoderOutput(videoEnc, timeoutUs);
270        }
271    }
272    ```
273
2748. 调用OH_VideoEncoder_NotifyEndOfStream()通知编码器结束。
275
276    ```c++
277    // Surface模式:通知视频编码器输入流已结束,只能使用此接口进行通知。
278    OH_AVErrCode ret = OH_VideoEncoder_NotifyEndOfStream(videoEnc);
279    if (ret != AV_ERR_OK) {
280        // 异常处理。
281    }
282    ```
283
2849. (可选)调用OH_VideoEncoder_Flush()刷新编码器。
285
286    调用OH_VideoEncoder_Flush接口后,编码器仍处于运行态,但会清除编码器中缓存的输入和输出数据及参数集如H.264格式的PPS/SPS287    此时需要调用[OH_VideoEncoder_Start](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_start)接口重新开始编码。
288
289    ```c++
290    // 通过codecMutex来避免调用Flush接口,状态切换后,编码线程还在跑会退出循环的问题。
291    std::unique_lock<std::shared_mutex> lock(codecMutex);
292    // 刷新编码器videoEnc。
293    OH_AVErrCode flushRet = OH_VideoEncoder_Flush(videoEnc);
294    if (flushRet != AV_ERR_OK) {
295        // 异常处理。
296    }
297
298    // 重新开始编码。
299    OH_AVErrCode startRet = OH_VideoEncoder_Start(videoEnc);
300    if (startRet != AV_ERR_OK) {
301        // 异常处理。
302    }
303    ```
304
30510. (可选)调用OH_VideoEncoder_Reset()重置编码器。
306
307    调用OH_VideoEncoder_Reset接口后,编码器回到初始化的状态,需要调用接口[OH_VideoEncoder_Configure](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_configure)和[OH_VideoEncoder_Prepare](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_prepare)重新配置。
308
309    ```c++
310    // 重置编码器videoEnc。
311    std::unique_lock<std::shared_mutex> lock(codecMutex);
312    OH_AVErrCode resetRet = OH_VideoEncoder_Reset(videoEnc);
313    if (resetRet != AV_ERR_OK) {
314        // 异常处理。
315    }
316
317    // 重新配置编码器参数。
318    auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
319    if (format == nullptr) {
320        // 异常处理。
321    }
322    OH_AVErrCode configRet = OH_VideoEncoder_Configure(videoEnc, format.get());
323    if (configRet != AV_ERR_OK) {
324        // 异常处理。
325    }
326
327    // 编码器重新就绪。
328    OH_AVErrCode prepareRet = OH_VideoEncoder_Prepare(videoEnc);
329    if (prepareRet != AV_ERR_OK) {
330        // 异常处理。
331    }
332    ```
333
334    > **注意:**
335    >
336    > 编码器回到初始化的状态,调用OH_VideoEncoder_Configure接口重新配置编码器参数时,同步模式需要重新配置OH_MD_KEY_ENABLE_SYNC_MODE为1,否则为异步模式。
337    >
338
33911. (可选)调用OH_VideoEncoder_Stop()停止编码器。
340
341    调用OH_VideoEncoder_Stop接口后,编码器保留了编码实例,释放输入输出buffer。开发者可以直接调用OH_VideoEncoder_Start接口继续编码,输入的第一个buffer需要携带参数集,从IDR帧开始送入。
342
343    ```c++
344    // 终止编码器videoEnc。
345    std::unique_lock<std::shared_mutex> lock(codecMutex);
346    OH_AVErrCode ret = OH_VideoEncoder_Stop(videoEnc);
347    if (ret != AV_ERR_OK) {
348        // 异常处理。
349    }
350    ```
351
35212. 调用OH_VideoEncoder_Destroy()销毁编码器实例,释放资源。
353
354    ```c++
355    // 注销编码器。
356    std::unique_lock<std::shared_mutex> lock(codecMutex);
357    OH_AVErrCode ret = AV_ERR_OK;
358    if (videoEnc != nullptr) {
359        ret = OH_VideoEncoder_Destroy(videoEnc);
360        videoEnc = nullptr;
361    }
362    if (ret != AV_ERR_OK) {
363        // 异常处理。
364    }
365    ```
366
367    > **说明:**
368    >
369    > 执行该步骤之后,需要开发者将videoEnc指向nullptr,防止野指针导致程序错误。
370    >
371
372### Buffer模式
373
374参考以下示例代码,可以完成Buffer模式下视频编码的全流程,实现同步模式的数据轮转。此处以输入YUV文件,编码成H.264格式为例。
375
3761. 创建编码器实例。
377
378    与Surface模式相同,此处不再赘述。
379
380    ```c++
381    // 通过codecname创建编码器,应用有特殊需求,比如选择支持某种分辨率规格的编码器,可先查询capability,再根据codec name创建编码器。
382    OH_AVCapability *capability = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_VIDEO_AVC, true);
383    const char *name = OH_AVCapability_GetName(capability);
384    OH_AVCodec *videoEnc = OH_VideoEncoder_CreateByName(name);
385    if (videoEnc == nullptr) {
386        printf("create videoEnc failed");
387        return;
388    }
389    ```
390
3912. 调用OH_VideoEncoder_Configure()配置编码器。
392
393    与Surface模式相同,此处不再赘述。
394
395    ```c++
396    auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
397    if (format == nullptr) {
398        // 异常处理。
399    }
400    // 写入format。
401    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_WIDTH, width); // 必须配置。
402    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_HEIGHT, height); // 必须配置。
403    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_PIXEL_FORMAT, pixelFormat);// 必须配置。
404    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_ENABLE_SYNC_MODE, 1); // 同步模式配置。
405    // 配置编码器。
406    OH_AVErrCode ret = OH_VideoEncoder_Configure(videoEnc, format.get());
407    if (ret != AV_ERR_OK) {
408        // 异常处理。
409    }
410    ```
411
412    > **注意:**
413    >
414    > 1. 要使能视频编码同步模式,必须将OH_MD_KEY_ENABLE_SYNC_MODE配置为1。
415    > 2. 同步模式在调用OH_VideoEncoder_Configure接口前不能调用OH_VideoEncoder_RegisterCallback或OH_VideoEncoder_RegisterParameterCallback接口,否则为异步模式。
416    >
417
4183. 调用OH_VideoEncoder_Prepare()编码器就绪。
419
420    该接口将在编码器运行前进行一些数据的准备工作。
421
422    ```c++
423    ret = OH_VideoEncoder_Prepare(videoEnc);
424    if (ret != AV_ERR_OK) {
425        // 异常处理。
426    }
427    ```
428
4294. 调用OH_VideoEncoder_Start()启动编码器。
430
431    配置输入文件、输出文件。
432
433    ```c++
434    // 配置待编码文件路径。
435    std::string_view inputFilePath = "/*yourpath*.yuv";
436    std::string_view outputFilePath = "/*yourpath*.h264";
437    std::unique_ptr<std::ifstream> inputFile = std::make_unique<std::ifstream>();
438    std::unique_ptr<std::ofstream> outputFile = std::make_unique<std::ofstream>();
439    if (inputFile != nullptr) {
440        inputFile->open(inputFilePath.data(), std::ios::in | std::ios::binary);
441    }
442    if (outputFile != nullptr) {
443        outputFile->open(outputFilePath.data(), std::ios::out | std::ios::binary | std::ios::ate);
444    }
445    // 启动编码器,开始编码。
446    OH_AVErrCode ret = OH_VideoEncoder_Start(videoEnc);
447    if (ret != AV_ERR_OK) {
448        // 异常处理。
449    }
450    ```
451
4525. 获取可用buffer并写入码流至编码器。
453
454    - 调用[OH_VideoEncoder_QueryInputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_queryinputbuffer)接口获取下一个可用的输入缓冲区(buffer)的索引(index)。
455    - 根据获取的索引(index),调用[OH_VideoEncoder_GetInputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_getinputbuffer)接口获取对应的缓冲区(buffer)实例。
456    - 将需要编码的数据写入该缓冲区(buffer)后,调用[OH_VideoEncoder_PushInputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_pushinputbuffer)接口将其送入编码输入队列进行编码。当所有待处理数据全部传递给编码器后,需要将flag标识成AVCODEC_BUFFER_FLAGS_EOS,通知编码器输入结束。
457
458
459    示例中的变量size、offset、pts、frameData、flags说明与Surface模式相同,此处不再赘述。
460
461    ```c++
462    bool EncoderInput(OH_AVCodec *videoEnc, int64_t timeoutUs)
463    {
464        uint32_t index;
465        std::shared_lock<std::shared_mutex> lock(codecMutex);
466
467        OH_AVErrCode ret = OH_VideoEncoder_QueryInputBuffer(videoEnc, &index, timeoutUs);
468        switch (ret) {
469            case AV_ERR_OK: {
470                OH_AVBuffer *buffer = OH_VideoEncoder_GetInputBuffer(videoEnc, index);
471                if (buffer == nullptr) {
472                    // 异常处理。
473                    return false;
474                }
475
476                // 写入图像数据。
477                int32_t frameSize = 0;
478                bool isFirstFrame = true;
479                // 获取视频宽跨距和高跨距。
480                if (isFirstFrame) {
481                    auto format = std::shared_ptr<OH_AVFormat>(OH_VideoEncoder_GetInputDescription(videoEnc), OH_AVFormat_Destroy);
482                    if (format == nullptr) {
483                        // 异常处理。
484                    }
485                    bool getIntRet = OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_STRIDE, &widthStride) &&
486                                     OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_SLICE_HEIGHT, &heightStride);
487                     if (!getIntRet) {
488                     	// 异常处理。
489                     }
490                    isFirstFrame = false;
491                }
492                if (widthStride == width && heightStride == height) {
493                    frameSize = width * height * 3 / 2; // NV12像素格式下,每帧数据大小的计算公式。
494                    // 处理文件流得到帧的长度,再将需要编码的数据写入到对应index的buffer中。
495                    uint8_t *addr = OH_AVBuffer_GetAddr(buffer);
496                    if (addr == nullptr) {
497                       // 异常处理
498                       return false;
499                    }
500                    if (inputFile != nullptr && inputFile->is_open()) {
501                        inputFile->read(reinterpret_cast<char *>(addr), frameSize);
502                    }
503                } else {
504                    // 如果跨距不等于宽,开发者需要按照跨距进行偏移,详情请参考视频编码Buffer模式“步骤-8. 写入编码图像”。
505                }
506
507                // 配置buffer info信息。
508                OH_AVCodecBufferAttr info;
509                info.size = frameSize;
510                info.offset = 0;
511                info.pts = 0;
512                OH_AVErrCode setBufferRet = OH_AVBuffer_SetBufferAttr(buffer, &info);
513                if (setBufferRet != AV_ERR_OK) {
514                    // 异常处理。
515                    return false;
516                }
517                // 送入编码输入队列进行编码,index为对应输入队列的下标。
518                OH_AVErrCode pushInputRet = OH_VideoEncoder_PushInputBuffer(videoEnc, index);
519                if (pushInputRet != AV_ERR_OK) {
520                    // 异常处理。
521                    return false;
522                }
523                if (inFile_->eof()) {
524                    inputDone = 1;
525                }
526                break;
527            }
528            case AV_ERR_TRY_AGAIN_LATER: {
529                break;
530            }
531            default: {
532                // 异常处理。
533                return false;
534            }
535        }
536        return true;
537    }
538    ```
539
540
5416. 获取可用buffer并释放编码帧。
542
543   - 调用[OH_VideoEncoder_QueryOutputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_queryoutputbuffer)接口获取下一个可用的输出缓冲区(buffer)的索引(index)。
544   - 根据获取的索引(index),调用[OH_VideoEncoder_GetOutputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_getoutputbuffer)接口获取对应的缓冲区(buffer)实例。
545   - 调用[OH_VideoEncoder_FreeOutputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_freeoutputbuffer)接口释放编码帧。
546
547
548    ```c++
549    bool EncoderOutput(OH_AVCodec *videoEnc, int64_t timeoutUs)
550    {
551        uint32_t index;
552        std::shared_lock<std::shared_mutex> lock(codecMutex);
553
554        OH_AVErrCode ret = OH_VideoEncoder_QueryOutputBuffer(videoEnc, &index, timeoutUs);
555        switch (ret) {
556            case AV_ERR_OK: {
557                OH_AVBuffer *buffer = OH_VideoEncoder_GetOutputBuffer(videoEnc, index);
558                if (buffer == nullptr) {
559                    // 异常处理。
560                    return false;
561                }
562
563                // 获取编码后信息。
564                OH_AVCodecBufferAttr info;
565                OH_AVErrCode getBufferRet = OH_AVBuffer_GetBufferAttr(buffer, &info);
566                if (getBufferRet != AV_ERR_OK) {
567                    // 异常处理。
568                    return false;
569                }
570                // 将编码完成帧数据buffer写入到对应输出文件中。
571                uint8_t *addr = OH_AVBuffer_GetAddr(buffer);
572                if (addr == nullptr) {
573                   // 异常处理
574                   return false;
575                }
576                if (outputFile != nullptr && outputFile->is_open()) {
577                    outputFile->write(reinterpret_cast<char *>(addr), info.size);
578                }
579                if (info.flags & AVCODEC_BUFFER_FLAGS_EOS) {
580                    outputDone = 1;
581                }
582                // 释放已完成处理的信息,index为对应buffer队列的下标。
583                OH_AVErrCode freeOutputRet = OH_VideoEncoder_FreeOutputBuffer(videoEnc, index);
584                if (freeOutputRet != AV_ERR_OK) {
585                    // 异常处理。
586                    return false;
587                }
588                break;
589            }
590            case AV_ERR_TRY_AGAIN_LATER: {
591                break;
592            }
593            case AV_ERR_STREAM_CHANGED: {
594                break;
595            }
596            default: {
597                // 异常处理。
598                return false;
599            }
600        }
601        return true;
602    }
603    ```
604
6057. 编码器送帧/出帧处理循环。
606
607    ```c++
608    bool result = true;
609    int64_t timeoutUs = 0; // 单位:微秒(us),负值:无限等待;0:立即退出;正值:等待指定时长后退出。
610
611    while (!outputDone && result) {
612        if (!inputDone) {
613            result = EncoderInput(videoEnc, timeoutUs);
614        }
615        if (!outputDone) {
616            result = EncoderOutput(videoEnc, timeoutUs);
617        }
618    }
619    ```
620
621后续流程(包括刷新、重置、停止和销毁编码器)与Surface模式基本一致,请参考[Surface模式](#surface模式)的步骤9-12。