视频编码
====

视频编码是多媒体处理流程中的重要环节，功能是将未压缩的视频数据压缩成视频码流，旨在降低原始视频数据的大小以便存储或传输。视频编码支持同步模式与异步模式两种运行机制，两者主要区别为buffer获取方式的同异步之分，开发者可根据自身业务选择适合的接口调用模式。

本文档主要介绍异步模式视频编码的实现流程，同步模式视频编码请参考[视频编码同步模式](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/synchronous-video-encoding)。根据编码前数据输入方式的不同，编码器支持Surface模式和Buffer模式两种输入模式，适用于不同的应用场景。

*   Surface模式。
    
    编码器通过[NativeWindow](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-nativewindow-nativewindow)来获取输入数据，可以与其他模块对接（如相机模块）。适用于与相机、屏幕录制等数据源直接对接的编码场景。
    
*   Buffer模式。
    
    编码器通过预分配的共享内存获取输入数据，开发者需将原始视频数据拷贝到预分配的共享内存中。适用于对文件或内存中的原始视频数据进行编码处理的场景。
    

| 差异 | Surface模式 | Buffer模式 |
| :-- | :-- | :-- |
| 配置 | 在调用OH\_VideoEncoder\_Prepare接口前，必须调用OH\_VideoEncoder\_GetSurface接口获取OHNativeWindow，用于传递视频数据。 | \- |
| 数据输入 | 通过OHNativeWindow获取输入帧，数据通常由生产者模块（如相机）直接写入。 | 通过OnNeedInputBuffer回调函数获取共享内存buffer信息，调用OH\_VideoEncoder\_PushInputBuffer送入数据。 |
| 输入结束 | 必须调用OH\_VideoEncoder\_NotifyEndOfStream接口通知编码器输入结束。 | 编码至最后一帧数据时，需将输入buffer的flags字段设置为AVCODEC\_BUFFER\_FLAGS\_EOS标志，以通知编码器输入结束。 |

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

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

#### 状态机调用关系

1.  初始化状态（Initialized）。
    *   初始创建编码器实例时，编码器处于Initialized状态。
    *   任何状态下，调用OH\_VideoEncoder\_Reset接口，可返回Initialized状态。
2.  配置状态（Configured）。
    *   Initialized状态下，调用OH\_VideoEncoder\_Configure接口配置编码器，配置成功后编码器进入Configured状态。
3.  就绪状态（Prepared）。
    *   Configured状态下，调用OH\_VideoEncoder\_Prepare接口进入Prepared状态。
    *   处于Executing状态时，调用OH\_VideoEncoder\_Stop接口可以使编码器返回到Prepared状态。
4.  运行状态（Executing）。
    *   Prepared状态下，调用OH\_VideoEncoder\_Start接口进入Executing状态。
    *   Executing状态具有三个子状态：Running、Flushed和End-of-Stream。
        *   Running：调用OH\_VideoEncoder\_Start接口进入Running子状态。
        *   Flushed：调用OH\_VideoEncoder\_Flush接口进入Flushed子状态。
        *   End-of-Stream：编码器接收到输入buffer的flags为[OH\_AVCodecBufferFlags](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avbuffer-info-h#oh_avcodecbufferflags)中的AVCODEC\_BUFFER\_FLAGS\_EOS，或者调用OH\_VideoEncoder\_NotifyEndOfStream接口时，进入End-of-Stream子状态。在此状态下，编码器不再接受新的输入，但是仍然会继续生成输出，直到输出到达尾帧。
5.  错误状态（Error）。
    *   在极少数情况下，编码器异常时进入Error状态。接口会返回错误码或通过OH\_AVCodecOnError回调抛出异常。
    *   Error状态下，可以调用OH\_VideoEncoder\_Reset接口返回Initialized状态，或者调用OH\_VideoEncoder\_Destroy接口进入到最后的Released状态。
6.  释放状态（Released）。
    *   使用完编码器后，必须调用OH\_VideoEncoder\_Destroy接口销毁编码器实例，使编码器进入Released状态。

**图1** 状态机调用关系示意图

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/7/v3/9meihp0KSmCT7qw4jqEPog/zh-cn_image_0000002594828724.png?HW-CC-KV=V1&HW-CC-Date=20260612T073508Z&HW-CC-Expire=86400&HW-CC-Sign=B48C5263A32BBFC7C4006D4DA47E0770E09EF71281A367E483774F2077A4E9DD)

#### 开发指导

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

**图2** 视频编码调用关系示意图

*   虚线表示可选。
    
*   实线表示必选。
    

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/cf/v3/183MtDTpSQGyJcMsbw1gYA/zh-cn_image_0000002625188171.png?HW-CC-KV=V1&HW-CC-Date=20260612T073508Z&HW-CC-Expire=86400&HW-CC-Sign=24C8032775C71F7591C255EEB397F8488C2B402768D81ADB3BFB738E37E1CA39)

#### \[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/e2/v3/gDLOeSZZQ_ixoE8c4F-Tzw/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073508Z&HW-CC-Expire=86400&HW-CC-Sign=7D8A221D7B9BDDC2ADAAE963CC024CC6617A00FDE1D861E1C61C89AEB5D644BC)

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

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

本部分示例代码按照C++17标准编写，仅作参考。开发者可以参考此部分，定义自己的buffer对象。

1.  添加头文件。
    
    #include <condition\_variable>
    #include <memory>
    #include <mutex>
    #include <queue>
    #include <shared\_mutex>
    
2.  编码器回调buffer的信息。
    
    struct CodecBufferInfo {
        CodecBufferInfo(uint32\_t index, OH\_AVBuffer \*buffer): index(index), buffer(buffer), isValid(true) {}
        CodecBufferInfo(uint32\_t index, OH\_AVFormat \*parameter): index(index), parameter(parameter), isValid(true) {}
        // 回调buffer。
        OH\_AVBuffer \*buffer = nullptr;
        // Surface模式下，输入回调的随帧参数，需要注册随帧通路后使用。
        OH\_AVFormat \*parameter = nullptr;
        // 回调buffer对应的index。
        uint32\_t index = 0;
        // 判断当前buffer信息是否有效。
        bool isValid = true;
    };
    
