|
|
@@ -4,6 +4,10 @@
|
|
|
|
|
|
#include "av_player.h"
|
|
|
#include "vframe.h"
|
|
|
+#include <QtConcurrent>
|
|
|
+#include <QApplication>
|
|
|
+#include <QDebug>
|
|
|
+#include <QElapsedTimer>
|
|
|
|
|
|
AVPlayerWidget::AVPlayerWidget(QWidget *parent)
|
|
|
: QWidget{parent}
|
|
|
@@ -11,6 +15,11 @@ AVPlayerWidget::AVPlayerWidget(QWidget *parent)
|
|
|
, m_openglWidget(new AVOpenGLWidget(this))
|
|
|
, m_isPlaying(false)
|
|
|
, m_isPaused(false)
|
|
|
+ , m_isLoading(false)
|
|
|
+ , m_playWatcher(new QFutureWatcher<bool>(this))
|
|
|
+ , m_loadingLabel(nullptr)
|
|
|
+ , m_loadingProgress(nullptr)
|
|
|
+ , m_loadingMovie(nullptr)
|
|
|
{
|
|
|
// 设置尺寸策略,确保能够正确填充空间
|
|
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
|
@@ -42,6 +51,17 @@ void AVPlayerWidget::setupUI()
|
|
|
// 添加OpenGL视频渲染组件
|
|
|
m_mainLayout->addWidget(m_openglWidget, 1);
|
|
|
|
|
|
+ // 创建加载状态UI组件(初始隐藏)
|
|
|
+ m_loadingLabel = new QLabel("正在连接...", this);
|
|
|
+ m_loadingLabel->setAlignment(Qt::AlignCenter);
|
|
|
+ m_loadingLabel->setStyleSheet("QLabel { background-color: rgba(0, 0, 0, 180); color: white; font-size: 16px; padding: 10px; border-radius: 5px; }");
|
|
|
+ m_loadingLabel->hide();
|
|
|
+
|
|
|
+ m_loadingProgress = new QProgressBar(this);
|
|
|
+ m_loadingProgress->setRange(0, 0); // 无限进度条
|
|
|
+ m_loadingProgress->setStyleSheet("QProgressBar { border: 2px solid grey; border-radius: 5px; text-align: center; } QProgressBar::chunk { background-color: #05B8CC; width: 20px; }");
|
|
|
+ m_loadingProgress->hide();
|
|
|
+
|
|
|
// 创建控制面板容器
|
|
|
QWidget *controlWidget = new QWidget(this);
|
|
|
controlWidget->setFixedHeight(60); // 设置控制面板固定高度
|
|
|
@@ -121,6 +141,9 @@ void AVPlayerWidget::connectSignals()
|
|
|
connect(m_stopButton, &QPushButton::clicked, this, &AVPlayerWidget::onStopButtonClicked);
|
|
|
connect(m_testPlayButton, &QPushButton::clicked, this, &AVPlayerWidget::onTestPlayButtonClicked);
|
|
|
connect(m_volumeSlider, &QSlider::valueChanged, this, &AVPlayerWidget::onVolumeChanged);
|
|
|
+
|
|
|
+ // 连接异步播放信号
|
|
|
+ connect(m_playWatcher, &QFutureWatcher<bool>::finished, this, &AVPlayerWidget::onAsyncPlayFinished);
|
|
|
}
|
|
|
|
|
|
void AVPlayerWidget::play(const QString &url)
|
|
|
@@ -267,6 +290,149 @@ void AVPlayerWidget::startPlay()
|
|
|
const QString url = m_urlEdit->text().trimmed();
|
|
|
if (!url.isEmpty()) {
|
|
|
qDebug() << "startPlay" << url;
|
|
|
- play(url);
|
|
|
+ playAsync(url); // 使用异步播放
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void AVPlayerWidget::playAsync(const QString &url)
|
|
|
+{
|
|
|
+ if (m_isLoading) {
|
|
|
+ qDebug() << "Already loading, ignoring new play request";
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ m_pendingUrl = url;
|
|
|
+ m_isLoading = true;
|
|
|
+
|
|
|
+ // 显示加载状态
|
|
|
+ showLoadingUI();
|
|
|
+ emit playLoadingStarted();
|
|
|
+
|
|
|
+ // 在后台线程执行播放逻辑
|
|
|
+ QFuture<bool> future = QtConcurrent::run([this, url]() -> bool {
|
|
|
+ try {
|
|
|
+ // 创建临时播放器用于测试连接
|
|
|
+ AVPlayer testPlayer;
|
|
|
+
|
|
|
+ // 设置超时时间(10秒)
|
|
|
+ QElapsedTimer timer;
|
|
|
+ timer.start();
|
|
|
+ const int timeoutMs = 10000;
|
|
|
+
|
|
|
+ bool success = testPlayer.play(url);
|
|
|
+
|
|
|
+ // 检查是否超时
|
|
|
+ if (timer.elapsed() > timeoutMs) {
|
|
|
+ qDebug() << "Connection timeout after" << timeoutMs << "ms";
|
|
|
+ testPlayer.clearPlayer();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!success) {
|
|
|
+ qDebug() << "Failed to connect to stream:" << url;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 连接成功,清理测试播放器
|
|
|
+ testPlayer.clearPlayer();
|
|
|
+ return true;
|
|
|
+
|
|
|
+ } catch (const std::exception &e) {
|
|
|
+ qDebug() << "Exception during async play:" << e.what();
|
|
|
+ return false;
|
|
|
+ } catch (...) {
|
|
|
+ qDebug() << "Unknown exception during async play";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ m_playWatcher->setFuture(future);
|
|
|
+}
|
|
|
+
|
|
|
+void AVPlayerWidget::startPlayAsync()
|
|
|
+{
|
|
|
+ const QString url = m_urlEdit->text().trimmed();
|
|
|
+ if (!url.isEmpty()) {
|
|
|
+ playAsync(url);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void AVPlayerWidget::onAsyncPlayFinished()
|
|
|
+{
|
|
|
+ m_isLoading = false;
|
|
|
+ hideLoadingUI();
|
|
|
+
|
|
|
+ bool success = m_playWatcher->result();
|
|
|
+ if (success) {
|
|
|
+ // 连接成功,使用主线程播放器开始播放
|
|
|
+ if (!m_player->play(m_pendingUrl)) {
|
|
|
+ QString errorMsg = "播放失败:无法初始化播放器";
|
|
|
+ qDebug() << errorMsg;
|
|
|
+ emit playError(errorMsg);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ m_isPlaying = true;
|
|
|
+ m_isPaused = false;
|
|
|
+ m_playButton->setEnabled(false);
|
|
|
+ m_pauseButton->setEnabled(true);
|
|
|
+ m_stopButton->setEnabled(true);
|
|
|
+ emit playStateChanged(true);
|
|
|
+ emit playLoadingFinished();
|
|
|
+ qDebug() << "Successfully started playing:" << m_pendingUrl;
|
|
|
+ } else {
|
|
|
+ QString errorMsg;
|
|
|
+ if (m_pendingUrl.startsWith("rtmp://")) {
|
|
|
+ errorMsg = "RTMP连接失败:请检查流地址是否正确,网络是否畅通";
|
|
|
+ } else if (m_pendingUrl.startsWith("http://") || m_pendingUrl.startsWith("https://")) {
|
|
|
+ errorMsg = "HTTP流连接失败:请检查网络连接和流地址";
|
|
|
+ } else {
|
|
|
+ errorMsg = "连接失败:不支持的协议或无效的流地址";
|
|
|
+ }
|
|
|
+ qDebug() << "Play failed for URL:" << m_pendingUrl;
|
|
|
+ emit playError(errorMsg);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void AVPlayerWidget::onAsyncPlayError(const QString &error)
|
|
|
+{
|
|
|
+ m_isLoading = false;
|
|
|
+ hideLoadingUI();
|
|
|
+ emit playError(error);
|
|
|
+}
|
|
|
+
|
|
|
+void AVPlayerWidget::showLoadingUI()
|
|
|
+{
|
|
|
+ if (m_loadingLabel && m_loadingProgress) {
|
|
|
+ // 计算居中位置
|
|
|
+ QRect rect = m_openglWidget->geometry();
|
|
|
+ int labelWidth = 200;
|
|
|
+ int labelHeight = 50;
|
|
|
+ int progressWidth = 300;
|
|
|
+ int progressHeight = 20;
|
|
|
+
|
|
|
+ m_loadingLabel->setGeometry(
|
|
|
+ rect.x() + (rect.width() - labelWidth) / 2,
|
|
|
+ rect.y() + (rect.height() - labelHeight) / 2 - 30,
|
|
|
+ labelWidth, labelHeight
|
|
|
+ );
|
|
|
+
|
|
|
+ m_loadingProgress->setGeometry(
|
|
|
+ rect.x() + (rect.width() - progressWidth) / 2,
|
|
|
+ rect.y() + (rect.height() - progressHeight) / 2 + 30,
|
|
|
+ progressWidth, progressHeight
|
|
|
+ );
|
|
|
+
|
|
|
+ m_loadingLabel->show();
|
|
|
+ m_loadingProgress->show();
|
|
|
+ m_loadingLabel->raise();
|
|
|
+ m_loadingProgress->raise();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void AVPlayerWidget::hideLoadingUI()
|
|
|
+{
|
|
|
+ if (m_loadingLabel && m_loadingProgress) {
|
|
|
+ m_loadingLabel->hide();
|
|
|
+ m_loadingProgress->hide();
|
|
|
}
|
|
|
}
|