【HarmonyOS 音频编码开发速览】
文档地址:developer.huawei.com/consumer/cn...
-
能力总览
• 将任意来源的 PCM 数据编码为 AAC、FLAC、MP3、G.711μ、OPUS、AMR-NB/WB 等格式。
• 典型使用场景:录音后封装、PCM 编辑后导出文件。
• AAC 默认使用 VBR,码率可能与设定值存在偏差。
-
核心流程(12 步)
-
引入头文件。
-
创建编码器(OH_AVCodec):可按 MIME 或编解码器名称创建。
-
注册回调:
- onError、onNeedInputBuffer、onNewOutputBuffer(onStreamChanged 暂不支持)。
-
配置参数:
必须项:采样率、声道数、采样格式;
可/必须项:码率、声道布局、ADTS、Profile 等,各格式差异见表。
-
Prepare → 6) Start → 7) 逐帧输入 PCM(需按格式计算单次样点数) →
-
取出码流 → 9) FreeOutputBuffer →
10)(可选)Flush / Reset → 11) Stop → 12) Destroy。
-
-
关键参数表
• 采样率/声道数范围: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)。
-
开发注意
• 必须按顺序调用 API,否则可能异常。
• 最后一帧需置 AVCODEC_BUFFER_FLAGS_EOS。
• 调用 Flush/Reset/Stop 后需重新 Start 并可能重新输入数据。
以下 Mermaid 流程图对应「最开始回答」中的 12 步开发顺序,可直接复制到 Mermaid Live Editor 渲染。
图中:
- 实线框为「必选步骤」
- 虚线框为「可选步骤」
- 箭头旁文字为调用接口名
下面给出
- 用 Mermaid 绘制的「音频编码状态/调用流程图」
- 一份可直接在 DevEco Studio 4.0(API 10+)里跑的 C++ 最小完整示例(CMake 工程),演示把 44.1 kHz/16-bit/立体声 PCM 编码为 AAC-LC(ADTS)并写入本地文件。
- Mermaid 图
- 最小可编译示例
目录结构
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 使用方式
- 把
test_44k_16bit_2ch.pcm
推送到/data/storage/el2/base/haps/entry/files/in.pcm
- ArkTS 侧调用:
ts
import entry from '@ohos.entry';
entry.encodeFile();
- 运行后可在同目录得到
out.aac
(ADTS 封装,可直接播放验证)。
至此,完整的 Mermaid 流程图 + 可跑代码实现已给出,可直接集成到 HarmonyOS 工程中。