前言
##### 大家都找到实习了吗,我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;
}
}