QT6引入QMediaPlaylist类

发布于:2025-06-05 ⋅ 阅读:(22) ⋅ 点赞:(0)

前言

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


网站公告

今日签到

点亮在社区的每一天
去签到