QT6开发高性能企业视频会议-5 Linux Audio开发

Linux系统音频技术简介

视频会议或者其他音视频通信应用都会涉及Audio/Voice的采集和播放,本文简单介绍Linux系统常用Audio开发框架和技术,并且配有示例代码。

更完整的代码和应用请访问下面地址免费获取: 

国内: https://gitee.com/sqmeeting

神旗视讯 -- 开源高性能音视频系统

目前,常用的Linux系统音频开发框架和SDK主要有如下几种:

Qt Multimedia

  • 简介:是Qt框架的一部分,提供了跨平台的音频处理功能,支持多种音频格式和设备,能方便地实现音频的播放、录制和处理。
  • 优势:基于Qt框架,具有良好的跨平台性,能在多种操作系统上运行,提供了简单易用的编程接口,适合快速开发跨平台音频应用。
  • 不足:在某些特定平台上,性能可能不如原生框架,以及潜在的兼容性问题。

ALSA(Advanced Linux Sound Architecture)

  • 简介:是Linux下的低级音频编程接口,为用户空间的应用程序提供了访问声卡驱动的途径,支持多种音频设备和功能,如音频捕获、播放、混音等。
  • 优势:提供了对音频硬件的直接控制,性能高,适用于对音频性能要求苛刻、需要深入控制硬件参数的应用开发,如专业音频处理软件。
  • 不足:编程接口相对底层,开发复杂,开发效率较低。

PulseAudio

  • 简介:是一个高级音频服务器,在ALSA之上提供了更高级的抽象层,支持网络音频、多设备混音、动态设备管理等功能。
  • 优势:提供了简单易用的编程接口,支持多种音频格式和设备,能方便地实现音频的混合、路由等功能,适合快速开发音频应用。
  • 不足:在处理一些复杂的音频任务时,性能可能不如ALSA。

JACK(Jack Audio Connection Kit)

  • 简介:是一个专业的音频处理框架,提供了低延迟、高精度的音频处理能力,支持多通道音频处理和实时音频处理。
  • 优势:具有极低的音频延迟,支持多通道音频处理,适用于专业音频制作、音乐创作等领域。
  • 不足:学习曲线较陡,开发难度较大,对硬件性能要求较高。

PortAudio

  • 简介:是一个跨平台的音频处理库,基于ALSA和OSS开发,旨在为开发者提供一个统一的、高层次的API,用于在不同操作系统上进行音频输入和输出操作。
  • 优势:跨平台兼容性良好,可用于OSX,Windows和各种主流Linux系统,音频设备支持非常丰富,API简单易用。
  • 不足:不支持压缩格式,部分极端场景有性能瓶颈。

PipeWire

  • 简介:是一个多媒体框架,旨在处理音频和视频流。它最初由 Wim Taymans 开发,目标是替代 PulseAudio 和 JACK,同时提供更好的视频处理能力。
  • 优势:统一的多媒体处理,能支持专业音频应用的低延迟技术,能支持复杂的多媒体路由和处理,并且可兼容PulseAudio和JACK应用。
  • 不足:比较新,很多在使用的Linux系统都不支持。国产Linux平台支持性有待测试。

在Linux上使用PortAudio采集和播放PCM音频

我门在Linux上开发视频会议App过程中,发现Qt6自带的Qt Multimedia模块在某国产Linux系统上播放声音存在部分兼容问题,讨论后决定为了全平台多设备兼容性使用PortAudio进行开发。

PortAudio的完整文档在这里

编译和安装PortAudio

获取PortAudio源代码

PortAudio的Git仓库

安装ALSA开发库
#安装alsa开发库
sudo apt-get install libasound2-dev
编译和Install PortAuido
./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库获取名字。

安装udev开发库
sudo apt-get install libudev-dev
使用udev获取友好名称
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);
}

你可能感兴趣的:(linux,运维,服务器)