鸿蒙音频编码

【HarmonyOS 音频编码开发速览】

文档地址:developer.huawei.com/consumer/cn...

  1. 能力总览

    • 将任意来源的 PCM 数据编码为 AAC、FLAC、MP3、G.711μ、OPUS、AMR-NB/WB 等格式。

    • 典型使用场景:录音后封装、PCM 编辑后导出文件。

    • AAC 默认使用 VBR,码率可能与设定值存在偏差。

  2. 核心流程(12 步)

    1. 引入头文件。

    2. 创建编码器(OH_AVCodec):可按 MIME 或编解码器名称创建。

    3. 注册回调:

      • onError、onNeedInputBuffer、onNewOutputBuffer(onStreamChanged 暂不支持)。
    4. 配置参数:

      必须项:采样率、声道数、采样格式;

      可/必须项:码率、声道布局、ADTS、Profile 等,各格式差异见表。

    5. Prepare → 6) Start → 7) 逐帧输入 PCM(需按格式计算单次样点数) →

    6. 取出码流 → 9) FreeOutputBuffer →

    10)(可选)Flush / Reset → 11) Stop → 12) Destroy。

  3. 关键参数表

    • 采样率/声道数范围:AAC-LC 8--96 kHz、1--8 ch;FLAC 8--96 kHz、1--8 ch;MP3 8--48 kHz、1--2 ch;OPUS 8--48 kHz、1--2 ch;AMR-NB 8 kHz/1 ch,AMR-WB 16 kHz/1 ch。

    • 单次输入样点数:AAC-LC=1024;HE-AAC=2048;FLAC 按采样率查表(如 44.1 kHz→4608)。

  4. 开发注意

    • 必须按顺序调用 API,否则可能异常。

    • 最后一帧需置 AVCODEC_BUFFER_FLAGS_EOS。

    • 调用 Flush/Reset/Stop 后需重新 Start 并可能重新输入数据。

以下 Mermaid 流程图对应「最开始回答」中的 12 步开发顺序,可直接复制到 Mermaid Live Editor 渲染。

图中:

  • 实线框为「必选步骤」
  • 虚线框为「可选步骤」
  • 箭头旁文字为调用接口名
