QT QML学习(三)

QT QML学习(三)——GST封装借鉴

学习资料:
1、W:\Download\565672 Qt Quick核心编程.pdf,安晓辉。

安晓辉的CSDN

2、在IMX6.0,飞凌嵌入式开发板上验证学习。GST API,界面框架用QT5 QML。
编译环境,ubuntu18.04。
qt5官方源码工程用例:/opt/Qt5.9.4/5.9.4/Src/qtmultimedia/examples/multimedia/video/qmlvideo
qt5官方源码工程camera:/opt/Qt5.9.4/5.9.4/Src/qtmultimedia/src/multimedia/camera
qt5官方源码工程video:/opt/Qt5.9.4/5.9.4/Src/qtmultimedia/src/multimedia/video
qt5官方源码工程audio:/opt/Qt5.9.4/5.9.4/Src/qtmultimedia/src/multimedia/audio
video对应的MediaPlayer、audio对应的QAudioReconder本项目中都可以正常使用,所以就以Camera为例借鉴实现。

audio

audio底层应该是用的alsa库驱动的音频硬件,在开发板上支持直接可以使用。代码还是大概看一下。
项目中使用的是QAudioRecorder类,需要包含头文件#include <QMetaObject>,用c++类封装了一个audioRecorder类,用于qml程序中使用的工厂类。封装了简单的几种方法用于录音。
audioRecorder.h

#ifndef AUDIORECORD_H
#define AUDIORECORD_H

#include <QObject>
#include <QTimer>
#include <QFile>
#include <QUrl>
#include <QMediaRecorder>
#include <QAudioRecorder>

class audioRecorder : public QObject
{
    Q_OBJECT
    Q_ENUMS(EncodingQuality)
    Q_ENUMS(ContainerName)
    Q_PROPERTY ( bool recording READ recording NOTIFY recordingChanged )
    Q_PROPERTY(QString name READ name NOTIFY nameChanged)
    Q_PROPERTY(ContainerName format READ format WRITE setFormat)
    Q_PROPERTY(QMultimedia::EncodingQuality quality READ quality WRITE setQuality)

public:
    enum ContainerName
    {
        WAV,
        MP3
    };
    enum EncodingQuality
    {
        VeryLowQuality,
        LowQuality,
        NormalQuality,
        HighQuality,
        VeryHighQuality
    };
    explicit audioRecorder(QObject *parent = nullptr);
    virtual ~audioRecorder();
    const bool recording() const;
    QString name() const;
    ContainerName format() const;
    QMultimedia::EncodingQuality quality() const;
    Q_INVOKABLE QString path() { return m_path;}
private:
    QString  m_path;
    bool m_recording;
    QString m_name;
    QAudioRecorder *m_audioRecorder;
    QAudioEncoderSettings m_audioSettings;

    ContainerName m_format;
    QMultimedia::EncodingQuality m_quality;
signals:
    void recordingChanged(bool);
    void nameChanged(QString);

public slots:
    void configInit();
    void setName(QString name);
    void setRecording(bool recording );
    void setFormat(const ContainerName format);
    void setQuality(const QMultimedia::EncodingQuality quality);

    void record();
    void pause();
    void stop();

    void getstate();
    void updateProgress(qint64 duration);
    void updateStatus(QMediaRecorder::Status status);
    void displayErrorMessage();
};

#endif // AUDIORECORD_H

以上为头文件的定义,注册了qml可以使用的枚举类型和属性。
其中有些注意问题,参考另一篇blogs:QT QUICK和C++结合编程


audioRecorder.cpp


...
void audioRecorder::record()
{
    qDebug() << "Entering record!";
    if (m_audioRecorder->state() == QMediaRecorder::StoppedState)
    {
        qDebug() << "recording....! ";
        m_audioRecorder->record();
        m_recording = true;
        emit recordingChanged(m_recording);
    }
}
...

跟踪QAudioRecorder中的底层实现。
record()在/opt/fsl-imx-xwayland/4.9.88-2.0.0/sysroots/cortexa9hf-neon-poky-linux-gnueabi/usr/include/qt5/QtMultimedia/qmediarecorder.h中定义
qml也有类似定义的类型为:CameraRecorder,可惜要结合Camera类型使用。开发板上用不了。
补充:QT源码查看方法
安装QT时要选择源码安装,QT源码路径:/opt/Qt5.9.4/5.9.4/Src/
从项目中跳转只能跳转到:/opt/fsl-imx-xwayland/4.9.88-2.0.0/sysroots/cortexa9hf-neon-poky-linux-gnueabi/usr/include,看不得cpp文件。
在QT源码路径:/opt/Qt5.9.4/5.9.4/Src/找对应头文件和源文件,如:
/opt/fsl-imx-xwayland/4.9.88-2.0.0/sysroots/cortexa9hf-neon-poky-linux-gnueabi/usr/include/qt5/QtMultimedia/qmediarecorder.h
对应在源码中的路径为:
/opt/Qt5.9.4/5.9.4/Src/qtmultimedia/src/multimedia/recording/qmediarecorder.h


qmediarecorder.cpp

void QMediaRecorder::record()
{
    Q_D(QMediaRecorder);

    d->actualLocation.clear();

    if (d->settingsChanged)
        d->_q_applySettings();

    // reset error
    d->error = NoError;
    d->errorString = QString();

    if (d->control)
        d->control->setState(RecordingState);
}

其中:d指针相关知识
Q_D宏用法

没搞懂,d指针的用法,反过来从gst跟踪。源码路径:
/opt/Qt5.9.4/5.9.4/Src/qtmultimedia/src/gsttools/qgstutils.cpp

GST的初始化

qgstreameraudiodecoderserviceplugin.cpp

