视频会议或者其他音视频通信应用都会涉及Audio/Voice的采集和播放,本文简单介绍Linux系统常用Audio开发框架和技术,并且配有示例代码。
更完整的代码和应用请访问下面地址免费获取:
国内: https://gitee.com/sqmeeting
神旗视讯 -- 开源高性能音视频系统
目前,常用的Linux系统音频开发框架和SDK主要有如下几种:
Qt Multimedia
ALSA(Advanced Linux Sound Architecture)
PulseAudio
JACK(Jack Audio Connection Kit)
PortAudio
PipeWire
我门在Linux上开发视频会议App过程中,发现Qt6自带的Qt Multimedia模块在某国产Linux系统上播放声音存在部分兼容问题,讨论后决定为了全平台多设备兼容性使用PortAudio进行开发。
PortAudio的完整文档在这里
PortAudio的Git仓库
#安装alsa开发库
sudo apt-get install libasound2-dev
./configure && make sudo make install
#include
Pa_Initialize();
int numDevices = Pa_GetDeviceCount();
for(int i=0; iname);
int defaultMicIndex = i;
}
if( i == Pa_GetDefaultOututDevice() )
{
QString defaultSpeakerName = QString::fromLocal8Bit(deviceInfo->name);
int defaultSpeakerIndex = i;
}
//input devices
if(deviceInfo->maxInputChannels > 0)
{
QString name = QString::fromLocal8Bit(deviceInfo->name);
int index = i;
}
//output devices
if(deviceInfo->maxOutputChannels > 0)
{
QString name = QString::fromLocal8Bit(deviceInfo->name);
int index = i;
}
}
采集和播放声音主要使用的PortAudio API是Pa_OpenStream和Pa_StartStream。采集和播放的调用流程完全一样,注意区分使用输出/输出设备即可。下面以采集声音为例。
//采集声音
#include
void start_capture(int input_device_index)
{
PaStreamParameters inputParameters;
PaError err = paNoError;
int i;
int totalFrames;
int numSamples;
int numBytes;
float max, val;
double average;
inputParameters.device = input_device_index;
inputParameters.channelCount = 1;
inputParameters.sampleFormat = paInt16;
inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
/* Record some audio. -------------------------------------------- */
PaStream inputStream
err =Pa_OpenStream(
&inputStream,
&inputParameters,
NULL, /* &outputParameters, */
48000,
1920,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
recordCallback,
NULL);
if( err == paNoError )
{
err = Pa_StartStream( inputStream );
}
}
int recordCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
if(inputBuffer == nullptr)
{
//handle empty audio data
}
else
{
//handel audio data
}
return 0;
}
PortAudio从ALSA接口中得到的设备名是Linux系统中的描述符名称,如hw:0,0,表示第一个声卡的第一个设备。如果希望得到易于辨认的友好名称,需要配合udev库获取名字。
sudo apt-get install libudev-dev
void get_device_name_udev(const char* cardName)
{
// Use udev to get the friendly name
struct udev_enumerate *enumerate = udev_enumerate_new(udev);
udev_enumerate_add_match_subsystem(enumerate, "sound");
udev_enumerate_scan_devices(enumerate);
struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate);
struct udev_list_entry *entry;
udev_list_entry_foreach(entry, devices) {
const char *path = udev_list_entry_get_name(entry);
struct udev_device *dev = udev_device_new_from_syspath(udev, path);
const char *devnode = udev_device_get_devnode(dev);
if (devnode && strstr(devnode, card_name)) {
const char *friendly_name = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE");
if (friendly_name) {
std::cout << " Friendly name: " << friendly_name << std::endl;
}
udev_device_unref(dev);
break;
}
udev_device_unref(dev);
}
udev_enumerate_unref(enumerate);
}