Qt源码-Qt多媒体音频框架

Qt 多媒体音频框架

  • 一、概述
  • 二、音频设计
    • [1. ALSA 基础](#1. ALSA 基础)
    • [2. Qt 音频类](#2. Qt 音频类)
      • [1. 接口实现](#1. 接口实现)
      • [2. alsa 插件实现](#2. alsa 插件实现)

一、概述

环境 详细
Qt版本 Qt 5.15
操作系统 Deepin v23
代码工具 Visual Code
源码 https://github.com/qt/qtmultimedia/tree/5.15

这里记录一下在Linux下Qt 的 Qt Multimedia 模块的设计,我目前先记录与音频相关的库的设计。不同Qt版本的设计有些不一样,需要看对应版本的源码设计。

二、音频设计

1. ALSA 基础

ALSA是 Advanced Linux Sound Architecture的缩写,即高级Linux声音架构。在Linux 2.6的内核版本后,ALSA目前已经成为了Linux的主流音频体系结构。

2. Qt 音频类

1. 接口实现

最重要的就是 QAudioInput和QAudioOutput两个类,作为音频输入输出的类。

这个类的源码位置在

bash 复制代码
src/multimedia/audio/qaudioinput.cpp

那我们开始看 QAudioInput 类的关键实现吧,在 qaudioinput.cpp 中都是使用的 qaudioinput.h 头文件中 QAbstractAudioInput 类型的 d 的成员方法。

如 QAudioInput::start() 实际使用的是 d->start();

cpp 复制代码
// qaudioinput.h
private:
    QAbstractAudioInput* d;
cpp 复制代码
// qaudioinput.cpp
void QAudioInput::start(QIODevice* device)
{
    d->start(device);
}
....
QAudioFormat QAudioInput::format() const
{
    return d->format();
}

在 QAudioInput 构造函数中,得知是d 是由工厂类 QAudioDeviceFactory 创建的

bash 复制代码
src/multimedia/audio/qaudiodevicefactory.cpp
cpp 复制代码
QAudioInput::QAudioInput(const QAudioFormat &format, QObject *parent):
    QObject(parent)
{
    d = QAudioDeviceFactory::createDefaultInputDevice(format);
    ...
}

QAudioDeviceFactory 工厂通过 createDefaultInputDevice() ⇒ \Rightarrow ⇒ createInputDevice() 函数调用。所以具体就看 createInputDevice() 函数实现 就可以了。

cpp 复制代码
QAbstractAudioInput* QAudioDeviceFactory::createDefaultInputDevice(QAudioFormat const &format)
{
    return createInputDevice(defaultDevice(QAudio::AudioInput), format);
}
cpp 复制代码
QAbstractAudioInput* QAudioDeviceFactory::createInputDevice(QAudioDeviceInfo const& deviceInfo, QAudioFormat const &format)
{
    if (deviceInfo.isNull())
        return new QNullInputDevice();

#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
    QAudioSystemFactoryInterface* plugin =
        qobject_cast<QAudioSystemFactoryInterface*>(
        	audioLoader()->instance(deviceInfo.realm())
        );
        
    if (plugin) {
        QAbstractAudioInput* p = plugin->createInput(deviceInfo.handle());
        if (p) p->setFormat(format);
        return p;
    }
#endif

    return new QNullInputDevice();
}

上面 createInputDevice 中 QAbstractAudioInput *p 就是 通过 audioLoader 加载的插件 plugin 创建出的。这样不同系统可以实现不同的插件,根据系统做到跨平台性。

QAudioSystemFactoryInterface* plugin 就是插件的接口父类。

在创建失败的时候,QAudioDeviceFactory 工厂也通过定义 QNullInputDevice 类。保证插件系统正常运行。

让我们看看QNullInputDevice 在 qaudiodevicefactory.cpp 的定义

cpp 复制代码
class QNullInputDevice : public QAbstractAudioInput
{
public:
    void start(QIODevice*) override { qWarning()<<"using null input device, none available";}
    QIODevice *start() override { qWarning()<<"using null input device, none available"; return nullptr; 
    void setBufferSize(int ) override {}
    int bufferSize() const override  { return 0; }
...
    void setVolume(qreal) override {}
    qreal volume() const override {return 1.0f;}
};

2. alsa 插件实现

那让我们继续看看音频功能的具体实现,音频插件的源码工程位置在 `

bash 复制代码
src/pligins/alsa/alsa.pro

在下面我们可以看到,不同平台的不同实现,有Linux、Windows、Android等。在我WIndows电脑的Qt5.14.2 上音频库则是使用的

windowsaudio 工程

我们找到Linux的alsa工程,从工程文件可以得到这个插件的名字叫 qtaudio_alsa

这个插件最后生成的名字叫 libqtaudio_alsa.so

在我的系统中安装的位置在

bash 复制代码
/usr/lib/x86_64-linux-gnu/qt5/plugins/audio/libqtaudio_alsa.so	

下面继续介绍这个插件

qalsaplugin.cpp:就是生成插件的接口类,对外使用,内部使用了ALSA实现功能

qalsaaudiodeviceinfo.cpp:封装的 音频设备信息,功能QAudioDeviceInfo 功能

qalsaaudioinput.cpp :音频输入类,实际 QAudioInput 的实现

qalsaaudiooutput.cpp:音频输出类,实际的 QAudioOutput 的实现

下面就是 qalsaaudioinput 的open函数实现,可以看到open函数使用了 snd_pcm_open 函数:

cpp 复制代码
bool QAlsaAudioDeviceInfo::open()
{
    int err = 0;
    QString dev;

    if (!availableDevices(mode).contains(device.toLocal8Bit()))
        return false;

#if SND_LIB_VERSION < 0x1000e  // 1.0.14
    if (device.compare(QLatin1String("default")) != 0)
        dev = deviceFromCardName(device);
    else
#endif
        dev = device;

    if(mode == QAudio::AudioOutput) {
        err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0);
    } else {
        err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0);
    }
    if(err < 0) {
        handle = 0;
        return false;
    }
    return true;
}
相关推荐
不是三毛没有半5 分钟前
Centos 7 安装wget
linux
清风fu杨柳8 分钟前
centos7 arm版本编译qt5.6.3详细说明
开发语言·arm开发·qt
清风fu杨柳8 分钟前
麒麟服务器工作站SP1 arm环境qt5.6.3源码编译
服务器·arm开发·qt
hunandede17 分钟前
FFmpeg 4.3 音视频-多路H265监控录放C++开发十三.2:avpacket中包含多个 NALU如何解析头部分析
c++·ffmpeg·音视频
声网23 分钟前
「人眼视觉不再是视频消费的唯一形式」丨智能编解码和 AI 视频生成专场回顾@RTE2024
人工智能·音视频
叫我龙翔35 分钟前
【计网】实现reactor反应堆模型 --- 多线程方案优化 ,OTOL方案
linux·运维·网络
mit6.82437 分钟前
[Docker#9] 存储卷 | Volume、Bind、Tmpfs | -v/mount | MySQL 灾难恢复 | 问题
linux·运维·docker·容器·架构
WangYaolove13141 小时前
请解释Python中的装饰器是什么?如何使用它们?
linux·数据库·python
7yewh1 小时前
嵌入式硬件实战提升篇(一)-泰山派RK3566制作多功能小手机
linux·arm开发·驱动开发·嵌入式硬件·物联网·智能手机·硬件架构
YRr YRr2 小时前
ubuntu ros 解决建完图后 保存的地图非常小的问题
linux·运维·ubuntu