3.  编码输入输出队列。
    
    class CodecBufferQueue {
    public:
        // 将回调buffer的信息传入队列。
        void Enqueue(const std::shared\_ptr<CodecBufferInfo> bufferInfo)
        {
            std::unique\_lock<std::mutex> lock(mutex\_);
            bufferQueue\_.push(bufferInfo);
            cond\_.notify\_all();
        }
    
        // 获取回调buffer的信息。
        std::shared\_ptr<CodecBufferInfo> Dequeue(int32\_t timeoutMs = 1000)
        {
            std::unique\_lock<std::mutex> lock(mutex\_);
            (void)cond\_.wait\_for(lock, std::chrono::milliseconds(timeoutMs), \[this\]() { return !bufferQueue\_.empty(); });
            if (bufferQueue\_.empty()) {
                return nullptr;
            }
            std::shared\_ptr<CodecBufferInfo> bufferInfo = bufferQueue\_.front();
            bufferQueue\_.pop();
            return bufferInfo;
        }
    
        // 清空队列，之前的回调buffer设置为不可用。
        void Flush()
        {
            std::unique\_lock<std::mutex> lock(mutex\_);
            while (!bufferQueue\_.empty()) {
                std::shared\_ptr<CodecBufferInfo> bufferInfo = bufferQueue\_.front();
                // Flush、Stop、Reset、Destroy操作之后，之前回调的buffer信息设置为无效。
                bufferInfo->isValid = false;
                bufferQueue\_.pop();
            }
        }
    
    private:
        std::mutex mutex\_;
        std::condition\_variable cond\_;
        std::queue<std::shared\_ptr<CodecBufferInfo>> bufferQueue\_;
    };
    
4.  全局变量。
    
    仅作参考，可以根据实际情况将其封装到对象中。
    
    // 视频帧宽度。
    int32\_t width = 320;
    // 视频帧高度。
    int32\_t height = 240;
    // 视频像素格式。
     OH\_AVPixelFormat pixelFormat = AV\_PIXEL\_FORMAT\_NV12;
    // 视频宽跨距。
    int32\_t widthStride = 0;
    // 视频高跨距。
    int32\_t heightStride = 0;
    // 编码器实例指针。
    OH\_AVCodec \*videoEnc = nullptr;
    // 编码器同步锁。
    std::shared\_mutex codecMutex;
    // 编码器输入队列。
    CodecBufferQueue inQueue;
    // 编码器输出队列。
    CodecBufferQueue outQueue;
    

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

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

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 <fstream>
    
2.  创建编码器实例。
    
    开发者可以通过名称或媒体类型创建编码器。示例中的变量说明如下：
    
    *   videoEnc：视频编码器实例的指针；
    *   capability：编解码器能力查询实例的指针；
    *   OH\_AVCODEC\_MIMETYPE\_VIDEO\_AVC：AVC格式视频编解码器。
    
    创建方式示例如下：
    
    // 通过codec name创建编码器，应用有特殊需求，比如选择支持某种分辨率规格的编码器，可先查询capability，再根据codec name创建编码器。
    OH\_AVCapability \*capability = OH\_AVCodec\_GetCapability(OH\_AVCODEC\_MIMETYPE\_VIDEO\_AVC, true);
    // 创建硬件编码器实例。
    OH\_AVCapability \*capability= OH\_AVCodec\_GetCapabilityByCategory(OH\_AVCODEC\_MIMETYPE\_VIDEO\_AVC, true, HARDWARE);
    const char \*codecName = OH\_AVCapability\_GetName(capability);
    OH\_AVCodec \*videoEnc = OH\_VideoEncoder\_CreateByName(codecName);
    
    // 通过MIME TYPE创建编码器。
    OH\_AVCodec \*videoEnc = OH\_VideoEncoder\_CreateByMime(OH\_AVCODEC\_MIMETYPE\_VIDEO\_AVC);
    
3.  调用OH\_VideoEncoder\_RegisterCallback()设置回调函数。
    
    注册回调函数指针集合OH\_AVCodecCallback，包括：
    
    *   OH\_AVCodecOnError 编码器运行错误，返回的错误码详情请参见[OH\_AVCodecOnError](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avcodec-base-h#oh_avcodeconerror)；
    *   OH\_AVCodecOnStreamChanged 码流信息变化，如格式变化等；
    *   OH\_AVCodecOnNeedInputBuffer 输入回调无作用，开发者通过获取的surface输入数据；
    *   OH\_AVCodecOnNewOutputBuffer 运行过程中产生了新的输出数据，即编码完成。
    
    回调函数的具体实现可参考[示例工程](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Media/AVCodec)。
    
    示例如下所示：
    
    // 设置OH\_AVCodecOnError 回调函数，编码异常。
    static void OnError(OH\_AVCodec \*codec, int32\_t errorCode, void \*userData)
    {
        // 回调的错误码由开发者判断处理。
        (void)codec;
        (void)errorCode;
        (void)userData;
    }
    
    // 设置OH\_AVCodecOnStreamChanged 回调函数，编码数据流变化
    static void OnStreamChanged(OH\_AVCodec \*codec, OH\_AVFormat \*format, void \*userData)
    {
        (void)codec;
        (void)format;
        (void)userData;
        // 可通过format获取到分辨率变化后的视频宽、高
        OH\_AVFormat\_GetIntValue(format, OH\_MD\_KEY\_VIDEO\_PIC\_WIDTH, &width);
        OH\_AVFormat\_GetIntValue(format, OH\_MD\_KEY\_VIDEO\_PIC\_HEIGHT, &height);
    }
    
    // 设置 OH\_AVCodecOnNeedInputBuffer 回调函数，编码输入帧送入数据队列。
    static void OnNeedInputBuffer(OH\_AVCodec \*codec, uint32\_t index, OH\_AVBuffer \*buffer, void \*userData)
    {
        // Surface模式下，该回调函数无作用，开发者通过获取的surface输入数据。
        (void)userData;
        (void)index;
        (void)buffer;
    }
    
    // 设置 OH\_AVCodecOnNewOutputBuffer 回调函数，编码完成帧送入输出队列。
    static void OnNewOutputBuffer(OH\_AVCodec \*codec, uint32\_t index, OH\_AVBuffer \*buffer, void \*userData)
    {
        // 完成帧的数据buffer和对应的index送入outQueue队列。
        (void)codec;
        (void)userData;
        outQueue.Enqueue(std::make\_shared<CodecBufferInfo>(index, buffer));
    }
    
    // 配置异步回调，调用 OH\_VideoEncoder\_RegisterCallback()接口。
    OH\_AVCodecCallback cb = {&OnError, &OnStreamChanged, &OnNeedInputBuffer, &OnNewOutputBuffer};
    OH\_AVErrCode ret = OH\_VideoEncoder\_RegisterCallback(videoEnc, cb, nullptr); // nullptr:开发者执行回调所依赖的数据userData为空。
    if (ret != AV\_ERR\_OK) {
        // 异常处理。
    }
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/95/v3/MZd_ViLzT9e_zPd3AqAq_g/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073508Z&HW-CC-Expire=86400&HW-CC-Sign=EFDCA49BAFCCE60587F6F0AFFA44262D050407D47E85BA7783AABCCBF5342015)
    
    在回调函数中，对数据队列进行操作时，需要注意多线程同步的问题。
    
