From b3c23d096c5753c0dd53c2317aa091461322f17f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=AE=E7=94=9F=E8=8B=A5=E6=A2=A6?= <1070753498@qq.com> Date: Wed, 9 Oct 2024 12:00:23 +0800 Subject: [PATCH] =?UTF-8?q?[=E6=B7=BB=E5=8A=A0QPlayer=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE]:=20=E5=9C=A8examples=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E4=B8=8B=E6=B7=BB=E5=8A=A0=E4=BA=86QPlayer=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=EF=BC=8C=E6=94=AF=E6=8C=81=E4=BA=86=E6=96=B0?= =?UTF-8?q?=E7=9A=84=E6=92=AD=E6=94=BE=E5=99=A8=E5=8A=9F=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在CMakeLists.txt中添加了新的子目录qplayer,并在其中定义了QPlayer的构建规则。 - 添加了qplayer相关的源文件和头文件,包括主窗口类、视频显示控件等。 - 在qplayer的CMakeLists.txt中链接了Qt6的MultimediaWidgets、Network等模块。 - 修改了其他示例项目和库以适应新的QPlayer项目结构,并确保所有项目都能正确链接Qt6库。 - 更新了3rdparty库的CMakeLists.txt,确保它们与Qt6库兼容。 --- examples/CMakeLists.txt | 1 + examples/common/controlwidget.cc | 7 +- examples/common/controlwidget.hpp | 8 +- examples/common/titlewidget.cc | 2 - examples/examples.pro | 3 +- examples/ffmpegplayer/CMakeLists.txt | 7 +- examples/ffmpegplayer/ffmpegplayer.pro | 2 + examples/ffmpegplayer/mainwindow.cpp | 92 ++-- examples/ffmpegplayer/mainwindow.h | 2 - examples/ffmpegtranscoder/CMakeLists.txt | 6 +- examples/mpvplayer/CMakeLists.txt | 8 +- examples/mpvplayer/mainwindow.cc | 120 ++--- examples/qplayer/CMakeLists.txt | 33 ++ examples/qplayer/main.cc | 82 +++ examples/qplayer/mainwindow.cc | 510 ++++++++++++++++++ examples/qplayer/mainwindow.hpp | 38 ++ examples/qplayer/qplayer.pro | 46 ++ examples/qplayer/videowidget.cc | 11 + examples/qplayer/videowidget.hpp | 10 + src/3rdparty/CMakeLists.txt | 4 +- src/3rdparty/qtlockedfile/CMakeLists.txt | 2 +- .../qtsingleapplication/CMakeLists.txt | 18 +- src/dump/CMakeLists.txt | 2 +- src/ffmpeg/CMakeLists.txt | 4 +- src/ffmpeg/audiorender/audiooutput.cc | 4 + src/mediaconfig/CMakeLists.txt | 2 +- src/mpv/CMakeLists.txt | 2 +- src/mpv/mpvwidget.cc | 2 +- src/utils/CMakeLists.txt | 2 +- tests/subtitle_unittest/CMakeLists.txt | 4 +- 30 files changed, 858 insertions(+), 176 deletions(-) create mode 100644 examples/qplayer/CMakeLists.txt create mode 100644 examples/qplayer/main.cc create mode 100644 examples/qplayer/mainwindow.cc create mode 100644 examples/qplayer/mainwindow.hpp create mode 100644 examples/qplayer/qplayer.pro create mode 100644 examples/qplayer/videowidget.cc create mode 100644 examples/qplayer/videowidget.hpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index b4e78f8..f61566c 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(ffmpegplayer) add_subdirectory(ffmpegtranscoder) +add_subdirectory(qplayer) if(BUILD_MPV) add_subdirectory(mpvplayer) diff --git a/examples/common/controlwidget.cc b/examples/common/controlwidget.cc index 0553dc4..2abe37b 100644 --- a/examples/common/controlwidget.cc +++ b/examples/common/controlwidget.cc @@ -131,9 +131,6 @@ ControlWidget::ControlWidget(QWidget *parent) : QWidget{parent} , d_ptr(new ControlWidgetPrivate(this)) { - setWindowFlags(windowFlags() | Qt::FramelessWindowHint | Qt::Tool); - setAttribute(Qt::WA_TranslucentBackground); //设置窗口背景透明 - d_ptr->setupUI(); buildConnect(); d_ptr->initModelButton(); @@ -213,7 +210,7 @@ void ControlWidget::setDuration(int value) setPosition(0); } -#ifdef MPV_ON +#if defined(MPV_ON) void ControlWidget::setChapters(const Mpv::Chapters &chapters) { QVector nodes; @@ -222,7 +219,7 @@ void ControlWidget::setChapters(const Mpv::Chapters &chapters) } d_ptr->slider->setNodes(nodes); } -#else +#elif defined(FFMPEG_ON) void ControlWidget::setChapters(const Ffmpeg::Chapters &chapters) { QVector nodes; diff --git a/examples/common/controlwidget.hpp b/examples/common/controlwidget.hpp index 07d61b1..f725c28 100644 --- a/examples/common/controlwidget.hpp +++ b/examples/common/controlwidget.hpp @@ -1,9 +1,9 @@ #ifndef CONTROLWIDGET_HPP #define CONTROLWIDGET_HPP -#ifdef MPV_ON +#if defined(MPV_ON) #include -#else +#elif defined(FFMPEG_ON) #include #endif @@ -21,9 +21,9 @@ class ControlWidget : public QWidget void setDuration(int value); [[nodiscard]] auto duration() const -> int; -#ifdef MPV_ON +#if defined(MPV_ON) void setChapters(const Mpv::Chapters &chapters); -#else +#elif defined(FFMPEG_ON) void setChapters(const Ffmpeg::Chapters &chapters); #endif [[nodiscard]] auto sliderGlobalPos() const -> QPoint; diff --git a/examples/common/titlewidget.cc b/examples/common/titlewidget.cc index 7b43f8c..4edd4c9 100644 --- a/examples/common/titlewidget.cc +++ b/examples/common/titlewidget.cc @@ -25,8 +25,6 @@ TitleWidget::TitleWidget(QWidget *parent) : QWidget{parent} , d_ptr(new TitleWidgetPrivate(this)) { - setWindowFlags(windowFlags() | Qt::FramelessWindowHint | Qt::Tool); - setAttribute(Qt::WA_TranslucentBackground); setupUI(); buildConnect(); diff --git a/examples/examples.pro b/examples/examples.pro index 09dc8a9..2f0607f 100644 --- a/examples/examples.pro +++ b/examples/examples.pro @@ -3,7 +3,8 @@ CONFIG += ordered SUBDIRS += \ ffmpegplayer \ - ffmpegtranscoder + ffmpegtranscoder \ + qplayer win32 { exists("C:/3rd/x64/mpv/include"){ diff --git a/examples/ffmpegplayer/CMakeLists.txt b/examples/ffmpegplayer/CMakeLists.txt index 8745840..0a1d974 100644 --- a/examples/ffmpegplayer/CMakeLists.txt +++ b/examples/ffmpegplayer/CMakeLists.txt @@ -24,6 +24,7 @@ set(PROJECT_SOURCES mainwindow.h) qt_add_executable(FfmpegPlayer MANUAL_FINALIZATION ${PROJECT_SOURCES}) +target_compile_definitions(FfmpegPlayer PRIVATE "FFMPEG_ON") target_link_libraries( FfmpegPlayer PRIVATE ffmpeg @@ -31,9 +32,9 @@ target_link_libraries( thirdparty dump utils - Qt6::Widgets - Qt6::Multimedia - Qt6::OpenGLWidgets) + Qt::Widgets + Qt::Multimedia + Qt::OpenGLWidgets) target_link_libraries(FfmpegPlayer PRIVATE PkgConfig::ffmpeg) if(CMAKE_HOST_APPLE) target_link_libraries( diff --git a/examples/ffmpegplayer/ffmpegplayer.pro b/examples/ffmpegplayer/ffmpegplayer.pro index 0278c70..bfe331e 100644 --- a/examples/ffmpegplayer/ffmpegplayer.pro +++ b/examples/ffmpegplayer/ffmpegplayer.pro @@ -6,6 +6,8 @@ TEMPLATE = app TARGET = FfmpegPlayer +DEFINES += FFMPEG_ON + LIBS += \ -l$$replaceLibName(ffmpeg) \ -l$$replaceLibName(mediaconfig) \ diff --git a/examples/ffmpegplayer/mainwindow.cpp b/examples/ffmpegplayer/mainwindow.cpp index 2c6082d..eb2977f 100644 --- a/examples/ffmpegplayer/mainwindow.cpp +++ b/examples/ffmpegplayer/mainwindow.cpp @@ -18,6 +18,7 @@ #include #include +#include #include class MainWindow::MainWindowPrivate @@ -32,14 +33,6 @@ class MainWindow::MainWindowPrivate titleWidget = new TitleWidget(q_ptr); titleWidget->setMinimumHeight(80); titleWidget->setVisible(false); - auto *bottomLayout = new QHBoxLayout; - bottomLayout->addStretch(); - bottomLayout->addWidget(controlWidget); - bottomLayout->addStretch(); - controlLayout = new QVBoxLayout; - controlLayout->addWidget(titleWidget); - controlLayout->addStretch(); - controlLayout->addLayout(bottomLayout); playlistModel = new PlaylistModel(q_ptr); playlistView = new PlayListView(q_ptr); @@ -47,9 +40,9 @@ class MainWindow::MainWindowPrivate playlistView->setCurrentIndex( playlistModel->index(playlistModel->playlist()->currentIndex(), 0)); //playlistView->setMaximumWidth(250); + playListMenu = new QMenu(q_ptr); menu = new QMenu(q_ptr); - videoMenu = new QMenu(QCoreApplication::translate("MainWindowPrivate", "Video"), q_ptr); audioMenu = new QMenu(QCoreApplication::translate("MainWindowPrivate", "Audio"), q_ptr); subMenu = new QMenu(QCoreApplication::translate("MainWindowPrivate", "Subtitles"), q_ptr); @@ -64,18 +57,29 @@ class MainWindow::MainWindowPrivate mediaInfoAction = new QAction(QCoreApplication::translate("MainWindowPrivate", "Media Info"), q_ptr); - playListMenu = new QMenu(q_ptr); - fpsTimer = new QTimer(q_ptr); - splitter = new QSplitter(q_ptr); - splitter->addWidget(playlistView); - initShortcut(); } ~MainWindowPrivate() = default; + void setupUI() + { + auto *bottomLayout = new QHBoxLayout; + bottomLayout->addStretch(); + bottomLayout->addWidget(controlWidget); + bottomLayout->addStretch(); + controlLayout = new QVBoxLayout; + controlLayout->addWidget(titleWidget); + controlLayout->addStretch(); + controlLayout->addLayout(bottomLayout); + + splitter = new QSplitter(q_ptr); + splitter->addWidget(playlistView); + q_ptr->setCentralWidget(splitter); + } + auto createToneMappingMenu() -> QMenu * { auto *group = new QActionGroup(q_ptr); @@ -242,6 +246,24 @@ class MainWindow::MainWindowPrivate controlWidget->setPosition(0); } + void addToPlaylist(const QList &urls) + { + auto *playlist = playlistModel->playlist(); + const int previousMediaCount = playlist->mediaCount(); + for (const auto &url : urls) { + if (isPlaylist(url)) { + playlist->load(url); + } else { + playlist->addMedia(url); + } + } + if (playlist->mediaCount() > previousMediaCount) { + auto index = playlistModel->index(previousMediaCount, 0); + playlistView->setCurrentIndex(index); + q_ptr->jump(index); + } + } + MainWindow *q_ptr; QScopedPointer playerPtr; @@ -254,6 +276,8 @@ class MainWindow::MainWindowPrivate PlayListView *playlistView; PlaylistModel *playlistModel; + QMenu *playListMenu; + QSplitter *splitter; QMenu *menu; @@ -271,12 +295,8 @@ class MainWindow::MainWindowPrivate QAction *mediaInfoAction; - QMenu *playListMenu; - QTimer *fpsTimer; - QSplitter *splitter; - Ffmpeg::ToneMapping::Type toneMappingType = Ffmpeg::ToneMapping::Type::AUTO; Ffmpeg::ColorUtils::Primaries::Type primarisType = Ffmpeg::ColorUtils::Primaries::Type::AUTO; }; @@ -287,7 +307,12 @@ MainWindow::MainWindow(QWidget *parent) { Ffmpeg::printFfmpegInfo(); - setupUI(); + // QPlatformMediaDevices中有一个static std::unique_ptr create(); + // 这个QPlatformMediaDevices最终会在主线程释放,需要在主线程创建 + // 如果在主线程创建,会导致退出程序崩溃,ASSERT: "m_threadId == GetCurrentThreadId()" + QMediaDevices mediaDevices; + + d_ptr->setupUI(); buildConnect(); initMenu(); initPlayListMenu(); @@ -369,7 +394,7 @@ void MainWindow::onOpenLocalMedia() if (urls.isEmpty()) { return; } - addToPlaylist(urls); + d_ptr->addToPlaylist(urls); } void MainWindow::onOpenWebMedia() @@ -379,7 +404,7 @@ void MainWindow::onOpenWebMedia() if (dialog.exec() != QDialog::Accepted) { return; } - addToPlaylist({QUrl(dialog.url())}); + d_ptr->addToPlaylist({QUrl(dialog.url())}); } void MainWindow::onRenderChanged(QAction *action) @@ -552,7 +577,7 @@ auto MainWindow::eventFilter(QObject *watched, QEvent *event) -> bool auto *ev = dynamic_cast(event); auto urls = ev->mimeData()->urls(); if (!urls.isEmpty()) { - addToPlaylist(urls); + d_ptr->addToPlaylist(urls); } } break; case QEvent::ContextMenu: { @@ -619,11 +644,6 @@ void MainWindow::keyPressEvent(QKeyEvent *event) } } -void MainWindow::setupUI() -{ - setCentralWidget(d_ptr->splitter); -} - void MainWindow::buildConnect() { connect(d_ptr->playerPtr.data(), @@ -776,21 +796,3 @@ void MainWindow::initPlayListMenu() d_ptr->playlistModel->playlist()->clear(); }); } - -void MainWindow::addToPlaylist(const QList &urls) -{ - auto *playlist = d_ptr->playlistModel->playlist(); - const int previousMediaCount = playlist->mediaCount(); - for (const auto &url : urls) { - if (isPlaylist(url)) { - playlist->load(url); - } else { - playlist->addMedia(url); - } - } - if (playlist->mediaCount() > previousMediaCount) { - auto index = d_ptr->playlistModel->index(previousMediaCount, 0); - d_ptr->playlistView->setCurrentIndex(index); - jump(index); - } -} diff --git a/examples/ffmpegplayer/mainwindow.h b/examples/ffmpegplayer/mainwindow.h index 8e8e8d9..a14be94 100644 --- a/examples/ffmpegplayer/mainwindow.h +++ b/examples/ffmpegplayer/mainwindow.h @@ -35,12 +35,10 @@ private slots: void keyPressEvent(QKeyEvent *event) override; private: - void setupUI(); void buildConnect(); void initMenu(); void renderMenu(); void initPlayListMenu(); - void addToPlaylist(const QList &urls); class MainWindowPrivate; QScopedPointer d_ptr; diff --git a/examples/ffmpegtranscoder/CMakeLists.txt b/examples/ffmpegtranscoder/CMakeLists.txt index da878f6..03026a9 100644 --- a/examples/ffmpegtranscoder/CMakeLists.txt +++ b/examples/ffmpegtranscoder/CMakeLists.txt @@ -37,9 +37,9 @@ target_link_libraries( thirdparty dump utils - Qt6::Widgets - Qt6::Multimedia - Qt6::OpenGLWidgets) + Qt::Widgets + Qt::Multimedia + Qt::OpenGLWidgets) target_link_libraries(FfmpegTranscoder PRIVATE PkgConfig::ffmpeg) if(CMAKE_HOST_APPLE) diff --git a/examples/mpvplayer/CMakeLists.txt b/examples/mpvplayer/CMakeLists.txt index b17f5af..fcbb489 100644 --- a/examples/mpvplayer/CMakeLists.txt +++ b/examples/mpvplayer/CMakeLists.txt @@ -36,10 +36,10 @@ target_link_libraries( thirdparty dump utils - Qt6::Widgets - Qt6::Multimedia - Qt6::Network - Qt6::OpenGLWidgets) + Qt::Widgets + Qt::Multimedia + Qt::Network + Qt::OpenGLWidgets) if(CMAKE_HOST_WIN32) target_link_libraries(MpvPlayer PRIVATE C:\\3rd\\x64\\mpv\\libmpv.dll.a) file(COPY C:\\3rd\\x64\\mpv\\libmpv-2.dll diff --git a/examples/mpvplayer/mainwindow.cc b/examples/mpvplayer/mainwindow.cc index 6e854aa..340d09e 100644 --- a/examples/mpvplayer/mainwindow.cc +++ b/examples/mpvplayer/mainwindow.cc @@ -46,6 +46,11 @@ class MainWindow::MainWindowPrivate logWindow->show(); logWindow->move(qApp->primaryScreen()->availableGeometry().topLeft()); + floatingWidget = new QWidget(q_ptr); + floatingWidget->setWindowFlags(floatingWidget->windowFlags() | Qt::FramelessWindowHint + | Qt::Tool); + floatingWidget->setAttribute(Qt::WA_TranslucentBackground); //设置窗口背景透明 + floatingWidget->setVisible(true); controlWidget = new ControlWidget(q_ptr); controlWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); titleWidget = new TitleWidget(q_ptr); @@ -213,17 +218,13 @@ class MainWindow::MainWindowPrivate { new QShortcut(QKeySequence::MoveToNextChar, q_ptr, q_ptr, [this] { mpvPlayer->seekRelative(5); - titleWidget->setText( + setTitleWidgetText( QCoreApplication::translate("MainWindowPrivate", "Fast forward: 5 seconds")); - titleWidget->setAutoHide(3000); - setTitleWidgetGeometry(true); }); new QShortcut(QKeySequence::MoveToPreviousChar, q_ptr, q_ptr, [this] { mpvPlayer->seekRelative(-5); - titleWidget->setText( + setTitleWidgetText( QCoreApplication::translate("MainWindowPrivate", "Fast return: 5 seconds")); - titleWidget->setAutoHide(3000); - setTitleWidgetGeometry(true); }); new QShortcut(QKeySequence::MoveToPreviousLine, q_ptr, q_ptr, [this] { controlWidget->setVolume(controlWidget->volume() + 10); @@ -234,18 +235,16 @@ class MainWindow::MainWindowPrivate new QShortcut(Qt::Key_Space, q_ptr, q_ptr, [this] { pause(); }); } - void setupUI() const + void setupUI() { -#ifndef Q_OS_WIN - auto controlLayout = new QHBoxLayout; + auto *controlLayout = new QHBoxLayout; controlLayout->addStretch(); controlLayout->addWidget(controlWidget); controlLayout->addStretch(); - auto layout = new QVBoxLayout(mpvWidget); + auto *layout = new QVBoxLayout(floatingWidget); layout->addWidget(titleWidget); layout->addStretch(); layout->addLayout(controlLayout); -#endif auto *splitter = new QSplitter(q_ptr); splitter->setHandleWidth(0); @@ -256,42 +255,32 @@ class MainWindow::MainWindowPrivate q_ptr->setCentralWidget(splitter); } + void setTitleWidgetText(const QString &text) + { + setTitleWidgetGeometry(true); + titleWidget->setText(text); + titleWidget->setAutoHide(3000); + } + void setControlWidgetGeometry(bool show = true) { -#ifdef Q_OS_WIN - controlWidget->setFixedSize({QWIDGETSIZE_MAX, QWIDGETSIZE_MAX}); - controlWidget->setMinimumWidth(mpvWidget->width() / 2); - controlWidget->adjustSize(); - if (controlWidget->width() * 2 < mpvWidget->width()) { - controlWidget->setMinimumWidth(mpvWidget->width() / 2); - } - auto margain = 10; - auto geometry = mpvWidget->geometry(); - auto p1 = QPoint(geometry.x() + (geometry.width() - controlWidget->width()) / 2.0, - geometry.bottomLeft().y() - controlWidget->height() - margain); - globalControlWidgetGeometry = {q_ptr->mapToGlobal(p1), controlWidget->size()}; - controlWidget->setFixedSize(globalControlWidgetGeometry.size()); - controlWidget->setGeometry(globalControlWidgetGeometry); + controlWidget->setVisible(true); + setFloatingWidgetGeometry(show); controlWidget->setVisible(show); -#else - controlWidget->setVisible(show); -#endif } void setTitleWidgetGeometry(bool show = true) { -#ifdef Q_OS_WIN - auto margain = 10; - auto geometry = mpvWidget->geometry(); - auto p1 = QPoint(geometry.x() + margain, geometry.y() + margain); - auto p2 = QPoint(geometry.topRight().x() - margain, geometry.y() + margain + 80); - globalTitlelWidgetGeometry = {q_ptr->mapToGlobal(p1), q_ptr->mapToGlobal(p2)}; - titleWidget->setFixedSize(globalTitlelWidgetGeometry.size()); - titleWidget->setGeometry(globalTitlelWidgetGeometry); + titleWidget->setVisible(true); + setFloatingWidgetGeometry(show); titleWidget->setVisible(show); -#else - titleWidget->setVisible(show); -#endif + } + + void setFloatingWidgetGeometry(bool show = true) + { + auto geometry = mpvWidget->geometry(); + geometry = QRect{q_ptr->mapToGlobal(geometry.topLeft()), geometry.size()}; + floatingWidget->setGeometry(geometry); } void pause() const { mpvPlayer->pauseAsync(); } @@ -303,12 +292,9 @@ class MainWindow::MainWindowPrivate QScopedPointer previewWidgetPtr; Mpv::MpvLogWindow *logWindow; + QWidget *floatingWidget; ControlWidget *controlWidget; TitleWidget *titleWidget; -#ifdef Q_OS_WIN - QRect globalTitlelWidgetGeometry; - QRect globalControlWidgetGeometry; -#endif PlayListView *playlistView; PlaylistModel *playlistModel; @@ -580,7 +566,6 @@ auto MainWindow::eventFilter(QObject *watched, QEvent *event) -> bool auto *e = static_cast(event); d_ptr->menu->exec(e->globalPos()); } break; -#ifdef Q_OS_WIN case QEvent::Resize: QMetaObject::invokeMethod( this, @@ -590,7 +575,6 @@ auto MainWindow::eventFilter(QObject *watched, QEvent *event) -> bool }, Qt::QueuedConnection); break; -#endif // case QEvent::MouseButtonPress: { // auto e = static_cast(event); // if (e->button() & Qt::LeftButton) { @@ -617,7 +601,6 @@ auto MainWindow::eventFilter(QObject *watched, QEvent *event) -> bool } } else if (watched == this) { switch (event->type()) { -#ifdef Q_OS_WIN case QEvent::Show: case QEvent::Move: QMetaObject::invokeMethod( @@ -628,50 +611,19 @@ auto MainWindow::eventFilter(QObject *watched, QEvent *event) -> bool }, Qt::QueuedConnection); break; - case QEvent::Hide: d_ptr->controlWidget->hide(); break; - case QEvent::HoverMove: - if (d_ptr->globalControlWidgetGeometry.isValid()) { - auto *e = static_cast(event); - bool contain = d_ptr->globalControlWidgetGeometry.contains( - e->globalPosition().toPoint()); - bool isVisible = d_ptr->controlWidget->isVisible(); - if (contain && !isVisible) { - d_ptr->setControlWidgetGeometry(true); - } else if (!contain && isVisible) { - d_ptr->controlWidget->hide(); - } - } - if (d_ptr->globalTitlelWidgetGeometry.isValid() && isFullScreen()) { - auto *e = static_cast(event); - bool contain = d_ptr->globalTitlelWidgetGeometry.contains( - e->globalPosition().toPoint()); - bool isVisible = d_ptr->titleWidget->isVisible(); - if (contain && !isVisible) { - d_ptr->titleWidget->setText(windowTitle()); - d_ptr->setTitleWidgetGeometry(true); - } else if (!contain && isVisible) { - d_ptr->titleWidget->hide(); - } - } - break; -#else + case QEvent::Hide: d_ptr->setFloatingWidgetGeometry(false); break; case QEvent::HoverMove: { - d_ptr->controlWidget->show(); - d_ptr->controlWidget->hide(); auto controlWidgetGeometry = d_ptr->controlWidget->geometry(); auto e = static_cast(event); bool contain = controlWidgetGeometry.contains(e->position().toPoint()); d_ptr->setControlWidgetGeometry(contain); if (isFullScreen()) { - d_ptr->titleWidget->show(); - d_ptr->titleWidget->hide(); auto titleWidgetGeometry = d_ptr->titleWidget->geometry(); contain = titleWidgetGeometry.contains(e->position().toPoint()); d_ptr->setTitleWidgetGeometry(contain); } break; } -#endif default: break; } } @@ -731,19 +683,15 @@ void MainWindow::buildConnect() &QMediaPlaylist::next); connect(d_ptr->controlWidget, &ControlWidget::seek, d_ptr->mpvPlayer, [this](int value) { d_ptr->mpvPlayer->seek(value); - d_ptr->titleWidget->setText( + d_ptr->setTitleWidgetText( tr("Fast forward to: %1") .arg(QTime::fromMSecsSinceStartOfDay(value * 1000).toString("hh:mm:ss"))); - d_ptr->titleWidget->setAutoHide(3000); - d_ptr->setTitleWidgetGeometry(true); }); connect(d_ptr->controlWidget, &ControlWidget::hoverPosition, this, &MainWindow::onPreview); connect(d_ptr->controlWidget, &ControlWidget::leavePosition, this, &MainWindow::onPreviewFinish); connect(d_ptr->controlWidget, &ControlWidget::volumeChanged, d_ptr->mpvPlayer, [this](int value) { d_ptr->mpvPlayer->setVolume(value); - d_ptr->titleWidget->setText(tr("Volume: %1%").arg(value)); - d_ptr->titleWidget->setAutoHide(3000); - d_ptr->setTitleWidgetGeometry(true); + d_ptr->setTitleWidgetText(tr("Volume: %1%").arg(value)); }); auto max = d_ptr->mpvPlayer->volumeMax(); d_ptr->controlWidget->setVolumeMax(max); @@ -753,9 +701,7 @@ void MainWindow::buildConnect() d_ptr->mpvPlayer, [this](double value) { d_ptr->mpvPlayer->setSpeed(value); - d_ptr->titleWidget->setText(tr("Speed: %1").arg(value)); - d_ptr->titleWidget->setAutoHide(3000); - d_ptr->setTitleWidgetGeometry(true); + d_ptr->setTitleWidgetText(tr("Speed: %1").arg(value)); }); connect(d_ptr->controlWidget, &ControlWidget::modelChanged, diff --git a/examples/qplayer/CMakeLists.txt b/examples/qplayer/CMakeLists.txt new file mode 100644 index 0000000..89239b7 --- /dev/null +++ b/examples/qplayer/CMakeLists.txt @@ -0,0 +1,33 @@ +find_package(Qt6 REQUIRED COMPONENTS MultimediaWidgets) + +set(PROJECT_SOURCES + ../common/controlwidget.cc + ../common/controlwidget.hpp + ../common/equalizerdialog.cpp + ../common/equalizerdialog.h + ../common/openwebmediadialog.cc + ../common/openwebmediadialog.hpp + ../common/playlistmodel.cpp + ../common/playlistmodel.h + ../common/playlistview.cc + ../common/playlistview.hpp + ../common/qmediaplaylist_p.cpp + ../common/qmediaplaylist_p.h + ../common/qmediaplaylist.cpp + ../common/qmediaplaylist.h + ../common/qplaylistfileparser.cpp + ../common/qplaylistfileparser.h + ../common/slider.cpp + ../common/slider.h + ../common/titlewidget.cc + ../common/titlewidget.hpp + main.cc + mainwindow.cc + mainwindow.hpp + videowidget.cc + videowidget.hpp) + +qt_add_executable(QPlayer MANUAL_FINALIZATION ${PROJECT_SOURCES}) +target_link_libraries(QPlayer PRIVATE mediaconfig thirdparty dump utils + Qt::MultimediaWidgets Qt::Network) +qt_finalize_executable(QPlayer) diff --git a/examples/qplayer/main.cc b/examples/qplayer/main.cc new file mode 100644 index 0000000..d8b1b2d --- /dev/null +++ b/examples/qplayer/main.cc @@ -0,0 +1,82 @@ +#include "mainwindow.hpp" + +#include <3rdparty/qtsingleapplication/qtsingleapplication.h> +#include +#include +#include +#include + +#include +#include +#include +#include + +#define AppName "QPlayer" + +void setAppInfo() +{ + qApp->setApplicationVersion(AppInfo::version.toString()); + qApp->setApplicationDisplayName(AppName); + qApp->setApplicationName(AppName); + qApp->setDesktopFileName(AppName); + qApp->setOrganizationDomain(AppInfo::organizationDomain); + qApp->setOrganizationName(AppInfo::organzationName); + qApp->setWindowIcon(qApp->style()->standardIcon(QStyle::SP_MediaPlay)); +} + +int main(int argc, char *argv[]) +{ +#if defined(Q_OS_WIN) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + if (!qEnvironmentVariableIsSet("QT_OPENGL")) { + QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); + } +#else + qputenv("QSG_RHI_BACKEND", "opengl"); +#endif + Utils::setHighDpiEnvironmentVariable(); + + Utils::setSurfaceFormatVersion(3, 3); + + SharedTools::QtSingleApplication::setAttribute(Qt::AA_ShareOpenGLContexts); + SharedTools::QtSingleApplication app(AppName, argc, argv); + if (app.isRunning()) { + qWarning() << "This is already running"; + if (app.sendMessage("raise_window_noop", 5000)) { + return EXIT_SUCCESS; + } + } +#ifdef Q_OS_WIN + if (!qFuzzyCompare(app.devicePixelRatio(), 1.0) + && QApplication::style()->objectName().startsWith(QLatin1String("windows"), + Qt::CaseInsensitive)) { + QApplication::setStyle(QLatin1String("fusion")); + } +#endif +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + app.setAttribute(Qt::AA_UseHighDpiPixmaps); + app.setAttribute(Qt::AA_DisableWindowContextHelpButton); +#endif + setAppInfo(); + Dump::BreakPad::instance()->setDumpPath(Utils::crashPath()); + QDir::setCurrent(app.applicationDirPath()); + + // 异步日志 + auto *log = Utils::LogAsync::instance(); + log->setLogPath(Utils::logPath()); + log->setAutoDelFile(true); + log->setAutoDelFileDays(7); + log->setOrientation(Utils::LogAsync::Orientation::StdAndFile); + log->setLogLevel(QtDebugMsg); + log->startWork(); + + // Make sure we honor the system's proxy settings + QNetworkProxyFactory::setUseSystemConfiguration(true); + + MainWindow w; + app.setActivationWindow(&w); + w.show(); + + auto ret = app.exec(); + log->stop(); + return ret; +} diff --git a/examples/qplayer/mainwindow.cc b/examples/qplayer/mainwindow.cc new file mode 100644 index 0000000..84c7a31 --- /dev/null +++ b/examples/qplayer/mainwindow.cc @@ -0,0 +1,510 @@ +#include "mainwindow.hpp" +#include "videowidget.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +void printAudioOuputDevice() +{ + const auto audioDevices = QMediaDevices::audioOutputs(); + for (const QAudioDevice &audioDevice : std::as_const(audioDevices)) { + qDebug() << audioDevice.id() << audioDevice.description() << audioDevice.isDefault(); + } +} + +class MainWindow::MainWindowPrivate +{ +public: + explicit MainWindowPrivate(MainWindow *q) + : q_ptr(q) + { + mediaDevices = new QMediaDevices(q_ptr); + + player = new QMediaPlayer(q_ptr); + audioOutput = new QAudioOutput(q_ptr); + player->setAudioOutput(audioOutput); + videoWidget = new VideoWidget(q_ptr); + player->setVideoOutput(videoWidget); + + floatingWidget = new QWidget(q_ptr); + floatingWidget->setWindowFlags(floatingWidget->windowFlags() | Qt::FramelessWindowHint + | Qt::Tool); + floatingWidget->setAttribute(Qt::WA_TranslucentBackground); //设置窗口背景透明 + floatingWidget->setVisible(true); + controlWidget = new ControlWidget(q_ptr); + controlWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + titleWidget = new TitleWidget(q_ptr); + titleWidget->setMinimumHeight(80); + + playlistModel = new PlaylistModel(q_ptr); + playlistView = new PlayListView(q_ptr); + playlistView->setModel(playlistModel); + playlistView->setCurrentIndex( + playlistModel->index(playlistModel->playlist()->currentIndex(), 0)); + //playlistView->setMaximumWidth(250); + playListMenu = new QMenu(q_ptr); + + menu = new QMenu(q_ptr); + + initShortcut(); + } + + void initShortcut() + { + new QShortcut(QKeySequence::MoveToNextChar, q_ptr, q_ptr, [this] { + auto position = player->position(); + position += 5000; + if (position > player->duration()) { + position = player->duration(); + } + setPosition(position); + setTitleWidgetText( + QCoreApplication::translate("MainWindowPrivate", "Fast forward: 5 seconds")); + }); + new QShortcut(QKeySequence::MoveToPreviousChar, q_ptr, q_ptr, [this] { + auto position = player->position(); + position -= 5000; + if (position < 0) { + position = 0; + } + setPosition(position); + setTitleWidgetText( + QCoreApplication::translate("MainWindowPrivate", "Fast return: 5 seconds")); + }); + new QShortcut(QKeySequence::MoveToPreviousLine, q_ptr, q_ptr, [this] { + controlWidget->setVolume(controlWidget->volume() + 10); + }); + new QShortcut(QKeySequence::MoveToNextLine, q_ptr, q_ptr, [this] { + controlWidget->setVolume(controlWidget->volume() - 10); + }); + new QShortcut(Qt::Key_Space, q_ptr, q_ptr, [this] { player->pause(); }); + } + + void setupUI() + { + auto *controlLayout = new QHBoxLayout; + controlLayout->addStretch(); + controlLayout->addWidget(controlWidget); + controlLayout->addStretch(); + auto *layout = new QVBoxLayout(floatingWidget); + layout->addWidget(titleWidget); + layout->addStretch(); + layout->addLayout(controlLayout); + + auto *splitter = new QSplitter(q_ptr); + splitter->addWidget(videoWidget); + splitter->addWidget(playlistView); + splitter->setSizes({200, 1}); + + q_ptr->setCentralWidget(splitter); + } + + void setTitleWidgetText(const QString &text) + { + setTitleWidgetGeometry(true); + titleWidget->setText(text); + titleWidget->setAutoHide(3000); + } + + void setControlWidgetGeometry(bool show = true) + { + controlWidget->setVisible(true); + setFloatingWidgetGeometry(show); + controlWidget->setVisible(show); + } + + void setTitleWidgetGeometry(bool show = true) + { + titleWidget->setVisible(true); + setFloatingWidgetGeometry(show); + titleWidget->setVisible(show); + } + + void setFloatingWidgetGeometry(bool show = true) + { + auto geometry = videoWidget->geometry(); + geometry = QRect{q_ptr->mapToGlobal(geometry.topLeft()), geometry.size()}; + floatingWidget->setGeometry(geometry); + } + + void addToPlaylist(const QList &urls) + { + auto *playlist = playlistModel->playlist(); + const int previousMediaCount = playlist->mediaCount(); + for (const auto &url : urls) { + if (isPlaylist(url)) { + playlist->load(url); + } else { + playlist->addMedia(url); + } + } + if (playlist->mediaCount() > previousMediaCount) { + auto index = playlistModel->index(previousMediaCount, 0); + playlistView->setCurrentIndex(index); + q_ptr->jump(index); + } + } + + void handleCursor(QMediaPlayer::MediaStatus status) + { +#ifndef QT_NO_CURSOR + if (status == QMediaPlayer::LoadingMedia || status == QMediaPlayer::BufferingMedia + || status == QMediaPlayer::StalledMedia) { + q_ptr->setCursor(QCursor(Qt::BusyCursor)); + } else { + q_ptr->unsetCursor(); + } +#endif + } + + void setPosition(qint64 position) + { + if (player->isSeekable()) { + player->setPosition(position); + } + } + + MainWindow *q_ptr; + + QMediaDevices *mediaDevices; + QMediaPlayer *player; + QAudioOutput *audioOutput; + VideoWidget *videoWidget; + + QWidget *floatingWidget; + ControlWidget *controlWidget; + TitleWidget *titleWidget; + + PlayListView *playlistView; + PlaylistModel *playlistModel; + QMenu *playListMenu; + QSplitter *splitter; + + QMenu *menu; +}; + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , d_ptr(new MainWindowPrivate(this)) +{ + d_ptr->setupUI(); + initMenu(); + initPlayListMenu(); + buildConnect(); + + setAttribute(Qt::WA_Hover); + d_ptr->videoWidget->installEventFilter(this); + d_ptr->playlistView->installEventFilter(this); + installEventFilter(this); + + resize(1000, 618); + printAudioOuputDevice(); +} + +MainWindow::~MainWindow() {} + +void MainWindow::onOpenLocalMedia() +{ + const auto path = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation) + .value(0, QDir::homePath()); + const auto filter = tr("Media (*)"); + const auto urls = QFileDialog::getOpenFileUrls(this, + tr("Open Media"), + QUrl::fromUserInput(path), + filter); + if (urls.isEmpty()) { + return; + } + d_ptr->addToPlaylist(urls); +} + +void MainWindow::onOpenWebMedia() +{ + OpenWebMediaDialog dialog(this); + dialog.setMinimumSize(size() / 2.0); + if (dialog.exec() != QDialog::Accepted) { + return; + } + d_ptr->addToPlaylist({QUrl(dialog.url())}); +} + +void MainWindow::onBufferingProgress(float progress) +{ + if (d_ptr->player->mediaStatus() == QMediaPlayer::StalledMedia) { + d_ptr->setTitleWidgetText(tr("Stalled %1%").arg(qRound(progress * 100.))); + } else { + d_ptr->setTitleWidgetText(tr("Buffering %1%").arg(qRound(progress * 100.))); + } +} + +void MainWindow::onDisplayError() +{ + if (d_ptr->player->error() != QMediaPlayer::NoError) { + d_ptr->titleWidget->setText(d_ptr->player->errorString()); + } +} + +void MainWindow::onStatusChanged(QMediaPlayer::MediaStatus status) +{ + d_ptr->handleCursor(status); + + switch (status) { + case QMediaPlayer::NoMedia: + case QMediaPlayer::LoadedMedia: d_ptr->setTitleWidgetText(QString()); break; + case QMediaPlayer::LoadingMedia: d_ptr->setTitleWidgetText(tr("Loading...")); break; + case QMediaPlayer::BufferingMedia: + case QMediaPlayer::BufferedMedia: + d_ptr->setTitleWidgetText( + tr("Buffering %1%").arg(qRound(d_ptr->player->bufferProgress() * 100.))); + break; + case QMediaPlayer::StalledMedia: + d_ptr->setTitleWidgetText( + tr("Stalled %1%").arg(qRound(d_ptr->player->bufferProgress() * 100.))); + break; + case QMediaPlayer::EndOfMedia: + QApplication::alert(this); + d_ptr->playlistModel->playlist()->next(); + break; + case QMediaPlayer::InvalidMedia: onDisplayError(); break; + } +} + +void MainWindow::onAudioOutputsChanged() +{ + printAudioOuputDevice(); + auto audioDevice = d_ptr->mediaDevices->defaultAudioOutput(); + d_ptr->player->audioOutput()->setDevice(audioDevice); +} + +void MainWindow::playlistPositionChanged(int currentItem) +{ + if (currentItem < 0) { + return; + } + auto url = d_ptr->playlistModel->playlist()->currentMedia(); + d_ptr->playlistView->setCurrentIndex(d_ptr->playlistModel->index(currentItem, 0)); + + d_ptr->player->setSource(url); + d_ptr->player->play(); +} + +void MainWindow::jump(const QModelIndex &index) +{ + if (index.isValid()) { + d_ptr->playlistModel->playlist()->setCurrentIndex(index.row()); + } +} + +auto MainWindow::eventFilter(QObject *watched, QEvent *event) -> bool +{ + if (watched == d_ptr->videoWidget) { + switch (event->type()) { + case QEvent::DragEnter: { + auto *e = static_cast(event); + e->acceptProposedAction(); + } break; + case QEvent::DragMove: { + auto *e = static_cast(event); + e->acceptProposedAction(); + } break; + case QEvent::Drop: { + auto *e = static_cast(event); + QList urls = e->mimeData()->urls(); + if (!urls.isEmpty()) { + d_ptr->addToPlaylist(urls); + } + } break; + case QEvent::ContextMenu: { + auto *e = static_cast(event); + d_ptr->menu->exec(e->globalPos()); + } break; + case QEvent::Resize: + QMetaObject::invokeMethod( + this, + [=] { + d_ptr->setControlWidgetGeometry(d_ptr->controlWidget->isVisible()); + d_ptr->setTitleWidgetGeometry(d_ptr->titleWidget->isVisible()); + }, + Qt::QueuedConnection); + break; + // case QEvent::MouseButtonPress: { + // auto e = static_cast(event); + // if (e->button() & Qt::LeftButton) { + // d_ptr->mpvPlayer->pause(); + // } + // } break; + case QEvent::MouseButtonDblClick: + if (isFullScreen()) { + showNormal(); + } else { + d_ptr->playlistView->hide(); + showFullScreen(); + } + break; + default: break; + } + } else if (watched == d_ptr->playlistView) { + switch (event->type()) { + case QEvent::ContextMenu: { + auto *e = static_cast(event); + d_ptr->playListMenu->exec(e->globalPos()); + } break; + default: break; + } + } else if (watched == this) { + switch (event->type()) { + case QEvent::Show: + case QEvent::Move: + QMetaObject::invokeMethod( + this, + [=] { + d_ptr->setControlWidgetGeometry(d_ptr->controlWidget->isVisible()); + d_ptr->setTitleWidgetGeometry(d_ptr->titleWidget->isVisible()); + }, + Qt::QueuedConnection); + break; + case QEvent::Hide: d_ptr->setFloatingWidgetGeometry(false); break; + case QEvent::HoverMove: { + auto controlWidgetGeometry = d_ptr->controlWidget->geometry(); + auto e = static_cast(event); + bool contain = controlWidgetGeometry.contains(e->position().toPoint()); + d_ptr->setControlWidgetGeometry(contain); + if (isFullScreen()) { + auto titleWidgetGeometry = d_ptr->titleWidget->geometry(); + contain = titleWidgetGeometry.contains(e->position().toPoint()); + d_ptr->setTitleWidgetGeometry(contain); + } + break; + } + default: break; + } + } + return QMainWindow::eventFilter(watched, event); +} + +void MainWindow::keyPressEvent(QKeyEvent *event) +{ + QMainWindow::keyPressEvent(event); + + qInfo() << "MainWindow Pressed key:" << event->key(); + switch (event->key()) { + case Qt::Key_Escape: + if (isFullScreen()) { + showNormal(); + } else { + showMinimized(); + } + break; + case Qt::Key_Q: qApp->quit(); break; + default: break; + } +} + +void MainWindow::buildConnect() +{ + connect(d_ptr->mediaDevices, + &QMediaDevices::audioOutputsChanged, + this, + &MainWindow::onAudioOutputsChanged); + + connect(d_ptr->player, + &QMediaPlayer::durationChanged, + d_ptr->controlWidget, + [this](qint64 duration) { d_ptr->controlWidget->setDuration(duration / 1000); }); + connect(d_ptr->player, + &QMediaPlayer::positionChanged, + d_ptr->controlWidget, + [this](qint64 position) { d_ptr->controlWidget->setPosition(position / 1000); }); + connect(d_ptr->player, &QMediaPlayer::mediaStatusChanged, this, &MainWindow::onStatusChanged); + connect(d_ptr->player, + &QMediaPlayer::bufferProgressChanged, + this, + &MainWindow::onBufferingProgress); + connect(d_ptr->player, &QMediaPlayer::errorChanged, this, &MainWindow::onDisplayError); + + connect(d_ptr->controlWidget, + &ControlWidget::previous, + d_ptr->playlistModel->playlist(), + &QMediaPlaylist::previous); + connect(d_ptr->controlWidget, + &ControlWidget::next, + d_ptr->playlistModel->playlist(), + &QMediaPlaylist::next); + // connect(d_ptr->controlWidget, &ControlWidget::hoverPosition, this, &MainWindow::onHoverSlider); + // connect(d_ptr->controlWidget, &ControlWidget::leavePosition, this, &MainWindow::onLeaveSlider); + connect(d_ptr->controlWidget, &ControlWidget::seek, d_ptr->player, [this](int value) { + qint64 position = value; + d_ptr->setPosition(position * 1000); + }); + connect(d_ptr->controlWidget, &ControlWidget::play, d_ptr->player, [this](bool checked) { + if (checked && !d_ptr->player->isPlaying()) { + d_ptr->player->play(); + } else if (checked) { + d_ptr->player->pause(); + } else if (!checked) { + d_ptr->player->play(); + } + }); + connect(d_ptr->controlWidget, + &ControlWidget::volumeChanged, + d_ptr->audioOutput, + [this](int value) { + d_ptr->audioOutput->setVolume(value / 100.0); + d_ptr->setTitleWidgetText(tr("Volume: %1").arg(value)); + }); + d_ptr->controlWidget->setVolume(50); + // connect(d_ptr->controlWidget, &ControlWidget::speedChanged, d_ptr->player, [this](double value) { + // d_ptr->playerPtr->addEvent(Ffmpeg::EventPtr(new Ffmpeg::SpeedEvent(value))); + // d_ptr->setTitleWidgetText(tr("Speed: %1").arg(value)); + // }); + connect(d_ptr->controlWidget, + &ControlWidget::modelChanged, + d_ptr->playlistModel->playlist(), + [this](int model) { + d_ptr->playlistModel->playlist()->setPlaybackMode( + static_cast(model)); + }); + connect(d_ptr->controlWidget, &ControlWidget::showList, d_ptr->playlistView, [this] { + d_ptr->playlistView->setVisible(!d_ptr->playlistView->isVisible()); + }); + + connect(d_ptr->playlistModel->playlist(), + &QMediaPlaylist::currentIndexChanged, + this, + &MainWindow::playlistPositionChanged); + connect(d_ptr->playlistView, &QAbstractItemView::activated, this, &MainWindow::jump); +} + +void MainWindow::initMenu() +{ + d_ptr->menu->addAction(tr("Open Local Media"), this, &MainWindow::onOpenLocalMedia); + d_ptr->menu->addAction(tr("Open Web Media"), this, &MainWindow::onOpenWebMedia); +} + +void MainWindow::initPlayListMenu() +{ + d_ptr->playListMenu->addAction(tr("Open Local Media"), this, &MainWindow::onOpenLocalMedia); + d_ptr->playListMenu->addAction(tr("Open Web Media"), this, &MainWindow::onOpenWebMedia); + d_ptr->playListMenu + ->addAction(tr("Remove the currently selected item"), d_ptr->playlistView, [this] { + auto indexs = d_ptr->playlistView->selectedAllIndexs(); + std::sort(indexs.begin(), indexs.end(), [&](QModelIndex left, QModelIndex right) { + return left.row() > right.row(); + }); + for (const auto &index : std::as_const(indexs)) { + d_ptr->playlistModel->playlist()->removeMedia(index.row()); + } + }); + d_ptr->playListMenu->addAction(tr("Clear"), d_ptr->playlistView, [this] { + d_ptr->playlistModel->playlist()->clear(); + }); +} diff --git a/examples/qplayer/mainwindow.hpp b/examples/qplayer/mainwindow.hpp new file mode 100644 index 0000000..5e6c8e4 --- /dev/null +++ b/examples/qplayer/mainwindow.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void onOpenLocalMedia(); + void onOpenWebMedia(); + + void onBufferingProgress(float progress); + void onDisplayError(); + void onStatusChanged(QMediaPlayer::MediaStatus status); + + void onAudioOutputsChanged(); + + void playlistPositionChanged(int /*currentItem*/); + void jump(const QModelIndex &index); + +protected: + auto eventFilter(QObject *watched, QEvent *event) -> bool override; + void keyPressEvent(QKeyEvent *event) override; + +private: + void buildConnect(); + void initMenu(); + void initPlayListMenu(); + + class MainWindowPrivate; + QScopedPointer d_ptr; +}; diff --git a/examples/qplayer/qplayer.pro b/examples/qplayer/qplayer.pro new file mode 100644 index 0000000..a82ad39 --- /dev/null +++ b/examples/qplayer/qplayer.pro @@ -0,0 +1,46 @@ +include(../../common.pri) + +QT += core gui widgets network multimediawidgets core5compat + +TEMPLATE = app + +TARGET = QPlayer + +LIBS += \ + -l$$replaceLibName(mediaconfig) \ + -l$$replaceLibName(thirdparty) \ + -l$$replaceLibName(dump) \ + -l$$replaceLibName(utils) + +include(../../src/3rdparty/3rdparty.pri) + +SOURCES += \ + ../common/controlwidget.cc \ + ../common/equalizerdialog.cpp \ + ../common/openwebmediadialog.cc \ + ../common/playlistmodel.cpp \ + ../common/playlistview.cc \ + ../common/qmediaplaylist_p.cpp \ + ../common/qmediaplaylist.cpp \ + ../common/qplaylistfileparser.cpp \ + ../common/slider.cpp \ + ../common/titlewidget.cc \ + main.cc \ + mainwindow.cc \ + videowidget.cc + +HEADERS += \ + ../common/controlwidget.hpp \ + ../common/equalizerdialog.h \ + ../common/openwebmediadialog.hpp \ + ../common/playlistmodel.h \ + ../common/playlistview.hpp \ + ../common/qmediaplaylist.h \ + ../common/qmediaplaylist_p.h \ + ../common/qplaylistfileparser.h \ + ../common/slider.h \ + ../common/titlewidget.hpp \ + mainwindow.hpp \ + videowidget.hpp + +DESTDIR = $$APP_OUTPUT_PATH diff --git a/examples/qplayer/videowidget.cc b/examples/qplayer/videowidget.cc new file mode 100644 index 0000000..b7ae342 --- /dev/null +++ b/examples/qplayer/videowidget.cc @@ -0,0 +1,11 @@ +#include "videowidget.hpp" + +VideoWidget::VideoWidget(QWidget *parent) + : QVideoWidget{parent} +{ + setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); + + QPalette p = palette(); + p.setColor(QPalette::Window, Qt::black); + setPalette(p); +} diff --git a/examples/qplayer/videowidget.hpp b/examples/qplayer/videowidget.hpp new file mode 100644 index 0000000..7ac2e68 --- /dev/null +++ b/examples/qplayer/videowidget.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +class VideoWidget : public QVideoWidget +{ + Q_OBJECT +public: + explicit VideoWidget(QWidget *parent = nullptr); +}; diff --git a/src/3rdparty/CMakeLists.txt b/src/3rdparty/CMakeLists.txt index b315c08..b05645b 100644 --- a/src/3rdparty/CMakeLists.txt +++ b/src/3rdparty/CMakeLists.txt @@ -8,8 +8,8 @@ set(PROJECT_SOURCES thirdparty_global.hpp) add_custom_library(thirdparty ${PROJECT_SOURCES}) -target_link_libraries(thirdparty PRIVATE shared_qtlockedfile Qt6::Network - Qt6::Widgets) +target_link_libraries(thirdparty PRIVATE shared_qtlockedfile Qt::Network + Qt::Widgets) if(CMAKE_HOST_WIN32) target_compile_definitions(thirdparty PRIVATE "THRIDPARTY_LIBRARY") diff --git a/src/3rdparty/qtlockedfile/CMakeLists.txt b/src/3rdparty/qtlockedfile/CMakeLists.txt index da604dc..b4d4af9 100644 --- a/src/3rdparty/qtlockedfile/CMakeLists.txt +++ b/src/3rdparty/qtlockedfile/CMakeLists.txt @@ -6,7 +6,7 @@ endif() add_library(shared_qtlockedfile STATIC ${OS_SOURCES} qtlockedfile.cpp qtlockedfile.h) -target_link_libraries(shared_qtlockedfile Qt6::Core) +target_link_libraries(shared_qtlockedfile Qt::Core) target_include_directories(shared_qtlockedfile PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") if(CMAKE_HOST_WIN32) diff --git a/src/3rdparty/qtsingleapplication/CMakeLists.txt b/src/3rdparty/qtsingleapplication/CMakeLists.txt index 923ab85..3a37d85 100644 --- a/src/3rdparty/qtsingleapplication/CMakeLists.txt +++ b/src/3rdparty/qtsingleapplication/CMakeLists.txt @@ -1,9 +1,11 @@ -add_library(shared_qtsingleapplication SHARED - qtsingleapplication.cpp qtsingleapplication.h - qtlocalpeer.cpp qtlocalpeer.h -) -target_link_libraries(shared_qtsingleapplication shared_qtlockedfile Qt6::Core Qt6::Network Qt6::Widgets) -target_include_directories(shared_qtsingleapplication PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") -if (WIN32) - target_compile_definitions(shared_qtsingleapplication PRIVATE "THRIDPARTY_LIBRARY" _UNICODE UNICODE) +add_library( + shared_qtsingleapplication SHARED + qtsingleapplication.cpp qtsingleapplication.h qtlocalpeer.cpp qtlocalpeer.h) +target_link_libraries(shared_qtsingleapplication shared_qtlockedfile Qt::Core + Qt::Network Qt::Widgets) +target_include_directories(shared_qtsingleapplication + PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") +if(WIN32) + target_compile_definitions(shared_qtsingleapplication + PRIVATE "THRIDPARTY_LIBRARY" _UNICODE UNICODE) endif() diff --git a/src/dump/CMakeLists.txt b/src/dump/CMakeLists.txt index ee32b6f..18a7b02 100644 --- a/src/dump/CMakeLists.txt +++ b/src/dump/CMakeLists.txt @@ -2,7 +2,7 @@ set(PROJECT_SOURCES breakpad.hpp breakpad.cc crashpad.hpp crashpad.cc dump_global.hpp) add_custom_library(dump ${PROJECT_SOURCES}) target_link_libraries( - dump PRIVATE utils Qt6::Widgets unofficial::breakpad::libbreakpad + dump PRIVATE utils Qt::Widgets unofficial::breakpad::libbreakpad unofficial::breakpad::libbreakpad_client crashpad::crashpad) if(CMAKE_HOST_WIN32) diff --git a/src/ffmpeg/CMakeLists.txt b/src/ffmpeg/CMakeLists.txt index 2d5f10f..8898900 100644 --- a/src/ffmpeg/CMakeLists.txt +++ b/src/ffmpeg/CMakeLists.txt @@ -109,8 +109,8 @@ set(PROJECT_SOURCES qt_add_resources(SOURCES videorender/shaders.qrc) add_custom_library(ffmpeg ${PROJECT_SOURCES} ${SOURCES}) -target_link_libraries(ffmpeg PRIVATE mediaconfig utils Qt6::Widgets - Qt6::Multimedia Qt6::OpenGLWidgets) +target_link_libraries(ffmpeg PRIVATE mediaconfig utils Qt::Widgets + Qt::Multimedia Qt::OpenGLWidgets) target_link_libraries(ffmpeg PRIVATE PkgConfig::ffmpeg PkgConfig::ass) if(CMAKE_HOST_SYSTEM_NAME MATCHES "Linux") target_link_libraries(ffmpeg PRIVATE PkgConfig::fontconfig expat::expat) diff --git a/src/ffmpeg/audiorender/audiooutput.cc b/src/ffmpeg/audiorender/audiooutput.cc index cdaa722..425007f 100644 --- a/src/ffmpeg/audiorender/audiooutput.cc +++ b/src/ffmpeg/audiorender/audiooutput.cc @@ -36,6 +36,10 @@ class AudioOutput::AudioOutputPrivate int sampleSize = 0; auto format = getAudioFormatFromCodecCtx(contextInfo->codecCtx(), sampleSize); + if (!audioDevice.isFormatSupported(format)) { + qWarning() << "Raw audio format not supported by backend, cannot play audio."; + return; + } audioConverterPtr.reset(new AudioFrameConverter(contextInfo->codecCtx(), format)); audioSinkPtr.reset(new QAudioSink(format)); diff --git a/src/mediaconfig/CMakeLists.txt b/src/mediaconfig/CMakeLists.txt index 2485887..cdb1e62 100644 --- a/src/mediaconfig/CMakeLists.txt +++ b/src/mediaconfig/CMakeLists.txt @@ -1,7 +1,7 @@ set(PROJECT_SOURCES equalizer.cc equalizer.hpp mediaconfig_global.hpp) add_custom_library(mediaconfig ${PROJECT_SOURCES}) -target_link_libraries(mediaconfig PRIVATE utils Qt6::Core) +target_link_libraries(mediaconfig PRIVATE utils Qt::Core) if(CMAKE_HOST_WIN32) target_compile_definitions(mediaconfig PRIVATE "MEDIACONFIG_LIBRARY") diff --git a/src/mpv/CMakeLists.txt b/src/mpv/CMakeLists.txt index e9c10e7..9ce57a0 100644 --- a/src/mpv/CMakeLists.txt +++ b/src/mpv/CMakeLists.txt @@ -13,7 +13,7 @@ set(PROJECT_SOURCES qthelper.hpp) add_custom_library(qmpv ${PROJECT_SOURCES}) -target_link_libraries(qmpv PRIVATE Qt6::Widgets Qt6::OpenGLWidgets) +target_link_libraries(qmpv PRIVATE Qt::Widgets Qt::OpenGLWidgets) if(CMAKE_HOST_WIN32) target_include_directories(qmpv PRIVATE "C:\\3rd\\x64\\mpv\\include") diff --git a/src/mpv/mpvwidget.cc b/src/mpv/mpvwidget.cc index 74700a9..59f83ce 100644 --- a/src/mpv/mpvwidget.cc +++ b/src/mpv/mpvwidget.cc @@ -9,7 +9,7 @@ MpvWidget::MpvWidget(QWidget *parent) setAttribute(Qt::WA_NativeWindow); setAttribute(Qt::WA_StyledBackground); - setStyleSheet("QWidget{background:black;}"); + setStyleSheet("QWidget{ background:black; }"); } MpvWidget::~MpvWidget() = default; diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index 2703bd8..75d7cd8 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -20,7 +20,7 @@ set(PROJECT_SOURCES utilstr.h) add_custom_library(utils ${PROJECT_SOURCES}) -target_link_libraries(utils PRIVATE Qt6::Widgets Qt6::Core5Compat) +target_link_libraries(utils PRIVATE Qt::Widgets Qt::Core5Compat) if(CMAKE_HOST_WIN32) target_compile_definitions(utils PRIVATE "UTILS_LIBRARY") diff --git a/tests/subtitle_unittest/CMakeLists.txt b/tests/subtitle_unittest/CMakeLists.txt index 3b711fc..ffe14b2 100644 --- a/tests/subtitle_unittest/CMakeLists.txt +++ b/tests/subtitle_unittest/CMakeLists.txt @@ -2,8 +2,8 @@ set(PROJECT_SOURCES main.cc mainwindow.cc mainwindow.hpp subtitlethread.cc subtitlethread.hpp) qt_add_executable(subtitle_unittest MANUAL_FINALIZATION ${PROJECT_SOURCES}) -target_link_libraries(subtitle_unittest PRIVATE Qt6::Widgets Qt6::Multimedia - Qt6::OpenGLWidgets ffmpeg utils) +target_link_libraries(subtitle_unittest PRIVATE Qt::Widgets Qt::Multimedia + Qt::OpenGLWidgets ffmpeg utils) target_link_libraries(subtitle_unittest PRIVATE PkgConfig::ffmpeg) if(CMAKE_HOST_APPLE)