视频编码同步模式
========

从API version 20开始，支持视频编码同步模式。

开发者可以调用本模块的Native API接口，完成同步模式的视频编码。

具体实现可参考[示例工程](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Media/AVCodec)。

当前支持的编码能力，请参考[AVCodec支持的格式](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/avcodec-support-formats#视频编码)。

视频编码的限制约束、支持的能力、状态机调用关系请参考[视频编码](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/video-encoding)。

#### 适用场景

通常情况下，推荐使用异步模式。若需要主动请求buffer去送帧，则可以采用同步模式。

#### 开发指导

详细的API说明请参考[VideoEncoder](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avcodec-videoencoder-h)。

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/85/v3/940HZN4vRA6yCq6rZy132Q/zh-cn_image_0000002634009238.png?HW-CC-KV=V1&HW-CC-Date=20260709T132516Z&HW-CC-Expire=86400&HW-CC-Sign=4BA3741D51083C2B591C12FAC5BEB08C8D13F29635B4C38ACC280A71604466F6)

#### \[h2\]在CMake脚本中链接动态库

target\_link\_libraries(sample PUBLIC libnative\_media\_codecbase.so)
target\_link\_libraries(sample PUBLIC libnative\_media\_core.so)
target\_link\_libraries(sample PUBLIC libnative\_media\_venc.so)

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/44/v3/_0PdKWZpRquwthHfHYfC5Q/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260709T132516Z&HW-CC-Expire=86400&HW-CC-Sign=48E791C6A9403B690AB165280B646F6BDDF9A88DDEC2A9680D33B1DA1EC6ED6D)

上述'sample'字样仅为示例，此处由开发者根据实际工程目录自定义。

#### \[h2\]定义基础结构

本部分示例代码按照C++17标准编写，仅作参考。

1.  添加头文件。
    
    #include <multimedia/player\_framework/native\_avcodec\_videoencoder.h>
    #include <multimedia/player\_framework/native\_avcapability.h>
    #include <multimedia/player\_framework/native\_avcodec\_base.h>
    #include <multimedia/player\_framework/native\_avformat.h>
    #include <multimedia/player\_framework/native\_avbuffer.h>
    #include <multimedia/player\_framework/native\_averrors.h>
    #include <native\_buffer/native\_buffer.h>
    #include <memory>
    #include <fstream>
    #include <mutex>
    #include <shared\_mutex>
    
2.  全局变量（仅作参考，可以根据实际情况将其封装到对象中）。
    
    // 视频帧宽度。
    int32\_t width = 320;
    // 视频帧高度。
    int32\_t height = 240;
    // 视频宽跨距。
    int32\_t widthStride = 0;
    // 视频高跨距。
    int32\_t heightStride = 0;
    // 视频像素格式。
    OH\_AVPixelFormat pixelFormat = AV\_PIXEL\_FORMAT\_NV12;
    // 编码器同步锁。
    std::shared\_mutex codecMutex;
    // 编码器实例指针。
    OH\_AVCodec \*videoEnc = nullptr;
    // 编码输出。
    bool outputDone = false;
    // 编码输入。
    bool inputDone = false;
    std::unique\_ptr<std::ifstream> inFile\_;
    

#### \[h2\]Surface模式

参考以下示例代码，可以完成Surface模式下视频编码的全流程，实现同步模式的数据轮转。此处以输入surface数据，编码成H.264格式为例。