4.  （可选）调用OH\_VideoEncoder\_RegisterParameterCallback()在Configure接口之前注册随帧通路回调。
    
    详情请参考[时域可分层视频编码](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/video-encoding-temporal-scalability)。
    
     // 4.1 编码输入参数回调OH\_VideoEncoder\_OnNeedInputParameter实现
     static void OnNeedInputParameter(OH\_AVCodec \*codec, uint32\_t index, OH\_AVFormat \*parameter, void \*userData)
     {
         // 输入帧parameter对应的index，送入InParameterIndexQueue队列
         // 输入帧的数据parameter送入InParameterQueue队列
         // 数据处理
         // 随帧参数写入
         // 配置OH\_MD\_KEY\_VIDEO\_ENCODER\_QP\_MAX 的值应大于等于OH\_MD\_KEY\_VIDEO\_ENCODER\_QP\_MIN
         OH\_AVFormat\_SetIntValue(parameter, OH\_MD\_KEY\_VIDEO\_ENCODER\_QP\_MAX, 30);
         OH\_AVFormat\_SetIntValue(parameter, OH\_MD\_KEY\_VIDEO\_ENCODER\_QP\_MIN, 20);
         inQueue.Enqueue(std::make\_shared<CodecBufferInfo>(index, parameter));
     }
    
     // 4.2 注册随帧参数回调
     OH\_VideoEncoder\_OnNeedInputParameter inParaCb = OnNeedInputParameter;
     OH\_VideoEncoder\_RegisterParameterCallback(videoEnc, inParaCb, NULL); // NULL:用户特定数据userData为空
    
5.  调用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)。
    
    目前支持的所有格式都必须配置以下选项：视频帧宽度、视频帧高度、视频像素格式。
    
    // 配置视频帧速率。
    double frameRate = 30.0;
    // 配置视频YUV值范围标志。
    int32\_t rangeFlag = 0;
    // 配置视频原色。
    int32\_t primary = static\_cast<int32\_t>(OH\_ColorPrimary::COLOR\_PRIMARY\_BT709);
    // 配置传输特性。
    int32\_t transfer = static\_cast<int32\_t>(OH\_TransferCharacteristic::TRANSFER\_CHARACTERISTIC\_BT709);
    // 配置最大矩阵系数。
    int32\_t matrix = static\_cast<int32\_t>(OH\_MatrixCoefficient::MATRIX\_COEFFICIENT\_IDENTITY);
    // 配置编码Profile。
    int32\_t profile = static\_cast<int32\_t>(OH\_AVCProfile::AVC\_PROFILE\_HIGH);
    // 配置编码比特率模式。
    int32\_t rateMode = static\_cast<int32\_t>(OH\_BitrateMode::BITRATE\_MODE\_VBR);
    // 配置关键帧的间隔，单位为毫秒。
    int32\_t iFrameInterval = 1000;
    // 配置质量稳定码率因子。
    int32\_t sqrFactor = 30;
    // 配置最大比特率，单位为bps。
    int64\_t maxBitRate = 20000000;
    // 配置比特率，单位为bps。
    int64\_t bitRate = 5000000;
    // 配置编码质量。
    int64\_t quality = 90;
    
    auto format = std::shared\_ptr<OH\_AVFormat>(OH\_AVFormat\_Create(), OH\_AVFormat\_Destroy);
    if (format == nullptr) {
        // 异常处理。
    }
    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\_SetDoubleValue(format.get(), OH\_MD\_KEY\_FRAME\_RATE, frameRate);
    OH\_AVFormat\_SetIntValue(format.get(), OH\_MD\_KEY\_RANGE\_FLAG, rangeFlag);
    OH\_AVFormat\_SetIntValue(format.get(), OH\_MD\_KEY\_COLOR\_PRIMARIES, primary);
    OH\_AVFormat\_SetIntValue(format.get(), OH\_MD\_KEY\_TRANSFER\_CHARACTERISTICS, transfer);
    OH\_AVFormat\_SetIntValue(format.get(), OH\_MD\_KEY\_MATRIX\_COEFFICIENTS, matrix);
    OH\_AVFormat\_SetIntValue(format.get(), OH\_MD\_KEY\_I\_FRAME\_INTERVAL, iFrameInterval);
    OH\_AVFormat\_SetIntValue(format.get(), OH\_MD\_KEY\_PROFILE, profile);
    // 只有当OH\_BitrateMode = BITRATE\_MODE\_CQ时，才需要配置OH\_MD\_KEY\_QUALITY。
    if (rateMode == static\_cast<int32\_t>(OH\_BitrateMode::BITRATE\_MODE\_CQ)) {
        OH\_AVFormat\_SetIntValue(format.get(), OH\_MD\_KEY\_QUALITY, quality);
    } else if (rateMode == static\_cast<int32\_t>(OH\_BitrateMode::BITRATE\_MODE\_SQR)) {
        // 只有当OH\_BitrateMode = BITRATE\_MODE\_SQR时，才需要配置OH\_MD\_KEY\_MAX\_BITRATE和OH\_MD\_KEY\_SQR\_FACTOR。
        OH\_AVFormat\_SetLongValue(format.get(), OH\_MD\_KEY\_MAX\_BITRATE, maxBitRate);
        OH\_AVFormat\_SetIntValue(format.get(), OH\_MD\_KEY\_SQR\_FACTOR, sqrFactor);
    } else if (rateMode == static\_cast<int32\_t>(OH\_BitrateMode::BITRATE\_MODE\_CBR) ||
               rateMode == static\_cast<int32\_t>(OH\_BitrateMode::BITRATE\_MODE\_VBR) ||
               rateMode == static\_cast<int32\_t>(OH\_BitrateMode::BITRATE\_MODE\_CBR\_HIGH\_QUALITY)){
        OH\_AVFormat\_SetLongValue(format.get(), OH\_MD\_KEY\_BITRATE, bitRate);
    }
    OH\_AVFormat\_SetIntValue(format.get(), OH\_MD\_KEY\_VIDEO\_ENCODE\_BITRATE\_MODE, rateMode);
    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/8a/v3/4AKj-_meRfOkvBCA_hEiTA/caution_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073508Z&HW-CC-Expire=86400&HW-CC-Sign=52B7D1436D5E4E0E1CC5F9520744373EB68D4B182DCC6E46A289EA041CD9F232)
    
    配置非必须参数错误时，会返回AV\_ERR\_INVALID\_VAL错误码。但OH\_VideoEncoder\_Configure()不会失败，而是使用默认值继续执行。
    