%% HarmonyOS 音频编码 12 步调用流程(对应官方文档顺序) flowchart TD A([开始]) --> B[1. 添加头文件\n#include] B --> C[2. 创建编码器\nOH_AudioCodec_CreateByMime / CreateByName] C --> D[3. 注册回调\nOH_AudioCodec_RegisterCallback] D --> E[4. 配置参数\nOH_AudioCodec_Configure] E --> F[5. 就绪\nOH_AudioCodec_Prepare] F --> G[6. 启动\nOH_AudioCodec_Start] G --> H[7. 循环输入 PCM\nOH_AudioCodec_PushInputBuffer] H --> I[8. 取出码流\nOH_AudioCodec_FreeOutputBuffer] I --> J{是否继续?} J -->|是| H J -->|否| K[11. 停止\nOH_AudioCodec_Stop] %% 可选分支 H -.->|9. 刷新队列| L[OH_AudioCodec_Flush] L -.-> G K -.->|10. 重置| M[OH_AudioCodec_Reset] M -.-> E K --> N[12. 销毁\nOH_AudioCodec_Destroy] N --> Z([结束])

下面给出

  1. 用 Mermaid 绘制的「音频编码状态/调用流程图」
  2. 一份可直接在 DevEco Studio 4.0(API 10+)里跑的 C++ 最小完整示例(CMake 工程),演示把 44.1 kHz/16-bit/立体声 PCM 编码为 AAC-LC(ADTS)并写入本地文件。

  1. Mermaid 图
%% 音频编码器状态机 + 调用时序(HarmonyOS AVCodec) stateDiagram-v2 [*] --> Init: OH_AudioCodec_CreateByMime(...) Init --> Configured: Configure(params) Configured --> Prepared: Prepare() Prepared --> Running: Start() Running --> Running: PushInputBuffer() ➜ FreeOutputBuffer() Running --> Flushed: Flush() %% 可选 Flushed --> Running: Start() Running --> Stopped: Stop() Stopped --> Prepared: Start() %% 可再次启动 Stopped --> Reset: Reset() %% 可选 Reset --> Configured: Configure() Running --> [*]: Destroy() Stopped --> [*]: Destroy() Reset --> [*]: Destroy()

  1. 最小可编译示例

目录结构

css 复制代码
AudioEncoderDemo/
 ├─ entry/
 │   ├─ src/main/cpp/
 │   │   ├─ native_audio_encoder.cpp (下面代码)
 │   │   └─ CMakeLists.txt
 │   └─ src/main/resources/rawfile/
 │       └─ test_44k_16bit_2ch.pcm   (原始 PCM, 任意长度)

2.1 CMakeLists.txt

cmake 复制代码
cmake_minimum_required(VERSION 3.16)
project(audio_encoder_demo)

set(CMAKE_CXX_STANDARD 17)

# HarmonyOS NDK
find_library(hilog-lib hilog_ndk.z)
find_library(native-buffer-lib native_buffer)
find_library(avcodec-lib libavcodec_base.z)

add_library(entry SHARED native_audio_encoder.cpp)
target_link_libraries(entry
        ${hilog-lib}
        ${native-buffer-lib}
        ${avcodec-lib}
        ohaudio
)

2.2 native_audio_encoder.cpp

cpp 复制代码
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <fcntl.h>

#include "napi/native_api.h"
#include "multimedia/audio_codec/audio_codec_api.h"
#include "hilog/log.h"

#undef LOG_DOMAIN
#undef LOG_TAG
#define LOG_DOMAIN 0x0001
#define LOG_TAG "AudioEncoder"

static const int SAMPLE_RATE   = 44100;
static const int CHANNEL_COUNT = 2;
static const int BIT_RATE      = 128000; // 128 kbps
static const int PCM_FRAME_SAMPLES = 1024; // AAC-LC 每帧 1024 样点
static const int PCM_FRAME_BYTES =
        PCM_FRAME_SAMPLES * CHANNEL_COUNT * sizeof(int16_t);

static OH_AVCodec *g_encoder = nullptr;
static int32_t g_fd_out = -1;          // 输出 ADTS 文件描述符
static bool g_input_done = false;

/* ---------- 工具 ---------- */
static void write_adts_header(uint8_t *buf, int frameLen) {
    const int profile = 2;     // AAC-LC
    const int freqIdx = 4;     // 44.1 kHz
    const int chanCfg = 2;     // 2 ch

    int fullLen = frameLen + 7;
    buf[0] = 0xFF;
    buf[1] = 0xF1;
    buf[2] = (profile - 1) << 6 | (freqIdx << 2) | (chanCfg >> 2);
    buf[3] = ((chanCfg & 3) << 6) | (fullLen >> 11);
    buf[4] = (fullLen >> 3) & 0xFF;
    buf[5] = ((fullLen & 7) << 5) | 0x1F;
    buf[6] = 0xFC;
}

/* ---------- 回调 ---------- */
static void OnError(OH_AVCodec *codec, int32_t errorCode, void *userData) {
    OH_LOG_ERROR(LOG_APP, "Encoder error %{public}d", errorCode);
}

static void OnOutputFormatChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData) {
    // AAC 暂未支持
}

static void OnNeedInputBuffer(OH_AVCodec *codec,
                              uint32_t index,
                              OH_AVBuffer *buffer,
                              void *userData) {
    if (g_input_done) return;

    uint8_t *addr = OH_AVBuffer_GetAddr(buffer);
    int32_t capacity = OH_AVBuffer_GetCapacity(buffer);
    ssize_t bytes = read(0, addr, capacity); // 从 stdin 读 PCM
    if (bytes <= 0) {
        OH_AudioCodec_Stop(codec);
        g_input_done = true;
        return;
    }
    OH_AVCodec_PushInputBuffer(codec, index);
}

static void OnNewOutputBuffer(OH_AVCodec *codec,
                              uint32_t index,
                              OH_AVBuffer *buffer,
                              OH_AVFormat *attr,
                              void *userData) {
    uint8_t *data = OH_AVBuffer_GetAddr(buffer);
    int32_t size  = OH_AVBuffer_GetSize(buffer);

    uint8_t adts[7];
    write_adts_header(adts, size);
    write(g_fd_out, adts, 7);
    write(g_fd_out, data, size);

    OH_AudioCodec_FreeOutputBuffer(codec, index);
}

