使用AVTranscoder实现视频转码(C/C++)
===========================

从API version 20开始支持使用NDK接口（C/C++）实现视频转码。

使用[AVTranscoder](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/media-kit-intro#avtranscoder)可以实现视频转码功能，从API 12开始，转码功能可在手机、平板、2in1设备上作为系统提供的基础能力使用。可以通过调用[canIUse](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/init#caniuse)接口来判断当前设备是否支持AVTranscoder。当canIUse("SystemCapability.Multimedia.Media.AVTranscoder")返回值为true时，表示可以使用转码能力。

本开发指导将以“开始转码-暂停转码-恢复转码-完成转码”的一次完整流程为示例，向开发者讲解AVTranscoder视频转码相关功能。

**图1** 转码状态变化示意图

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/85/v3/IEu5F3GgRtyS_2AIYmSkHA/zh-cn_image_0000002594828802.png?HW-CC-KV=V1&HW-CC-Date=20260612T073525Z&HW-CC-Expire=86400&HW-CC-Sign=E99AB061F384EFDAC6713E1846B59CC7079CD1565EC716261558AFA6CBB14AA5)

状态的详细说明请参考[OH\_AVTranscoder\_State](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-avtranscoder-base-h#oh_avtranscoder_state)。

#### 开发步骤及注意事项

在CMake脚本中链接动态库。

target\_link\_libraries(entry PUBLIC libavtranscoder.so libace\_napi.z.so)

开发者使用系统日志能力时，需引入如下头文件：

#include <hilog/log.h>

并需要在CMake脚本中链接如下动态库:

target\_link\_libraries(sample PUBLIC libhilog\_ndk.z.so)

开发者通过引入avtranscoder.h和avtranscoder\_base.h头文件，使用转码相关API。

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

1.  创建转码实例：调用OH\_AVTranscoder\_Create()。
    
    OH\_AVTranscoder \*transcoder = OH\_AVTranscoder\_Create();
    
2.  设置回调事件函数：使用OH\_AVTranscoder\_SetStateCallback()、OH\_AVTranscoder\_SetErrorCallback()和OH\_AVTranscoder\_SetProgressUpdateCallback()接口设置回调转码状态、转码错误事件和转码进度更新回调函数，搭配全流程场景使用。支持的回调事件包括：
    
    | 事件类型 | 说明 |
    | :-- | :-- |
    | OH\_AVTranscoder\_OnStateChange | 可选事件，回调转码过程中状态。 |
    | OH\_AVTranscoder\_OnError | 可选事件，回调转码错误事件。 |
    | OH\_AVTranscoder\_OnProgressUpdate | 可选事件，回调转码进度更新事件。 |
    
    应用使用OH\_AVTranscoder\_SetStateCallback()、OH\_AVTranscoder\_SetErrorCallback()和OH\_AVTranscoder\_SetProgressUpdateCallback()接口设置回调转码状态、转码错误事件和转码进度更新回调函数，可以获取更多信息，还可以通过设置userData自定义数据。
    
    typedef struct NdkAVTransCoderUser {
       using StateChangeFunc = std::function<void()>;
       NdkAVTransCoderUser();
       NdkAVTransCoderUser(const NdkAVTransCoderUser &other) = delete;
       NdkAVTransCoderUser &operator=(const NdkAVTransCoderUser &other) = delete;
       ~NdkAVTransCoderUser();
    
       void OnStateChangeCb(OH\_AVTranscoder \*transcoder, OH\_AVTranscoder\_State state);
       void OnErrorCb(OH\_AVTranscoder \*transcoder, int32\_t errorCode, const char  \*errorMsg);
       void OnProgressUpdateCb(OH\_AVTranscoder \*transcoder, int progress);
       std::map<uint32\_t, StateChangeFunc> stateChangeFuncs\_;
    
       OH\_AVTranscoder \*transcoder = nullptr;
       int32\_t errorCode = AV\_ERR\_OK;
       OH\_AVTranscoder\_State transCoderState = AVTRANSCODER\_PREPARED;
       int avTranscoderProgress = 0;
       int inStartStateCount = 0;
    } NdkAVTransCoderUser;
    
    void NdkAVTransCoderUser::OnProgressUpdateCb(OH\_AVTranscoder \*transcoder, int  progress)
    {
       LOG("NdkAVTransCoderUser OnProgressUpdateCb progress: %{public}d", progress);
       this->avTranscoderProgress = progress;
    }
    
    void NdkAVTransCoderUser::OnErrorCb(OH\_AVTranscoder \*transcoder, int32\_t  errorCode, const char \*errorMsg)
    {
       LOG("NdkAVTransCoderUser OnErrorCb errorCode: %{public}d ,errorMsg: %{public} s", errorCode,
          errorMsg == nullptr ? "unknown" : errorMsg);
       this->errorCode = errorCode;
    }
    
    void NdkAVTransCoderUser::OnStateChangeCb(OH\_AVTranscoder \*transcoder,  OH\_AVTranscoder\_State state)
    {
       this->transCoderState = state;
       if (transcoder == nullptr) {
          return;
       }
       int32\_t ret = -1;
       switch (state) {
          case AVTRANSCODER\_PREPARED: {
                this->transCoderState = AVTRANSCODER\_PREPARED;
                if (this->stateChangeFuncs\_.count(AVTRANSCODER\_PREPARED) > 0) {
                   this->stateChangeFuncs\_\[AVTRANSCODER\_PREPARED\]();
                }
                break;
          }
          case AVTRANSCODER\_STARTED: {
                this->transCoderState = AVTRANSCODER\_STARTED;
                if (this->stateChangeFuncs\_.count(AVTRANSCODER\_STARTED) > 0) {
                   this->stateChangeFuncs\_\[AVTRANSCODER\_STARTED\]();
                }
                break;
          }
          case AVTRANSCODER\_PAUSED: {
                this->transCoderState = AVTRANSCODER\_PAUSED;
                if (this->stateChangeFuncs\_.count(AVTRANSCODER\_PAUSED) > 0) {
                   this->stateChangeFuncs\_\[AVTRANSCODER\_PAUSED\]();
                }
                break;
          }
          case AVTRANSCODER\_CANCELLED: {
                this->transCoderState = AVTRANSCODER\_CANCELLED;
                if (this->stateChangeFuncs\_.count(AVTRANSCODER\_CANCELLED) > 0) {
                   this->stateChangeFuncs\_\[AVTRANSCODER\_CANCELLED\]();
                }
                break;
          }
          case AVTRANSCODER\_COMPLETED: {
                this->transCoderState = AVTRANSCODER\_COMPLETED;
                if (this->stateChangeFuncs\_.count(AVTRANSCODER\_COMPLETED) > 0) {
                   this->stateChangeFuncs\_\[AVTRANSCODER\_COMPLETED\]();
                }
                break;
          }
          default:
                break;
       }
    }
    
    static void AvTranscoderStateChangeCbImpl(OH\_AVTranscoder \*transcoder,  OH\_AVTranscoder\_State state, void \*userData)
    {
       LOG("AvTranscoderStateChangeCbImpl state: %{public}d", state);
       NdkAVTransCoderUser \*ndkAVTransCoderUser =  reinterpret\_cast<NdkAVTransCoderUser \*>(userData);
       if (ndkAVTransCoderUser == nullptr || transcoder == nullptr) {
          LOGE("AvTranscoderStateChangeCbImpl ndkAVTransCoderUser or transcoder is  nullptr");
          return;
       }
       ndkAVTransCoderUser->OnStateChangeCb(transcoder, state);
    }
    
    static void AvTranscoderErrorCbImpl(OH\_AVTranscoder \*transcoder, int32\_t  errorCode, const char \*errorMsg,
                                        void \*userData)
    {
       LOG("AvTranscoderErrorCbImpl errorCode: %{public}d, errorMsg: %{public}s",  errorCode,
          errorMsg == nullptr ? "unknown" : errorMsg);
       NdkAVTransCoderUser \*ndkAVTransCoderUser =  reinterpret\_cast<NdkAVTransCoderUser \*>(userData);
       if (ndkAVTransCoderUser == nullptr || transcoder == nullptr) {
          LOGE("AvTranscoderErrorCbImpl ndkAVTransCoderUser or transcoder is  nullptr");
          return;
       }
       ndkAVTransCoderUser->OnErrorCb(transcoder, errorCode, errorMsg);
    }
    
    static void AvTranscoderProgressUpdateCbImpl(OH\_AVTranscoder \*transcoder, int  progress, void \*userData)
    {
       LOG("AvTranscoderProgressUpdateCbImpl progress: %{public}d", progress);
       NdkAVTransCoderUser \*ndkAVTransCoderUser =  reinterpret\_cast<NdkAVTransCoderUser \*>(userData);
       if (ndkAVTransCoderUser == nullptr || transcoder == nullptr) {
          LOGE("AvTranscoderProgressUpdateCbImpl ndkAVTransCoderUser or transcoder is  nullptr");
          return;
       }
       ndkAVTransCoderUser->OnProgressUpdateCb(transcoder, progress);
    }
    static napi\_value OHAvTranscoderNdkPlay(napi\_env env, napi\_callback\_info info)
    {
       OH\_AVTranscoder\_SetStateCallback(transcoder, AvTranscoderStateChangeCbImpl,  transcoderUser); // 设置状态回调
       OH\_AVTranscoder\_SetErrorCallback(transcoder, AvTranscoderErrorCbImpl,  transcoderUser); // 设置错误码回调
       OH\_AVTranscoder\_SetProgressUpdateCallback(transcoder,  AvTranscoderProgressUpdateCbImpl, transcoderUser); // 设置进度值回调
    }
    
3.  使用OH\_AVTranscoderConfig\_Create()创建转码配置实例。
    
    OH\_AVTranscoder\_Config \*config = OH\_AVTranscoderConfig\_Create();
    
4.  设置转码源视频的文件描述符：调用OH\_AVTranscoderConfig\_SetSrcFD()设置源视频文件信息。
    
    OH\_AVTranscoderConfig\_SetSrcFD(config, srcFd, srcOffset, length); // 设置转码源视频FD
    
5.  设置转码输出视频的文件描述符：调用OH\_AVTranscoderConfig\_SetDstFD()设置目标视频文件描述符。
    
    OH\_AVTranscoderConfig\_SetDstFD(config, dstFd); // 设置转码的目标文件描述符dstFd。
    
6.  （可选）设置转码输出视频的编码格式：调用OH\_AVTranscoderConfig\_SetDstVideoType()设置输出视频的编码格式。
    
    OH\_AVTranscoderConfig\_SetDstVideoType(config, "video/avc"); // 视频编码格式，可选。
    
7.  （可选）设置转码输出音频的编码格式：调用OH\_AVTranscoderConfig\_SetDstAudioType()设置输出音频的编码格式。
    
    OH\_AVTranscoderConfig\_SetDstAudioType(config, "audio/mp4a-latm"); // 音频编码格式，可选。
    
8.  设置转码输出视频文件的封装格式：调用OH\_AVTranscoderConfig\_SetDstFileType()设置输出视频文件的封装格式。
    
    OH\_AVTranscoderConfig\_SetDstFileType(config, AV\_OUTPUT\_FORMAT\_MPEG\_4); // 封装格式。
    
9.  （可选）设置转码输出音频的码率：调用OH\_AVTranscoderConfig\_SetDstAudioBitrate()设置输出音频的码率。
    
    const std::int32\_t AUDIO\_BITRATE = 200000;
    OH\_AVTranscoderConfig\_SetDstAudioBitrate(config, AUDIO\_BITRATE); // 音频比特率，可选。
    
10.  （可选）设置转码输出视频的码率：调用OH\_AVTranscoderConfig\_SetDstVideoBitrate()设置输出视频的码率。
     
     const std::int32\_t VIDEO\_BITRATE = 3000000;
     OH\_AVTranscoderConfig\_SetDstVideoBitrate(config, VIDEO\_BITRATE); // 视频比特率，可选。
     
11.  （可选）设置转码输出视频的分辨率：调用OH\_AVTranscoderConfig\_SetDstVideoResolution()设置输出视频的分辨率。
     
     const std::int32\_t VIDEO\_WIDTH = 1280;
     const std::int32\_t VIDEO\_HEIGHT = 720;
     OH\_AVTranscoderConfig\_SetDstVideoResolution(config, VIDEO\_WIDTH, VIDEO\_HEIGHT);
     
12.  准备转码：调用OH\_AVTranscoder\_Prepare()成功之后进入AVTRANSCODER\_PREPARED状态。
     
     OH\_AVErrCode errCode = AV\_ERR\_OK;
     errCode = OH\_AVTranscoder\_Prepare(transcoder, config); // 准备转码
     
13.  开始转码：调用OH\_AVTranscoder\_Start()成功之后进入AVTRANSCODER\_STARTED状态。
     
     OH\_AVErrCode errCode = AV\_ERR\_OK;
     errCode = OH\_AVTranscoder\_Start(transcoder); // 开始转码
     
14.  （可选）暂停转码：调用OH\_AVTranscoder\_Pause()成功之后进入AVTRANSCODER\_PAUSED状态。
     
     OH\_AVErrCode errCode = AV\_ERR\_OK;
     errCode = OH\_AVTranscoder\_Pause(transcoder); // 暂停转码
     
15.  （可选）恢复转码：调用OH\_AVTranscoder\_Resume()成功之后重新进入AVTRANSCODER\_STARTED状态。
     
     OH\_AVErrCode errCode = AV\_ERR\_OK;
     errCode = OH\_AVTranscoder\_Resume(transcoder); // 恢复转码
     
16.  （可选）取消转码：调用OH\_AVTranscoder\_Cancel()成功之后进入AVTRANSCODER\_CANCELLED状态。
     
     OH\_AVErrCode errCode = AV\_ERR\_OK;
     errCode = OH\_AVTranscoder\_Cancel(transcoder); // 取消转码
     
17.  释放转码配置参数：调用OH\_AVTranscoderConfig\_Release()销毁实例，释放资源。
     
     OH\_AVErrCode errCode = AV\_ERR\_OK;
     errCode = OH\_AVTranscoderConfig\_Release(config); // 释放转码配置实例
     
18.  释放转码资源：调用OH\_AVTranscoder\_Release()销毁实例，释放资源。
     
     OH\_AVErrCode errCode = AV\_ERR\_OK;
     errCode = OH\_AVTranscoder\_Release(transcoder); // 释放转码实例
     

#### 运行完整示例

1.  新建工程，下载[示例工程](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/DocsSample/Media/AVTranscoder/AVTranscoderNDK)，并将示例工程的以下资源复制到对应目录。
    
    AVTranscoderNDK
    entry/src/main/ets/
    └── pages
        └── Index.ets（转码界面）
    entry/src/main/
    ├── cpp
    │   ├── types
    │   │   └── libentry
    │   │       └── Index.d.ts（NDK函数对应的js映射）
    │   ├── CMakeLists.txt（CMake脚本）
    │   └── napi\_init.cpp（NDK函数）
    └── resources
        ├── base
        │   └── element
        │       ├── color.json
        │       ├── float.json
        │       └── string.json
        └── rawfile
            └── src.mp4（视频资源）
    
2.  编译新建工程并运行。