6.  获取surface。
    
    获取编码器Surface模式的OHNativeWindow输入，获取surface需要在调用OH\_VideoEncoder\_Prepare接口之前完成。
    
    // 获取需要输入的surface，以进行编码。
    OHNativeWindow \*nativeWindow;
    OH\_AVErrCode ret = OH\_VideoEncoder\_GetSurface(videoEnc, &nativeWindow);
    if (ret != AV\_ERR\_OK) {
        // 异常处理。
    }
    // 通过OHNativeWindow\*变量类型，可通过生产者接口获取待填充数据地址。
    
    OHNativeWindow\*变量类型的使用方法请参考图形子系统 [OHNativeWindow](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-nativewindow)。
    
7.  调用OH\_VideoEncoder\_Prepare()编码器就绪。
    
    该接口将在编码器运行前进行一些数据的准备工作。
    
    OH\_AVErrCode ret = OH\_VideoEncoder\_Prepare(videoEnc);
    if (ret != AV\_ERR\_OK) {
        // 异常处理。
    }
    
8.  调用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) {
        // 异常处理。
    }
    
9.  （可选）OH\_VideoEncoder\_SetParameter()在运行过程中动态配置编码器参数。
    
    OH\_AVFormat \*format = OH\_AVFormat\_Create();
    
    // 支持动态请求IDR帧
    OH\_AVFormat\_SetIntValue(format, OH\_MD\_KEY\_REQUEST\_I\_FRAME, true);
    // 支持动态重置比特率
    int64\_t bitRate = 2000000;
    OH\_AVFormat\_SetLongValue(format, OH\_MD\_KEY\_BITRATE, bitRate);
    // 支持动态重置视频帧速率
    double frameRate = 60.0;
    OH\_AVFormat\_SetDoubleValue(format, OH\_MD\_KEY\_FRAME\_RATE, frameRate);
    // 支持动态设置QP值
    // 配置OH\_MD\_KEY\_VIDEO\_ENCODER\_QP\_MAX 的值应大于等于OH\_MD\_KEY\_VIDEO\_ENCODER\_QP\_MIN
    OH\_AVFormat\_SetIntValue(format, OH\_MD\_KEY\_VIDEO\_ENCODER\_QP\_MAX, 30);
    OH\_AVFormat\_SetIntValue(format, OH\_MD\_KEY\_VIDEO\_ENCODER\_QP\_MIN, 20);
    
    int32\_t ret = OH\_VideoEncoder\_SetParameter(videoEnc, format);
    if (ret != AV\_ERR\_OK) {
        // 异常处理
    }
    OH\_AVFormat\_Destroy(format);
    
10.  写入编码图像。
     
     在之前的第6步中，开发者已经对OH\_VideoEncoder\_GetSurface接口返回的OHNativeWindow\*类型变量进行配置。因为编码所需的数据，由配置的surface进行持续地输入，所以开发者无需对OnNeedInputBuffer回调函数进行处理，也无需使用OH\_VideoEncoder\_PushInputBuffer接口输入数据。
     
     在变分辨率场景中，此规则也同样适用。
     
11.  （可选）调用OH\_VideoEncoder\_PushInputParameter()通知编码器随帧参数配置输入完成。
     
     在之前的第4步中，开发者已经注册随帧通路回调。
     
     以下示例中：
     
     *   index：回调函数OnNeedInputParameter传入的参数，与buffer唯一对应的标识。
     
     std::shared\_ptr<CodecBufferInfo> bufferInfo = inQueue.Dequeue();
     std::shared\_lock<std::shared\_mutex> lock(codecMutex);
     if (bufferInfo == nullptr || !bufferInfo->isValid) {
         // 异常处理。
     }
     // 值由开发者决定。
     int32\_t isIFrame;
     OH\_AVFormat\_SetIntValue(bufferInfo->parameter, OH\_MD\_KEY\_REQUEST\_I\_FRAME, isIFrame);
     OH\_AVErrCode ret = OH\_VideoEncoder\_PushInputParameter(videoEnc, bufferInfo->index);
     if (ret != AV\_ERR\_OK) {
         // 异常处理。
     }
     
12.  调用OH\_VideoEncoder\_NotifyEndOfStream()通知编码器结束。
     
     // Surface模式：通知视频编码器输入流已结束，只能使用此接口进行通知。
     // 不能像Buffer模式中将flag设为AVCODEC\_BUFFER\_FLAGS\_EOS，再调用OH\_VideoEncoder\_PushInputBuffer接口通知编码器输入结束。
     OH\_AVErrCode ret = OH\_VideoEncoder\_NotifyEndOfStream(videoEnc);
     if (ret != AV\_ERR\_OK) {
         // 异常处理。
     }
     
