11 codec-音频编解码
作者:陈媛媛 | 最后修改:2026-10-29
一、概述
codec 核心库 是合宙的软件编解码库,可以实现不同的音频格式相互转换,以适应平台格式要求,流量控制,或者播放器的要求
1.1 audio,exaudio,codec 三个库文件的区别和联系
- audio,codec是核心库,可以调用的最底层的代码,exaudio 是扩展库
- audio 库和 exaudio 库都是实现了录音和播放,但是 exaudio 使用更加简单,去掉了历史原因造成的 audio 的冗余参数
- codec 库 是实现了音频数据的编码和解码,不会对音频直接播放,audio 和 exaudio 可以对编解码后的音频进行播放
二、核心示例
1、核心示例是指:使用本库文件提供的核心 API,开发的基础业务逻辑的演示代码;
2、核心示例的作用是:帮助开发者快速理解如何使用本库,所以核心示例的逻辑都比较简单;
3、更加完整和详细的 demo,请参考 LuatOS 仓库 中各个产品目录下的相关示例:demo/codec:
-- 解码
local decoder = codec.create(codec.MP3, true) -- 初始化解码器
codec.info(decoder ,"/luadb/1.mp3") --需要解码的文件解析
local decode_buffer = zbuff.create(54594) --创建一个可以存放音频的缓存
local decode_result = codec.data(decoder , decode_buffer,4096) -- 使用默认编码等级0
codec.release(decoder ) -- 关闭解码器并释放资源
-- 编码
local encoder= codec.create(codec.AMR,false) -- 初始化编码器
local encode_result = codec.encode(encoder, in_buffer, out_buffer, 0) -- 开始编码
codec.release(encoder) -- 关闭编码器并释放资源
三、常量详解
核心库常量,顾名思义是由合宙 LuatOS 内核固件中定义的、不可重新赋值或修改的固定值,在脚本代码中不需要声明,可直接调用;
| 常量 | 类型 | 解释 | 参数示例 |
|---|---|---|---|
| codec.MP3 | number | MP3格式,仅支持解码 | local decoder = codec.create(codec.MP3, true) -- 初始化解码器 |
| codec.AMR | number | AMR_NB格式,支持编解码 | local encoder = codec.create(codec.AMR, false, 7) |
| codec.AMR_WB | number | AMR_WB格式,仅在支持volte 的固件支持编解码 | local encoder = codec.create(codec.AMR_WB, false, 8)--创建一个amr-wb的encoder,编码等级默认8 |
| codec.ULAW | number | G711 μ-law格式 | local encoder = codec.create(codec.ALAW, false) --开启G711 μ-law 编码器 |
| codec.ALAW | number | G711 A-law格式 | local encoder = codec.create(codec.ALAW, false) --开启G711 A-law 编码器 |
| codec.WAV | number | WAV格式,本质就是 PCM 数据,不需要编解码 | local decoder = codec.create(codec.WAV, true) -- 直接按 WAV 头解析 |
| codec.OPUS | number | OPUS格式,需固件编入 LUAT_SUPPORT_OPUS | local encoder = codec.create(codec.OPUS, false) |
| codec.OGG_OPUS | number | OGG 封装的 OPUS,源码里该项为 NULL,暂未实现 | codec.create(codec.OGG_OPUS, true) -- 当前返回 nil |
| codec.SPEEX_NB | number | Speex 窄带(8kHz),需 LUAT_SUPPORT_SPEEX | local encoder = codec.create(codec.SPEEX_NB, false) |
| codec.SPEEX_WB | number | Speex 宽带(16kHz),需 LUAT_SUPPORT_SPEEX | local encoder = codec.create(codec.SPEEX_WB, false) |
| codec.SPEEX_UWB | number | Speex 超宽带(32kHz),需 LUAT_SUPPORT_SPEEX | local encoder = codec.create(codec.SPEEX_UWB, false) |
| codec.G729 | number | G.729A/B格式,8kHz,10 字节/帧,需 LUAT_SUPPORT_G729 | local encoder = codec.create(codec.G729, false, 1) -- 1=Annex B |
| codec.G722 | number | G.722格式,16kHz,20ms 帧:640 字节 PCM ↔ 320 字节码流,需 LUAT_SUPPORT_G722 | local encoder = codec.create(codec.G722, false, 0) -- 0=64000bps |
| codec.VDDA_3V3 | number | codec 供电电压 3.3V(枚举值 0) | -- 供 BSP/音频驱动配置 codec 电源电压时使用 |
| codec.VDDA_1V8 | number | codec 供电电压 1.8V(枚举值 1) | -- 供 BSP/音频驱动配置 codec 电源电压时使用 |
四、函数详解
4.1 codec.create(type, isDecoder, quality)
功能
创建编解码器
参数
type
参数含义:需要编解码的目标格式;
数据类型:number;
取值范围:codec.MP3,codec.AMR,codec.AMR_WB,codec.ULAW,codec.ALAW,codec.WAV,codec.G729,codec.G722(部分固件还支持 codec.OPUS,codec.SPEEX_NB,codec.SPEEX_WB,codec.SPEEX_UWB);
是否必选:必须传入此参数;
注意事项:
1. 编码支持codec.AMR,codec.AMR_WB,codec.ULAW,codec.ALAW;
2. 解码支持codec.MP3,codec.AMR,codec.AMR_WB,codec.ULAW,codec.ALAW,codec.G729,codec.G722;
3. codec.WAV 是 PCM 数据加文件头,不需要真正编解码,创建后可直接用 codec.data 取数据;
4. codec.G729 / codec.G722 的第三个参数含义与 AMR 完全不同:
G729 编码器 0=仅 Annex A(固定 10 字节/帧)、1=启用 Annex B VAD/DTX(输出 0/2/10 字节变长帧);
G729 解码器 0=裸 10 字节帧流、1=长度前缀容器(每帧 1 字节长度 + 负载);
G722 0=64000bps(默认)、1=56000bps、2=48000bps,默认不能沿用 AMR 的 7;
5. codec.OPUS / codec.SPEEX_NB / codec.SPEEX_WB / codec.SPEEX_UWB / codec.G729 / codec.G722 只有在固件编入了对应宏
(LUAT_SUPPORT_OPUS / LUAT_SUPPORT_SPEEX / LUAT_SUPPORT_G729 / LUAT_SUPPORT_G722)时才注册,
脚本里取到 nil 就说明当前固件没编这个格式;codec.OGG_OPUS 在源码支持表里是 NULL,暂无实现;
参数示例:codec.AMR,编码或者解码为codec.AMR 格式的音频 ;
isDecoder
参数含义:是否为解码器;
数据类型:boolean;
取值范围:true,false;
是否必选:必须传入此参数;
注意事项:true为解码器,false为编码器;
参数示例:true 表示解码器;
quality
参数含义:编码等级;
数据类型:number;
取值范围:0-7或0-8;
是否必选:非必须传入此参数;
注意事项:
1. 当codec.create 的 type 为codec.AMR,取值范围是 0~7 值越大消耗的空间越多(0,2.11MB/小时,7,5.49MB/小时),音质越高,默认0
2. 当codec.create 的 type 为codec.AMR_WB,取值范围是 0~8,值越大消耗的空间越多(0,2.97MB/小时,7,10.73MB/小时),音质越高,默认0
3. 默认为0,如果填入此值,则必须和codec.encode的**quality**保持一致
参数示例:0,音频质量为最差;
返回值
参数含义:codec.create 的返回值;
数据类型:userdata 或 nil;
取值范围:对象;
是否必选:必须传入此参数;
注意事项:如果创建失败,则返回nil;
示例
-- 创建解码器
local decoder = codec.create(codec.MP3)--创建一个mp3的decoder
-- 创建编码器
local encoder = codec.create(codec.AMR, false)--创建一个amr的encoder
-- 创建编码器
local encoder = codec.create(codec.AMR_WB, false, 8)--创建一个amr-wb的encoder,音频等级为8
4.2 codec.info(decoder, file_path)
功能
从文件中解析出音频信息,仅仅在解码模式下可用
参数
decoder
参数含义:使用的解码器对象(codec.create 返回值);
数据类型:userdata;
取值范围:无特别限制;
是否必选:必须传入此参数;
注意事项:codec.create 必须使用解码模式
参数示例:无 ;
file_path
参数含义:音频文件路径;
数据类型:string;
取值范围:无特别限制,
是否必选:必须传入此参数;
注意事项:该文件必须存在,并且没被其他task 占用
参数示例:"/luadb/xxx.mp3",xxx.mp3 音频信息 ;
返回值
local result, audio_format, num_channels, sample_rate, bits_per_sample, is_signed= codec.info(decoder, file_path)
result
参数含义:是否解析成功;
数据类型:boolean;
取值范围:true,false;
是否必选:必须接收此参数;
注意事项:true 表示解析成功,false 表示解析失败
audio_format
参数含义:音频格式;
数据类型:number;
取值范围:1.pcm, 2.mp3, 4.amr_nb, 5.amr_wb, 6.G711 ULAW, 7.G711 ALAW;
是否必选:必须接收此参数;
num_channels
参数含义:声音通道数;
数据类型:number;
取值范围:>0;
是否必选:必须接收此参数;
sample_rate
参数含义:采样频率;
数据类型:number;
取值范围:8000,16000,24000,32000,48000,20050,44100;
是否必选:必须接收此参数;
is_signed
参数含义:是否有符号;
数据类型:boolean;
取值范围:true:有符号,false:无符号;
是否必选:必须接收此参数;
local result, audio_format, num_channels, sample_rate, bits_per_sample, is_signed= codec.info(coder, "xxx")
4.3 codec.data(decoder, out_buff,size)
功能
从文件中解析出原始音频数据,比如从 MP3 文件里解析出 PCM 数据,这里的文件路径已经在 codec.info 传入,不需要再次传入
参数
decoder
参数含义:使用的解码器对象(codec.create 返回值);
数据类型:userdata;
取值范围:无特别限制;
是否必选:必须传入此参数;
注意事项:codec.create 必须使用解码模式
参数示例:无 ;
out_buff
参数含义:存放输出数据的zbuff;
数据类型:zbuff;
取值范围:zbuff 的 空间需要大于等于16K(过小的数据可能导致解码后播放不流畅),;
是否必选:必须传入此参数;
注意事项:无特别要求
size
参数含义:最少解码出多少字节的音频数据;
数据类型:number;
取值范围:> 0 ;
是否必选:非必须传入此参数;
注意事项:默认值为16384
返回值
成功 true, 失败 false
示例
local buff = zbuff.create(16*1024)
local result = codec.data(coder, buff,4096)
4.4 codec.encode(encode, in_buffer, out_buffer, quality)
功能
编码音频数据
参数
encode
参数含义:使用的编码器对象(codec.create 返回值);
数据类型:userdata;
取值范围:无特别限制;
是否必选:必须传入此参数;
注意事项:codec.create 必须使用编码模式
参数示例:无 ;
in_buffer
参数含义:需要编码的源数据;
数据类型:zbuff;
取值范围:无特别限制;
是否必选:必须传入此参数;
注意事项:无
参数示例:无 ;
out_buffer
参数含义:编码后输出数据的位置;
数据类型:zbuff;
取值范围:无特别限制;
是否必选:必须传入此参数;
注意事项:自动添加到buff的尾部,如果空间大小不足,会自动扩展,但是会额外消耗时间,甚至会失败,所以尽量一开始就给足空间
参数示例:无 ;
quality
参数含义:音频质量等级;
数据类型:int;
取值范围:0~7 或者0~8;
是否必选:非必须传入此参数;
注意事项:
1. 当codec.create 的 type 为codec.AMR,取值范围是 0~7 值越大消耗的空间越多(0,2.11MB/小时,7,5.49MB/小时),音质越高,默认0
2. 当codec.create 的 type 为codec.AMR_WB,取值范围是 0~8,值越大消耗的空间越多(0,2.97MB/小时,7,10.73MB/小时),音质越高,默认0
3. 默认为0,如果填入此值,则必须和codec.create 的quality 保持一致
参数示例:0,音频质量为最差;
返回值
成功 true, 失败 false
示例
codec.encode(amr_coder, inbuf, outbuf)
4.5 codec.release(coder)
功能
释放创建的编解码器
参数
decoder
参数含义:编解码器的对象(codec.create 返回值);
数据类型:userdata;
取值范围:无特别限制;
是否必选:必须传入此参数;
注意事项:需要先用codec.create创建对象
参数示例:无 ;
返回值
成功 true, 失败 false
示例
codec.release(coder)
4.6 codec.dtmf_decode(pcm, sample_rate, opts)
功能
把一段 PCM16LE 音频解码成 DTMF 按键序列(电话拨号音识别)。常用于通话录音里提取对方按了哪些键、或者自测 DTMF 编解码链路。
参数
pcm
参数含义:待解码的 PCM 数据;
数据类型:string(PCM16LE 原始字节流)、zbuff、table(样本数组,每项一个 -32768~32767 的整数);
取值范围:任意长度的 PCM16LE 数据;
是否必选:必须传入此参数;
注意事项:三种入参形式都支持,zbuff 会按已用长度读取,table 会逐项转成 int16;
参数示例:录音拿到的 PCM、codec.dtmf_encode 的返回值;
sample_rate
参数含义:PCM 采样率;
数据类型:number;
取值范围:常见 8000、16000;
是否必选:可选,默认 8000;
注意事项:必须和 PCM 数据本身的采样率一致,不一致会导致识别不出或乱码;
参数示例:8000;
opts
参数含义:解码参数表,一般不用传;
数据类型:table;
取值范围:见下方字段说明;
是否必选:可选;
注意事项:
frameMs 分析帧长(ms),默认 40
stepMs 滑动步进(ms),不传时等于 frameMs,默认 40
detectRatio 主频能量相对门限,默认 5(内部按 Q10 定点 5×1024 保存)
powerRatio 能量比值门限,默认 2.5(Q10 定点 2560)
twistDb 双频幅度差容限(dB),整数,范围 0~12,默认 6
minConsecutive 至少连续命中多少帧才算一个按键,默认 2
参数示例:{frameMs = 40, stepMs = 40}
返回值
local seq, events = codec.dtmf_decode(pcm, 8000, {frameMs = 40, stepMs = 40})
seq
含义说明:解码出的 DTMF 序列,按出现顺序拼接,例如 "123#";
没有识别到任何按键时返回空字符串 "";
数据类型:string;
events
含义说明:逐事件明细,每一项是一个 table,字段如下:
symbol 识别到的字符(1 个字符)
startSample 该按键在 PCM 中的起始样本序号
endSample 该按键在 PCM 中的结束样本序号
frames 该按键连续命中的帧数
数据类型:table(数组,下标从 1 开始);
示例
-- 先编码一段按键音,再解码回来,验证链路是否通
local pcm = codec.dtmf_encode("123#", 8000, {toneMs = 100, pauseMs = 50})
local seq, events = codec.dtmf_decode(pcm, 8000, {frameMs = 40, stepMs = 40})
log.info("dtmf", seq, #events) -- 预期 123# 与 4 个事件
for i, e in ipairs(events) do
log.info("dtmf", i, e.symbol, e.startSample, e.endSample, e.frames)
end
注意事项
- 该接口需要固件编入
LUAT_USE_AUDIO_DTMF,没有编入时codec.dtmf_decode为 nil; - 采样率必须与数据一致;8kHz 是 DTMF 的标准采样率,16kHz 也能识别但要在参数里写对;
- 识别质量依赖音量和信噪比,电话链路上音量偏低时可以适当放宽
detectRatio(调小)或minConsecutive(调小); - 传入空数据或无法分配的极端情况,返回的是
""和空表,不会报错。
4.7 codec.dtmf_encode(seq, sample_rate, opts)
功能
把一串 DTMF 字符编码成 PCM16LE 音频,用来做“自动拨号音”“按键音回放”或者自测解码链路。
参数
seq
参数含义:要编码的 DTMF 字符序列;
数据类型:string;
取值范围:0-9、A-D、*、#;
是否必选:必须传入此参数;
注意事项:只认上述字符,传其它字符会编不出对应的双音;
参数示例:"123#","*0#A";
sample_rate
参数含义:输出 PCM 的采样率;
数据类型:number;
取值范围:常见 8000、16000;
是否必选:可选,默认 8000;
注意事项:要和后续播放/解码的采样率保持一致;
参数示例:8000;
opts
参数含义:编码参数表,一般不用传;
数据类型:table;
取值范围:见下方字段说明;
是否必选:可选;
注意事项:
toneMs 每个按键音的持续时长(ms),默认 100
pauseMs 两个按键音之间的静默时长(ms),默认 50
amplitude 幅度,默认 0.7;
传 0~1 之间的小数时按比例处理(0.7 = 满幅的 70%);
传大于 1 的数值时按 Q15 绝对值处理(最大 32767);
参数示例:{toneMs = 100, pauseMs = 50, amplitude = 0.7}
返回值
local pcm = codec.dtmf_encode("123#", 8000, {toneMs = 100, pauseMs = 50, amplitude = 0.7})
pcm
含义说明:编码后的 PCM16LE 音频数据,可以直接送 audio 播放,或者喂给 codec.dtmf_decode 自测;
数据类型:zbuff(用 #pcm 取长度,pcm:get() 取原始字节);
示例
-- 生成 100ms 按键音 + 50ms 间隔的 "123#",8kHz 单声道
local pcm = codec.dtmf_encode("123#", 8000, {toneMs = 100, pauseMs = 50, amplitude = 0.7})
log.info("dtmf", "PCM 字节数", #pcm)
-- 需要直接播放时,把 PCM 转成字符串再交给音频接口
local raw = pcm:get()
注意事项
- 同样需要固件编入
LUAT_USE_AUDIO_DTMF; - 输出是单声道、PCM16LE,没有 WAV 头,需要文件播放时得自己加 44 字节头(见
audio.make_head); - 电话线路的标准要求单音最短 40ms,
toneMs不要小于 40,否则对端识别不到。
4.8 codec.g729prof(on, reset)
功能
G.729A 编解码器的逐阶段周期剖析器(profile)。打开后,每跑一段编解码就能读到各阶段累计的 CPU 周期数和调用次数,用来定位 G.729 的耗时热点。
参数
on
参数含义:开关统计;
数据类型:boolean;
取值范围:true = 启用并清零统计;false = 关闭并清零;不传 = 只读取当前统计;
是否必选:可选;
注意事项:传 true 时内部会先把计数复位,所以每次测量前传一次 true 即可;
参数示例:true;
reset
参数含义:只清零计数器,保持当前启用状态不变;
数据类型:boolean;
取值范围:true = 只清零;
是否必选:可选;
注意事项:只想重新计时、不想改开关状态时用这个,等价于“保持状态再清零一次”;
参数示例:true;
返回值
local st = codec.g729prof()
st
含义说明:读取时返回统计表,其余情况返回 nil;
st.enabled boolean,当前是否启用
st.slots number,阶段数量
st["enc.lpc"] / st["dec.xxx"] … 每个阶段一条记录 {ticks = 累计周期数, calls = 调用次数}
数据类型:nil | table;
示例
codec.g729prof(true) -- 启用并清零,然后跑若干帧编解码
-- ... 这里是编解码业务代码 ...
local st = codec.g729prof() -- 读取统计
log.info("prof", "enabled", st.enabled, "slots", st.slots)
for name, v in pairs(st) do
if type(v) == "table" then
log.info("prof", name, v.ticks, v.calls)
end
end
codec.g729prof(false) -- 关闭
注意事项
- 该接口只有在固件同时编入
LUAT_SUPPORT_G729和LUAT_USE_G729_PROFILE时才注册,普通 G.729 固件里取到的是 nil; - 它是调试工具,不要留在量产脚本里跑——统计本身有开销,而且返回的表会占内存;
- 每个阶段的耗时受输入信号影响很大(VAD 打开时静音帧走的分支不同),对比数据时要用同一段音频。
五、产品支持说明
不同产品的LuatOS固件,对codec核心库的支持情况如下:
1、Air780EX2/Air700ECP/Air780EPM/Air780EGP/Air8101系列 不支持;
2、Air700ECH/Air780EHM/EHV/EGH/EGG/EHU/EHN/Air8000系列 1-10/101-110号、13/113号、15/115号固件支持;
各产品固件支持核心库列表详细说明参考下面链接:
Air780EX2/Air700ECP/Air780EPM/Air780EGP固件支持列表