• 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-decoding.md)。
17
18## 适用场景
19
20通常情况下,推荐使用异步模式。若需要主动请求buffer去送帧,则可以采用同步模式。
21
22
23## 开发指导
24
25详细的API说明请参考[VideoDecoder](../../reference/apis-avcodec-kit/_video_decoder.md)。
26
27- 虚线表示可选。
28
29- 实线表示必选。
30
31![Invoking relationship of video decode stream](figures/synchronous-video-decode.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_vdec.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_videodecoder.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    #include <string.h>
65    ```
66
672. 全局变量(仅作参考,可以根据实际情况将其封装到对象中)。
68
69    ```c++
70    // 视频帧宽度。
71    int32_t width = 320;
72    // 视频帧高度。
73    int32_t height = 240;
74    // 视频像素格式。
75    OH_AVPixelFormat pixelFormat = AV_PIXEL_FORMAT_NV12;
76    // 解码器同步锁。
77    std::shared_mutex codecMutex;
78    // 解码器实例指针。
79    OH_AVCodec *videoDec = nullptr;
80    // 解码输出。
81    bool outputDone = false;
82    // 解码输入。
83    bool inputDone = false;
84    std::unique_ptr<std::ifstream> inFile_;
85    ```
86
87### Surface模式
88
89参考以下示例代码,可以完成Surface模式下视频解码的全流程,实现同步模式的数据轮转。此处以输入H.264码流文件,解码送显输出为例。
90
91
921. 创建解码器实例。
93
94    通过名称创建解码器。示例中的变量说明如下:
95
96    - videoDec:视频解码器实例的指针。
97    - capability:解码器能力查询实例的指针。
98    - OH_AVCODEC_MIMETYPE_VIDEO_AVC:AVC格式视频编解码器。
99
100    ```c++
101    // 创建硬件解码器实例。
102    OH_AVCapability *capability= OH_AVCodec_GetCapabilityByCategory(OH_AVCODEC_MIMETYPE_VIDEO_AVC, false, HARDWARE);
103    const char *name = OH_AVCapability_GetName(capability);
104    OH_AVCodec *videoDec = OH_VideoDecoder_CreateByName(name);
105    if (videoDec == nullptr) {
106        printf("create videoDec failed");
107        return;
108    }
109    ```
1102. 调用OH_VideoDecoder_Configure()配置解码器。
111
112    - 详细可配置选项的说明请参考[媒体数据键值对](../../reference/apis-avcodec-kit/_codec_base.md#媒体数据键值对)。
113    - 参数校验规则请参考[OH_VideoDecoder_Configure()](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_configure)。
114    - 参数取值范围可以通过能力查询接口获取,具体示例请参考[获取支持的编解码能力](obtain-supported-codecs.md)。
115
116    目前支持的所有格式都必须配置以下选项:视频帧宽度、视频帧高度。
117
118    ```c++
119
120    auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
121    if (format == nullptr) {
122        // 异常处理。
123    }
124    // 写入format。
125    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_WIDTH, width); // 必须配置。
126    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_HEIGHT, height); // 必须配置。
127    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_PIXEL_FORMAT, pixelFormat);
128    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_ENABLE_SYNC_MODE, 1); // 同步模式配置。
129    // 配置解码器。
130    OH_AVErrCode ret = OH_VideoDecoder_Configure(videoDec, format.get());
131    if (ret != AV_ERR_OK) {
132        // 异常处理。
133    }
134    ```
135
136    > **注意:**
137    >
138    > 1. 要使能视频解码同步模式,必须将OH_MD_KEY_ENABLE_SYNC_MODE配置为1。
139    > 2. 同步模式在调用OH_VideoDecoder_Configure接口前不能调用OH_VideoDecoder_RegisterCallback接口,否则为异步模式。
140    >
141
1423. 设置surface。
143
144   示例中的变量说明如下:
145   - nativeWindow:获取方式请参考[视频解码Surface模式](video-decoding.md#surface模式)的“步骤-6:设置surface”。
146
147    ```c++
148    // 设置surface。
149    // 配置送显窗口参数。
150    OH_AVErrCode ret = OH_VideoDecoder_SetSurface(videoDec, nativeWindow);
151    if (ret != AV_ERR_OK) {
152        // 异常处理。
153    }
154    ```
155
156
1574. 调用OH_VideoDecoder_Prepare()解码器就绪。
158
159    该接口将在解码器运行前进行一些数据的准备工作。
160
161    ```c++
162    OH_AVErrCode ret = OH_VideoDecoder_Prepare(videoDec);
163    if (ret != AV_ERR_OK) {
164        // 异常处理。
165    }
166    ```
167
1685. 调用OH_VideoDecoder_Start()启动解码器。
169
170    ```c++
171    // 启动解码器,开始解码。
172    OH_AVErrCode ret = OH_VideoDecoder_Start(videoDec);
173    if (ret != AV_ERR_OK) {
174        // 异常处理。
175    }
176    ```
177
1786. 获取可用buffer并写入码流至解码器。
179
180    - 调用[OH_VideoDecoder_QueryInputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_queryinputbuffer)接口获取下一个可用的输入缓冲区(buffer)的索引(index)。
181    - 根据获取的索引(index),调用[OH_VideoDecoder_GetInputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_getinputbuffer)接口获取对应的缓冲区(buffer)实例。
182    - 将待解码数据写入该缓冲区(buffer)后,调用[OH_VideoDecoder_PushInputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_pushinputbuffer)接口提交至解码器进行解码。当所有待处理数据全部传递给解码器后,需要将flag标识成AVCODEC_BUFFER_FLAGS_EOS,通知解码器输入结束。
183
184
185    送入输入队列进行解码,示例中的变量说明如下:
186    - size、offset、pts、frameData:输入尺寸、偏移量、时间戳、帧数据等字段信息,获取方式可以参考[媒体数据解析](./audio-video-demuxer.md#开发步骤)“步骤-9:开始解封装,循环获取sample”。
187    - flags:缓冲区标记的类别,请参考[OH_AVCodecBufferFlags](../../reference/apis-avcodec-kit/_core.md#oh_avcodecbufferflags)。
188
189    ```c++
190    bool DecoderInput(OH_AVCodec *videoDec, int64_t timeoutUs)
191    {
192        uint32_t index;
193        std::shared_lock<std::shared_mutex> lock(codecMutex);
194
195        OH_AVErrCode ret = OH_VideoDecoder_QueryInputBuffer(videoDec, &index, timeoutUs);
196        switch (ret) {
197            case AV_ERR_OK: {
198                OH_AVBuffer *buffer = OH_VideoDecoder_GetInputBuffer(videoDec, index);
199                if (buffer == nullptr) {
200                    // 异常处理。
201                    return false;
202                }
203                // 写入码流数据。
204                uint8_t *addr = OH_AVBuffer_GetAddr(buffer);
205                if (addr == nullptr) {
206                   // 异常处理。
207                   return false;
208                }
209                // buffer数据填充。
210                int32_t capacity = OH_AVBuffer_GetCapacity(buffer);
211                if (size > capacity) {
212                    // 异常处理。
213                }
214                memcpy(addr, frameData, size);
215
216                OH_AVCodecBufferAttr info;
217                // buffer属性配置。
218                // 配置帧数据的输入尺寸、偏移量、时间戳等字段信息。
219                info.size = size;
220                info.offset = offset;
221                info.pts = pts;
222                if (inFile_->eof()) {
223                    info.flags = AVCODEC_BUFFER_FLAGS_EOS;
224                } else {
225                    info.flags = flags;
226                }
227                OH_AVErrCode setBufferRet = OH_AVBuffer_SetBufferAttr(buffer, &info);
228                if (setBufferRet != AV_ERR_OK) {
229                    // 异常处理。
230                    return false;
231                }
232                OH_AVErrCode pushInputRet = OH_VideoDecoder_PushInputBuffer(videoDec, index);
233                if (pushInputRet != AV_ERR_OK) {
234                    // 异常处理。
235                    return false;
236                }
237                if (inFile_->eof()) {
238                    inputDone = 1;
239                }
240                break;
241            }
242            case AV_ERR_TRY_AGAIN_LATER: {
243                break;
244            }
245            default: {
246                // 异常处理。
247                return false;
248            }
249        }
250        return true;
251    }
252    ```
253
2547. 获取可用buffer显示并释放解码帧。
255
256   - 调用[OH_VideoDecoder_QueryOutputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_queryoutputbuffer)接口获取下一个可用的输出缓冲区(buffer)的索引(index)。
257   - 根据获取的索引(index),调用[OH_VideoDecoder_GetOutputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_getoutputbuffer)接口获取对应的缓冲区(buffer)实例。
258   - 根据开发者设置的isRender标志决定后续操作:若无需送显,则调用[OH_VideoDecoder_FreeOutputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_freeoutputbuffer)接口释放解码帧。若需送显,则可调用[OH_VideoDecoder_RenderOutputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_renderoutputbuffer)接口显示并自动释放解码帧,或调用[OH_VideoDecoder_RenderOutputBufferAtTime](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_renderoutputbufferattime)接口在指定时间点显示并释放解码帧。
259
260
261    ```c++
262    bool DecoderOutput(OH_AVCodec *videoDec, int64_t timeoutUs)
263    {
264        uint32_t index;
265        std::shared_lock<std::shared_mutex> lock(codecMutex);
266
267        OH_AVErrCode ret = OH_VideoDecoder_QueryOutputBuffer(videoDec, &index, timeoutUs);
268        switch (ret) {
269            case AV_ERR_OK: {
270                OH_AVBuffer *buffer = OH_VideoDecoder_GetOutputBuffer(videoDec, index);
271                if (buffer == nullptr) {
272                    // 异常处理。
273                    return false;
274                }
275
276                // 获取解码后信息。
277                OH_AVCodecBufferAttr info;
278                OH_AVErrCode getBufferRet = OH_AVBuffer_GetBufferAttr(buffer, &info);
279                if (getBufferRet != AV_ERR_OK) {
280                    // 异常处理。
281                    return false;
282                }
283                if (info.flags & AVCODEC_BUFFER_FLAGS_EOS) {
284                    outputDone = 1;
285                }
286
287                // 解码输出数据处理。
288                // 值由开发者决定。
289                bool isRender;
290                bool isNeedRenderAtTime;
291                OH_AVErrCode result = AV_ERR_OK;
292                if (isRender) {
293                    // 显示并释放已完成处理的信息,index为对应buffer队列的下标。
294                    if (isNeedRenderAtTime){
295                        // 获取系统绝对时间,renderTimestamp由开发者结合业务指定显示时间。
296                        int64_t renderTimestamp =
297                            std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
298                        result = OH_VideoDecoder_RenderOutputBufferAtTime(videoDec, index, renderTimestamp);
299                    } else {
300                        result = OH_VideoDecoder_RenderOutputBuffer(videoDec, index);
301                    }
302                } else {
303                    // 释放已完成处理的信息。
304                    result = OH_VideoDecoder_FreeOutputBuffer(videoDec, index);
305                }
306                if (result != AV_ERR_OK) {
307                    // 异常处理。
308                    return false;
309                }
310                break;
311            }
312            case AV_ERR_TRY_AGAIN_LATER: {
313                break;
314            }
315            case AV_ERR_STREAM_CHANGED: {
316                auto format = std::shared_ptr<OH_AVFormat>(OH_VideoDecoder_GetOutputDescription(videoDec), OH_AVFormat_Destroy);
317                if (format == nullptr) {
318                    // 异常处理。
319                }
320                // 获取新宽高。
321                bool getIntRet = OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_PIC_WIDTH, &width) &&
322                                 OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_PIC_HEIGHT, &height);
323                if (!getIntRet) {
324             	    // 异常处理。
325                }
326                break;
327            }
328            default: {
329                // 异常处理。
330                return false;
331            }
332        }
333        return true;
334    }
335    ```
336
3378. 解码器送帧/出帧处理循环。
338
339    ```c++
340    bool result = true;
341    int64_t timeoutUs = 0; // 单位:微秒(us),负值:无限等待;0:立即退出;正值:指定时间后结束后退出。
342
343    while (!outputDone && result) {
344        if (!inputDone) {
345            result = DecoderInput(videoDec, timeoutUs);
346        }
347        if (!outputDone) {
348            result = DecoderOutput(videoDec, timeoutUs);
349        }
350    }
351    ```
352
3539. (可选)调用OH_VideoDecoder_Flush()刷新解码器。
354
355    调用OH_VideoDecoder_Flush接口后,解码器仍处于运行态,但会清除解码器中缓存的输入和输出数据及参数集如H.264格式的PPS/SPS356    此时需要调用[OH_VideoDecoder_Start](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_start)接口重新开始解码。
357
358    ```c++
359    // 通过codecMutex来避免调用Flush接口,状态切换后,解码线程还在跑会退出循环的问题。
360    std::unique_lock<std::shared_mutex> lock(codecMutex);
361    // 刷新解码器videoDec。
362    OH_AVErrCode ret = OH_VideoDecoder_Flush(videoDec);
363    if (ret != AV_ERR_OK) {
364        // 异常处理。
365    }
366
367    // 重新开始解码。
368    ret = OH_VideoDecoder_Start(videoDec);
369    if (ret != AV_ERR_OK) {
370        // 异常处理。
371    }
372    ```
373
37410. (可选)调用OH_VideoDecoder_Reset()重置解码器。
375
376    调用OH_VideoDecoder_Reset接口后,解码器回到初始化的状态,需要调用接口[OH_VideoDecoder_Configure](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_configure)、[OH_VideoDecoder_SetSurface](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_setsurface)和[OH_VideoDecoder_Prepare](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_prepare)重新配置。
377
378    ```c++
379    // 重置解码器videoDec。
380    std::unique_lock<std::shared_mutex> lock(codecMutex);
381    OH_AVErrCode resetRet = OH_VideoDecoder_Reset(videoDec);
382    if (resetRet != AV_ERR_OK) {
383        // 异常处理。
384    }
385
386    // 重新配置解码器参数。
387    auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
388    if (format == nullptr) {
389        // 异常处理。
390    }
391    OH_AVErrCode configRet = OH_VideoDecoder_Configure(videoDec, format.get());
392    if (configRet != AV_ERR_OK) {
393        // 异常处理。
394    }
395
396    // Surface模式需要重新配置surface,而Buffer模式不需要配置surface。
397    OH_AVErrCode setRet = OH_VideoDecoder_SetSurface(videoDec, nativeWindow);
398    if (setRet != AV_ERR_OK) {
399        // 异常处理。
400    }
401    // 解码器重新就绪。
402    OH_AVErrCode prepareRet = OH_VideoDecoder_Prepare(videoDec);
403    if (prepareRet != AV_ERR_OK) {
404        // 异常处理。
405    }
406    ```
407
408    > **注意:**
409    >
410    > 解码器回到初始化的状态,调用OH_VideoDecoder_Configure接口重新配置解码器参数时,同步模式需要重新配置OH_MD_KEY_ENABLE_SYNC_MODE为1,否则为异步模式。
411    >
412
41311. (可选)调用OH_VideoDecoder_Stop()停止解码器。
414
415    调用OH_VideoDecoder_Stop()后,解码器保留解码实例,释放输入输出buffer。
416
417    ```c++
418    // 终止解码器videoDec。
419    std::unique_lock<std::shared_mutex> lock(codecMutex);
420    OH_AVErrCode ret = OH_VideoDecoder_Stop(videoDec);
421    if (ret != AV_ERR_OK) {
422        // 异常处理。
423    }
424    ```
425
42612. 调用OH_VideoDecoder_Destroy()销毁解码器实例,释放资源。
427
428    ```c++
429    // 调用OH_VideoDecoder_Destroy,注销解码器。
430    std::unique_lock<std::shared_mutex> lock(codecMutex);
431    OH_AVErrCode ret = AV_ERR_OK;
432    if (videoDec != nullptr) {
433        ret = OH_VideoDecoder_Destroy(videoDec);
434        videoDec = nullptr;
435    }
436    if (ret != AV_ERR_OK) {
437        // 异常处理。
438    }
439    ```
440
441    > **说明:**
442    >
443    > 执行该步骤之后,需要开发者将videoDec指向nullptr,防止野指针导致程序错误。
444    >
445
446### Buffer模式
447
448参考以下示例代码,可以完成Buffer模式下视频解码的全流程,实现同步模式的数据轮转。此处以输入H.264码流文件,解码成YUV文件为例。
449
4501. 创建解码器实例。
451
452    与Surface模式相同,此处不再赘述。
453
454    ```c++
455    // 通过codecname创建解码器,应用有特殊需求,比如选择支持某种分辨率规格的解码器,可先查询capability,再根据codec name创建解码器。
456    OH_AVCapability *capability = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_VIDEO_AVC, false);
457    const char *name = OH_AVCapability_GetName(capability);
458    OH_AVCodec *videoDec = OH_VideoDecoder_CreateByName(name);
459    if (videoDec == nullptr) {
460        printf("create videoDec failed");
461        return;
462    }
463    ```
464
4652. 调用OH_VideoDecoder_Configure()配置解码器。
466
467    与Surface模式相同,此处不再赘述。
468
469    ```c++
470
471    auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
472    if (format == nullptr) {
473        // 异常处理。
474    }
475    // 写入format。
476    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_WIDTH, width); // 必须配置。
477    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_HEIGHT, height); // 必须配置。
478    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_PIXEL_FORMAT, pixelFormat);
479    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_ENABLE_SYNC_MODE, 1); // 同步模式配置。
480    // 配置解码器。
481    OH_AVErrCode ret = OH_VideoDecoder_Configure(videoDec, format.get());
482    if (ret != AV_ERR_OK) {
483        // 异常处理。
484    }
485    ```
486
487    > **注意:**
488    >
489    > 1. 要使能视频解码同步模式,必须将OH_MD_KEY_ENABLE_SYNC_MODE配置为1。
490    > 2. 同步模式在调用OH_VideoDecoder_Configure接口前不能调用OH_VideoDecoder_RegisterCallback接口,否则为异步模式。
491    >
492
4933. 调用OH_VideoDecoder_Prepare()解码器就绪。
494
495    该接口将在解码器运行前进行一些数据的准备工作。
496
497    ```c++
498    OH_AVErrCode ret = OH_VideoDecoder_Prepare(videoDec);
499    if (ret != AV_ERR_OK) {
500        // 异常处理。
501    }
502    ```
503
5044. 调用OH_VideoDecoder_Start()启动解码器。
505
506    ```c++
507    std::unique_ptr<std::ofstream> outputFile = std::make_unique<std::ofstream>();
508    if (outputFile != nullptr) {
509        outputFile->open("/*yourpath*.yuv", std::ios::out | std::ios::binary | std::ios::ate);
510    }
511    // 启动解码器,开始解码。
512    OH_AVErrCode ret = OH_VideoDecoder_Start(videoDec);
513    if (ret != AV_ERR_OK) {
514        // 异常处理。
515    }
516    ```
517
5185. 获取可用buffer并写入码流至解码器。
519
520    - 调用[OH_VideoDecoder_QueryInputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_queryinputbuffer)接口获取下一个可用的输入缓冲区(buffer)的索引(index)。
521    - 根据获取的索引(index),调用[OH_VideoDecoder_GetInputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_getinputbuffer)接口获取对应的缓冲区(buffer)实例。
522    - 将待解码数据写入该缓冲区(buffer)后,调用[OH_VideoDecoder_PushInputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_pushinputbuffer)接口提交至解码器进行解码。当所有待处理数据全部传递给解码器后,需要将flag标识成AVCODEC_BUFFER_FLAGS_EOS,通知解码器输入结束。
523
524    示例中的变量size、offset、pts、frameData、flags说明与Surface模式相同,此处不再赘述。
525
526    ```c++
527    bool DecoderInput(OH_AVCodec *videoDec, int64_t timeoutUs)
528    {
529        uint32_t index;
530        std::shared_lock<std::shared_mutex> lock(codecMutex);
531
532        OH_AVErrCode ret = OH_VideoDecoder_QueryInputBuffer(videoDec, &index, timeoutUs);
533        switch (ret) {
534            case AV_ERR_OK: {
535                OH_AVBuffer *buffer = OH_VideoDecoder_GetInputBuffer(videoDec, index);
536                if (buffer == nullptr) {
537                    // 异常处理。
538                    return false;
539                }
540                // 写入码流数据。
541                uint8_t *addr = OH_AVBuffer_GetAddr(buffer);
542                if (addr == nullptr) {
543                   // 异常处理。
544                   return false;
545                }
546                // buffer数据填充。
547                int32_t capacity = OH_AVBuffer_GetCapacity(buffer);
548                if (size > capacity) {
549                    // 异常处理。
550                }
551                memcpy(addr, frameData, size);
552
553                OH_AVCodecBufferAttr info;
554                // buffer属性配置。
555                // 配置帧数据的输入尺寸、偏移量、时间戳等字段信息。
556                info.size = size;
557                info.offset = offset;
558                info.pts = pts;
559                if (inFile_->eof()) {
560                    info.flags = AVCODEC_BUFFER_FLAGS_EOS;
561                } else {
562                    info.flags = flags;
563                }
564                OH_AVErrCode setBufferRet = OH_AVBuffer_SetBufferAttr(buffer, &info);
565                if (setBufferRet != AV_ERR_OK) {
566                    // 异常处理。
567                    return false;
568                }
569                OH_AVErrCode pushInputRet = OH_VideoDecoder_PushInputBuffer(videoDec, index);
570                if (pushInputRet != AV_ERR_OK) {
571                    // 异常处理。
572                    return false;
573                }
574                if (inFile_->eof()) {
575                    inputDone = 1;
576                }
577                break;
578            }
579            case AV_ERR_TRY_AGAIN_LATER: {
580                break;
581            }
582            default: {
583                // 异常处理。
584                return false;
585            }
586        }
587        return true;
588    }
589    ```
590
5916. 获取可用buffer并释放解码帧。
592
593   - 调用[OH_VideoDecoder_QueryOutputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_queryoutputbuffer)接口获取下一个可用的输出缓冲区(buffer)的索引(index)。
594   - 根据获取的索引(index),调用[OH_VideoDecoder_GetOutputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_getoutputbuffer)接口获取对应的缓冲区(buffer)实例。
595   - 调用[OH_VideoDecoder_FreeOutputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_freeoutputbuffer)接口释放解码帧。
596
597    ```c++
598    bool DecoderOutput(OH_AVCodec *videoDec, int64_t timeoutUs)
599    {
600        uint32_t index;
601        int32_t cropTop = 0;
602        int32_t cropBottom = 0;
603        int32_t cropLeft = 0;
604        int32_t cropRight = 0;
605        int32_t widthStride = 0;
606        int32_t heightStride = 0;
607        std::shared_lock<std::shared_mutex> lock(codecMutex);
608
609        OH_AVErrCode ret = OH_VideoDecoder_QueryOutputBuffer(videoDec, &index, timeoutUs);
610        switch (ret) {
611            case AV_ERR_OK: {
612                OH_AVBuffer *buffer = OH_VideoDecoder_GetOutputBuffer(videoDec, index);
613                if (buffer == nullptr) {
614                    // 异常处理。
615                    return false;
616                }
617
618                // 获取解码后信息。
619                OH_AVCodecBufferAttr info;
620                OH_AVErrCode getBufferRet = OH_AVBuffer_GetBufferAttr(buffer, &info);
621                if (getBufferRet != AV_ERR_OK) {
622                    // 异常处理。
623                    return false;
624                }
625                if (info.flags & AVCODEC_BUFFER_FLAGS_EOS) {
626                    outputDone = 1;
627                }
628
629                // 释放已完成处理的信息,index为对应buffer队列的下标。
630                OH_AVErrCode freeOutputRet = OH_VideoDecoder_FreeOutputBuffer(videoDec, index);
631                if (freeOutputRet != AV_ERR_OK) {
632                    // 异常处理。
633                    return false;
634                }
635                break;
636            }
637            case AV_ERR_TRY_AGAIN_LATER: {
638                break;
639            }
640            case AV_ERR_STREAM_CHANGED: {
641                auto format = std::shared_ptr<OH_AVFormat>(OH_VideoDecoder_GetOutputDescription(videoDec), OH_AVFormat_Destroy);
642                if (format == nullptr) {
643                    // 异常处理。
644                }
645                // 获取到变化后的视频宽、高、跨距。
646                bool getIntRet = OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_PIC_WIDTH, &width) &&
647                           OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_PIC_HEIGHT, &height) &&
648                           OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_STRIDE, &widthStride) &&
649                           OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_SLICE_HEIGHT, &heightStride) &&
650                           // 获取裁剪矩形信息可选。
651                           OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_CROP_TOP, &cropTop) &&
652                           OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_CROP_BOTTOM, &cropBottom) &&
653                           OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_CROP_LEFT, &cropLeft) &&
654                           OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_CROP_RIGHT, &cropRight);
655                if (!getIntRet) {
656                 	// 异常处理。
657                }
658                break;
659            }
660            default: {
661                // 异常处理。
662                return false;
663            }
664        }
665        return true;
666    }
667    ```
668
6697. 解码器送帧/出帧处理循环。
670
671    ```c++
672    bool result = true;
673    int64_t timeoutUs = 0; // 单位:微秒(us),负值:无限等待;0:立即退出;正值:等待指定时长后退出。
674
675    while (!outputDone && result) {
676        if (!inputDone) {
677            result = DecoderInput(videoDec, timeoutUs);
678        }
679        if (!outputDone) {
680            result = DecoderOutput(videoDec, timeoutUs);
681        }
682    }
683    ```
684
685后续流程(包括刷新、重置停止和销毁解码器)与Surface模式基本一致,请参考[Surface模式](#surface模式)的步骤9-12。