13.  调用OH\_VideoEncoder\_FreeOutputBuffer()释放编码帧。
     
     以下示例中，bufferInfo的成员变量：
     
     *   index：回调函数OnNewOutputBuffer传入的参数，与buffer唯一对应的标识；
     *   buffer：回调函数OnNewOutputBuffer传入的参数，可以通过[OH\_AVBuffer\_GetAddr](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avbuffer-h#oh_avbuffer_getaddr)接口得到共享内存地址的指针；
     *   isValid：bufferInfo中存储的buffer实例是否有效。
     
     std::shared\_ptr<CodecBufferInfo> bufferInfo = outQueue.Dequeue();
     std::shared\_lock<std::shared\_mutex> lock(codecMutex);
     if (bufferInfo == nullptr || !bufferInfo->isValid) {
         // 异常处理。
     }
     int32\_t qpAverage = 20;
     double mseValue = 0.0;
     OH\_AVFormat \*format = OH\_AVBuffer\_GetParameter(bufferInfo->buffer);
     OH\_AVFormat\_GetIntValue(format, OH\_MD\_KEY\_VIDEO\_ENCODER\_QP\_AVERAGE, &qpAverage);
     OH\_AVFormat\_GetDoubleValue(format, OH\_MD\_KEY\_VIDEO\_ENCODER\_MSE, &mseValue);
     OH\_AVFormat\_Destroy(format);
     // 获取编码后信息。
     OH\_AVCodecBufferAttr info;
     int32\_t ret = OH\_AVBuffer\_GetBufferAttr(bufferInfo->buffer, &info);
     if (ret != AV\_ERR\_OK) {
         // 异常处理。
     }
     // 将编码完成帧数据buffer写入到对应输出文件中。
     outputFile->write(reinterpret\_cast<char \*>(OH\_AVBuffer\_GetAddr(bufferInfo->buffer)), info.size);
     // 释放已完成写入的数据，index为对应输出队列下标
     ret = OH\_VideoEncoder\_FreeOutputBuffer(videoEnc, bufferInfo->index);
     if (ret != AV\_ERR\_OK) {
         // 异常处理。
     }
     
14.  （可选）调用OH\_VideoEncoder\_Flush()刷新编码器。
     
     调用OH\_VideoEncoder\_Flush接口后，编码器仍处于运行态，但会清除编码器中缓存的输入和输出数据及参数集如H.264格式的PPS/SPS。
     
     此时需要调用OH\_VideoEncoder\_Start接口重新开始编码。
     
     std::unique\_lock<std::shared\_mutex> lock(codecMutex);
     // 刷新编码器videoEnc。
     OH\_AVErrCode flushRet = OH\_VideoEncoder\_Flush(videoEnc);
     if (flushRet != AV\_ERR\_OK) {
         // 异常处理。
     }
     inQueue.Flush();
     outQueue.Flush();
     // 重新开始编码。
     OH\_AVErrCode startRet = OH\_VideoEncoder\_Start(videoEnc);
     if (startRet != AV\_ERR\_OK) {
         // 异常处理。
     }
     
15.  （可选）调用OH\_VideoEncoder\_Reset()重置编码器。
     
     调用OH\_VideoEncoder\_Reset接口后，编码器将回到初始化的状态，需要调用OH\_VideoEncoder\_Configure接口和OH\_VideoEncoder\_Prepare接口重新配置。
     
     std::unique\_lock<std::shared\_mutex> lock(codecMutex);
     // 重置编码器videoEnc。
     OH\_AVErrCode resetRet = OH\_VideoEncoder\_Reset(videoEnc);
     if (resetRet != AV\_ERR\_OK) {
         // 异常处理。
     }
     inQueue.Flush();
     outQueue.Flush();
     // 重新配置编码器参数。
     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) {
         // 异常处理。
     }
     
16.  （可选）调用OH\_VideoEncoder\_Stop()停止编码器。
     
     调用OH\_VideoEncoder\_Stop接口后，编码器保留了编码实例，释放输入输出buffer。开发者可以直接调用OH\_VideoEncoder\_Start接口继续编码。
     
     std::unique\_lock<std::shared\_mutex> lock(codecMutex);
     // 终止编码器videoEnc。
     OH\_AVErrCode ret = OH\_VideoEncoder\_Stop(videoEnc);
     if (ret != AV\_ERR\_OK) {
         // 异常处理。
     }
     inQueue.Flush();
     outQueue.Flush();
     
17.  调用OH\_VideoEncoder\_Destroy()销毁编码器实例，释放资源。
     
     ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/6e/v3/SMDMfVvFRu2LuzvCOTisZg/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073508Z&HW-CC-Expire=86400&HW-CC-Sign=950BAADD1F26FFBE230322F1E750C86428C1E4CDD2487F4B19C3A939260B9BFB)
     
     1.  不能在回调函数中调用；
     2.  执行该步骤之后，需要开发者将videoEnc指向nullptr，防止野指针导致程序错误。
     
     std::unique\_lock<std::shared\_mutex> lock(codecMutex);
     // 释放nativeWindow实例。
     if(nativeWindow != nullptr){
         OH\_NativeWindow\_DestroyNativeWindow(nativeWindow);
         nativeWindow = nullptr;
     }
     // 调用OH\_VideoEncoder\_Destroy，注销编码器。
     OH\_AVErrCode ret = AV\_ERR\_OK;
     if (videoEnc != nullptr) {
         OH\_VideoEncoder\_Destroy(videoEnc);
         videoEnc = nullptr;
     }
     inQueue.Flush();
     outQueue.Flush();
     

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

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

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 <fstream>
    
2.  创建编码器实例。
    
    与Surface模式相同，此处不再赘述。
    
    // 通过codec name创建编码器，应用有特殊需求，比如选择支持某种分辨率规格的编码器，可先查询capability，再根据codec name创建编码器。
    OH\_AVCapability \*capability = OH\_AVCodec\_GetCapability(OH\_AVCODEC\_MIMETYPE\_VIDEO\_AVC, true);
    const char \*codecName = OH\_AVCapability\_GetName(capability);
    OH\_AVCodec \*videoEnc = OH\_VideoEncoder\_CreateByName(codecName);
    
    // 通过MIME TYPE创建编码器。
    OH\_AVCodec \*videoEnc = OH\_VideoEncoder\_CreateByMime(OH\_AVCODEC\_MIMETYPE\_VIDEO\_AVC);
    
