最近在做音乐播放器的项目,先简单记录分享一下我是如何解析读取本地音乐文件的:
一、解析读取本地音乐文件
首先,在QML中获取本地音乐文件的URL
import QtQuick.Controls
import QtQuick.Dialogs
Button{
width: 80
height: 50
anchors.centerIn: parent
onClicked: {
musicfile.open()
}
}
FileDialog{
id:musicfile
title:"选择导入的音乐"
nameFilters:["音乐文件(*.mp3 *.flac *.wav)"] //筛选音乐文件
fileMode: FileDialog.OpenFile //单次选择一个文件
onAccepted: {
console.log("选择的文件路径为:",selectedFile)
//输出 file:///本地磁盘路径 ,即URL
}
}
然后在 .pro 项目文件中添加multimedia模块,不然在c++中无法访问到QMediaPlayer,QMediaMetaData等音视频,元数据类
QT += quick multimedia
在 .h 头文件中声明读取函数,用在QML获取的本地音乐文件的URL作为参数
public:
Q_INVOKABLE void getdata(const QString &file);
并在 .cpp 中进行实现
void MediaInfo::getdata(const QString &file)
{
QUrl url(file); //用file初始化url
QString localpath=url.toLocalFile(); //将url转换为本地磁盘路径
//即 去除掉前缀file:///
QMediaPlayer* player=new QMediaPlayer(this); //避免内存泄漏
//申请内存,并用MediaInfo作为父对象,让MediaInfo实例销毁时自动销毁player
player->setSource(file); //传入url,设置媒体源file
connect(player,&QMediaPlayer::metaDataChanged,this,[=](){ //当player的元数据更新时,MediaInfo对象执行lambda函数
const QMediaMetaData &data=player->metaData(); //获取当前元数据对象
QString title = data.stringValue(QMediaMetaData::Title); //转换QVariant为QString
//同理QString title=meta.value(QMediaMetaData::Title).toString();
QString author = data.stringValue(QMediaMetaData::ContributingArtist);
QString album = data.stringValue(QMediaMetaData::AlbumTitle);
qint64 time = data.value(QMediaMetaData::Duration).toLongLong(); //qint64是Qt定义的整型,相当于c++的longlong
qDebug() << "标题:" << title;
qDebug() << "艺术家:" << author;
qDebug() << "唱片集:" << album;
qDebug() << "时长:" << time;
qDebug() << "本地位置:" << localpath;
});
}
最后在QML中创建对象,调用即可
FileDialog{
...
onAccepted: {
mediasolve.getdata(selectedFile)
}
}
MediaInfo{
id:mediasolve
}
二、音视频处理的异步传输问题
但值得注意的是,音视频的解码提取数据是异步传输的,即需要等到数据被处理完成后,qt才能接收到数据。
所以,如果在数据还没处理完时,直接调用成员属性往往会出现类似undefined的现象,那该怎么办呢?其实也简单,处理完数据后释放一个信号就好了,让qml接收到信号后,再获取对象即可。我沿用上篇的Q_PROPERTY代码暴露的属性做个示例吧:
//mediainfo.h
......
class MediaInfo : public QObject
{
Q_OBJECT
public:
...
Q_INVOKABLE void getdata(const QString &file);
Q_PROPERTY(QString currenttitle READ currenttitle WRITE setCurrenttitle NOTIFY currenttitleChanged FINAL)
//暴露currenttitle属性
//下面代码都是qt自动生成的,没有改动
QString currenttitle() const;
void setCurrenttitle(const QString &newCurrenttitle);
signals:
void currenttitleChanged();
private:
QString m_currenttitle;
};
...
//mediainfo.cpp
void MediaInfo::getdata(const QString &file)
{
......
qDebug() << "标题:" << title;
......
setCurrenttitle(title); //在之前的函数最后加入这句代码
});
}
QString MediaInfo::currenttitle() const
{
return m_currenttitle;
}
void MediaInfo::setCurrenttitle(const QString &newCurrenttitle)
{
if (m_currenttitle == newCurrenttitle)
return;
m_currenttitle = newCurrenttitle;
emit currenttitleChanged();
}
然后在qml中对比实验一下
Button{
...
onClicked: {
musicfile.open()
}
}
FileDialog{
...
onAccepted: {
mediasolve.getdata(selectedFile)
console.log(mediasolve.currenttitle) //打印为空
}
}
Connections{
target: mediasolve
function onCurrenttitleChanged(){
console.log(mediasolve.currenttitle) //正确打印title
}
}
MediaInfo{
id:mediasolve
}
一般来说,都运行过getdata()函数了,不应该已经设置好值了吗,但由于异步传输的问题,在运行到onAccepted()中的console.log()时,数据根本没有解析完,也就只能赋空值了。而后者是在接收到数据处理完成的信号后再执行的,故而能够正确打印。
第一次碰到异步传输的问题时确实很头疼,但懂得用信号解决问题后其实并算不上麻烦,在多加运用后也便好处理了