QMediaService* QGstreamerAudioDecoderServicePlugin::create(const QString &key)
{
    QGstUtils::initializeGst();//初始化

    if (key == QLatin1String(Q_MEDIASERVICE_AUDIODECODER))
        return new QGstreamerAudioDecoderService;

    qWarning() << "Gstreamer audio decoder service plugin: unsupported key:" << key;
    return 0;
}
QMultimedia::SupportEstimate QGstreamerAudioDecoderServicePlugin::hasSupport(const QString &mimeType,const QStringList &codecs) const
{
    if (m_supportedMimeTypeSet.isEmpty())
        updateSupportedMimeTypes();

    return QGstUtils::hasSupport(mimeType, codecs, m_supportedMimeTypeSet);
}
QGstUtils::hasSupport(mimeType, codecs, m_supportedMimeTypeSet);

里面会返回支持的GST类型,至于GST plugin怎么差异化封装进QSound等类,如下:
..\Src\qtmultimedia\src\multimedia\qmediaserviceprovider.cpp

    Returns a default provider of media services.
*/
QMediaServiceProvider *QMediaServiceProvider::defaultServiceProvider()
{
    return qt_defaultMediaServiceProvider != 0
            ? qt_defaultMediaServiceProvider
            : static_cast<QMediaServiceProvider *>(pluginProvider());
}
...
void QMediaServiceProvider::setDefaultServiceProvider(QMediaServiceProvider *provider)
{
    qt_defaultMediaServiceProvider = provider;
}

Camera

QGstUtils::cameraDriver--->GstElement *CameraBinSession::buildCameraSource()--->
D:\Qt\Qt5.9.1\5.9.1\Src\qtmultimedia\src\plugins\gstreamer\camerabin\camerabinsession.h定义了一些gst元件用于摄像头的音频视频的录制。

    GstElement *m_audioSrc;
    GstElement *m_audioConvert;
    GstElement *m_capsFilter;
    GstElement *m_fileSink;
    GstElement *m_audioEncoder;
    GstElement *m_videoEncoder;
    GstElement *m_muxer;

camerabinsession.cpp

CameraBinSession::CameraBinSession(GstElementFactory *sourceFactory, QObject *parent)...
{
	m_camerabin = gst_element_factory_make(QT_GSTREAMER_CAMERABIN_ELEMENT_NAME, "camerabin");
    g_signal_connect(G_OBJECT(m_camerabin), "notify::idle", G_CALLBACK(updateBusyStatus), this);
    g_signal_connect(G_OBJECT(m_camerabin), "element-added",  G_CALLBACK(elementAdded), this);
    g_signal_connect(G_OBJECT(m_camerabin), "element-removed",  G_CALLBACK(elementRemoved), this);
    qt_gst_object_ref_sink(m_camerabin);
}

imx6.0开发板上确实有camera元件,为通用元件没有指定硬件的

GObject
 +----GInitiallyUnowned
       +----GstObject
             +----GstElement
                   +----GstBin
                         +----GstPipeline
                               +----GstCameraBin

以supportedFrameRates一个函数为例:

QList< QPair<int,int> > CameraBinSession::supportedFrameRates(const QSize &frameSize, bool *continuous) const
{
    QList< QPair<int,int> > res;

    GstCaps *supportedCaps = this->supportedCaps(QCamera::CaptureVideo);

    if (!supportedCaps)
        return res;

    GstCaps *caps = 0;

    if (frameSize.isEmpty()) {
        caps = gst_caps_copy(supportedCaps);
    } else {
        GstCaps *filter = QGstUtils::videoFilterCaps();
        gst_caps_set_simple(
                    filter,
                    "width", G_TYPE_INT, frameSize.width(),
                    "height", G_TYPE_INT, frameSize.height(),
                     NULL);

        caps = gst_caps_intersect(supportedCaps, filter);
        gst_caps_unref(filter);
    }
    gst_caps_unref(supportedCaps);

    //simplify to the list of rates only:
    caps = gst_caps_make_writable(caps);
    for (uint i=0; i<gst_caps_get_size(caps); i++) {
        GstStructure *structure = gst_caps_get_structure(caps, i);
        gst_structure_set_name(structure, "video/x-raw");
        const GValue *oldRate = gst_structure_get_value(structure, "framerate");
        GValue rate;
        memset(&rate, 0, sizeof(rate));
        g_value_init(&rate, G_VALUE_TYPE(oldRate));
        g_value_copy(oldRate, &rate);
        gst_structure_remove_all_fields(structure);
        gst_structure_set_value(structure, "framerate", &rate);
    }
#if GST_CHECK_VERSION(1,0,0)
    caps = gst_caps_simplify(caps);
#else
    gst_caps_do_simplify(caps);
#endif

    for (uint i=0; i<gst_caps_get_size(caps); i++) {
        GstStructure *structure = gst_caps_get_structure(caps, i);
        const GValue *rateValue = gst_structure_get_value(structure, "framerate");
        readValue(rateValue, &res, continuous);
    }

    qSort(res.begin(), res.end(), rateLessThan);

#if CAMERABIN_DEBUG
    qDebug() << "Supported rates:" << caps;
    qDebug() << res;
#endif

    gst_caps_unref(caps);

    return res;
}

定义的宏很多是字符串,用于查找gst支持的属性。
const GValue *rateValue = gst_structure_get_value(structure, "framerate");查找支持的分辨率时的API。


GST API

gst_structure_get_value

const GValue *
gst_structure_get_value (const GstStructure * structure,
                         const gchar * fieldname)

一个 GstStructure描述一种媒体类型。一个被数据流通过的pad(negotiated pad)存在功能集(capabilities set),每种功能只包含一个GstStructure结构。
以下篇幅进入:
GST学习的章节

posted @   wuya178  阅读(527)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示