3.  调用OH\_VideoEncoder\_RegisterCallback()设置回调函数。
    
    注册回调函数指针集合OH\_AVCodecCallback，包括：
    
    *   OH\_AVCodecOnError 编码器运行错误，返回的错误码详情请参见[OH\_AVCodecOnError](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avcodec-base-h#oh_avcodeconerror)；
    *   OH\_AVCodecOnStreamChanged 码流信息变化，如格式变化等；
    *   OH\_AVCodecOnNeedInputBuffer 运行过程中需要新的输入数据，即编码器已准备好，可以输入YUV/RGB数据；
    *   OH\_AVCodecOnNewOutputBuffer 运行过程中产生了新的输出数据，即编码完成。
    
    开发者可以通过处理该回调报告的信息，确保编码器正常运转。
    
    回调函数的具体实现可参考[示例工程](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Media/AVCodec)。
    
    bool isFirstFrame = true;
    int32\_t qpAverage = 20;
    double mseValue = 0.0;
    
    // 编码异常回调OH\_AVCodecOnError实现。
    static void OnError(OH\_AVCodec \*codec, int32\_t errorCode, void \*userData)
    {
        // 回调的错误码由开发者判断处理。
        (void)codec;
        (void)errorCode;
        (void)userData;
    }
    
    // 编码数据流变化回调OH\_AVCodecOnStreamChanged实现。
    static void OnStreamChanged(OH\_AVCodec \*codec, OH\_AVFormat \*format, void \*userData)
    {
        // Buffer模式下，该回调函数无作用。
        (void)codec;
        (void)format;
        (void)userData;
    }
    
    // 编码输入回调OH\_AVCodecOnNeedInputBuffer实现。
    static void OnNeedInputBuffer(OH\_AVCodec \*codec, uint32\_t index, OH\_AVBuffer \*buffer, void \*userData)
    {
        // 获取视频宽跨距、高跨距。
        if (isFirstFrame) {
            auto format = std::shared\_ptr<OH\_AVFormat>(OH\_VideoEncoder\_GetInputDescription(codec), OH\_AVFormat\_Destroy);
            if (format == nullptr) {
                // 异常处理。
            }
            bool ret = OH\_AVFormat\_GetIntValue(format.get(), OH\_MD\_KEY\_VIDEO\_STRIDE, &widthStride) &&
                       OH\_AVFormat\_GetIntValue(format.get(), OH\_MD\_KEY\_VIDEO\_SLICE\_HEIGHT, &heightStride);
            if (!ret) {
                // 异常处理。
            }
            isFirstFrame = false;
        }
        // 输入帧的数据buffer和对应的index送入inQueue队列。
        (void)codec;
        (void)userData;
        inQueue.Enqueue(std::make\_shared<CodecBufferInfo>(index, buffer));
    }
    
    // 编码输出回调OH\_AVCodecOnNewOutputBuffer实现
    static void OnNewOutputBuffer(OH\_AVCodec \*codec, uint32\_t index, OH\_AVBuffer \*buffer, void \*userData)
    {
        // 获取视频帧的平均量化参数，平方误差
        OH\_AVFormat \*format = OH\_AVBuffer\_GetParameter(buffer);
        OH\_AVFormat\_GetIntValue(format, OH\_MD\_KEY\_VIDEO\_ENCODER\_QP\_AVERAGE, &qpAverage);
        OH\_AVFormat\_GetDoubleValue(format, OH\_MD\_KEY\_VIDEO\_ENCODER\_MSE, &mseValue);
        OH\_AVFormat\_Destroy(format);
        // 完成帧的数据buffer和对应的index送入outQueue队列
        (void)userData;
        outQueue.Enqueue(std::make\_shared<CodecBufferInfo>(index, buffer));
    }
    
    // 配置异步回调，调用OH\_VideoEncoder\_RegisterCallback接口。
    OH\_AVCodecCallback cb = {&OnError, &OnStreamChanged, &OnNeedInputBuffer, &OnNewOutputBuffer};
    OH\_AVErrCode ret = OH\_VideoEncoder\_RegisterCallback(videoEnc, cb, nullptr);
    if (ret != AV\_ERR\_OK) {
        // 异常处理。
    }
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/b7/v3/CzWxECOTRnmJf06guP-pwA/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073508Z&HW-CC-Expire=86400&HW-CC-Sign=2FE62D62A5EF8DD2CD403EBF8B7657ED4438E525D3D8088CC7ED50A1548C041C)
    
    在回调函数中，对数据队列进行操作时，需要注意多线程同步的问题。
    
4.  调用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\_AVErrCode ret = OH\_VideoEncoder\_Configure(videoEnc, format.get());
    if (ret != AV\_ERR\_OK) {
        // 异常处理。
    }
    
5.  调用OH\_VideoEncoder\_Prepare()编码器就绪。
    
    该接口将在编码器运行前进行一些数据的准备工作。
    
    OH\_AVErrCode ret = OH\_VideoEncoder\_Prepare(videoEnc);
    if (ret != AV\_ERR\_OK) {
        // 异常处理。
    }
    
6.  调用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) {
        // 异常处理。
    }
    
7.  （可选）在运行过程中动态配置编码器参数。
    
    OH\_AVFormat \*format = OH\_AVFormat\_Create();
    
    // 支持动态请求IDR帧
    OH\_AVFormat\_SetIntValue(format, OH\_MD\_KEY\_REQUEST\_I\_FRAME, true);
    // 支持动态重置比特率
    int64\_t bitRate = 2000000;
    OH\_AVFormat\_SetLongValue(format, OH\_MD\_KEY\_BITRATE, bitRate);
    // 支持动态重置视频帧速率
    double frameRate = 60.0;
    OH\_AVFormat\_SetDoubleValue(format, OH\_MD\_KEY\_FRAME\_RATE, frameRate);
    
    int32\_t ret = OH\_VideoEncoder\_SetParameter(videoEnc, format);
    if (ret != AV\_ERR\_OK) {
        // 异常处理
    }
    OH\_AVFormat\_Destroy(format);
    
