• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Synchronous Video Decoding
2
3<!--Kit: AVCodec Kit-->
4<!--Subsystem: Multimedia-->
5<!--Owner: @zhanghongran-->
6<!--Designer: @dpy2650--->
7<!--Tester: @cyakee-->
8<!--Adviser: @zengyawen-->
9
10Starting from API version 20, video decoding in synchronous mode is supported.
11
12You can call native APIs to perform synchronous video decoding.
13
14For details about the supported decoding capabilities, see [AVCodec Supported Formats](avcodec-support-formats.md#video-decoding).
15
16For details about the restrictions, supported capabilities, and state machine call relationships of video decoding, see [Video Decoding](video-decoding.md).
17
18## When to Use
19
20Asynchronous mode is generally recommended for most use cases. Synchronous mode can be used if you need to actively request buffers for frame delivery.
21
22
23## How to Develop
24
25Read [VideoDecoder](../../reference/apis-avcodec-kit/_video_decoder.md) for the API reference.
26
27The figure below shows the call relationship of synchronous video decoding.
28
29- The dotted line indicates an optional operation.
30
31- The solid line indicates a mandatory operation.
32
33![Call relationship of synchronous video decoding](figures/synchronous-video-decode.png)
34
35### Linking the Dynamic Libraries in the CMake Script
36
37``` cmake
38target_link_libraries(sample PUBLIC libnative_media_codecbase.so)
39target_link_libraries(sample PUBLIC libnative_media_core.so)
40target_link_libraries(sample PUBLIC libnative_media_vdec.so)
41```
42
43> **NOTE**
44>
45> The word **sample** in the preceding code snippet is only an example. Use the actual project directory name.
46>
47
48### Defining the Basic Structure
49
50The sample code provided in this section adheres to the C++17 standard and is for reference only.
51
521. Add the header files.
53
54    ```c++
55    #include <multimedia/player_framework/native_avcodec_videodecoder.h>
56    #include <multimedia/player_framework/native_avcapability.h>
57    #include <multimedia/player_framework/native_avcodec_base.h>
58    #include <multimedia/player_framework/native_avformat.h>
59    #include <multimedia/player_framework/native_avbuffer.h>
60    #include <multimedia/player_framework/native_averrors.h>
61    #include <native_buffer/native_buffer.h>
62    #include <memory>
63    #include <fstream>
64    #include <mutex>
65    #include <shared_mutex>
66    #include <string.h>
67    ```
68
692. Configure global variables.
70
71    These global variables are for reference only. They can be encapsulated into an object based on service requirements.
72
73    ```c++
74    // Video frame width.
75    int32_t width = 320;
76    // Video frame height.
77    int32_t height = 240;
78    // Video pixel format.
79    OH_AVPixelFormat pixelFormat = AV_PIXEL_FORMAT_NV12;
80    // Decoder synchronization lock.
81    std::shared_mutex codecMutex;
82    // Pointer to the decoder instance.
83    OH_AVCodec *videoDec = nullptr;
84    // Decoding output.
85    bool outputDone = false;
86    // Decoding input.
87    bool inputDone = false;
88    std::unique_ptr<std::ifstream> inFile_;
89    ```
90
91### Surface Mode
92
93The following walks you through how to implement the entire video decoding process in surface mode and implement data rotation in synchronous mode. In this example, an H.264 stream file is input, decoded, and rendered.
94
95
961. Create a decoder instance.
97
98    Create a decoder by name. In the code snippet below, the following variables are used:
99
100    - **videoDec**: pointer to the video decoder instance.
101    - **capability**: pointer to the decoder's capability.
102    - **OH_AVCODEC_MIMETYPE_VIDEO_AVC**: AVC video codec.
103
104    ```c++
105    // Create a hardware decoder instance.
106    OH_AVCapability *capability= OH_AVCodec_GetCapabilityByCategory(OH_AVCODEC_MIMETYPE_VIDEO_AVC, false, HARDWARE);
107    const char *name = OH_AVCapability_GetName(capability);
108    OH_AVCodec *videoDec = OH_VideoDecoder_CreateByName(name);
109    if (videoDec == nullptr) {
110        printf("create videoDec failed");
111        return;
112    }
113    ```
1142. Call **OH_VideoDecoder_Configure()** to configure the decoder.
115
116    - For details about the configurable options, see [Media Data Key-Value Pairs](../../reference/apis-avcodec-kit/_codec_base.md#media-data-key-value-pairs).
117    - For details about the parameter verification rules, see [OH_VideoDecoder_Configure()](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_configure).
118    - The parameter value ranges can be obtained through the capability query interface. For details, see [Obtaining Supported Codecs](obtain-supported-codecs.md).
119
120    Currently, the following options must be configured for all supported formats: video frame width and height.
121
122    ```c++
123
124    auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
125    if (format == nullptr) {
126        // Handle exceptions.
127    }
128    // Set the format.
129    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_WIDTH, width); // Mandatory.
130    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_HEIGHT, height); // Mandatory.
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); // Set synchronous mode.
133    // Configure the decoder.
134    OH_AVErrCode ret = OH_VideoDecoder_Configure(videoDec, format.get());
135    if (ret != AV_ERR_OK) {
136        // Handle exceptions.
137    }
138    ```
139
140    > **NOTE**
141    >
142    > To enable video decoding in synchronous mode, **OH_MD_KEY_ENABLE_SYNC_MODE** must be set to **1**.
143    >
144    > To use synchronous mode, do not call **OH_VideoDecoder_RegisterCallback** in prior to **OH_VideoDecoder_Configure**. Otherwise, the decoder will run in asynchronous mode instead.
145
146
1473. Set the surface.
148
149   In the code snippet below, the following variables are used:
150   - **nativeWindow**: For details about how to obtain the native window, see step 6 in [Surface Mode](video-decoding.md#surface-mode).
151
152    ```c++
153    // Set the surface.
154    // Set the window parameters.
155    OH_AVErrCode ret = OH_VideoDecoder_SetSurface(videoDec, nativeWindow);
156    if (ret != AV_ERR_OK) {
157        // Handle exceptions.
158    }
159    ```
160
161
1624. Call **OH_VideoDecoder_Prepare()** to prepare internal resources for the decoder.
163
164    ```c++
165    OH_AVErrCode ret = OH_VideoDecoder_Prepare(videoDec);
166    if (ret != AV_ERR_OK) {
167        // Handle exceptions.
168    }
169    ```
170
1715. Call **OH_VideoDecoder_Start()** to start the decoder.
172
173    ```c++
174    // Start the decoder.
175    OH_AVErrCode ret = OH_VideoDecoder_Start(videoDec);
176    if (ret != AV_ERR_OK) {
177        // Handle exceptions.
178    }
179    ```
180
1816. Obtain an available buffer and write the bitstream to the decoder.
182
183    - Call [OH_VideoDecoder_QueryInputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_queryinputbuffer) to obtain the index of the next available input buffer.
184    - Based on this index, call [OH_VideoDecoder_GetInputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_getinputbuffer) to obtain the buffer instance.
185    - Write the data to be decoded into the buffer, and call [OH_VideoDecoder_PushInputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_pushinputbuffer) to push it to the decoder for decoding. When all the data to be processed has been passed to the decoder, set flag to **AVCODEC_BUFFER_FLAGS_EOS** to notify the decoder that the input is complete.
186
187    Send the input queue for decoding. In the code snippet below, the following variables are used:
188    - **size**, **offset**, **pts**, and **frameData**: size, offset, timestamp, and frame data. For details about how to obtain such information, see step 9 in [Media Data Demultiplexing](./audio-video-demuxer.md#how-to-develop).
189    - **flags**: type of the buffer flag. For details, see [OH_AVCodecBufferFlags](../../reference/apis-avcodec-kit/_core.md#oh_avcodecbufferflags).
190
191    ```c++
192    bool DecoderInput(OH_AVCodec *videoDec, int64_t timeoutUs)
193    {
194        uint32_t index;
195        std::shared_lock<std::shared_mutex> lock(codecMutex);
196
197        OH_AVErrCode ret = OH_VideoDecoder_QueryInputBuffer(videoDec, &index, timeoutUs);
198        switch (ret) {
199            case AV_ERR_OK: {
200                OH_AVBuffer *buffer = OH_VideoDecoder_GetInputBuffer(videoDec, index);
201                if (buffer == nullptr) {
202                    // Handle exceptions.
203                    return false;
204                }
205                // Write stream data.
206                uint8_t *addr = OH_AVBuffer_GetAddr(buffer);
207                if (addr == nullptr) {
208                   // Handle exceptions.
209                   return false;
210                }
211                // Fill in the buffer.
212                int32_t capacity = OH_AVBuffer_GetCapacity(buffer);
213                if (size > capacity) {
214                    // Handle exceptions.
215                }
216                memcpy(addr, frameData, size);
217
218                OH_AVCodecBufferAttr info;
219                // Set the buffer attributes.
220                // Configure the size, offset, and timestamp of the frame data.
221                info.size = size;
222                info.offset = offset;
223                info.pts = pts;
224                if (inFile_->eof()) {
225                    info.flags = AVCODEC_BUFFER_FLAGS_EOS;
226                } else {
227                    info.flags = flags;
228                }
229                OH_AVErrCode setBufferRet = OH_AVBuffer_SetBufferAttr(buffer, &info);
230                if (setBufferRet != AV_ERR_OK) {
231                    // Handle exceptions.
232                    return false;
233                }
234                OH_AVErrCode pushInputRet = OH_VideoDecoder_PushInputBuffer(videoDec, index);
235                if (pushInputRet != AV_ERR_OK) {
236                    // Handle exceptions.
237                    return false;
238                }
239                if (inFile_->eof()) {
240                    inputDone = 1;
241                }
242                break;
243            }
244            case AV_ERR_TRY_AGAIN_LATER: {
245                break;
246            }
247            default: {
248                // Handle exceptions.
249                return false;
250            }
251        }
252        return true;
253    }
254    ```
255
2567. Obtain an available buffer and release the decoded frame.
257
258   - Call [OH_VideoDecoder_QueryOutputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_queryoutputbuffer) to obtain the index of the next available input buffer.
259   - Bsed on this index, call [OH_VideoDecoder_GetOutputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_getoutputbuffer) to obtain the buffer instance.
260   - Determine the subsequent operations based on the **isRender** flag. If the decoded frame does not need to be rendered, call [OH_VideoDecoder_FreeOutputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_freeoutputbuffer) to release the decoded frame. If the decoded frame needs to be rendered, you can call [OH_VideoDecoder_RenderOutputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_renderoutputbuffer) to display the decoded frame and automatically release it, or call [OH_VideoDecoder_RenderOutputBufferAtTime](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_renderoutputbufferattime) to display the decoded frame at a specified time and then release it.
261
262    ```c++
263    bool DecoderOutput(OH_AVCodec *videoDec, int64_t timeoutUs)
264    {
265        uint32_t index;
266        std::shared_lock<std::shared_mutex> lock(codecMutex);
267
268        OH_AVErrCode ret = OH_VideoDecoder_QueryOutputBuffer(videoDec, &index, timeoutUs);
269        switch (ret) {
270            case AV_ERR_OK: {
271                OH_AVBuffer *buffer = OH_VideoDecoder_GetOutputBuffer(videoDec, index);
272                if (buffer == nullptr) {
273                    // Handle exceptions.
274                    return false;
275                }
276
277                // Obtain the decoded information.
278                OH_AVCodecBufferAttr info;
279                OH_AVErrCode getBufferRet = OH_AVBuffer_GetBufferAttr(buffer, &info);
280                if (getBufferRet != AV_ERR_OK) {
281                    // Handle exceptions.
282                    return false;
283                }
284                if (info.flags & AVCODEC_BUFFER_FLAGS_EOS) {
285                    outputDone = 1;
286                }
287
288                // Handle the decoded output data.
289                // You can determine the value.
290                bool isRender;
291                bool isNeedRenderAtTime;
292                OH_AVErrCode result = AV_ERR_OK;
293                if (isRender) {
294                    // Render the data and free the output buffer. index is the index of the buffer.
295                    if (isNeedRenderAtTime){
296                        // Obtain the system absolute time, and call renderTimestamp to display the time based on service requirements.
297                        int64_t renderTimestamp =
298                            std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
299                        result = OH_VideoDecoder_RenderOutputBufferAtTime(videoDec, index, renderTimestamp);
300                    } else {
301                        result = OH_VideoDecoder_RenderOutputBuffer(videoDec, index);
302                    }
303                } else {
304                    // Free the output buffer.
305                    result = OH_VideoDecoder_FreeOutputBuffer(videoDec, index);
306                }
307                if (result != AV_ERR_OK) {
308                    // Handle exceptions.
309                    return false;
310                }
311                break;
312            }
313            case AV_ERR_TRY_AGAIN_LATER: {
314                break;
315            }
316            case AV_ERR_STREAM_CHANGED: {
317                auto format = std::shared_ptr<OH_AVFormat>(OH_VideoDecoder_GetOutputDescription(videoDec), OH_AVFormat_Destroy);
318                if (format == nullptr) {
319                    // Handle exceptions.
320                }
321                // Obtain the new width and height.
322                bool getIntRet = OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_PIC_WIDTH, &width) &&
323                                 OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_PIC_HEIGHT, &height);
324                if (!getIntRet) {
325             	    // Handle exceptions.
326                }
327                break;
328            }
329            default: {
330                // Handle exceptions.
331                return false;
332            }
333        }
334        return true;
335    }
336    ```
337
3388. Enable the decoder to input and output frames in a loop.
339
340    ```c++
341    bool result = true;
342    int64_t timeoutUs = 0; // Unit: μs. A negative value means to wait infinitely. The value 0 means to return immediately. A positive value means to wait for the specified time before exiting.
343
344    while (!outputDone && result) {
345        if (!inputDone) {
346            result = DecoderInput(videoDec, timeoutUs);
347        }
348        if (!outputDone) {
349            result = DecoderOutput(videoDec, timeoutUs);
350        }
351    }
352    ```
353
3549. (Optional) Call **OH_VideoDecoder_Flush()** to refresh the decoder.
355
356    After **OH_VideoDecoder_Flush** is called, the decoder remains in the Running state, but the input and output data and parameter set (such as the H.264 PPS/SPS) buffered in the decoder are cleared.
357
358    In this case, you need to call [OH_VideoDecoder_Start](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_start) to start decoding again.
359
360    ```c++
361    // Use codecMutex to avoid the problem where the decoding thread keeps running and exits the loop after the Flush API is called and the state is changed.
362    std::unique_lock<std::shared_mutex> lock(codecMutex);
363    // Refresh the decoder.
364    OH_AVErrCode ret = OH_VideoDecoder_Flush(videoDec);
365    if (ret != AV_ERR_OK) {
366        // Handle exceptions.
367    }
368
369    // Start decoding again.
370    ret = OH_VideoDecoder_Start(videoDec);
371    if (ret != AV_ERR_OK) {
372        // Handle exceptions.
373    }
374    ```
375
37610. (Optional) Call **OH_VideoDecoder_Reset()** to reset the decoder.
377
378    After **OH_VideoDecoder_Reset** is called, the decoder returns to the Initialized state. To continue decoding, you must call [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), and [OH_VideoDecoder_Prepare](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_prepare) in sequence.
379
380    ```c++
381    // Reset the decoder.
382    std::unique_lock<std::shared_mutex> lock(codecMutex);
383    OH_AVErrCode resetRet = OH_VideoDecoder_Reset(videoDec);
384    if (resetRet != AV_ERR_OK) {
385        // Handle exceptions.
386    }
387
388    // Reconfigure the decoder.
389    auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
390    if (format == nullptr) {
391        // Handle exceptions.
392    }
393    OH_AVErrCode configRet = OH_VideoDecoder_Configure(videoDec, format.get());
394    if (configRet != AV_ERR_OK) {
395        // Handle exceptions.
396    }
397
398    // Reconfigure the surface in surface mode. This is not required in buffer mode.
399    OH_AVErrCode setRet = OH_VideoDecoder_SetSurface(videoDec, nativeWindow);
400    if (setRet != AV_ERR_OK) {
401        // Handle exceptions.
402    }
403    // The decoder is ready again.
404    OH_AVErrCode prepareRet = OH_VideoDecoder_Prepare(videoDec);
405    if (prepareRet != AV_ERR_OK) {
406        // Handle exceptions.
407    }
408    ```
409
410    > **NOTE**
411    >
412    > When the decoder returns to the initialized state, you must call **OH_VideoDecoder_Configure** to set **OH_MD_KEY_ENABLE_SYNC_MODE** to **1** to reconfigure the decoder parameters. Otherwise, the decoder will run in asynchronous mode.
413
41411. (Optional) Call **OH_VideoDecoder_Stop()** to stop the decoder.
415
416    After **OH_VideoDecoder_Stop()** is called, the decoder retains the decoding instance and releases the input and output buffers.
417
418    ```c++
419    // Stop the decoder.
420    std::unique_lock<std::shared_mutex> lock(codecMutex);
421    OH_AVErrCode ret = OH_VideoDecoder_Stop(videoDec);
422    if (ret != AV_ERR_OK) {
423        // Handle exceptions.
424    }
425    ```
426
42712. Call **OH_VideoDecoder_Destroy()** to destroy the decoder instance and release resources.
428
429    ```c++
430    // Call OH_VideoDecoder_Destroy to destroy the decoder.
431    std::unique_lock<std::shared_mutex> lock(codecMutex);
432    OH_AVErrCode ret = AV_ERR_OK;
433    if (videoDec != nullptr) {
434        ret = OH_VideoDecoder_Destroy(videoDec);
435        videoDec = nullptr;
436    }
437    if (ret != AV_ERR_OK) {
438        // Handle exceptions.
439    }
440    ```
441
442    > **NOTE**
443    >
444    > After the call, you must set a null pointer to the decoder to prevent program errors caused by wild pointers.
445
446### Buffer Mode
447
448The following walks you through how to implement the entire video decoding process in buffer mode and implement data rotation in synchronous mode. In this example, an H.264 stream file is input and decoded into a YUV file.
449
4501. Create a decoder instance.
451
452    The procedure is the same as that in surface mode and is not described here.
453
454    ```c++
455    // To create a decoder by name, call OH_AVCapability_GetName to obtain the codec names available and then call OH_VideoDecoder_CreateByName. If your application has special requirements, for example, expecting a decoder that supports a certain resolution, you can call OH_AVCodec_GetCapability to query the capability first.
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. Call **OH_VideoDecoder_Configure()** to configure the decoder.
466
467    The procedure is the same as that in surface mode and is not described here.
468
469    ```c++
470
471    auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
472    if (format == nullptr) {
473        // Handle exceptions.
474    }
475    // Set the format.
476    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_WIDTH, width); // Mandatory.
477    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_HEIGHT, height); // Mandatory.
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); // Set synchronous mode.
480    // Configure the decoder.
481    OH_AVErrCode ret = OH_VideoDecoder_Configure(videoDec, format.get());
482    if (ret != AV_ERR_OK) {
483        // Handle exceptions.
484    }
485    ```
486
487    > **NOTE**
488    >
489    > To enable video decoding in synchronous mode, **OH_MD_KEY_ENABLE_SYNC_MODE** must be set to **1**.
490    >
491    > To use synchronous mode, do not call **OH_VideoDecoder_RegisterCallback** in prior to **OH_VideoDecoder_Configure**. Otherwise, the decoder will run in asynchronous mode instead.
492
4933. Call **OH_VideoDecoder_Prepare()** to prepare internal resources for the decoder.
494
495   ```c++
496OH_AVErrCode ret = OH_VideoDecoder_Prepare(videoDec);
497    if (ret != AV_ERR_OK) {
498        // Handle exceptions.
499    }
500    ```
501
5024. Call **OH_VideoDecoder_Start()** to start the decoder.
503
504    ```c++
505    std::unique_ptr<std::ofstream> outputFile = std::make_unique<std::ofstream>();
506    if (outputFile != nullptr) {
507        outputFile->open("/*yourpath*.yuv", std::ios::out | std::ios::binary | std::ios::ate);
508    }
509    // Start the decoder.
510    OH_AVErrCode ret = OH_VideoDecoder_Start(videoDec);
511    if (ret != AV_ERR_OK) {
512        // Handle exceptions.
513    }
514    ```
515
5165. Obtain an available buffer and write the bitstream to the decoder.
517
518    - Call [OH_VideoDecoder_QueryInputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_queryinputbuffer) to obtain the index of the next available input buffer.
519    - Based on this index, call [OH_VideoDecoder_GetInputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_getinputbuffer) to obtain the buffer instance.
520    - Write the data to be decoded into the buffer, and call [OH_VideoDecoder_PushInputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_pushinputbuffer) to push it to the decoder for decoding. When all the data to be processed has been passed to the decoder, set flag to **AVCODEC_BUFFER_FLAGS_EOS** to notify the decoder that the input is complete.
521
522    The meanings of the variables **size**, **offset**, **pts**, **frameData**, and **flags** in the example are the same as those in surface mode.
523
524    ```c++
525    bool DecoderInput(OH_AVCodec *videoDec, int64_t timeoutUs)
526    {
527        uint32_t index;
528        std::shared_lock<std::shared_mutex> lock(codecMutex);
529
530        OH_AVErrCode ret = OH_VideoDecoder_QueryInputBuffer(videoDec, &index, timeoutUs);
531        switch (ret) {
532            case AV_ERR_OK: {
533                OH_AVBuffer *buffer = OH_VideoDecoder_GetInputBuffer(videoDec, index);
534                if (buffer == nullptr) {
535                    // Handle exceptions.
536                    return false;
537                }
538                // Write stream data.
539                uint8_t *addr = OH_AVBuffer_GetAddr(buffer);
540                if (addr == nullptr) {
541                   // Handle exceptions.
542                   return false;
543                }
544                // Fill in the buffer.
545                int32_t capacity = OH_AVBuffer_GetCapacity(buffer);
546                if (size > capacity) {
547                    // Handle exceptions.
548                }
549                memcpy(addr, frameData, size);
550
551                OH_AVCodecBufferAttr info;
552                // Set the buffer attributes.
553                // Configure the size, offset, and timestamp of the frame data.
554                info.size = size;
555                info.offset = offset;
556                info.pts = pts;
557                if (inFile_->eof()) {
558                    info.flags = AVCODEC_BUFFER_FLAGS_EOS;
559                } else {
560                    info.flags = flags;
561                }
562                OH_AVErrCode setBufferRet = OH_AVBuffer_SetBufferAttr(buffer, &info);
563                if (setBufferRet != AV_ERR_OK) {
564                    // Handle exceptions.
565                    return false;
566                }
567                OH_AVErrCode pushInputRet = OH_VideoDecoder_PushInputBuffer(videoDec, index);
568                if (pushInputRet != AV_ERR_OK) {
569                    // Handle exceptions.
570                    return false;
571                }
572                if (inFile_->eof()) {
573                    inputDone = 1;
574                }
575                break;
576            }
577            case AV_ERR_TRY_AGAIN_LATER: {
578                break;
579            }
580            default: {
581                // Handle exceptions.
582                return false;
583            }
584        }
585        return true;
586    }
587    ```
588
5896. Obtain an available buffer and release the decoded frame.
590
591   - Call [OH_VideoDecoder_QueryOutputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_queryoutputbuffer) to obtain the index of the next available input buffer.
592   - Based on this index, call [OH_VideoDecoder_GetOutputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_getoutputbuffer) to obtain the buffer instance.
593   - Call [OH_VideoDecoder_FreeOutputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_freeoutputbuffer) to release the decoded frame.
594
595    ```c++
596    bool DecoderOutput(OH_AVCodec *videoDec, int64_t timeoutUs)
597    {
598        uint32_t index;
599        int32_t cropTop = 0;
600        int32_t cropBottom = 0;
601        int32_t cropLeft = 0;
602        int32_t cropRight = 0;
603        int32_t widthStride = 0;
604        int32_t heightStride = 0;
605        std::shared_lock<std::shared_mutex> lock(codecMutex);
606
607        OH_AVErrCode ret = OH_VideoDecoder_QueryOutputBuffer(videoDec, &index, timeoutUs);
608        switch (ret) {
609            case AV_ERR_OK: {
610                OH_AVBuffer *buffer = OH_VideoDecoder_GetOutputBuffer(videoDec, index);
611                if (buffer == nullptr) {
612                    // Handle exceptions.
613                    return false;
614                }
615
616                // Obtain the decoded information.
617                OH_AVCodecBufferAttr info;
618                OH_AVErrCode getBufferRet = OH_AVBuffer_GetBufferAttr(buffer, &info);
619                if (getBufferRet != AV_ERR_OK) {
620                    // Handle exceptions.
621                    return false;
622                }
623                if (info.flags & AVCODEC_BUFFER_FLAGS_EOS) {
624                    outputDone = 1;
625                }
626
627                // Free the output buffer. index is the index of the buffer.
628                OH_AVErrCode freeOutputRet = OH_VideoDecoder_FreeOutputBuffer(videoDec, index);
629                if (freeOutputRet != AV_ERR_OK) {
630                    // Handle exceptions.
631                    return false;
632                }
633                break;
634            }
635            case AV_ERR_TRY_AGAIN_LATER: {
636                break;
637            }
638            case AV_ERR_STREAM_CHANGED: {
639                auto format = std::shared_ptr<OH_AVFormat>(OH_VideoDecoder_GetOutputDescription(videoDec), OH_AVFormat_Destroy);
640                if (format == nullptr) {
641                    // Handle exceptions.
642                }
643                // Obtain the new video width, height, and stride.
644                bool getIntRet = OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_PIC_WIDTH, &width) &&
645                           OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_PIC_HEIGHT, &height) &&
646                           OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_STRIDE, &widthStride) &&
647                           OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_SLICE_HEIGHT, &heightStride) &&
648                           // (Optional) Obtain the cropped rectangle information.
649                           OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_CROP_TOP, &cropTop) &&
650                           OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_CROP_BOTTOM, &cropBottom) &&
651                           OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_CROP_LEFT, &cropLeft) &&
652                           OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_CROP_RIGHT, &cropRight);
653                if (!getIntRet) {
654                 	// Handle exceptions.
655                }
656                break;
657            }
658            default: {
659                // Handle exceptions.
660                return false;
661            }
662        }
663        return true;
664    }
665    ```
666
6677. Enable the decoder to input and output frames in a loop.
668
669    ```c++
670    bool result = true;
671    int64_t timeoutUs = 0; // Unit: μs. A negative value means to wait infinitely. The value 0 means to return immediately. A positive value means to wait for the specified time before exiting.
672
673    while (!outputDone && result) {
674        if (!inputDone) {
675            result = DecoderInput(videoDec, timeoutUs);
676        }
677        if (!outputDone) {
678            result = DecoderOutput(videoDec, timeoutUs);
679        }
680    }
681    ```
682
683The subsequent processes (including refreshing, resetting, stopping, and destroying the decoder) are the same as those in surface mode. For details, see steps 9–12 in [Surface Mode](#surface-mode).
684