# MediaPlayer **Repository Path**: alon-1787/media-player ## Basic Information - **Project Name**: MediaPlayer - **Description**: 用QT做的视频播放器,打算支持: 1. 进度条 2. 视频列表 3. 音量控制和播放速度控制 4. 上下首视频切换, 5. 全屏操作 6.其他 - **Primary Language**: C++ - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2023-03-23 - **Last Updated**: 2025-10-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # MediaPlayer ## 介绍 用QT做的视频播放器,打算支持: 1. 进度条 2. 视频列表 3. 音量控制和播放速度控制 4. 上下首视频切换, 5. 全屏操作 6.其他 ## 效果预览 ## 制作步骤 ### 初始化项目 + 创建项目 Player ,使用QWidget初始化 + 创建gitee项目,复制下链接,克隆到本地 + 将项目和gitee连接起来 + 项目目录,右键git bush here 打开。git clone XXXXXX + 打开隐藏文件,将克隆下的文件,包括 `.git` 复制到项目路径 + git add . + git commit -m "初始化" + git push + 头文件: ```cpp #ifndef PLAYER_H #define PLAYER_H #include #include #include #include #include QT_BEGIN_NAMESPACE namespace Ui { class Player; } QT_END_NAMESPACE class Player : public QWidget { Q_OBJECT public: Player(QWidget *parent = nullptr); ~Player(); private slots: void open(); private: Ui::Player *ui; QMediaPlayer *m_player = nullptr; QMediaPlaylist *m_playlist = nullptr; QVideoWidget *m_videoWidget = nullptr; QListWidget *m_playlistView = nullptr; }; #endif // PLAYER_H ``` + cpp文件 ``` #include "Player.h" #include "ui_Player.h" #include "playercontrols.h" #include #include #include #include #include #include #include #include Player::Player(QWidget *parent) : QWidget(parent) , ui(new Ui::Player) { ui->setupUi(this); } Player::~Player() { delete ui; } ``` ### 导入控制台 + 控制条,比如进度条,音量,静音,播放,下一首,上一首,暂停,停止,这些都完全可以封装为一个类,方便以后直接使用。 + 比如这里已经有了一个`playercontrols.h` 和 `playercontrols.cpp` 文件,可以直接导入控制条 + 效果如下: ![image-20230323173718631](assets/image-20230323173718631.png) + 代码使用步骤如下,可以直接复制粘贴拿来使用,而不用以后每次都自己重新敲代码了: ```tex 1. 加入pro文件 2. #include "playercontrols.h" 3. 在构造函数里面添加: PlayerControls *controls = new PlayerControls(this); 4. 如果想排版的话: QBoxLayout *controlLayout = new QHBoxLayout; controlLayout->addWidget(controls); QBoxLayout *layout = new QVBoxLayout; layout->addLayout(controlLayout); setLayout(layout); ``` + playercontrols.h 代码: ```cpp #ifndef PLAYERCONTROLS_H #define PLAYERCONTROLS_H #include #include QT_BEGIN_NAMESPACE class QAbstractButton; class QAbstractSlider; class QComboBox; QT_END_NAMESPACE class PlayerControls : public QWidget { Q_OBJECT public: explicit PlayerControls(QWidget *parent = nullptr); QMediaPlayer::State state() const; int volume() const; bool isMuted() const; qreal playbackRate() const; public slots: void setState(QMediaPlayer::State state); void setVolume(int volume); void setMuted(bool muted); void setPlaybackRate(float rate); signals: void play(); void pause(); void stop(); void next(); void previous(); void changeVolume(int volume); void changeMuting(bool muting); void changeRate(qreal rate); private slots: void playClicked(); void muteClicked(); void updateRate(); void onVolumeSliderValueChanged(); private: QMediaPlayer::State m_playerState = QMediaPlayer::StoppedState; bool m_playerMuted = false; QAbstractButton *m_playButton = nullptr; QAbstractButton *m_stopButton = nullptr; QAbstractButton *m_nextButton = nullptr; QAbstractButton *m_previousButton = nullptr; QAbstractButton *m_muteButton = nullptr; QAbstractSlider *m_volumeSlider = nullptr; QComboBox *m_rateBox = nullptr; }; #endif // PLAYERCONTROLS_H ``` + playercontrols.h 代码: ```cpp #include "playercontrols.h" #include #include #include #include #include #include PlayerControls::PlayerControls(QWidget *parent) : QWidget(parent) { m_playButton = new QToolButton(this); m_playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay)); connect(m_playButton, &QAbstractButton::clicked, this, &PlayerControls::playClicked); m_stopButton = new QToolButton(this); m_stopButton->setIcon(style()->standardIcon(QStyle::SP_MediaStop)); m_stopButton->setEnabled(false); connect(m_stopButton, &QAbstractButton::clicked, this, &PlayerControls::stop); m_nextButton = new QToolButton(this); m_nextButton->setIcon(style()->standardIcon(QStyle::SP_MediaSkipForward)); connect(m_nextButton, &QAbstractButton::clicked, this, &PlayerControls::next); m_previousButton = new QToolButton(this); m_previousButton->setIcon(style()->standardIcon(QStyle::SP_MediaSkipBackward)); connect(m_previousButton, &QAbstractButton::clicked, this, &PlayerControls::previous); m_muteButton = new QToolButton(this); m_muteButton->setIcon(style()->standardIcon(QStyle::SP_MediaVolume)); connect(m_muteButton, &QAbstractButton::clicked, this, &PlayerControls::muteClicked); m_volumeSlider = new QSlider(Qt::Horizontal, this); m_volumeSlider->setRange(0, 100); connect(m_volumeSlider, &QSlider::valueChanged, this, &PlayerControls::onVolumeSliderValueChanged); m_rateBox = new QComboBox(this); m_rateBox->addItem("0.5x", QVariant(0.5)); m_rateBox->addItem("1.0x", QVariant(1.0)); m_rateBox->addItem("2.0x", QVariant(2.0)); m_rateBox->setCurrentIndex(1); connect(m_rateBox, QOverload::of(&QComboBox::activated), this, &PlayerControls::updateRate); QBoxLayout *layout = new QHBoxLayout; layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(m_stopButton); layout->addWidget(m_previousButton); layout->addWidget(m_playButton); layout->addWidget(m_nextButton); layout->addWidget(m_muteButton); layout->addWidget(m_volumeSlider); layout->addWidget(m_rateBox); setLayout(layout); } QMediaPlayer::State PlayerControls::state() const { return m_playerState; } void PlayerControls::setState(QMediaPlayer::State state) { if (state != m_playerState) { m_playerState = state; switch (state) { case QMediaPlayer::StoppedState: m_stopButton->setEnabled(false); m_playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay)); break; case QMediaPlayer::PlayingState: m_stopButton->setEnabled(true); m_playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPause)); break; case QMediaPlayer::PausedState: m_stopButton->setEnabled(true); m_playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay)); break; } } } int PlayerControls::volume() const { qreal linearVolume = QAudio::convertVolume(m_volumeSlider->value() / qreal(100), QAudio::LogarithmicVolumeScale, QAudio::LinearVolumeScale); return qRound(linearVolume * 100); } void PlayerControls::setVolume(int volume) { qreal logarithmicVolume = QAudio::convertVolume(volume / qreal(100), QAudio::LinearVolumeScale, QAudio::LogarithmicVolumeScale); m_volumeSlider->setValue(qRound(logarithmicVolume * 100)); } bool PlayerControls::isMuted() const { return m_playerMuted; } void PlayerControls::setMuted(bool muted) { if (muted != m_playerMuted) { m_playerMuted = muted; m_muteButton->setIcon(style()->standardIcon(muted ? QStyle::SP_MediaVolumeMuted : QStyle::SP_MediaVolume)); } } void PlayerControls::playClicked() { switch (m_playerState) { case QMediaPlayer::StoppedState: case QMediaPlayer::PausedState: emit play(); break; case QMediaPlayer::PlayingState: emit pause(); break; } } void PlayerControls::muteClicked() { emit changeMuting(!m_playerMuted); } qreal PlayerControls::playbackRate() const { return m_rateBox->itemData(m_rateBox->currentIndex()).toDouble(); } void PlayerControls::setPlaybackRate(float rate) { for (int i = 0; i < m_rateBox->count(); ++i) { if (qFuzzyCompare(rate, float(m_rateBox->itemData(i).toDouble()))) { m_rateBox->setCurrentIndex(i); return; } } m_rateBox->addItem(QString("%1x").arg(rate), QVariant(rate)); m_rateBox->setCurrentIndex(m_rateBox->count() - 1); } void PlayerControls::updateRate() { emit changeRate(playbackRate()); } void PlayerControls::onVolumeSliderValueChanged() { emit changeVolume(volume()); } ``` ### 显示控制条: + 完成刚刚的导入以后,在主界面的cpp的构造函数当中,加入代码,将控制条显示出来: + 完整代码参考: ```cpp #include "Player.h" #include "ui_Player.h" #include "playercontrols.h" #include #include #include #include #include Player::Player(QWidget *parent) : QWidget(parent) , ui(new Ui::Player) { ui->setupUi(this); PlayerControls *controls = new PlayerControls(this); // 创建控制台 QBoxLayout *controlLayout = new QHBoxLayout; controlLayout->addWidget(controls); QBoxLayout *layout = new QVBoxLayout; layout->addLayout(controlLayout); setLayout(layout); } Player::~Player() { delete ui; } ``` + 运行以后,如果可以正常看到进度条和按钮,就表示正常。 ### 自定义播放界面的类: + 新建一个C++类 ![image-20230323183632566](assets/image-20230323183632566.png) + 命名为`VideoWidget`,继承自`VideoWidget` ![image-20230323183807918](assets/image-20230323183807918.png) + 在创建的 `videowidget.h`文件中,添加库和重写双击事件的代码: ```cpp #ifndef VIDEOWIDGET_H #define VIDEOWIDGET_H #include // 添加头文件 class VideoWidget : public QVideoWidget { Q_OBJECT public: explicit VideoWidget(QWidget *parent = nullptr); protected: // 重新双击事件 void mouseDoubleClickEvent(QMouseEvent *event) override; }; #endif // VIDEOWIDGET_H ``` + 改写`videowidget.cpp`文件 ```cpp #include "videowidget.h" #include #include VideoWidget::VideoWidget(QWidget *parent) : QVideoWidget(parent) { setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); } // 重写双击事件,可以全屏 void VideoWidget::mouseDoubleClickEvent(QMouseEvent *event) { setFullScreen(!isFullScreen()); event->accept(); } ``` ### 构造函数new出需要的功能 + 在player.cpp构造函数中添加: ```cpp // 创建播放器 m_player = new QMediaPlayer(this); // 创建播放列表 m_playlist = new QMediaPlaylist(); // 创建播放界面 m_videoWidget = new VideoWidget(this); m_player->setVideoOutput(m_videoWidget); // 播放的显示列表 m_playlistView = new QListView(this); ``` + 指定布局: ```cpp QBoxLayout *displayLayout = new QHBoxLayout; displayLayout->addWidget(m_videoWidget, 2); displayLayout->addWidget(m_playlistView); ``` ### 创建打开文件的按钮: + 早构造函数中添加代码: ```cpp // 创建打开文件按钮: QPushButton *openButton = new QPushButton(tr("Open"), this); connect(openButton, &QPushButton::clicked, this, [this](){ open(); qInfo()<isEmpty()) { m_playlistView->setCurrentRow(0); // 默认第一个 m_player->setPlaylist(m_playlist); m_player->play(); } }); ``` + 书写打开文件的槽函数: 这里自行添加相应的头文件。 ```cpp static bool isPlaylist(const QUrl &url) // Check for ".m3u" playlists. { if (!url.isLocalFile()) return false; const QFileInfo fileInfo(url.toLocalFile()); return fileInfo.exists() && !fileInfo.suffix().compare(QLatin1String("m3u"), Qt::CaseInsensitive); } void Player::open() { QFileDialog fileDialog(this); fileDialog.setAcceptMode(QFileDialog::AcceptOpen); fileDialog.setWindowTitle(tr("Open Files")); QStringList supportedMimeTypes = m_player->supportedMimeTypes(); if (!supportedMimeTypes.isEmpty()) { supportedMimeTypes.append("audio/x-m3u"); // MP3 playlists fileDialog.setMimeTypeFilters(supportedMimeTypes); } fileDialog.setDirectory(QStandardPaths::standardLocations(QStandardPaths::MoviesLocation).value(0, QDir::homePath())); if (fileDialog.exec() == QDialog::Accepted) { QList urls = fileDialog.selectedUrls(); for (auto &url: urls) { m_playlistView->addItem(url.fileName()); if (isPlaylist(url)) m_playlist->load(url); else m_playlist->addMedia(url); } } } ``` ### 创建全屏按钮 + 构造函数中: ``` // 全屏按钮: QPushButton *m_fullScreenButton = new QPushButton(tr("FullScreen"), this); ``` ### 写信号链接: 大多数直接复制就能用,因为大多是控制条代码已经封装好的,直接写在构造函数里面: ``` // 控制条的控制功能: connect(controls, &PlayerControls::play, m_player, &QMediaPlayer::play); connect(controls, &PlayerControls::pause, m_player, &QMediaPlayer::pause); connect(controls, &PlayerControls::stop, m_player, &QMediaPlayer::stop); connect(controls, &PlayerControls::next, m_playlist, &QMediaPlaylist::next); connect(controls, &PlayerControls::previous, m_playlist, &QMediaPlaylist::previous); connect(controls, &PlayerControls::changeVolume, m_player, &QMediaPlayer::setVolume); connect(controls, &PlayerControls::changeMuting, m_player, &QMediaPlayer::setMuted); connect(controls, &PlayerControls::changeRate, m_player, &QMediaPlayer::setPlaybackRate); connect(controls, &PlayerControls::stop, m_videoWidget, QOverload<>::of(&QVideoWidget::update)); // 控制播放进度: connect(m_player, &QMediaPlayer::stateChanged, controls, &PlayerControls::setState); connect(m_player, &QMediaPlayer::volumeChanged, controls, &PlayerControls::setVolume); connect(m_player, &QMediaPlayer::mutedChanged, controls, &PlayerControls::setMuted); // 全屏按钮: connect(m_fullScreenButton,&QPushButton::clicked,m_videoWidget,[=](){ m_videoWidget->setFullScreen(1); }); ``` ### 进度条 + 在头文件中添加: ``` void seek(int); ``` + 构造函数中,添加代码: ``` // 进度条: m_slider = new QSlider(Qt::Horizontal, this); m_slider->setRange(0, m_player->duration() / 1000); m_labelDuration = new QLabel(this); connect(m_slider, &QSlider::sliderMoved, this, &Player::seek); ``` + 在cpp文件中添加: ``` void Player::seek(int seconds) { m_player->setPosition(seconds * 1000); } ``` ### 整个布局: ``` // 播放窗口的垂直布局: QBoxLayout *displayLayout = new QHBoxLayout; displayLayout->addWidget(m_videoWidget, 2); displayLayout->addWidget(m_playlistView); // 创建控制台 QBoxLayout *controlLayout = new QHBoxLayout; controlLayout->setContentsMargins(0, 0, 0, 0); controlLayout->addWidget(openButton); controlLayout->addStretch(1); controlLayout->addWidget(controls); controlLayout->addStretch(1); controlLayout->addWidget(m_fullScreenButton); // 整个的layout QBoxLayout *layout = new QVBoxLayout; layout->addLayout(displayLayout); QHBoxLayout *hLayout = new QHBoxLayout; hLayout->addWidget(m_slider); hLayout->addWidget(m_labelDuration); layout->addLayout(hLayout); layout->addLayout(controlLayout); setLayout(layout); ``` + 效果如下 ![image-20230323203314535](assets/image-20230323203314535.png) + 测试一下功能: 可以播放,也可以调节速度,可以全屏播放,问题不大。 ![image-20230323201248586](assets/image-20230323201248586.png) ### 增加进度条调节视频播放功能 ### 增加点击列表切换功能