QT6引入QMediaPlaylist类
前言
##### 大家都找到实习了吗,我boss投了470份,目前还是0面😭####
1.QMediaPlaylist是做什么的
在QT5中QMediaPlayer和QMediaPlaylist都被用来管理音频模块,QMediaPlayer负责音频解析,播放暂停,音量调节等功能,而QMediaPlaylist负责多个音频的播放管理,如单曲循环,随机播放,顺序播放,播放相关的槽函数处理
2.QT6的音频模块
QT6中对音频模块进行了大调整,QMediaPlayer解析歌曲改为信号槽式,本身功能也被拆分为QMediaPlayer,QAudioOutput两部分;并将QMediaPlaylist删除,如果仍要使用这个模块,则需要自己搓一个,于是也就有了本文
3.MediaPlaylist
这是我和ai仿照QMediaPlaylist制作的类,用以实现其常用功能,其接口与QT5的QMediaPlaylist保持一致,并在我的项目中引入了这个模块,成功完成任务
// MediaPlaylist.h
#ifndef MEDIAPLAYLIST_H
#define MEDIAPLAYLIST_H#include <QObject>
#include <QList>
#include <QUrl>
#include <QMediaPlayer>class MediaPlaylist : public QObject
{Q_OBJECT
public:enum PlaybackMode {CurrentItemOnce,CurrentItemInLoop,Sequential,Loop,Random};Q_ENUM(PlaybackMode)void bindToPlayer(QMediaPlayer *player);void unbindPlayer();bool isBoundToPlayer() const;explicit MediaPlaylist(QObject *parent = nullptr);int currentIndex() const;QUrl currentMedia() const;int nextIndex(int steps = 1) const;int previousIndex(int steps = 1) const;void addMedia(const QUrl &content);void addMedia(const QList<QUrl> &items);void insertMedia(int index, const QUrl &content);void insertMedia(int index, const QList<QUrl> &items);bool removeMedia(int pos);bool removeMedia(int start, int end);void clear();bool isEmpty() const;int mediaCount() const;QUrl media(int index) const;PlaybackMode playbackMode() const;void setPlaybackMode(PlaybackMode mode);void shuffle();void setCurrentIndex(int index);void next();void previous();void connectToPlayer(QMediaPlayer *player);signals:void currentIndexChanged(int index);void currentMediaChanged(const QUrl &content);void playbackModeChanged(PlaybackMode mode);void mediaAboutToBeInserted(int start, int end);void mediaInserted(int start, int end);void mediaAboutToBeRemoved(int start, int end);void mediaRemoved(int start, int end);void mediaChanged(int start, int end);
private slots:void handlePlayerStateChanged(QMediaPlayer::PlaybackState state);void handleMediaFinished();private:QList<QUrl> m_mediaList;int m_currentIndex = -1;PlaybackMode m_playbackMode = Sequential;QMediaPlayer *m_player = nullptr;int randomIndex() const;void updatePlayerSource();QMediaPlayer *m_boundPlayer = nullptr;bool m_autoPlayOnBind = true; bool m_autoPlayNext = false;
};#endif // MEDIAPLAYLIST_H
// MediaPlaylist.cpp
#include "MediaPlaylist.h"
#include <QRandomGenerator>MediaPlaylist::MediaPlaylist(QObject *parent) : QObject(parent)
{
}int MediaPlaylist::currentIndex() const
{return m_currentIndex;
}QUrl MediaPlaylist::currentMedia() const
{if (m_currentIndex >= 0 && m_currentIndex < m_mediaList.size()) {return m_mediaList.at(m_currentIndex);}return QUrl();
}int MediaPlaylist::nextIndex(int steps) const
{if (m_mediaList.isEmpty()) {return -1;}switch (m_playbackMode) {case CurrentItemOnce:case CurrentItemInLoop:return m_currentIndex;case Sequential:return (m_currentIndex + steps) % m_mediaList.size();case Loop:return (m_currentIndex + steps) % m_mediaList.size();case Random:return randomIndex();}return -1;
}int MediaPlaylist::previousIndex(int steps) const
{if (m_mediaList.isEmpty()) {return -1;}switch (m_playbackMode) {case CurrentItemOnce:case CurrentItemInLoop:return m_currentIndex;case Sequential:case Loop: {int index = m_currentIndex - steps;while (index < 0) {index += m_mediaList.size();}return index;}case Random:return randomIndex();}return -1;
}void MediaPlaylist::addMedia(const QUrl &content)
{insertMedia(m_mediaList.size(), content);
}void MediaPlaylist::addMedia(const QList<QUrl> &items)
{insertMedia(m_mediaList.size(), items);
}void MediaPlaylist::insertMedia(int index, const QUrl &content)
{insertMedia(index, QList<QUrl>() << content);
}void MediaPlaylist::insertMedia(int index, const QList<QUrl> &items)
{if (items.isEmpty()) {return;}int start = qBound(0, index, m_mediaList.size());int end = start + items.size() - 1;emit mediaAboutToBeInserted(start, end);for (int i = 0; i < items.size(); ++i) {m_mediaList.insert(start + i, items.at(i));}emit mediaInserted(start, end);if (m_currentIndex >= start) {m_currentIndex += items.size();}if (m_mediaList.size() == items.size()) {setCurrentIndex(0);}
}bool MediaPlaylist::removeMedia(int pos)
{return removeMedia(pos, pos);
}bool MediaPlaylist::removeMedia(int start, int end)
{if (start < 0 || end >= m_mediaList.size() || start > end) {return false;}emit mediaAboutToBeRemoved(start, end);for (int i = end; i >= start; --i) {m_mediaList.removeAt(i);}emit mediaRemoved(start, end);if (m_currentIndex >= start && m_currentIndex <= end) {if (m_mediaList.isEmpty()) {setCurrentIndex(-1);} else {setCurrentIndex(qMin(start, m_mediaList.size() - 1));}} else if (m_currentIndex > end) {m_currentIndex -= (end - start + 1);}return true;
}void MediaPlaylist::clear()
{if (m_mediaList.isEmpty()) {return;}removeMedia(0, m_mediaList.size() - 1);
}bool MediaPlaylist::isEmpty() const
{return m_mediaList.isEmpty();
}int MediaPlaylist::mediaCount() const
{return m_mediaList.size();
}QUrl MediaPlaylist::media(int index) const
{if (index >= 0 && index < m_mediaList.size()) {return m_mediaList.at(index);}return QUrl();
}MediaPlaylist::PlaybackMode MediaPlaylist::playbackMode() const
{return m_playbackMode;
}void MediaPlaylist::setPlaybackMode(PlaybackMode mode)
{if (m_playbackMode != mode) {m_playbackMode = mode;emit playbackModeChanged(mode);}
}void MediaPlaylist::shuffle()
{if (m_mediaList.size() < 2) {return;}int current = m_currentIndex;QList<QUrl> shuffled;while (!m_mediaList.isEmpty()) {int index = QRandomGenerator::global()->bounded(m_mediaList.size());shuffled.append(m_mediaList.takeAt(index));}m_mediaList = shuffled;m_currentIndex = m_mediaList.indexOf(currentMedia());emit mediaChanged(0, m_mediaList.size() - 1);
}void MediaPlaylist::setCurrentIndex(int index)
{if (index == m_currentIndex) {return;}if (index >= m_mediaList.size()) {index = -1;}int oldIndex = m_currentIndex;m_currentIndex = index;if (oldIndex != m_currentIndex) {emit currentIndexChanged(m_currentIndex);emit currentMediaChanged(currentMedia());updatePlayerSource();}
}void MediaPlaylist::next()
{if (m_mediaList.isEmpty()) {return;}setCurrentIndex(nextIndex());m_boundPlayer->play();
}void MediaPlaylist::previous()
{if (m_mediaList.isEmpty()) {return;}setCurrentIndex(previousIndex());m_boundPlayer->play();
}void MediaPlaylist::connectToPlayer(QMediaPlayer *player)
{bindToPlayer(player);//if (m_player) {disconnect(m_player, &QMediaPlayer::playbackStateChanged,this, &MediaPlaylist::handlePlayerStateChanged);}m_player = player;if (m_player) {connect(m_player, &QMediaPlayer::playbackStateChanged,this, &MediaPlaylist::handlePlayerStateChanged);}//
}void MediaPlaylist::handlePlayerStateChanged(QMediaPlayer::PlaybackState state)
{if (!m_boundPlayer || m_mediaList.isEmpty()) {return;}// 仅在播放停止且不是用户主动停止时处理if (state == QMediaPlayer::StoppedState &&m_boundPlayer->mediaStatus() == QMediaPlayer::EndOfMedia) {switch (m_playbackMode) {case CurrentItemOnce:// 单曲播放一次,不自动继续break;case CurrentItemInLoop:// 单曲循环m_boundPlayer->setPosition(0);if (m_autoPlayNext) {m_boundPlayer->play();}break;case Sequential:case Loop:// 顺序播放或列表循环if (m_autoPlayNext) {next();}break;case Random:// 随机播放if (m_autoPlayNext) {setCurrentIndex(randomIndex());}break;}}// 处理播放错误情况else if (state == QMediaPlayer::StoppedState &&m_boundPlayer->error() != QMediaPlayer::NoError) {qWarning() << "Playback error:" << m_boundPlayer->errorString();// emit playbackErrorOccurred(m_boundPlayer->error(), m_boundPlayer->errorString());}
}int MediaPlaylist::randomIndex() const
{if (m_mediaList.isEmpty()) {return -1;}if (m_mediaList.size() == 1) {return 0;}int index;do {index = QRandomGenerator::global()->bounded(m_mediaList.size());} while (index == m_currentIndex && m_mediaList.size() > 1);return index;
}void MediaPlaylist::updatePlayerSource()
{if (!m_boundPlayer || m_currentIndex < 0 || m_currentIndex >= m_mediaList.size()) {return;}m_boundPlayer->setSource(m_mediaList.at(m_currentIndex));
}
void MediaPlaylist::bindToPlayer(QMediaPlayer *player)
{if (m_boundPlayer == player) {return;}unbindPlayer(); // 先解绑现有的if (player) {m_boundPlayer = player;// 连接播放器状态信号connect(player, &QMediaPlayer::playbackStateChanged,this, &MediaPlaylist::handlePlayerStateChanged);// 连接媒体状态信号(用于检测播放结束)connect(player, &QMediaPlayer::mediaStatusChanged,this, [this](QMediaPlayer::MediaStatus status) {if (status == QMediaPlayer::EndOfMedia) {this->handleMediaFinished();}});// 连接错误信号connect(player, &QMediaPlayer::errorOccurred,this, [this]() {qWarning() << "Player error:" << m_boundPlayer->errorString();});// 自动设置第一个媒体并播放(如果列表不为空)if (!m_mediaList.isEmpty() && m_currentIndex < 0) {setCurrentIndex(0);}// 如果已经有当前选中的媒体,更新播放器源if (m_currentIndex >= 0 && m_currentIndex < m_mediaList.size()) {updatePlayerSource();// 如果设置了自动播放且播放器当前是停止状态,则开始播放if (m_autoPlayOnBind && m_boundPlayer->playbackState() == QMediaPlayer::StoppedState) {m_boundPlayer->play();}}}
}void MediaPlaylist::unbindPlayer()
{if (m_boundPlayer) {disconnect(m_boundPlayer, nullptr, this, nullptr);m_boundPlayer = nullptr;}
}
bool MediaPlaylist::isBoundToPlayer() const
{return m_boundPlayer != nullptr;
}void MediaPlaylist::handleMediaFinished()
{if (!m_boundPlayer) return;switch (m_playbackMode) {case CurrentItemInLoop:m_boundPlayer->setPosition(0);m_boundPlayer->play();break;case CurrentItemOnce:// 不做任何操作,保持停止状态break;default:// 其他模式自动播放下一个next();break;}
}