• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Synchronous Video Encoding
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 encoding in synchronous mode is supported.
11
12You can call native APIs to perform synchronous video encoding.
13
14For details about the supported encoding capabilities, see [AVCodec Supported Formats](avcodec-support-formats.md#video-encoding).
15
16For details about the restrictions, supported capabilities, and state machine call relationships of video encoding, see [Video Encoding](video-encoding.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 [VideoEncoder](../../reference/apis-avcodec-kit/_video_encoder.md) for the API reference.
26
27The figure below shows the call relationship of synchronous video encoding.
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 encoding](figures/synchronous-video-encode.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_venc.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_videoencoder.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    ```
67
682. Configure global variables.
69
70    These global variables are for reference only. They can be encapsulated into an object based on service requirements.
71
72    ```c++
73    // Video frame width.
74    int32_t width = 320;
75    // Video frame height.
76    int32_t height = 240;
77    // Video width stride.
78    int32_t widthStride = 0;
79    // Video height stride.
80    int32_t heightStride = 0;
81    // Video pixel format.
82    OH_AVPixelFormat pixelFormat = AV_PIXEL_FORMAT_NV12;
83    // Encoder synchronization lock.
84    std::shared_mutex codecMutex;
85    // Pointer to the encoder instance.
86    OH_AVCodec *videoEnc = nullptr;
87    // Encoding output.
88    bool outputDone = false;
89    // Encoding input.
90    bool inputDone = false;
91    std::unique_ptr<std::ifstream> inFile_;
92    ```
93
94### Surface Mode
95
96The following walks you through how to implement the entire video encoding process in surface mode and implement data rotation in synchronous mode. In this example, surface data is input and encoded into a H.264 stream.
97
98
991. Create an encoder instance.
100
101    Create an encoder by name. In the code snippet below, the following variables are used:
102
103    - **videoEnc**: pointer to the video encoder instance.
104    - **capability**: pointer to the encoder's capability.
105    - [OH_AVCODEC_MIMETYPE_VIDEO_AVC](../../reference/apis-avcodec-kit/_codec_base.md#oh_avcodec_mimetype_video_avc): video codec in AVC format.
106
107    ```c++
108    // Create a hardware encoder instance.
109    OH_AVCapability *capability= OH_AVCodec_GetCapabilityByCategory(OH_AVCODEC_MIMETYPE_VIDEO_AVC, true, HARDWARE);
110    const char *name = OH_AVCapability_GetName(capability);
111    OH_AVCodec *videoEnc = OH_VideoEncoder_CreateByName(name);
112    if (videoEnc == nullptr) {
113        printf("create videoEnc failed");
114        return;
115    }
116    ```
117
1182. Call **OH_VideoEncoder_Configure()** to configure the encoder.
119
120    - For details about the configurable options, see [Media Data Key-Value Pairs](../../reference/apis-avcodec-kit/_codec_base.md#media-data-key-value-pairs).
121    - For details about the parameter verification rules, see [OH_VideoEncoder_Configure()](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_configure).
122    - The parameter value ranges can be obtained through the capability query interface. For details, see [Obtaining Supported Codecs](obtain-supported-codecs.md).
123
124    Currently, the following options must be configured for all supported formats: video frame width, video frame height, and video pixel format.
125
126    ```c++
127
128    auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
129    if (format == nullptr) {
130        // Handle exceptions.
131    }
132    // Set the format.
133    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_WIDTH, width); // Mandatory.
134    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_HEIGHT, height); // Mandatory.
135    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_PIXEL_FORMAT, pixelFormat); // Mandatory.
136    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_ENABLE_SYNC_MODE, 1); // Set the synchronous mode.
137    // Configure the encoder.
138    OH_AVErrCode ret = OH_VideoEncoder_Configure(videoEnc, format.get());
139    if (ret != AV_ERR_OK) {
140        // Handle exceptions.
141    }
142    ```
143
144    > **NOTE**
145    >
146    > To enable video encoding in synchronous mode, **OH_MD_KEY_ENABLE_SYNC_MODE** must be set to **1**.
147    >
148    > To use synchronous mode, do not call **OH_VideoEncoder_RegisterCallback** or **OH_VideoEncoder_RegisterParameterCallback** in prior to **OH_VideoEncoder_Configure**. Otherwise, the encoder will run in asynchronous mode instead.
149    >
150    > Synchronous mode is not supported for frame-specific channels in surface mode.
151
1523. Set the surface.
153
154   In the code snippet below, the following variables are used:
155
156   - **nativeWindow**: For details about how to obtain the native window, see step 6 in [Surface Mode](video-encoding.md#surface-mode).
157
158    ```c++
159    // Obtain the surface used for data input.
160    OH_AVErrCode ret = OH_VideoEncoder_GetSurface(videoEnc, &nativeWindow);
161    if (ret != AV_ERR_OK) {
162        // Handle exceptions.
163    }
164    ```
165
1664. Call **OH_VideoEncoder_Prepare()** to prepare internal resources for the encoder.
167
168    ```c++
169    OH_AVErrCode ret = OH_VideoEncoder_Prepare(videoEnc);
170    if (ret != AV_ERR_OK) {
171        // Handle exceptions.
172    }
173    ```
174
1755. Call **OH_VideoEncoder_Start()** to start the encoder.
176
177    ```c++
178    // Configure the paths of the input and output files.
179    std::string_view outputFilePath = "/*yourpath*.h264";
180    std::unique_ptr<std::ofstream> outputFile = std::make_unique<std::ofstream>();
181    if (outputFile != nullptr) {
182        outputFile->open(outputFilePath.data(), std::ios::out | std::ios::binary | std::ios::ate);
183    }
184    // Start the encoder.
185    OH_AVErrCode ret = OH_VideoEncoder_Start(videoEnc);
186    if (ret != AV_ERR_OK) {
187        // Handle exceptions.
188    }
189    ```
190
1916. Obtain an available buffer and release the encoded frame.
192
193   - Call [OH_VideoEncoder_QueryOutputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_queryoutputbuffer) to obtain the index of the next available output buffer.
194   - Based on this index, call [OH_VideoEncoder_GetOutputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_getoutputbuffer) to obtain the buffer instance.
195   - Call [OH_VideoEncoder_FreeOutputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_freeoutputbuffer) to release the encoded frame.
196
197    ```c++
198    bool EncoderOutput(OH_AVCodec *videoEnc, int64_t timeoutUs)
199    {
200        uint32_t index;
201        std::shared_lock<std::shared_mutex> lock(codecMutex);
202
203        OH_AVErrCode ret = OH_VideoEncoder_QueryOutputBuffer(videoEnc, &index, timeoutUs);
204        switch (ret) {
205            case AV_ERR_OK: {
206                OH_AVBuffer *buffer = OH_VideoEncoder_GetOutputBuffer(videoEnc, index);
207                if (buffer == nullptr) {
208                    // Handle exceptions.
209                    return false;
210                }
211
212                // Obtain the encoded information.
213                OH_AVCodecBufferAttr info;
214                OH_AVErrCode getBufferRet = OH_AVBuffer_GetBufferAttr(buffer, &info);
215                if (getBufferRet != AV_ERR_OK) {
216                    // Handle exceptions.
217                    return false;
218                }
219                if (info.flags & AVCODEC_BUFFER_FLAGS_EOS) {
220                    outputDone = 1;
221                }
222
223                // Write the encoded frame data (specified by buffer) to the output file.
224                uint8_t *addr = OH_AVBuffer_GetAddr(buffer);
225                if (addr == nullptr) {
226                   // Handle exceptions.
227                   return false;
228                }
229                if (outputFile != nullptr && outputFile->is_open()) {
230                    outputFile->write(reinterpret_cast<char *>(addr), info.size);
231                }
232                // Free the output buffer. index is the index of the buffer.
233                OH_AVErrCode freeOutputRet = OH_VideoEncoder_FreeOutputBuffer(videoEnc, index);
234                if (freeOutputRet != AV_ERR_OK) {
235                    // Handle exceptions.
236                    return false;
237                }
238                break;
239            }
240            case AV_ERR_TRY_AGAIN_LATER: {
241                break;
242            }
243            case AV_ERR_STREAM_CHANGED: {
244                auto format = std::shared_ptr<OH_AVFormat>(OH_VideoEncoder_GetOutputDescription(videoEnc), OH_AVFormat_Destroy);
245                if (format == nullptr) {
246                    // Handle exceptions.
247                }
248                // Obtain the new width and height.
249                bool getIntRet = OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_WIDTH, &width) &&
250                                 OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_HEIGHT, &height);
251                if (!getIntRet) {
252                 	// Handle exceptions.
253                }
254                break;
255            }
256            default: {
257                // Handle exceptions.
258                return false;
259            }
260        }
261        return true;
262    }
263    ```
264
2657. Enable the encoder to output frames in a loop.
266
267   ```c++
268    bool result = true;
269    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.
270
271    while (!outputDone && result) {
272        if (!outputDone ) {
273            result = EncoderOutput(videoEnc, timeoutUs);
274        }
275    }
276   ```
277
2788. Call **OH_VideoEncoder_NotifyEndOfStream()** to notify the encoder of EOS.
279
280    ```c++
281    // In surface mode, you only need to call this API to notify the encoder of EOS.
282    OH_AVErrCode ret = OH_VideoEncoder_NotifyEndOfStream(videoEnc);
283    if (ret != AV_ERR_OK) {
284        // Handle exceptions.
285    }
286    ```
287
2889. (Optional) Call **OH_VideoEncoder_Flush()** to refresh the encoder.
289
290    After **OH_VideoEncoder_Flush** is called, the encoder remains in the Running state, but the input and output data and parameter set (such as the H.264 PPS/SPS) buffered in the encoder are cleared.
291
292    To continue encoding, you must call [OH_VideoEncoder_Start](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_start) again.
293
294    ```c++
295    // Use codecMutex to avoid the problem where the encoding thread keeps running and exits the loop after the Flush API is called and the state is changed.
296    std::unique_lock<std::shared_mutex> lock(codecMutex);
297    // Refresh the encoder.
298    OH_AVErrCode flushRet = OH_VideoEncoder_Flush(videoEnc);
299    if (flushRet != AV_ERR_OK) {
300        // Handle exceptions.
301    }
302
303    // Start encoding again.
304    OH_AVErrCode startRet = OH_VideoEncoder_Start(videoEnc);
305    if (startRet != AV_ERR_OK) {
306        // Handle exceptions.
307    }
308    ```
309
31010. (Optional) Call **OH_VideoEncoder_Reset()** to reset the encoder.
311
312    After **OH_VideoEncoder_Reset** is called, the encoder returns to the initialized state. To continue encoding, you must call **[OH_VideoEncoder_Configure](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_configure)** and then **[OH_VideoEncoder_Prepare](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_prepare)**.
313
314    ```c++
315    // Reset the encoder.
316    std::unique_lock<std::shared_mutex> lock(codecMutex);
317    OH_AVErrCode resetRet = OH_VideoEncoder_Reset(videoEnc);
318    if (resetRet != AV_ERR_OK) {
319        // Handle exceptions.
320    }
321
322    // Reconfigure the encoder.
323    auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
324    if (format == nullptr) {
325        // Handle exceptions.
326    }
327    OH_AVErrCode configRet = OH_VideoEncoder_Configure(videoEnc, format.get());
328    if (configRet != AV_ERR_OK) {
329        // Handle exceptions.
330    }
331
332    // The encoder is ready again.
333    OH_AVErrCode prepareRet = OH_VideoEncoder_Prepare(videoEnc);
334    if (prepareRet != AV_ERR_OK) {
335        // Handle exceptions.
336    }
337    ```
338
339    > **NOTE**
340    >
341    > When the encoder returns to the initialized state, you must call **OH_VideoEncoder_Configure** to set **OH_MD_KEY_ENABLE_SYNC_MODE** to **1** to reconfigure the encoder parameters. Otherwise, the encoder will run in asynchronous mode.
342
34311. (Optional) Call **OH_VideoEncoder_Stop()** to stop the encoder.
344
345    After **OH_VideoEncoder_Stop** is called, the encoder retains the encoding instance and releases the input and output buffers. You can directly call **OH_VideoEncoder_Start** to continue encoding. The first **buffer** passed must carry the parameter set, starting from the IDR frame.
346
347    ```c++
348    // Stop the encoder.
349    std::unique_lock<std::shared_mutex> lock(codecMutex);
350    OH_AVErrCode ret = OH_VideoEncoder_Stop(videoEnc);
351    if (ret != AV_ERR_OK) {
352        // Handle exceptions.
353    }
354    ```
355
35612. Call **OH_VideoEncoder_Destroy()** to destroy the encoder instance and release resources.
357
358    ```c++
359    // Destroy the encoder.
360    std::unique_lock<std::shared_mutex> lock(codecMutex);
361    OH_AVErrCode ret = AV_ERR_OK;
362    if (videoEnc != nullptr) {
363        ret = OH_VideoEncoder_Destroy(videoEnc);
364        videoEnc = nullptr;
365    }
366    if (ret != AV_ERR_OK) {
367        // Handle exceptions.
368    }
369    ```
370
371    > **NOTE**
372    >
373    > After the call, you must set a null pointer to the encoder to prevent program errors caused by wild pointers.
374
375### Buffer Mode
376
377The following walks you through how to implement the entire video encoding process in buffer mode and implement data rotation in synchronous mode. It uses the YUV file input and H.264 encoding format as an example.
378
3791. Create an encoder instance.
380
381    The procedure is the same as that in surface mode and is not described here.
382
383    ```c++
384    // Create an encoder by name. If your application has special requirements, for example, expecting an encoder that supports a certain resolution, you can call OH_AVCodec_GetCapability to query the capability first.
385    OH_AVCapability *capability = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_VIDEO_AVC, true);
386    const char *name = OH_AVCapability_GetName(capability);
387    OH_AVCodec *videoEnc = OH_VideoEncoder_CreateByName(name);
388    if (videoEnc == nullptr) {
389        printf("create videoEnc failed");
390        return;
391    }
392    ```
393
3942. Call **OH_VideoEncoder_Configure()** to configure the encoder.
395
396    The procedure is the same as that in surface mode and is not described here.
397
398    ```c++
399    auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
400    if (format == nullptr) {
401        // Handle exceptions.
402    }
403    // Set the format.
404    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_WIDTH, width); // Mandatory.
405    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_HEIGHT, height); // Mandatory.
406    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_PIXEL_FORMAT, pixelFormat); // Mandatory.
407    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_ENABLE_SYNC_MODE, 1); // Set the synchronous mode.
408    // Configure the encoder.
409    OH_AVErrCode ret = OH_VideoEncoder_Configure(videoEnc, format.get());
410    if (ret != AV_ERR_OK) {
411        // Handle exceptions.
412    }
413    ```
414
415    > **NOTE**
416    >
417    > To enable video encoding in synchronous mode, **OH_MD_KEY_ENABLE_SYNC_MODE** must be set to **1**.
418    >
419    > To use synchronous mode, do not call **OH_VideoEncoder_RegisterCallback** or **OH_VideoEncoder_RegisterParameterCallback** in prior to **OH_VideoEncoder_Configure**. Otherwise, the encoder will run in asynchronous mode instead.
420
4213. Call **OH_VideoEncoder_Prepare()** to prepare internal resources for the encoder.
422
423    ```c++
424    ret = OH_VideoEncoder_Prepare(videoEnc);
425    if (ret != AV_ERR_OK) {
426        // Handle exceptions.
427    }
428    ```
429
4304. Call **OH_VideoEncoder_Start()** to start the encoder.
431
432    Configure the input and output files.
433
434    ```c++
435    // Configure the paths of the input and output files.
436    std::string_view inputFilePath = "/*yourpath*.yuv";
437    std::string_view outputFilePath = "/*yourpath*.h264";
438    std::unique_ptr<std::ifstream> inputFile = std::make_unique<std::ifstream>();
439    std::unique_ptr<std::ofstream> outputFile = std::make_unique<std::ofstream>();
440    if (inputFile != nullptr) {
441        inputFile->open(inputFilePath.data(), std::ios::in | std::ios::binary);
442    }
443    if (outputFile != nullptr) {
444        outputFile->open(outputFilePath.data(), std::ios::out | std::ios::binary | std::ios::ate);
445    }
446    // Start the encoder.
447    OH_AVErrCode ret = OH_VideoEncoder_Start(videoEnc);
448    if (ret != AV_ERR_OK) {
449        // Handle exceptions.
450    }
451    ```
452
4535. Obtain an available buffer and write the bitstream to the encoder.
454
455    - Call [OH_VideoEncoder_QueryInputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_queryinputbuffer) to obtain the index of the next available input buffer.
456    - Based on this index, call [OH_VideoEncoder_GetInputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_getinputbuffer) to obtain the buffer instance.
457    - Write the data to be encoded to the buffer, and call [OH_VideoEncoder_PushInputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_pushinputbuffer) to push it into the encoding input queue for encoding. When all the data to be processed has been passed to the encoder, set flag to **AVCODEC_BUFFER_FLAGS_EOS** to notify the encoder that the input is complete.
458
459    The meanings of the variables **size**, **offset**, **pts**, **frameData**, and **flags** in the example are the same as those in surface mode.
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                    // Handle exceptions.
473                    return false;
474                }
475
476                // Write image data.
477                int32_t frameSize = 0;
478                bool isFirstFrame = true;
479                // Obtain the video width stride and height stride.
480                if (isFirstFrame) {
481                    auto format = std::shared_ptr<OH_AVFormat>(OH_VideoEncoder_GetInputDescription(videoEnc), OH_AVFormat_Destroy);
482                    if (format == nullptr) {
483                        // Handle exceptions.
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                     	// Handle exceptions.
489                     }
490                    isFirstFrame = false;
491                }
492                if (widthStride == width && heightStride == height) {
493                    frameSize = width * height * 3 / 2; // Formula for calculating the data size of each frame in NV12 pixel format.
494                    // Process the file stream and obtain the frame length, and then write the data to encode to the buffer of the specified index.
495                    uint8_t *addr = OH_AVBuffer_GetAddr(buffer);
496                    if (addr == nullptr) {
497                       // Handle exceptions.
498                       return false;
499                    }
500                    if (inputFile != nullptr && inputFile->is_open()) {
501                        inputFile->read(reinterpret_cast<char *>(addr), frameSize);
502                    }
503                } else {
504                    // If the stride is not equal to the width, perform offset based on the stride. For details, see step 8 in "Buffer Mode in Video Encoding".
505                }
506
507                // Configure the buffer information.
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                    // Handle exceptions.
515                    return false;
516                }
517                // Send the data to the input buffer for encoding. index is the index of the buffer.
518                OH_AVErrCode pushInputRet = OH_VideoEncoder_PushInputBuffer(videoEnc, index);
519                if (pushInputRet != AV_ERR_OK) {
520                    // Handle exceptions.
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                // Handle exceptions.
533                return false;
534            }
535        }
536        return true;
537    }
538    ```
539
540
5416. Obtain an available buffer and release the encoded frame.
542
543   - Call [OH_VideoEncoder_QueryOutputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_queryoutputbuffer) to obtain the index of the next available output buffer.
544   - Based on this index, call [OH_VideoEncoder_GetOutputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_getoutputbuffer) to obtain the buffer instance.
545   - Call [OH_VideoEncoder_FreeOutputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_freeoutputbuffer) to release the encoded frame.
546
547    ```c++
548    bool EncoderOutput(OH_AVCodec *videoEnc, int64_t timeoutUs)
549    {
550        uint32_t index;
551        std::shared_lock<std::shared_mutex> lock(codecMutex);
552
553        OH_AVErrCode ret = OH_VideoEncoder_QueryOutputBuffer(videoEnc, &index, timeoutUs);
554        switch (ret) {
555            case AV_ERR_OK: {
556                OH_AVBuffer *buffer = OH_VideoEncoder_GetOutputBuffer(videoEnc, index);
557                if (buffer == nullptr) {
558                    // Handle exceptions.
559                    return false;
560                }
561
562                // Obtain the encoded information.
563                OH_AVCodecBufferAttr info;
564                OH_AVErrCode getBufferRet = OH_AVBuffer_GetBufferAttr(buffer, &info);
565                if (getBufferRet != AV_ERR_OK) {
566                    // Handle exceptions.
567                    return false;
568                }
569                // Write the encoded frame data (specified by buffer) to the output file.
570                uint8_t *addr = OH_AVBuffer_GetAddr(buffer);
571                if (addr == nullptr) {
572                   // Handle exceptions.
573                   return false;
574                }
575                if (outputFile != nullptr && outputFile->is_open()) {
576                    outputFile->write(reinterpret_cast<char *>(addr), info.size);
577                }
578                if (info.flags & AVCODEC_BUFFER_FLAGS_EOS) {
579                    outputDone = 1;
580                }
581                // Free the output buffer. index is the index of the buffer.
582                OH_AVErrCode freeOutputRet = OH_VideoEncoder_FreeOutputBuffer(videoEnc, index);
583                if (freeOutputRet != AV_ERR_OK) {
584                    // Handle exceptions.
585                    return false;
586                }
587                break;
588            }
589            case AV_ERR_TRY_AGAIN_LATER: {
590                break;
591            }
592            case AV_ERR_STREAM_CHANGED: {
593                break;
594            }
595            default: {
596                // Handle exceptions.
597                return false;
598            }
599        }
600        return true;
601    }
602    ```
603
6047. Enable the encoder to input and output frames in a loop.
605
606    ```c++
607    bool result = true;
608    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.
609
610    while (!outputDone && result) {
611        if (!inputDone) {
612            result = EncoderInput(videoEnc, timeoutUs);
613        }
614        if (!outputDone) {
615            result = EncoderOutput(videoEnc, timeoutUs);
616        }
617    }
618    ```
619
620The subsequent processes (including refreshing, resetting, stopping, and destroying the encoder) are the same as those in surface mode. For details, see steps 9–12 in [Surface Mode](#surface-mode).
621