1.  创建编码器实例。
    
    通过名称创建编码器。示例中的变量说明如下：
    
    *   videoEnc：视频编码器实例的指针。
    *   capability：编码器能力查询实例的指针。
    *   [OH\_AVCODEC\_MIMETYPE\_VIDEO\_AVC](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avcodec-base-h#变量)：AVC格式视频编解码器。
    
    // 创建硬件编码器实例。
    OH\_AVCapability \*capability= OH\_AVCodec\_GetCapabilityByCategory(OH\_AVCODEC\_MIMETYPE\_VIDEO\_AVC, true, HARDWARE);
    const char \*name = OH\_AVCapability\_GetName(capability);
    OH\_AVCodec \*videoEnc = OH\_VideoEncoder\_CreateByName(name);
    if (videoEnc == nullptr) {
        printf("create videoEnc failed");
        return;
    }
    
2.  调用OH\_VideoEncoder\_Configure()配置编码器。
    
    *   详细可配置选项的说明请参考[媒体数据键值对](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-codecbase#媒体数据键值对)。
    *   参数校验规则请参考[OH\_VideoEncoder\_Configure()](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avcodec-videoencoder-h#oh_videoencoder_configure)。
    *   参数取值范围可以通过能力查询接口获取，具体示例请参考[获取支持的编解码能力](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/obtain-supported-codecs)。
    
    目前支持的所有格式都必须配置以下选项：视频帧宽度、视频帧高度、视频像素格式。
    
    auto format = std::shared\_ptr<OH\_AVFormat>(OH\_AVFormat\_Create(), OH\_AVFormat\_Destroy);
    if (format == nullptr) {
        // 异常处理。
    }
    // 写入format。
    OH\_AVFormat\_SetIntValue(format.get(), OH\_MD\_KEY\_WIDTH, width); // 必须配置。
    OH\_AVFormat\_SetIntValue(format.get(), OH\_MD\_KEY\_HEIGHT, height); // 必须配置。
    OH\_AVFormat\_SetIntValue(format.get(), OH\_MD\_KEY\_PIXEL\_FORMAT, pixelFormat);// 必须配置。
    OH\_AVFormat\_SetIntValue(format.get(), OH\_MD\_KEY\_ENABLE\_SYNC\_MODE, 1); // 同步模式配置。
    // 配置编码器。
    OH\_AVErrCode ret = OH\_VideoEncoder\_Configure(videoEnc, format.get());
    if (ret != AV\_ERR\_OK) {
        // 异常处理。
    }
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/27/v3/Ez00Ii86Qq669wW2_bABpg/caution_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260709T132516Z&HW-CC-Expire=86400&HW-CC-Sign=DF160E1AD3120EB11F5367A52604128C3F5321B67DAE81358CD05A5C3170EBD3)
    
    1.  要使能视频编码同步模式，必须将OH\_MD\_KEY\_ENABLE\_SYNC\_MODE配置为1。
    2.  同步模式在调用OH\_VideoEncoder\_Configure接口前不能调用OH\_VideoEncoder\_RegisterCallback或OH\_VideoEncoder\_RegisterParameterCallback接口，否则为异步模式。
    3.  不支持Surface模式的随帧通路的同步模式。
    
3.  设置surface。
    
    示例中的变量说明如下：
    
    nativeWindow：获取方式请参考[视频编码Surface模式](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/video-encoding#surface模式)的“步骤-6：设置surface”。
    
    // 获取需要输入的surface，以进行编码。
    OH\_AVErrCode ret = OH\_VideoEncoder\_GetSurface(videoEnc, &nativeWindow);
    if (ret != AV\_ERR\_OK) {
        // 异常处理。
    }
    
4.  调用OH\_VideoEncoder\_Prepare()编码器就绪。
    
    该接口将在编码器运行前进行一些数据的准备工作。
    
    OH\_AVErrCode ret = OH\_VideoEncoder\_Prepare(videoEnc);
    if (ret != AV\_ERR\_OK) {
        // 异常处理。
    }
    
5.  调用OH\_VideoEncoder\_Start()启动编码器。
    
    // 配置待编码文件路径。
    std::string\_view outputFilePath = "/\*yourpath\*.h264";
    std::unique\_ptr<std::ofstream> outputFile = std::make\_unique<std::ofstream>();
    if (outputFile != nullptr) {
        outputFile->open(outputFilePath.data(), std::ios::out | std::ios::binary | std::ios::ate);
    }
    // 启动编码器，开始编码。
    OH\_AVErrCode ret = OH\_VideoEncoder\_Start(videoEnc);
    if (ret != AV\_ERR\_OK) {
        // 异常处理。
    }
    
6.  获取可用buffer并释放编码帧。
    
    *   调用[OH\_VideoEncoder\_QueryOutputBuffer](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avcodec-videoencoder-h#oh_videoencoder_queryoutputbuffer)接口获取下一个可用的输出缓冲区（buffer）的索引（index）。
    *   根据获取的索引（index），调用[OH\_VideoEncoder\_GetOutputBuffer](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avcodec-videoencoder-h#oh_videoencoder_getoutputbuffer)接口获取对应的缓冲区（buffer）实例。
    *   调用[OH\_VideoEncoder\_FreeOutputBuffer](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avcodec-videoencoder-h#oh_videoencoder_freeoutputbuffer)接口释放编码帧。
    
    bool EncoderOutput(OH\_AVCodec \*videoEnc, int64\_t timeoutUs)
    {
        uint32\_t index;
        std::shared\_lock<std::shared\_mutex> lock(codecMutex);
    
        OH\_AVErrCode ret = OH\_VideoEncoder\_QueryOutputBuffer(videoEnc, &index, timeoutUs);
        switch (ret) {
            case AV\_ERR\_OK: {
                OH\_AVBuffer \*buffer = OH\_VideoEncoder\_GetOutputBuffer(videoEnc, index);
                if (buffer == nullptr) {
                    // 异常处理。
                    return false;
                }
            
                // 获取编码后信息。
                OH\_AVCodecBufferAttr info;
                OH\_AVErrCode getBufferRet = OH\_AVBuffer\_GetBufferAttr(buffer, &info);
                if (getBufferRet != AV\_ERR\_OK) {
                    // 异常处理。
                    return false;
                }
                if (info.flags & AVCODEC\_BUFFER\_FLAGS\_EOS) {
                    outputDone = 1;
                }
    
                // 将编码完成帧数据buffer写入到对应输出文件中。
                uint8\_t \*addr = OH\_AVBuffer\_GetAddr(buffer);
                if (addr == nullptr) {
                   // 异常处理 。
                   return false;
                }
                if (outputFile != nullptr && outputFile->is\_open()) {
                    outputFile->write(reinterpret\_cast<char \*>(addr), info.size);
                }
                // 释放已完成写入的数据，index为对应输出队列下标。
                OH\_AVErrCode freeOutputRet = OH\_VideoEncoder\_FreeOutputBuffer(videoEnc, index);
                if (freeOutputRet != AV\_ERR\_OK) {
                    // 异常处理。
                    return false;
                }
                break;
            }
            case AV\_ERR\_TRY\_AGAIN\_LATER: {
                break;
            }
            case AV\_ERR\_STREAM\_CHANGED: {
                auto format = std::shared\_ptr<OH\_AVFormat>(OH\_VideoEncoder\_GetOutputDescription(videoEnc), OH\_AVFormat\_Destroy);
                if (format == nullptr) {
                    // 异常处理。
                }
                // 获取新宽高。
                bool getIntRet = OH\_AVFormat\_GetIntValue(format.get(), OH\_MD\_KEY\_WIDTH, &width) &&
                                 OH\_AVFormat\_GetIntValue(format.get(), OH\_MD\_KEY\_HEIGHT, &height);
                if (!getIntRet) {
                    // 异常处理。
                }
                break;
            }
            default: {
                // 异常处理。
                return false;
            }
        }
        return true;
    }
    
7.  编码器出帧处理循环。
    
    bool result = true;
    int64\_t timeoutUs = 0; // 单位：微秒（us），负值：无限等待；0：立即退出；正值：等待指定时长后退出。
    
    while (!outputDone && result) {
        if (!outputDone ) {
            result = EncoderOutput(videoEnc, timeoutUs);
        }
    }
    
8.  调用OH\_VideoEncoder\_NotifyEndOfStream()通知编码器结束。
    
    // Surface模式：通知视频编码器输入流已结束，只能使用此接口进行通知。
    OH\_AVErrCode ret = OH\_VideoEncoder\_NotifyEndOfStream(videoEnc);
    if (ret != AV\_ERR\_OK) {
        // 异常处理。
    }
    
9.  （可选）调用OH\_VideoEncoder\_Flush()刷新编码器。
    
    调用OH\_VideoEncoder\_Flush接口后，编码器仍处于运行态，但会清除编码器中缓存的输入和输出数据及参数集（如H.264格式的PPS/SPS）。此时需要调用OH\_VideoEncoder\_Start接口重新开始编码。
    
    // 通过codecMutex来避免调用Flush接口，状态切换后，编码线程还在跑会退出循环的问题。
    std::unique\_lock<std::shared\_mutex> lock(codecMutex);
    // 刷新编码器videoEnc。
    OH\_AVErrCode flushRet = OH\_VideoEncoder\_Flush(videoEnc);
    if (flushRet != AV\_ERR\_OK) {
        // 异常处理。
    }
    
    // 重新开始编码。
    OH\_AVErrCode startRet = OH\_VideoEncoder\_Start(videoEnc);
    if (startRet != AV\_ERR\_OK) {
        // 异常处理。
    }
    
10.  （可选）调用OH\_VideoEncoder\_Reset()重置编码器。
     
     调用OH\_VideoEncoder\_Reset接口后，编码器回到初始化的状态，需要调用接口[OH\_VideoEncoder\_Configure](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avcodec-videoencoder-h#oh_videoencoder_configure)和[OH\_VideoEncoder\_Prepare](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avcodec-videoencoder-h#oh_videoencoder_prepare)重新配置。
     
     // 重置编码器videoEnc。
     std::unique\_lock<std::shared\_mutex> lock(codecMutex);
     OH\_AVErrCode resetRet = OH\_VideoEncoder\_Reset(videoEnc);
     if (resetRet != AV\_ERR\_OK) {
         // 异常处理。
     }
     
     // 重新配置编码器参数。
     auto format = std::shared\_ptr<OH\_AVFormat>(OH\_AVFormat\_Create(), OH\_AVFormat\_Destroy);
     if (format == nullptr) {
         // 异常处理。
     }
     OH\_AVErrCode configRet = OH\_VideoEncoder\_Configure(videoEnc, format.get());
     if (configRet != AV\_ERR\_OK) {
         // 异常处理。
     }
     
     // 编码器重新就绪。
     OH\_AVErrCode prepareRet = OH\_VideoEncoder\_Prepare(videoEnc);
     if (prepareRet != AV\_ERR\_OK) {
         // 异常处理。
     }
     
     ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/f4/v3/nOAHG8gUSpO10xoNy_NJLQ/caution_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260709T132516Z&HW-CC-Expire=86400&HW-CC-Sign=87D46E44FFA11BADDFE55EE2B4AB0B197CA5B6A4405A95EA1869F6129B631650)
     
     编码器回到初始化的状态，调用OH\_VideoEncoder\_Configure接口重新配置编码器参数时，同步模式需要重新配置OH\_MD\_KEY\_ENABLE\_SYNC\_MODE为1，否则为异步模式。
     
11.  （可选）调用OH\_VideoEncoder\_Stop()停止编码器。
     
     调用OH\_VideoEncoder\_Stop接口后，编码器保留了编码实例，释放输入输出buffer。开发者可以直接调用OH\_VideoEncoder\_Start接口继续编码。
     
     // 终止编码器videoEnc。
     std::unique\_lock<std::shared\_mutex> lock(codecMutex);
     OH\_AVErrCode ret = OH\_VideoEncoder\_Stop(videoEnc);
     if (ret != AV\_ERR\_OK) {
         // 异常处理。
     }
     
12.  调用OH\_VideoEncoder\_Destroy()销毁编码器实例，释放资源。
     
     // 注销编码器。
     std::unique\_lock<std::shared\_mutex> lock(codecMutex);
     OH\_AVErrCode ret = AV\_ERR\_OK;
     if (videoEnc != nullptr) {
         OH\_VideoEncoder\_Destroy(videoEnc);
         videoEnc = nullptr;
     }
     
     ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/3a/v3/x49hz2yUS6Cs9YeOB1BGmw/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260709T132516Z&HW-CC-Expire=86400&HW-CC-Sign=EED9DABC1B832E35B4CCB76060E72AE156C6C3A010343A4803F92604F018B493)
     
     执行该步骤之后，需要开发者将videoEnc指向nullptr，防止野指针导致程序错误。
     

#### \[h2\]Buffer模式

参考以下示例代码，可以完成Buffer模式下视频编码的全流程，实现同步模式的数据轮转。此处以输入YUV文件，编码成H.264格式为例。

1.  创建编码器实例。
    
    与Surface模式相同，此处不再赘述。
    
    // 通过codecname创建编码器，应用有特殊需求，比如选择支持某种分辨率规格的编码器，可先查询capability，再根据codec name创建编码器。
    OH\_AVCapability \*capability = OH\_AVCodec\_GetCapability(OH\_AVCODEC\_MIMETYPE\_VIDEO\_AVC, true);
    const char \*name = OH\_AVCapability\_GetName(capability);
    OH\_AVCodec \*videoEnc = OH\_VideoEncoder\_CreateByName(name);
    if (videoEnc == nullptr) {
        printf("create videoEnc failed");
        return;
    }
    
2.  调用OH\_VideoEncoder\_Configure()配置编码器。
    
    与Surface模式相同，此处不再赘述。
    
    auto format = std::shared\_ptr<OH\_AVFormat>(OH\_AVFormat\_Create(), OH\_AVFormat\_Destroy);
    if (format == nullptr) {
        // 异常处理。
    }
    // 写入format。
    OH\_AVFormat\_SetIntValue(format.get(), OH\_MD\_KEY\_WIDTH, width); // 必须配置。
    OH\_AVFormat\_SetIntValue(format.get(), OH\_MD\_KEY\_HEIGHT, height); // 必须配置。
    OH\_AVFormat\_SetIntValue(format.get(), OH\_MD\_KEY\_PIXEL\_FORMAT, pixelFormat);// 必须配置。
    OH\_AVFormat\_SetIntValue(format.get(), OH\_MD\_KEY\_ENABLE\_SYNC\_MODE, 1); // 同步模式配置。
    // 配置编码器。
    OH\_AVErrCode ret = OH\_VideoEncoder\_Configure(videoEnc, format.get());
    if (ret != AV\_ERR\_OK) {
        // 异常处理。
    }
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/20/v3/4edzmEPrTOeony_frILlFw/caution_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260709T132516Z&HW-CC-Expire=86400&HW-CC-Sign=2F49ECC08B85C453E89E7A8C3C69DE5242DA2EDB47D4644C0B00A8C99D499FC1)
    
    1.  要使能视频编码同步模式，必须将OH\_MD\_KEY\_ENABLE\_SYNC\_MODE配置为1。
    2.  同步模式在调用OH\_VideoEncoder\_Configure接口前不能调用OH\_VideoEncoder\_RegisterCallback或OH\_VideoEncoder\_RegisterParameterCallback接口，否则为异步模式。
    
3.  调用OH\_VideoEncoder\_Prepare()编码器就绪。
    
    该接口将在编码器运行前进行一些数据的准备工作。
    
    ret = OH\_VideoEncoder\_Prepare(videoEnc);
    if (ret != AV\_ERR\_OK) {
        // 异常处理。
    }
    
4.  调用OH\_VideoEncoder\_Start()启动编码器。
    
    配置输入文件、输出文件。
    
    // 配置待编码文件路径。
    std::string\_view inputFilePath = "/\*yourpath\*.yuv";
    std::string\_view outputFilePath = "/\*yourpath\*.h264";
    std::unique\_ptr<std::ifstream> inputFile = std::make\_unique<std::ifstream>();
    std::unique\_ptr<std::ofstream> outputFile = std::make\_unique<std::ofstream>();
    if (inputFile != nullptr) {
        inputFile->open(inputFilePath.data(), std::ios::in | std::ios::binary);
    }
    if (outputFile != nullptr) {
        outputFile->open(outputFilePath.data(), std::ios::out | std::ios::binary | std::ios::ate);
    }
    // 启动编码器，开始编码。
    OH\_AVErrCode ret = OH\_VideoEncoder\_Start(videoEnc);
    if (ret != AV\_ERR\_OK) {
        // 异常处理。
    }
    
5.  获取可用buffer并写入码流至编码器
    
    *   调用[OH\_VideoEncoder\_QueryInputBuffer](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avcodec-videoencoder-h#oh_videoencoder_queryinputbuffer)接口获取下一个可用的输入缓冲区（buffer）的索引（index）。
    *   根据获取的索引（index），调用[OH\_VideoEncoder\_GetInputBuffer](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avcodec-videoencoder-h#oh_videoencoder_getinputbuffer)接口获取对应的缓冲区（buffer）实例。
    *   将需要编码的数据写入该缓冲区（buffer）后，调用[OH\_VideoEncoder\_PushInputBuffer](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avcodec-videoencoder-h#oh_videoencoder_pushinputbuffer)接口将其送入编码输入队列进行编码。当最后一帧数据被送入编码输入队列时，需要将flag标识成[AVCODEC\_BUFFER\_FLAGS\_EOS](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avbuffer-info-h#oh_avcodecbufferflags)，通知编码器输入结束。
    
    示例中的变量size、offset、pts、frameData、flags说明与Surface模式相同，此处不再赘述。
    
    bool EncoderInput(OH\_AVCodec \*videoEnc, int64\_t timeoutUs)
    {
        uint32\_t index;
        std::shared\_lock<std::shared\_mutex> lock(codecMutex);
    
        OH\_AVErrCode ret = OH\_VideoEncoder\_QueryInputBuffer(videoEnc, &index, timeoutUs);
        switch (ret) {
            case AV\_ERR\_OK: {
                OH\_AVBuffer \*buffer = OH\_VideoEncoder\_GetInputBuffer(videoEnc, index);
                if (buffer == nullptr) {
                    // 异常处理。
                    return false;
                }
    
                // 写入图像数据。
                int32\_t frameSize = 0;
                bool isFirstFrame = true;
                // 获取视频宽跨距和高跨距。
                if (isFirstFrame) {
                    auto format = std::shared\_ptr<OH\_AVFormat>(OH\_VideoEncoder\_GetInputDescription(videoEnc), OH\_AVFormat\_Destroy);
                    if (format == nullptr) {
                        // 异常处理。
                    }
                    bool getIntRet = OH\_AVFormat\_GetIntValue(format.get(), OH\_MD\_KEY\_VIDEO\_STRIDE, &widthStride) &&
                                     OH\_AVFormat\_GetIntValue(format.get(), OH\_MD\_KEY\_VIDEO\_SLICE\_HEIGHT, &heightStride);
                     if (!getIntRet) {
                        // 异常处理。
                     }
                    isFirstFrame = false;
                }
                if (widthStride == width && heightStride == height) {
                    frameSize = width \* height \* 3 / 2; // NV12像素格式下，每帧数据大小的计算公式。
                    // 处理文件流得到帧的长度，再将需要编码的数据写入到对应index的buffer中。
                    uint8\_t \*addr = OH\_AVBuffer\_GetAddr(buffer);
                    if (addr == nullptr) {
                       // 异常处理
                       return false;
                    }
                    if (inputFile != nullptr && inputFile->is\_open()) {
                        inputFile->read(reinterpret\_cast<char \*>(addr), frameSize);
                    }
                } else {
                    // 如果跨距不等于宽，开发者需要按照跨距进行偏移，详情请参考视频编码Buffer模式“步骤-8. 写入编码图像”。
                }
    
                // 配置buffer info信息。
                OH\_AVCodecBufferAttr info;
                info.size = frameSize;
                info.offset = 0;
                // 注意此处和Surface模式不同，pts需要应用填充，可根据预期显示的时间进行计算写入，如：帧数 \* 1000000 / frameRate。
                info.pts = 0;
                // 输入最后一帧数据时，设置AVCODEC\_BUFFER\_FLAGS\_EOS标识。
                // info.flags = AVCODEC\_BUFFER\_FLAGS\_EOS;
                // 避免flags随机初始化为AVCODEC\_BUFFER\_FLAGS\_EOS导致使用异常，flags需要赋值如0（普通帧标识）。
                info.flags = 0;
                OH\_AVErrCode setBufferRet = OH\_AVBuffer\_SetBufferAttr(buffer, &info);
                if (setBufferRet != AV\_ERR\_OK) {
                    // 异常处理。
                    return false;
                }
                // 送入编码输入队列进行编码，index为对应输入队列的下标。
                OH\_AVErrCode pushInputRet = OH\_VideoEncoder\_PushInputBuffer(videoEnc, index);
                if (pushInputRet != AV\_ERR\_OK) {
                    // 异常处理。
                    return false;
                }
                if (inFile\_->eof()) {
                    inputDone = 1;
                }
                break;
            }
            case AV\_ERR\_TRY\_AGAIN\_LATER: {
                break;
            }
            default: {
                // 异常处理。
                return false;
            }
        }
        return true;
    }
    
6.  获取可用buffer并释放编码帧。
    
    *   调用[OH\_VideoEncoder\_QueryOutputBuffer](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avcodec-videoencoder-h#oh_videoencoder_queryoutputbuffer)接口获取下一个可用的输出缓冲区（buffer）的索引（index）。
    *   根据获取的索引（index），调用[OH\_VideoEncoder\_GetOutputBuffer](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avcodec-videoencoder-h#oh_videoencoder_getoutputbuffer)接口获取对应的缓冲区（buffer）实例。
    *   调用[OH\_VideoEncoder\_FreeOutputBuffer](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avcodec-videoencoder-h#oh_videoencoder_freeoutputbuffer)接口释放编码帧。
    
    bool EncoderOutput(OH\_AVCodec \*videoEnc, int64\_t timeoutUs)
    {
        uint32\_t index;
        std::shared\_lock<std::shared\_mutex> lock(codecMutex);
    
        OH\_AVErrCode ret = OH\_VideoEncoder\_QueryOutputBuffer(videoEnc, &index, timeoutUs);
        switch (ret) {
            case AV\_ERR\_OK: {
                OH\_AVBuffer \*buffer = OH\_VideoEncoder\_GetOutputBuffer(videoEnc, index);
                if (buffer == nullptr) {
                    // 异常处理。
                    return false;
                }
            
                // 获取编码后信息。
                OH\_AVCodecBufferAttr info;
                OH\_AVErrCode getBufferRet = OH\_AVBuffer\_GetBufferAttr(buffer, &info);
                if (getBufferRet != AV\_ERR\_OK) {
                    // 异常处理。
                    return false;
                }
                // 将编码完成帧数据buffer写入到对应输出文件中。
                uint8\_t \*addr = OH\_AVBuffer\_GetAddr(buffer);
                if (addr == nullptr) {
                   // 异常处理
                   return false;
                }
                if (outputFile != nullptr && outputFile->is\_open()) {
                    outputFile->write(reinterpret\_cast<char \*>(addr), info.size);
                }
                if (info.flags & AVCODEC\_BUFFER\_FLAGS\_EOS) {
                    outputDone = 1;
                }
                // 释放已完成处理的信息，index为对应buffer队列的下标。
                OH\_AVErrCode freeOutputRet = OH\_VideoEncoder\_FreeOutputBuffer(videoEnc, index);
                if (freeOutputRet != AV\_ERR\_OK) {
                    // 异常处理。
                    return false;
                }
                break;
            }
            case AV\_ERR\_TRY\_AGAIN\_LATER: {
                break;
            }
            case AV\_ERR\_STREAM\_CHANGED: {
                break;
            }
            default: {
                // 异常处理。
                return false;
            }
        }
        return true;
    }
    
7.  编码器送帧/出帧处理循环。
    
    bool result = true;
    int64\_t timeoutUs = 0; // 单位：微秒（us），负值：无限等待；0：立即退出；正值：等待指定时长后退出。
    
    while (!outputDone && result) {
        if (!inputDone) {
            result = EncoderInput(videoEnc, timeoutUs);
        }
        if (!outputDone) {
            result = EncoderOutput(videoEnc, timeoutUs);
        }
    }
    

后续流程（包括刷新、重置、停止和销毁编码器）与Surface模式基本一致，请参考[Surface模式](#surface模式)的步骤9-12。