当前位置: 首页 > news >正文

如何写一个WebRTC ACE音频应用处理模块

编写源代码:

// 标准输入输出头文件,提供printf等函数
#include <stdio.h>
// 文件控制操作头文件,提供open、fcntl等函数
#include <fcntl.h>
// UNIX标准函数头文件,提供read、write、close等函数
#include <unistd.h>
// 文件状态头文件,提供stat、fstat等函数
#include <sys/stat.h>
// 基本系统数据类型头文件
#include <sys/types.h>
// WebRTC音频处理主头文件
#include "audio_processing.h"
// WebRTC通用类型定义头文件
#include <module_common_types.h>
// 本地WebRTC AEC头文件
#include "webrtc_aec.h"
​
// 功能宏定义,启用各个音频处理模块
#define WEBRTC_AEC  // 启用回声消除
#define WEBRTC_HP   // 启用高通滤波
#define WEBRTC_LE   // 启用电平估计
#define WEBRTC_NS   // 启用噪声抑制
#define WEBRTC_VAD  // 启用语音活动检测
​
/* WebRTC支持参数说明:* 采样率: 8KHZ 和 16KHZ* 声道数: 1 (单声道)* 位深度: 16 bit* 帧时长: 10 ms* */
​
// 全局变量存储音频参数 - 使用单例模式思想管理全局状态
static unsigned int webrtc_sample_rate = 0;      // 采样率
static unsigned int webrtc_sample_bits = 0;      // 位深度
static unsigned int webrtc_sample_time_ms = 0;   // 帧时长(毫秒)
static unsigned int webrtc_sample_channel = 0;   // 声道数
​
using namespace webrtc;  // 使用WebRTC命名空间
​
// AEC设备参数结构体 - 封装帧相关参数
typedef struct {unsigned int frame_len;  // 帧长度(字节数)int time;                // 时间参数
} AEC_DEV;
AEC_DEV webrtc_dev;  // 全局AEC设备实例
​
bool webrtc_enable = false;  // WebRTC使能标志,控制模块状态
​
// WebRTC音频处理核心对象指针 - 使用工厂模式创建
AudioProcessing *webrtc_apm = NULL;        // 音频处理管理器
AudioFrame *webrtc_far_frame = NULL;       // 远端帧(扬声器数据)
AudioFrame *webrtc_near_frame = NULL;      // 近端帧(麦克风数据)
​
/*** 初始化WebRTC音频处理模块* 功能: 配置音频参数,创建处理对象,启用各个处理模块* 性能: 一次性初始化,运行时开销小,但创建对象较耗时* 设计模式: 工厂模式 + 单例模式思想* * @param rate 采样率指针(支持8000/16000Hz)* @param channel 声道数指针(强制为1)* @param bits 位深度指针(强制为16bit)* @param time 帧时长(毫秒)* @return 成功返回0,失败返回-1*/
int tang_apm_init(unsigned int *rate, unsigned int *channel, unsigned int *bits, unsigned int time)
{int32_t sample_rate_hz = 0;                    // 采样率(Hz)int num_render_channels = 0;                   // 渲染声道数int num_capture_input_channels = 0;            // 采集输入声道数int num_capture_output_channels = 0;           // 采集输出声道数
​printf("webrtc aec ingenic_apm_init !\n");// 单例检查:防止重复初始化if (webrtc_enable == true) {printf("webrtc aec has initted!\n");return 0;}
​// 参数验证和强制转换 - 策略模式:参数适配if ((*rate != 8000) && (*rate != 16000)) {printf("Error: webrtc not support this rate: %d, force to 8KHZ\n", *rate);*rate = 8000;  // 不支持的采样率强制设为8KHz}
​if (*channel != 1) {printf("Error: webrtc not support this channel: %d, force to 1 channel !!\n", *channel);*channel = 1;  // 强制单声道}if (*bits != 16) {printf("Error: webrtc not support this bits: %d, force to 16 bits\n", *bits);*bits = 16;  // 强制16bit}
​// 参数赋值 - 数据封装webrtc_sample_rate = sample_rate_hz = *rate;webrtc_sample_channel = num_capture_output_channels = num_capture_input_channels = num_render_channels = *channel;webrtc_sample_bits = *bits;webrtc_sample_time_ms = time;
​// 创建远端音频帧对象 - 工厂方法webrtc_far_frame = new AudioFrame();if(!webrtc_far_frame) {printf("webrtc_far_frame new erro\n");return -1;}
​// 创建近端音频帧对象 - 工厂方法webrtc_near_frame = new AudioFrame();if(!webrtc_far_frame) {  // 注意:这里应该是检查webrtc_near_frameprintf("webrtc_near_frame new erro\n");delete webrtc_far_frame;  // 资源清理return -1;}
​// 计算帧长度:采样率 × 时间(秒) × 声道数 × 字节数 per samplewebrtc_dev.frame_len = webrtc_sample_rate * webrtc_sample_time_ms *webrtc_sample_channel * (webrtc_sample_bits / 8) / 1000;webrtc_dev.time = 1;
​// 创建音频处理管理器 - 工厂模式webrtc_apm = AudioProcessing::Create(0);if(webrtc_apm == NULL) {printf("AudioProcessing::Create() error !\n");delete webrtc_near_frame;delete webrtc_far_frame;return -1;}
​// 配置基础音频参数webrtc_apm->set_sample_rate_hz(sample_rate_hz);                           // 设置采样率webrtc_apm->set_num_reverse_channels(num_render_channels);                // 设置反向声道数webrtc_apm->set_num_channels(num_capture_input_channels, num_capture_output_channels);  // 设置输入输出声道数
​
// 配置各个音频处理模块 - 装饰器模式:动态添加功能
#ifdef WEBRTC_AEC// 回声消除配置webrtc_apm->echo_cancellation()->Enable(true);                           // 启用AECwebrtc_apm->echo_cancellation()->enable_metrics(true);                   // 启用指标计算webrtc_apm->echo_cancellation()->enable_delay_logging(true);             // 启用延迟日志webrtc_apm->echo_cancellation()->enable_drift_compensation(false);       // 禁用漂移补偿webrtc_apm->echo_cancellation()->set_suppression_level(EchoCancellation::kLowSuppression);  // 设置抑制级别
#endif
​
#ifdef WEBRTC_HP// 高通滤波器配置webrtc_apm->high_pass_filter()->Enable(true);  // 启用高通滤波
#endif
​
#ifdef WEBRTC_LE// 电平估计配置webrtc_apm->level_estimator()->Enable(true);   // 启用电平估计
#endif
​
#ifdef WEBRTC_NS// 噪声抑制配置webrtc_apm->noise_suppression()->Enable(true);                           // 启用噪声抑制webrtc_apm->noise_suppression()->set_level(NoiseSuppression::kVeryHigh); // 设置抑制级别为最高
#endif
​
#ifdef WEBRTC_VAD// 语音活动检测配置webrtc_apm->voice_detection()->Enable(true);                            // 启用VADwebrtc_apm->voice_detection()->set_frame_size_ms(10);                   // 设置帧大小为10mswebrtc_apm->voice_detection()->set_likelihood(VoiceDetection::kLowLikelihood);  // 设置检测灵敏度
#endif
​webrtc_apm->Initialize();  // 初始化音频处理器
​webrtc_enable = true;  // 设置使能标志
​return 0;
}
​
/*** 调试信息输出函数* 功能: 打印当前音频处理器的所有配置状态* 性能: 仅用于调试,生产环境应禁用* 设计模式: 访问者模式 - 遍历访问各个处理器状态*/
void dump()
{int ret;// 获取基础配置信息int sample_rate_hz_dump = webrtc_apm->sample_rate_hz();int num_input_channels_dump = webrtc_apm->num_input_channels();int num_output_channels_dump = webrtc_apm->num_output_channels();int num_reverse_channels_dump = webrtc_apm->num_reverse_channels();int stream_delay_ms_dump = webrtc_apm->stream_delay_ms();printf("sample rate : %d\n", sample_rate_hz_dump);printf("num_input_channels : %d\n", num_input_channels_dump);printf("num_output_channels : %d\n", num_output_channels_dump);printf("num_reverse_channels : %d\n", num_reverse_channels_dump);printf("stream_delay_ms : %d\n", stream_delay_ms_dump);
​/* AEC状态输出 */ret = webrtc_apm->echo_cancellation()->is_enabled();if(ret) {printf("AEC enable !\n");ret = webrtc_apm->echo_cancellation()->is_drift_compensation_enabled();if(ret) {printf("\t\tenable_drift_compensation");ret = webrtc_apm->echo_cancellation()->device_sample_rate_hz();printf("\t\t\tdevice_sample_rate_hz : %d\n", ret);ret = webrtc_apm->echo_cancellation()->stream_drift_samples();printf("\t\t\tstream_drift_samples : %d\n", ret);}
​ret = webrtc_apm->echo_cancellation()->suppression_level();printf("\t\tsuppression_level : %d\n", ret);
​ret = webrtc_apm->echo_cancellation()->are_metrics_enabled();if(ret) {printf("\t\tenable_metrics\n");}
​ret = webrtc_apm->echo_cancellation()->is_delay_logging_enabled();if(ret) {printf("\t\tenable_delay_logging\n");}}
​/* 高通滤波器状态输出 */ret = webrtc_apm->high_pass_filter()->is_enabled();if(ret)printf("HighPassFilter is enabled\n");
​/* 电平估计状态输出 */ret = webrtc_apm->level_estimator()->is_enabled();if(ret) {printf("LevelEstimator is enable\n");}
​/* 噪声抑制状态输出 */ret = webrtc_apm->noise_suppression()->is_enabled();if(ret) {printf("NoiseSuppression is enabled !\n");ret = webrtc_apm->noise_suppression()->level();printf("\t\tNoiseSuppression : %d\n", ret);}
​/* 语音活动检测状态输出 */ret = webrtc_apm->voice_detection()->is_enabled();if(ret) {printf("voice activity detection is enable !\n");
​ret = webrtc_apm->voice_detection()->likelihood();printf("\t\tlikelihood : %d\n", ret);
​ret = webrtc_apm->voice_detection()->frame_size_ms();printf("\t\tframe size per ms : %d\n", ret);}
}
​
/*** 设置远端帧数据(扬声器输出)* 功能: 将扬声器数据送入AEC作为参考信号* 性能: 实时处理,每帧调用一次,计算复杂度中等* 设计模式: 命令模式 - 封装数据处理命令* * @param buf 扬声器数据缓冲区* @return 成功返回0,失败返回-1*/
int tang_apm_set_far_frame(short *buf)
{int i, ret;// 配置远端帧参数webrtc_far_frame->_audioChannel = 1;  // 单声道webrtc_far_frame->_frequencyInHz = webrtc_sample_rate;  // 采样率webrtc_far_frame->_payloadDataLengthInSamples = webrtc_far_frame->_frequencyInHz/100;  // 每帧样本数
​// 数据预处理:左移1位(可能为了放大信号)for(i=0; i<webrtc_far_frame->_payloadDataLengthInSamples; i++)webrtc_far_frame->_payloadData[i] = buf[i] << 1;
​// 分析反向流(远端信号)ret = webrtc_apm->AnalyzeReverseStream(webrtc_far_frame);if(ret < 0) {printf("AnalyzeReverseStream() error : %d\n", ret);return -1;}
​return 0;
}
​
/*** 设置近端帧数据(麦克风输入)并处理* 功能: 处理麦克风输入,应用AEC、NS、VAD等效果* 性能: 实时处理,计算密集型,性能关键路径* 设计模式: 模板方法模式 - 定义处理流程* * @param input 麦克风输入数据* @param output 处理后的输出数据* @param time 时间参数* @return 成功返回0,失败返回-1*/
int tang_apm_set_near_frame(short *input, short *output, int time)
{int i, ret;// 配置近端帧参数webrtc_near_frame->_audioChannel = 1;  // 单声道webrtc_near_frame->_frequencyInHz = webrtc_sample_rate;  // 采样率webrtc_near_frame->_payloadDataLengthInSamples = webrtc_near_frame->_frequencyInHz/100;  // 每帧样本数
​// 拷贝输入数据到近端帧for(i = 0; i < webrtc_near_frame->_payloadDataLengthInSamples; i++)webrtc_near_frame->_payloadData[i] = input[i];
​webrtc_apm->set_stream_delay_ms(2);  // 设置流延迟为2ms
​// 处理音频流 - 核心处理函数ret = webrtc_apm->ProcessStream(webrtc_near_frame);if(ret < 0) {printf("AnalyzeReverseStream() error : %d\n", ret);  // 注意:错误信息应该是ProcessStreamreturn -1;}
​
// VAD后处理:如果检测到无语音,静音输出
#ifdef WEBRTC_VADret = webrtc_apm->voice_detection()->stream_has_voice();if(ret == 0)for(i = 0; i < webrtc_near_frame->_payloadDataLengthInSamples; i++)webrtc_near_frame->_payloadData[i] = 0;  // 静音处理
​
#endif
​// 拷贝处理结果到输出缓冲区memcpy(output, webrtc_near_frame->_payloadData,webrtc_near_frame->_payloadDataLengthInSamples * sizeof(short));return 0;
}
​
/*** 销毁WebRTC音频处理模块* 功能: 清理所有资源,释放内存* 性能: 一次性调用,释放系统资源* 设计模式: 资源获取即初始化(RAII)的反向操作* * @return 成功返回0*/
int tang_apm_destroy(void)
{// 状态检查if (webrtc_enable == false) {printf("webrtc aec has initted!\n");  // 注意:应该是"not initted"或"already destroyed"return 0;}//dump();  // 调试信息输出(被注释)printf("ingenic_apm_destroy!\n");// 销毁音频处理器 - 工厂模式的销毁方法AudioProcessing::Destroy(webrtc_apm);webrtc_apm = NULL;// 释放音频帧内存 - 注意:第二个条件应该是webrtc_far_frameif (webrtc_near_frame)delete webrtc_near_frame;if (webrtc_near_frame)  // BUG:这里应该是webrtc_far_framedelete webrtc_far_frame;
​webrtc_enable = false;  // 重置使能标志return 0;
}
​
/*** 获取缓冲区长度* 功能: 返回每帧音频数据的字节长度* 性能: 直接返回预计算值,无计算开销* * @return 帧长度(字节数)*/
int webrtc_aec_get_buffer_length(void)
{return webrtc_dev.frame_len;
}
​
/*** WebRTC AEC处理入口函数* 功能: 封装完整的AEC处理流程* 性能: 实时处理,组合了两个核心处理函数* 设计模式: 外观模式 - 简化复杂子系统接口* * @param buf_record 录音数据缓冲区* @param buf_play 播放数据缓冲区* @param buf_result 处理结果缓冲区* @param time 时间参数*/
void webrtc_aec_calculate(void *buf_record, void *buf_play, void *buf_result, unsigned int time)
{// 处理流程:先设置远端参考信号,再处理近端信号ingenic_apm_set_far_frame((short *)buf_play);      // 设置扬声器数据ingenic_apm_set_near_frame((short *)buf_record, (short *)buf_result, time);  // 处理麦克风数据
}

整体分析和改进建议:

设计模式分析:

  1. 工厂模式AudioProcessing::Create() 创建处理器

  2. 单例模式思想:全局状态管理,防止重复初始化

  3. 装饰器模式:动态组合各种音频处理功能

  4. 外观模式webrtc_aec_calculate() 提供简化接口

  5. 策略模式:参数适配和验证

性能分析:

  • 优点:模块化设计,实时处理能力

  • 缺点:存在内存拷贝,可优化数据流

  • 计算复杂度:中等,适合实时音频处理

发现的问题:

  1. 内存释放逻辑有bug(重复检查webrtc_near_frame)

  2. 错误信息描述不准确

  3. 缺少错误处理和边界检查

改进建议:

  1. 修复内存释放bug

  2. 增加参数验证和错误处理

  3. 考虑使用智能指针管理资源

  4. 优化数据拷贝,减少内存操作

Makefile

# 获取当前工作目录的绝对路径
DIR_CUR := $(shell pwd)
​
# 编译器前缀设置 - 修改为支持多种架构
# 默认使用gcc,但可以通过环境变量覆盖
CC ?= gcc
CXX ?= g++
STRIP ?= strip
​
# 根据架构自动设置编译器标志
# 检测当前架构
UNAME_M := $(shell uname -m)
​
# 设置架构特定的编译标志
ifeq ($(UNAME_M), armv7l)# ARM 32位架构CFLAGS += -marm -mfpu=neon -mfloat-abi=hardARCH = arm32
else ifeq ($(UNAME_M), aarch64)# ARM 64位架构CFLAGS += -march=armv8-aARCH = arm64
else ifeq ($(UNAME_M), x86_64)# x86_64架构ARCH = x86_64
else# 其他架构ARCH = unknown
endif
​
# 安装路径配置
DESTDIR =           # 目标目录,通常用于打包或交叉编译
PREFIX = /usr       # 安装前缀
BINDIR = $(PREFIX)/bin/      # 二进制文件安装目录
LIBDIR = $(PREFIX)/lib/      # 库文件安装目录
INCDIR = $(PREFIX)/include/  # 头文件安装目录
​
# 编译标志设置
CFLAGS += -I$(DIR_CUR)/include -O3    # 添加头文件路径和优化级别
CFLAGS += -fPIC -g -D_FORTIFY_SOURCE=2 -DBUILDCFG -Wall -Wextra -Wno-unused-parameter #-DBT_ALONE
# -fPIC: 生成位置无关代码,用于共享库
# -g: 包含调试信息
# -D_FORTIFY_SOURCE=2: 加强缓冲区溢出保护
# -DBUILDCFG: 自定义构建配置宏
# -Wall -Wextra: 启用所有警告
# -Wno-unused-parameter: 忽略未使用参数警告
​
# 链接标志设置
LDFLAGS += -L$(DIR_CUR)/lib/ -lwebrtc_audio_processing -lpthread
# -L: 添加库搜索路径
# -l: 链接指定的库
​
# 源文件和目标文件设置
SRCS = $(wildcard *.cpp)              # 使用通配符获取所有.cpp文件
OBJS = $(patsubst %.cpp,%.c.o,$(SRCS)) # 将.cpp文件名转换为.o文件名
​
# 目标输出文件名
TARGET = libwebrtc.so
​
# 安装命令
INSTALL = install
​
# 导出环境变量给子进程
export CFLAGS LDFLAGS
​
# 默认目标
all: $(TARGET)
​
# 主要目标规则:构建共享库
$(TARGET): $(OBJS)$(CXX) $(OBJS) $(LDFLAGS) -shared -o $@# 使用C++编译器将目标文件链接成共享库# -shared: 生成共享库# $@: 代表目标文件名
​
# 模式规则:将.cpp文件编译为.o文件
%.c.o:%.cpp$(CXX) $(CFLAGS) -c $^ -o $@# -c: 只编译不链接# $^: 代表所有依赖文件# $@: 代表目标文件
​
# 安装目标
install:$(INSTALL) -d $(DESTDIR)$(LIBDIR)          # 创建库目录$(INSTALL) -d $(DESTDIR)$(INCDIR)          # 创建头文件目录$(INSTALL) -m 755 $(DIR_CUR)/$(TARGET) $(DESTDIR)$(LIBDIR)  # 安装主库文件cp -a  $(DIR_CUR)/lib/libwebrtc_audio_processing.so* $(DESTDIR)$(LIBDIR)  # 复制依赖库$(INSTALL) -m 666 $(DIR_CUR)/include/webrtc_aec.h $(DESTDIR)$(INCDIR)     # 安装头文件
​
# 卸载目标
uninstall:-rm -f $(DESTDIR)$(INCDIR)/webrtc_aec.h    # 删除头文件-rm -f $(DESTDIR)$(LIBDIR)/$(TARGET)       # 删除主库文件-rm -f $(DESTDIR)$(LIBDIR)/libwebrtc_audio_processing.so*  # 删除依赖库
​
# 清理目标:删除编译生成的目标文件
clean:-rm -f $(OBJS)
​
# 深度清理目标:同时删除最终目标文件
distclean: clean-rm -f $(TARGET)
​
# 声明伪目标,避免与同名文件冲突
.PHONY: all clean install uninstall $(TARGET)

主要修改内容:

  1. 架构检测:添加了自动检测当前系统架构的逻辑

  2. 编译器设置:将硬编码的mipsel编译器改为通用的gcc/g++,支持通过环境变量覆盖

    CC = mipsel-linux- STRIP = mipsel-linux-strip

  3. 架构特定标志:为arm32和arm64设置了合适的编译标志

  4. 安装目录创建:在install目标中添加了目录创建步骤

  5. 变量使用:使用$(TARGET)变量代替硬编码的文件名

使用方式:

# 自动检测架构编译
make
​
# 交叉编译(需要在环境中设置CC、CXX等变量)
export CC=arm-linux-gnueabihf-gcc
export CXX=arm-linux-gnueabihf-g++
make
​
# 安装到指定目录
make install DESTDIR=/tmp/install

这样修改后的Makefile可以自动适应不同的架构环境,包括arm32、arm64和x86_64。

函数调用流程:

tang_apm_init()↓
webrtc_aec_get_buffer_length()  [可选,用于查询缓冲区大小]↓
webrtc_aec_calculate()          [循环调用,实时处理]↓
tang_apm_destroy()

使用场景:

  • 实时语音通信:消除扬声器到麦克风的回声

  • 音频处理应用:需要实时回声消除的嵌入式系统

  • 跨平台开发:C接口便于不同平台集成

http://www.dtcms.com/a/541776.html

相关文章:

  • 当机器拥有感觉:从电子皮肤到视频神经系统的具身智能革命
  • 快速搭建网站服务器网站推广策划方案
  • 【Linux基础知识系列:第一百六十三篇】创建虚拟网络:Linux网络桥接
  • 东方财经报道|深兰科技落户张江,AI医疗与情感陪伴并进,拓展智能未来版图
  • 跨区域多院区如何破局?浙江三甲医院实现核心医疗系统国产化重构
  • 做网站的怎么挣钱wordpress设计漂亮的页面
  • 【前端】圆角和非圆角实现渐变边框的区别(border)
  • 模板网站免费淘宝网页版官网
  • 苏州建设工程招标在哪个网站电子商务网站建设需要什么
  • 网站建设丿金手指花总9志愿北京网站注册
  • Linux魔法设备:/dev/null、/dev/zero、/dev/full详解
  • 【系统分析师】预测试卷一:综合知识题目及答案详解
  • 引领未来网络新体验——全方位解析外置WiFi模块的魅力与应用
  • 湖南的商城网站建设怎么注册公司公众号
  • vue3封装table组件及属性介绍
  • 北京网站排名seo网络营销的步骤
  • 【电脑软件】定时语音播报助手v1.0
  • 房屋产权地址备案在那个网站做网站建设与管理是干嘛的
  • 分库分表详解,以及ShardingJDBC介绍
  • Linux小课堂: NGINX反向代理服务器配置与实践
  • 做网站的人多吗c 做网站源码实例
  • C++核心组件与构建过程全解析
  • 探秘XZ压缩:以“极致小巧”重塑数据存储效率
  • 【笔试真题】- 电信-2025.10.17
  • 网站悬浮窗商标注册网官方查询
  • 基于python大数据的特产推荐系统
  • AI整理详细解释C++中的内存对象模型
  • DDC可编程控制器风机水泵空调节能控制器哪家技术强
  • 撰写网站栏目规划百度网址大全
  • 【案例实战】鸿蒙开发:web页面如何适配深色模式