static OH_AVCodecCallback g_callback = {
        .onError = OnError,
        .onStreamChanged = OnOutputFormatChanged,
        .onNeedInputBuffer = OnNeedInputBuffer,
        .onNewOutputBuffer = OnNewOutputBuffer,
};

/* ---------- NAPI 接口 ---------- */
static napi_value EncodeFile(napi_env env, napi_callback_info info) {
    g_encoder = OH_AudioCodec_CreateByMime(OH_AVCODEC_MIMETYPE_AUDIO_AAC, true);
    if (!g_encoder) {
        OH_LOG_ERROR(LOG_APP, "Create encoder failed");
        return nullptr;
    }

    OH_AVFormat *fmt = OH_AVFormat_Create();
    OH_AVFormat_SetIntValue(fmt, OH_MD_KEY_AUD_SAMPLE_RATE, SAMPLE_RATE);
    OH_AVFormat_SetIntValue(fmt, OH_MD_KEY_AUD_CHANNEL_COUNT, CHANNEL_COUNT);
    OH_AVFormat_SetIntValue(fmt, OH_MD_KEY_AUDIO_SAMPLE_FORMAT, SAMPLE_S16LE);
    OH_AVFormat_SetLongValue(fmt, OH_MD_KEY_BITRATE, BIT_RATE);
    OH_AVFormat_SetIntValue(fmt, OH_MD_KEY_AAC_IS_ADTS, 1); // 输出 ADTS

    OH_AudioCodec_RegisterCallback(g_encoder, &g_callback, nullptr);
    OH_AudioCodec_Configure(g_encoder, fmt);
    OH_AVFormat_Destroy(fmt);

    OH_AudioCodec_Prepare(g_encoder);

    g_fd_out = open("/data/storage/el2/base/haps/entry/files/out.aac",
                    O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (g_fd_out < 0) {
        OH_LOG_ERROR(LOG_APP, "open output failed");
        return nullptr;
    }

    OH_AudioCodec_Start(g_encoder);
    while (!g_input_done) {
        usleep(10 * 1000); // 简单阻塞等待
    }

    OH_AudioCodec_Stop(g_encoder);
    OH_AudioCodec_Destroy(g_encoder);
    close(g_fd_out);
    OH_LOG_INFO(LOG_APP, "encode done");
    return nullptr;
}

/* ---------- 注册 NAPI ---------- */
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
    napi_property_descriptor desc[] = {
        {"encodeFile", nullptr, EncodeFile, nullptr, nullptr, nullptr, napi_default, nullptr}
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "entry",
    .nm_priv = nullptr,
    .reserved = {0},
};

extern "C" __attribute__((constructor)) void RegisterEntryModule() {
    napi_module_register(&demoModule);
}

2.3 使用方式

  1. test_44k_16bit_2ch.pcm 推送到 /data/storage/el2/base/haps/entry/files/in.pcm
  2. ArkTS 侧调用:
ts 复制代码
import entry from '@ohos.entry';
entry.encodeFile();
  1. 运行后可在同目录得到 out.aac(ADTS 封装,可直接播放验证)。

至此,完整的 Mermaid 流程图 + 可跑代码实现已给出,可直接集成到 HarmonyOS 工程中。

相关推荐
whysqwhw1 小时前
鸿蒙各种生命周期
harmonyos
whysqwhw2 小时前
鸿蒙音频解码
harmonyos
whysqwhw2 小时前
鸿蒙视频解码
harmonyos
whysqwhw2 小时前
鸿蒙视频编码
harmonyos
ajassi20002 小时前
开源 Arkts 鸿蒙应用 开发(十八)通讯--Ble低功耗蓝牙服务器
华为·开源·harmonyos
前端世界3 小时前
在鸿蒙应用中快速接入地图功能:从配置到实战案例全解析
华为·harmonyos
江拥羡橙5 小时前
【基础-判断】HarmonyOS提供了基础的应用加固安全能力,包括混淆、加密和代码签名能力
安全·华为·typescript·harmonyos
前端世界16 小时前
HarmonyOS 实战:用 @Observed + @ObjectLink 玩转多组件实时数据更新
华为·harmonyos
zhanshuo17 小时前
HarmonyOS 实战:从输入框到完整表单,教你一步步搞定用户输入处理
harmonyos