8.  调用OH\_VideoEncoder\_PushInputBuffer()写入编码图像。
    
    送入输入队列进行编码，以下示例中：
    
    *   widthStride: 获取到的buffer数据的宽跨距。
    *   heightStride：获取到的buffer数据的高跨距。
    
    bufferInfo的成员变量：
    
    *   buffer：回调函数OnNeedInputBuffer传入的参数，可以通过[OH\_AVBuffer\_GetAddr](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avbuffer-h#oh_avbuffer_getaddr)接口得到共享内存地址的指针；
    *   index：回调函数OnNeedInputBuffer传入的参数，与buffer唯一对应的标识；
    *   isValid：bufferInfo中存储的buffer实例是否有效。
    
    std::shared\_ptr<CodecBufferInfo> bufferInfo = inQueue.Dequeue();
    std::shared\_lock<std::shared\_mutex> lock(codecMutex);
    if (bufferInfo == nullptr || !bufferInfo->isValid) {
        // 异常处理。
    }
    // 写入图像数据。
    int32\_t frameSize = 0;
    if (widthStride == width && heightStride == height) {
        frameSize = width \* height \* 3 / 2; // NV12像素格式下，每帧数据大小的计算公式
        int32\_t capacity = OH\_AVBuffer\_GetCapacity(bufferInfo->buffer);
        if (frameSize > capacity) {
            // 异常处理。
        }
        // 处理文件流得到帧的长度，再将需要编码的数据写入到对应index的buffer中。
        uint8\_t \*addr = OH\_AVBuffer\_GetAddr(bufferInfo->buffer);
        if (addr == nullptr) {
           // 异常处理。
        }
        if (inputFile != nullptr && inputFile->is\_open()) {
            inputFile->read(reinterpret\_cast<char \*>(addr), frameSize);
        }
    } else {
        // 如果跨距不等于宽，需要开发者按照跨距进行偏移，具体可参考以下示例。
    }
    // 配置buffer info信息。
    OH\_AVCodecBufferAttr info;
    info.size = frameSize;
    info.offset = 0;
    // 注意此处和Surface模式不同，pts需要应用填充，可根据预期显示的时间进行计算写入，如：帧数 \* 1000000 / frameRate。
    info.pts = 0;
    // 避免flags随机初始化为AVCODEC\_BUFFER\_FLAGS\_EOS导致使用异常，flags需要赋值如0（普通帧标识）。
    info.flags = 0;
    OH\_AVErrCode setBufferRet = OH\_AVBuffer\_SetBufferAttr(bufferInfo->buffer, &info);
    if (setBufferRet != AV\_ERR\_OK) {
        // 异常处理。
    }
    // 配置buffer 随帧信息。
    // 值由开发者决定。
    int32\_t isIFrame;
    auto parameter = std::shared\_ptr<OH\_AVFormat>(OH\_AVBuffer\_GetParameter(bufferInfo->buffer), OH\_AVFormat\_Destroy);
    if (parameter == nullptr) {
        // 异常处理。
    }
    OH\_AVFormat\_SetIntValue(parameter.get(), OH\_MD\_KEY\_REQUEST\_I\_FRAME, isIFrame);
    OH\_AVErrCode parameterRet = OH\_AVBuffer\_SetParameter(bufferInfo->buffer, parameter.get());
    if (parameterRet != AV\_ERR\_OK) {
        // 异常处理。
    }
    // 送入编码输入队列进行编码，index为对应输入队列的下标。
    OH\_AVErrCode pushInputRet = OH\_VideoEncoder\_PushInputBuffer(videoEnc, bufferInfo->index);
    if (pushInputRet != AV\_ERR\_OK) {
        // 异常处理。
    }
    
    对跨距进行偏移，以NV12图像为例，示例如下：
    
    以NV12图像为例，width、height、wStride、hStride图像排布参考下图：
    
    *   OH\_MD\_KEY\_WIDTH表示width；
    *   OH\_MD\_KEY\_HEIGHT表示height；
    *   OH\_MD\_KEY\_VIDEO\_STRIDE表示wStride；
    *   OH\_MD\_KEY\_VIDEO\_SLICE\_HEIGHT表示hStride。
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/37/v3/95zG-ebbRbuWoysQQkVRQw/zh-cn_image_0000002625108315.png?HW-CC-KV=V1&HW-CC-Date=20260612T073508Z&HW-CC-Expire=86400&HW-CC-Sign=7CFFB5E46F2479E8BCBB591EE68C950B61959E8AD1607564395FF5015A6492EF)
    
    添加头文件。
    
    #include <string.h>
    
    使用示例：
    
    struct Rect   // 源内存区域的宽、高，由开发者自行设置。
    {
        int32\_t width;
        int32\_t height;
    };
    
    struct DstRect // 目标内存区域的宽跨距、高跨距，通过接口OH\_VideoEncoder\_GetInputDescription获取。
    {
        int32\_t wStride;
        int32\_t hStride;
    };
    
    struct SrcRect // 源内存区域的宽跨距、高跨距，由开发者自行设置。
    {
        int32\_t wStride;
        int32\_t hStride;
    };
    
    Rect rect = {320, 240};
    DstRect dstRect = {320, 256};
    SrcRect srcRect = {320, 250};
    uint8\_t\* dst = new uint8\_t\[dstRect.hStride \* dstRect.wStride \* 3 / 2\]; // 目标内存区域的指针。
    uint8\_t\* src = new uint8\_t\[srcRect.hStride \* srcRect.wStride \* 3 / 2\]; // 源内存区域的指针。
    uint8\_t\* dstTemp = dst;
    uint8\_t\* srcTemp = src;
    rect.height = ((rect.height + 1) / 2)  \* 2; // 避免height为奇数。
    rect.width = ((rect.width + 1) / 2)  \* 2; // 避免width为奇数。
    
    // Y 将Y区域的源数据复制到另一个区域的目标数据中。
    for (int32\_t i = 0; i < rect.height; ++i) {
        // 将源数据的一行数据复制到目标数据的一行中。
        memcpy(dstTemp, srcTemp, rect.width);
        // 更新源数据和目标数据的指针，进行下一行的复制。每更新一次源数据和目标数据的指针都向下移动一个wStride。
        dstTemp += dstRect.wStride;
        srcTemp += srcRect.wStride;
    }
    // padding。
    // 更新源数据和目标数据的指针，指针都向下移动一个padding。
    dstTemp += (dstRect.hStride - rect.height) \* dstRect.wStride;
    srcTemp += (srcRect.hStride - rect.height) \* srcRect.wStride;
    rect.height >>= 1;
    // UV 将UV区域的源数据复制到另一个区域的目标数据中。
    for (int32\_t i = 0; i < rect.height; ++i) {
        memcpy(dstTemp, srcTemp, rect.width);
        dstTemp += dstRect.wStride;
        srcTemp += srcRect.wStride;
    }
    
    delete\[\] dst;
    dst = nullptr;
    delete\[\] src;
    src = nullptr;
    
    硬件编码在处理buffer数据时（推送数据前），需要开发者拷贝宽、高对齐后的图像数据到输入回调的AVbuffer中。
    
    一般需要获取数据的宽、高、跨距、像素格式来保证编码输入数据被正确的处理。
    
    具体实现请参考：[Buffer模式](#buffer模式)的步骤3-调用OH\_VideoEncoder\_RegisterCallback接口设置回调函数来获取数据的宽、高、跨距、像素格式。
    
9.  通知编码器结束。
    
    在编码过程中，当最后一帧数据被送入编码输入队列时，需要设置bufferInfo的flag标识为AVCODEC\_BUFFER\_FLAGS\_EOS，通知编码器输入结束。
    
    以下示例中，bufferInfo的成员变量：
    
    *   index：回调函数OnNeedInputBuffer传入的参数，与buffer唯一对应的标识；
    *   buffer：回调函数OnNeedInputBuffer传入的参数，可以通过[OH\_AVBuffer\_GetAddr](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avbuffer-h#oh_avbuffer_getaddr)接口得到共享内存地址的指针;
    *   isValid：bufferInfo中存储的buffer实例是否有效。
    
    std::shared\_ptr<CodecBufferInfo> bufferInfo = inQueue.Dequeue();
    std::shared\_lock<std::shared\_mutex> lock(codecMutex);
    if (bufferInfo == nullptr || !bufferInfo->isValid) {
        // 异常处理。
    }
    // 写入最后一帧图像数据，参考"步骤-8. 写入编码图像"。
    // 配置buffer info信息，设置AVCODEC\_BUFFER\_FLAGS\_EOS标识。
    OH\_AVCodecBufferAttr info;
    info.size = frameSize;
    info.offset = 0;
    // 注意此处和Surface模式不同，pts需要应用填充，可根据预期显示的时间进行计算写入，如：帧数 \* 1000000 / frameRate。
    info.pts = 0;
    info.flags = AVCODEC\_BUFFER\_FLAGS\_EOS;
    OH\_AVErrCode setBufferRet = OH\_AVBuffer\_SetBufferAttr(bufferInfo->buffer, &info);
    if (setBufferRet != AV\_ERR\_OK) {
        // 异常处理。
    }
    OH\_AVErrCode pushInputRet = OH\_VideoEncoder\_PushInputBuffer(videoEnc, bufferInfo->index);
    if (pushInputRet != AV\_ERR\_OK) {
        // 异常处理。
    }
    
10.  调用OH\_VideoEncoder\_FreeOutputBuffer()释放编码帧。
     
     与Surface模式相同，此处不再赘述。
     
     std::shared\_ptr<CodecBufferInfo> bufferInfo = outQueue.Dequeue();
     std::shared\_lock<std::shared\_mutex> lock(codecMutex);
     if (bufferInfo == nullptr || !bufferInfo->isValid) {
         // 异常处理。
     }
     // 获取编码后信息。
     OH\_AVCodecBufferAttr info;
     OH\_AVErrCode getBufferRet = OH\_AVBuffer\_GetBufferAttr(bufferInfo->buffer, &info);
     if (getBufferRet != AV\_ERR\_OK) {
         // 异常处理。
     }
     // 将编码完成帧数据buffer写入到对应输出文件中。
     uint8\_t \*addr = OH\_AVBuffer\_GetAddr(bufferInfo->buffer);
     if (addr == nullptr) {
        // 异常处理。
     }
     if (outputFile != nullptr && outputFile->is\_open()) {
         outputFile->write(reinterpret\_cast<char \*>(addr), info.size);
     }
     // 释放已完成写入的数据，index为对应输出队列的下标。
     OH\_AVErrCode freeOutputRet = OH\_VideoEncoder\_FreeOutputBuffer(videoEnc, bufferInfo->index);
     if (freeOutputRet != AV\_ERR\_OK) {
         // 异常处理。
     }
     

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

#### 注意事项

1.  Buffer模式不支持10bit的图像数据。
2.  由于硬件编码器资源有限，每个编码器在使用完毕后都必须调用OH\_VideoEncoder\_Destroy接口销毁实例，并释放资源。
3.  Flush，Reset，Stop，Destroy接口需在非回调线程中调用。接口执行时会阻塞等待所有已触发的回调执行完毕，再将执行结果返回给开发者。
4.  一旦调用Flush，Reset，Stop接口，会触发系统回收OH\_AVBuffer，开发者不可对之前回调函数获取到的OH\_AVBuffer继续进行操作。
5.  在Buffer模式下，开发者通过输入回调函数OH\_AVCodecOnNeedInputBuffer获取到OH\_AVBuffer的指针实例后，必须调用OH\_VideoEncoder\_PushInputBuffer接口来通知系统该实例已被使用完毕。确保系统能够将该实例里面的数据进行编码。如果开发者通过调用OH\_AVBuffer\_GetNativeBuffer接口获取到OH\_NativeBuffer指针实例，并且该实例的生命周期超过了当前的OH\_AVBuffer指针实例，那么开发者需手动拷贝数据，并自行管理新生成的OH\_NativeBuffer实例的生命周期，确保其正确使用和释放。
6.  为确保系统服务的持续可用性，当检测到应用存在异常实例占用行为时，系统将自动介入。开发者应注意：持续的实例管理不当可能导致进程被终止。

#### 视频编码支持的能力

| 支持的能力 | 使用简述 |
| :-- | :-- |
| 分层编码、设置LTR帧、参考帧 | 具体可参考：[时域可分层视频编码](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/video-encoding-temporal-scalability)。 |
| 支持历史帧repeat编码 | 具体可参考：native\_avcodec\_base.h\[变量\][OH\_MD\_KEY\_VIDEO\_ENCODER\_REPEAT\_PREVIOUS\_FRAME\_AFTER](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avcodec-base-h#变量)中的OH\_MD\_KEY\_VIDEO\_ENCODER\_REPEAT\_PREVIOUS\_FRAME\_AFTER和OH\_MD\_KEY\_VIDEO\_ENCODER\_REPEAT\_PREVIOUS\_MAX\_COUNT。 |
| 支持的能力 | 使用简述 |
| \-------------------------- | \------------------------------------------------- |
| 运行时配置编码器参数，包括帧率、码率、QPMin/QPMax | 通过调用OH\_VideoEncoder\_SetParameter()配置， 具体可参考下文中：Surface模式的步骤-9 |
| 随帧设置编码QPMin/QPMax | 通过调用OH\_VideoEncoder\_RegisterParameterCallback()注册随帧参数回调时配置，具体可参考下文中：Surface模式的步骤-4 |
| 分层编码，LTR设置 | 具体可参考：[时域可分层视频编码](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/video-encoding-temporal-scalability) |
| 获取编码每帧平均量化参数（QPAverage）、平方误差（mseValue） | 在配置回调函数OnNewOutputBuffer()时获取，具体可参考下文中：Surface模式的步骤-3 |
| 变分辨率 | 编码器支持输入图像分辨率发生变化。目前仅Surface模式支持且图像的宽、高不能超过OH\_VideoEncoder\_Configure接口配置的宽、高，具体可参考下文中：Surface模式的步骤-5 |