跳转至

11 codec-音频编解码

作者:陈媛媛 | 最后修改:2026-10-29

一、概述

codec 核心库 是合宙的软件编解码库,可以实现不同的音频格式相互转换,以适应平台格式要求,流量控制,或者播放器的要求

1.1 audio,exaudio,codec 三个库文件的区别和联系

  1. audio,codec是核心库,可以调用的最底层的代码,exaudio 是扩展库
  2. audio 库和 exaudio 库都是实现了录音和播放,但是 exaudio 使用更加简单,去掉了历史原因造成的 audio 的冗余参数
  3. 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

注意事项

  1. 该接口需要固件编入 LUAT_USE_AUDIO_DTMF,没有编入时 codec.dtmf_decode 为 nil;
  2. 采样率必须与数据一致;8kHz 是 DTMF 的标准采样率,16kHz 也能识别但要在参数里写对;
  3. 识别质量依赖音量和信噪比,电话链路上音量偏低时可以适当放宽 detectRatio(调小)或 minConsecutive(调小);
  4. 传入空数据或无法分配的极端情况,返回的是 "" 和空表,不会报错。

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()

注意事项

  1. 同样需要固件编入 LUAT_USE_AUDIO_DTMF;
  2. 输出是单声道、PCM16LE,没有 WAV 头,需要文件播放时得自己加 44 字节头(见 audio.make_head);
  3. 电话线路的标准要求单音最短 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)            -- 关闭

注意事项

  1. 该接口只有在固件同时编入 LUAT_SUPPORT_G729 和 LUAT_USE_G729_PROFILE 时才注册,普通 G.729 固件里取到的是 nil;
  2. 它是调试工具,不要留在量产脚本里跑——统计本身有开销,而且返回的表会占内存;
  3. 每个阶段的耗时受输入信号影响很大(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固件支持列表

Air700ECH/Air780EHM/EHV/EGH/EGG/EHU/EHN固件支持列表

Air8000系列所有型号固件支持列表

Air8101系列所有型号固件支持列表

搜索