文章目录
-
- 前言
- [1. Opus 是什么?](#1. Opus 是什么?)
-
- [1.1 定位与标准](#1.1 定位与标准)
- [1.2 主要能力(摘自公开说明与 `opus/include/opus.h` 文档)](#1.2 主要能力(摘自公开说明与
opus/include/opus.h文档)) - [1.3 库对外 API 轮廓](#1.3 库对外 API 轮廓)
- [1.4 与本仓库 `opus/` 目录的关系](#1.4 与本仓库
opus/目录的关系)
- [2. 本工程中的 Opus 与音频业务](#2. 本工程中的 Opus 与音频业务)
-
- [2.1 目录结构(与移植相关部分)](#2.1 目录结构(与移植相关部分))
- [2.2 应用侧示例:CI1302 Opus 解码](#2.2 应用侧示例:CI1302 Opus 解码)
- [3. WS63 平台特点与移植目标](#3. WS63 平台特点与移植目标)
- [4. 编译与集成方式](#4. 编译与集成方式)
-
- [4.1 方式 A:海思 SDK / `build_component`(推荐,与本仓库 CMakeLists 一致)](#4.1 方式 A:海思 SDK /
build_component(推荐,与本仓库 CMakeLists 一致)) - [4.2 方式 B:独立 CMake 子工程 / 交叉编译](#4.2 方式 B:独立 CMake 子工程 / 交叉编译)
- [4.3 方式 C:GN(`BUILD.gn`)静态库](#4.3 方式 C:GN(
BUILD.gn)静态库)
- [4.1 方式 A:海思 SDK / `build_component`(推荐,与本仓库 CMakeLists 一致)](#4.1 方式 A:海思 SDK /
- [5. 头文件与链接检查清单](#5. 头文件与链接检查清单)
- [6. 内存与实时性建议](#6. 内存与实时性建议)
- [7. 功能验证建议](#7. 功能验证建议)
- [8. 常见问题](#8. 常见问题)
- [9. 参考链接](#9. 参考链接)
- 总结
前言
Opus 是互联网语音与音频场景中最常用的开放编解码器之一,适合 VoIP、对讲、语音播报、流媒体等。海思 WS63 一类 IoT SoC 常跑 LiteOS / OpenHarmony 衍生内核 ,RAM 与 CPU 有限,需要在「功能完整」与「体积/内存」之间做裁剪。本仓库已在项目根目录提供 opus/ 源码树,且其中的 opus/CMakeLists.txt 已按 海思 SDK 组件(build_component) 风格编写,便于与 WS63 工程链对接。
本文说明 Opus 是什么、能做什么 ,并结合本仓库布局给出在 WS63 工程 中 编译集成、链接与使用 的实操要点(含与本项目 ci1302 Opus 解码 示例的衔接)。
1. Opus 是什么?
Opus 是一款完全开放、免版税且功能强大的音频编解码器。它在互联网上的交互式语音和音乐传输方面无可匹敌,同时也适用于存储和流媒体应用。它由互联网工程任务组 (IETF) 制定为RFC 6716标准 ,该标准融合了 Skype 的 SILK 编解码器和 Xiph.Org 的 CELT 编解码器的技术。

1.1 定位与标准
- IETF RFC 6716 定义了 Opus 比特流格式与解码过程。
- 实现融合了 Skype SILK (语音)与 Xiph CELT (通用音频)两条技术路线,由 IETF Codec 工作组 标准化。
- github地址:https://github.com/xiph/opus
- 官方资料与下载:https://opus-codec.org/
1.2 主要能力(摘自公开说明与 opus/include/opus.h 文档)
| 维度 | 典型范围 |
|---|---|
| 采样率 | 8 kHz~48 kHz |
| 帧长 | 2.5 ms~60 ms(多种离散长度) |
| 声道 | 单声道、立体声,以及多流 API 下的多声道场景 |
| 码率 | 约 6 kb/s~510 kb/s(与模式/帧长有关) |
| 特性 | 语音与音乐、CBR/VBR、丢包隐藏(PLC)等 |
1.3 库对外 API 轮廓
嵌入式最常用的是 解码 (播放 TTS、网络语音)或 编码(上行录音):
- 解码 :
opus_decoder_create/opus_decoder_init、opus_decode、opus_decoder_destroy(或仅init配合静态缓冲,见下文)。 - 编码 :
opus_encoder_create、opus_encode、opus_encoder_destroy。 - 头文件入口:
opus.h(内部包含opus_types.h、opus_defines.h)。
比特流若走 RTP ,需遵守 RFC 7587 ;若存 文件 ,通常用 Ogg 封装(RFC 7845) ,需额外 opusfile / libogg 等,与本仓库「裸 Opus 帧」解码场景不同。
1.4 与本仓库 opus/ 目录的关系
- 根目录
opus/为 Xiph 官方 Opus 参考实现 的完整源码树(含celt/、silk/、dnn/等)。 - 上游版本号由
opus/cmake/OpusPackageVersion.cmake等解析;本仓库opus/CMakeLists.txt在无法读取版本时 回退为 1.5.2,与常见 HiSilicon 集成版本一致。 - 与上游唯一显著差异 :根目录
opus/CMakeLists.txt不是通用add_library(opus ...),而是为 海思build_component()准备的 组件描述 (源列表来自*_sources.mk,生成config.h)。在 非海思构建系统 中需自行等价实现(见第 4 节)。
2. 本工程中的 Opus 与音频业务
2.1 目录结构(与移植相关部分)
text
opus/
├── CMakeLists.txt # 海思 LiteOS 组件式构建(本仓库已提供)
├── cmake/
│ ├── config.h.cmake.in # 生成 config.h 的模板
│ └── ...
├── include/ # 对外头文件:opus.h 等
├── celt/、silk/、src/ # 编解码核心实现
├── *_sources.mk # 参与编译的源文件列表(由 CMakeLists 读取)
└── ...
2.2 应用侧示例:CI1302 Opus 解码
在 src/audio/ci1302/ 中,当宏 CI1302_TTS_DECODE_OPUS_TO_PCM 打开时,会编译 ci1302_opus_decode.c ,通过 opus.h 调用解码接口,将 Opus 帧解为 PCM。该实现刻意使用 opus_decoder_get_size + opus_decoder_init + 静态缓冲 ,避免在 堆很小的 LiteOS 上频繁 opus_decoder_create 导致 OPUS_ALLOC_FAIL。
未链接真实 libopus 时,可用 ci1302_opus_decoder_weak_stub.c 提供弱符号占位,便于分阶段联调。
结论 :WS63 上「移植 Opus」在工程层面的落点是:把 opus/ 编成静态库并链到最终镜像 ,保证 #include "opus.h" 与链接符号解析成功。
3. WS63 平台特点与移植目标
- WS63 为海思面向连接类 IoT 的芯片平台,软件栈常见 LiteOS-M + CMSIS-RTOS2 + 厂商驱动 ,构建可能是 GN(OpenHarmony 系) 或 CMake +
build_component。 - 无 MMU 或资源紧张 时,建议:
- 使用与本仓库
opus/CMakeLists.txt一致的OPUS_DISABLE_INTRINSICS等选项,避免依赖难以在 MCU 上统一验证的 SIMD/内联汇编路径; - 解码器状态尽量 静态分配或可控堆 ,避免与 WiFi 等任务抢堆导致
LOS_TaskCreate失败 (与ci1302_opus_decode.c注释一致)。
- 使用与本仓库
4. 编译与集成方式
CMakeLists文件:
bash
#===============================================================================
# @brief cmake file --- Opus 静态库(LiteOS / HiSilicon SDK build_component 集成)
# @details 风格对齐 open_source/lvgl/CMakeLists.txt;源文件列表与上游 silk/celt/opus 的 .mk 保持一致。
# 与 ESP-IDF 工程(如 xiaozhi-esp32/main/CMakeLists.txt 中 esp_audio_codec 链 libopus)
# 为两套集成方式,本文件仅用于 sdkv106 中 LiteOS 组件构建。
# Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
#===============================================================================
set(COMPONENT_NAME "opus")
set(CMAKE_OPUS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
list(APPEND CMAKE_MODULE_PATH "${CMAKE_OPUS_SOURCE_DIR}/cmake")
include(OpusFunctions)
include(OpusPackageVersion)
get_package_version(PACKAGE_VERSION PROJECT_VERSION)
if(NOT PACKAGE_VERSION OR PACKAGE_VERSION STREQUAL "0")
set(PACKAGE_VERSION "1.5.2")
endif()
configure_file(
${CMAKE_OPUS_SOURCE_DIR}/cmake/config.h.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/config.h
@ONLY)
get_opus_sources(SILK_SOURCES ${CMAKE_OPUS_SOURCE_DIR}/silk_sources.mk silk_sources_rel)
get_opus_sources(SILK_SOURCES_FLOAT ${CMAKE_OPUS_SOURCE_DIR}/silk_sources.mk silk_sources_float_rel)
get_opus_sources(CELT_SOURCES ${CMAKE_OPUS_SOURCE_DIR}/celt_sources.mk celt_sources_rel)
get_opus_sources(OPUS_SOURCES ${CMAKE_OPUS_SOURCE_DIR}/opus_sources.mk opus_sources_rel)
get_opus_sources(OPUS_SOURCES_FLOAT ${CMAKE_OPUS_SOURCE_DIR}/opus_sources.mk opus_sources_float_rel)
set(SOURCES "")
foreach(src ${silk_sources_rel} ${silk_sources_float_rel} ${celt_sources_rel} ${opus_sources_rel}
${opus_sources_float_rel})
list(APPEND SOURCES "${CMAKE_OPUS_SOURCE_DIR}/${src}")
endforeach()
set(PUBLIC_HEADER
${CMAKE_OPUS_SOURCE_DIR}/include)
set(PRIVATE_HEADER
${CMAKE_OPUS_SOURCE_DIR}
${CMAKE_OPUS_SOURCE_DIR}/celt
${CMAKE_OPUS_SOURCE_DIR}/silk
${CMAKE_OPUS_SOURCE_DIR}/silk/float
${CMAKE_CURRENT_BINARY_DIR})
set(COMPONENT_PUBLIC_CCFLAGS)
set(COMPONENT_CCFLAGS
-Wno-jump-misses-init
-Wno-error=unused-parameter
-Wno-error=sign-compare
)
# 与上游 CMake 默认接近:C99 变长数组、关闭 SIMD/内联汇编路径(适合 Cortex-M 等无 x86/NEON 运行时检测环境)
# HAVE_LRINT / HAVE_LRINTF:避免 celt/float_cast.h 走 #else 里的 #warning(与 cmake/OpusConfig.cmake 中 check_symbol_exists 一致)
set(PRIVATE_DEFINES
HAVE_CONFIG_H
OPUS_BUILD
VAR_ARRAYS
OPUS_DISABLE_INTRINSICS
DISABLE_DEBUG_FLOAT
ENABLE_HARDENING
HAVE_LRINT
HAVE_LRINTF)
set(WHOLE_LINK
true
)
set(MAIN_COMPONENT
false
)
set(LIB_OUT_PATH ${BIN_DIR}/${CHIP}/libs/${TARGET_COMMAND})
build_component()
4.1 方式 A:海思 SDK / build_component(推荐,与本仓库 CMakeLists 一致)
若父工程已支持海思 build_component() (与 open_source 中 LVGL、opus 等组件相同模式):
- 将
opus/置于 SDK 约定的open_source/opus或工程内等价路径。 - 确保
opus/CMakeLists.txt被组件扫描到,configure_file能在CMAKE_CURRENT_BINARY_DIR生成config.h。 PRIVATE_DEFINES(本仓库已设)通常包括:HAVE_CONFIG_H、OPUS_BUILDVAR_ARRAYS(C99 变长数组,需工具链支持)OPUS_DISABLE_INTRINSICSDISABLE_DEBUG_FLOAT、ENABLE_HARDENINGHAVE_LRINT、HAVE_LRINTF(与float_cast.h行为一致,避免多余 warning)
PUBLIC_HEADER指向opus/include,业务模块 include 目录 增加该路径(或拷贝生成的config.h可见路径)。- 最终 静态库 与
WHOLE_LINK等由build_component()处理,应用链接时-lopus或等价组件依赖 即可。
4.2 方式 B:独立 CMake 子工程 / 交叉编译
若需在 PC 上先验证 ARM 静态库:
bash
cd opus
mkdir build && cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE=/path/to/arm-none-eabi.cmake \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=OFF \
-DOPUS_BUILD_PROGRAMS=OFF \
-DOPUS_BUILD_TESTING=OFF
cmake --build .
说明:上游 官方 opus/CMakeLists.txt(若与本文档「海思定制版」不同)选项名可能为 OPUS_FIXED_POINT 、OPUS_FLOAT_APPROX 等,需以 当前树内 CMakeLists.txt 与 cmake/OpusConfig.cmake 为准。本仓库根目录 opus/CMakeLists.txt 已 不采用 通用 add_library,直接 cmake .. 可能需改用 官方 release 包 或 自行写 add_library 包装 *_sources.mk。
4.3 方式 C:GN(BUILD.gn)静态库
当前 xiaohong 的 BUILD.gn 若尚未列出 opus 源文件,可新增 static_library("opus"):
sources:需与silk_sources.mk、celt_sources.mk、opus_sources.mk及 float 列表一致,或写脚本从.mk生成 GN 列表(维护成本较高)。defines:与opus/CMakeLists.txt中PRIVATE_DEFINES对齐,并保证config.h在 include 路径中(可预生成后include_dirs += ["//path/to/opus/build_gen"])。include_dirs:opus、opus/celt、opus/silk、opus/silk/float、生成config.h的目录。
5. 头文件与链接检查清单
| 步骤 | 说明 |
|---|---|
| 头文件 | 添加 opus/include ;若编译报 config.h missing ,检查是否执行了 configure_file 或手动放置生成结果。 |
| 符号 | 确认最终 elf/map 中含 opus_decode 、opus_decoder_init 等,而非仅弱符号桩。 |
| 重复定义 | 同时链 SDK 自带 opus 与本仓库 opus 会冲突,应 二选一。 |
| 浮点 | WS63 工具链若使用 硬浮点 ABI ,需全工程一致;Opus 本树含 float 路径源 (见 *_float_rel),与 DISABLE_DEBUG_FLOAT 等宏共同决定行为。 |
6. 内存与实时性建议
- 解码器状态 :
opus_decoder_get_size(channels)给出 单实例 所需字节数;单声道常见约 十几 KB 量级 (随版本与宏略变),ci1302_opus_decode.c使用 20 KiB 静态区 并opus_decoder_init,避免堆碎片。 - 栈 :
opus_decode内部有一定栈深度,解码任务栈 不宜过小(建议 ≥ 2~4 KB 起步,以实测为准)。 - CPU :48 kHz、20 ms 帧在 无 NEON 的 Cortex-M 上仍可能占用可观 MIPS,需在目标板上 profiling。
- DNN / LPCNet :新版 Opus 含 深度学习扩展(如 DRED) ,嵌入式若不需要,应通过 源列表 / CMake 选项 关闭相关编译单元以减体积(具体以当前
opus_sources.mk与构建脚本为准)。
7. 功能验证建议
- 单元级 :在板端调用
opus_decoder_init→opus_decode,输入 已知 Opus 测试帧 (可由 PCopus_demo生成),检查 返回值是否为样本数、PCM 是否合理。 - 业务级 :打开
CI1302_TTS_DECODE_OPUS_TO_PCM,走 CI1302 TTS 管线 ,听音并观察 CPU 占用与欠载。 - 异常 :若返回
OPUS_ALLOC_FAIL (-7),优先检查 堆与静态缓冲策略 ,与ci1302_opus_decode.c设计对齐。
8. 常见问题
| 现象 | 可能原因 | 处理方向 |
|---|---|---|
找不到 config.h |
未运行 configure_file 或未加入 include 路径 |
按 opus/CMakeLists.txt 生成并包含 CMAKE_CURRENT_BINARY_DIR |
| 链接 undefined reference | 未链 opus 静态库或源列表不全 | 检查 WHOLE_LINK 、GN deps |
| 弱符号桩一直打印 | 未链真实 libopus | 移除桩或调整链接顺序,确保强符号覆盖 |
| 解码杂音/失败 | 采样率/声道与流不一致 | opus_decoder_init 的 Fs、channels 与编码端一致 |
| RAM 暴涨 | 多路 create 或堆分配过多 |
单实例 + opus_decoder_ctl 限制 + 静态缓冲 |
9. 参考链接
- Opus 官网:https://opus-codec.org/
- RFC 6716:https://www.rfc-editor.org/rfc/rfc6716
- RTP 封装 RFC 7587:https://www.rfc-editor.org/rfc/rfc7587
- Ogg Opus RFC 7845:https://www.rfc-editor.org/rfc/rfc7845
- 上游仓库(GitLab):https://gitlab.xiph.org/xiph/opus
- 本仓库
opus/cmake/README.md:通用 CMake 构建说明(与海思组件 CMake 并存时以 根目录opus/CMakeLists.txt为准)
总结
Opus 是 RFC 标准化的低延迟音频编解码库,适合 WS63 上的语音对讲、播报与网络音频。本仓库 opus/ 已含 完整源码 及 面向海思 LiteOS build_component 的 CMakeLists.txt ,移植核心是:生成 config.h、编译全量 mk 所列源文件、导出 include、与业务(如 ci1302_opus_decode.c)正确链接 。在 RAM 紧张 场景下,优先采用 opus_decoder_init + 静态工作区 ,并控制 仅链接一份 Opus 、避免与 SDK 重复集成。按第 7 节在板端做 解码与听音验证 后,再投入量产参数(帧长、码率、任务优先级)。