QT QML学习(三)
QT QML学习(三)——GST封装借鉴
学习资料:
1、W:\Download\565672 Qt Quick核心编程.pdf,安晓辉。
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指针的用法,反过来从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学习的章节
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 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代理 了,记录一下