avplayerwidget.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. #include "avplayerwidget.h"
  2. #include "avopenglwidget.h"
  3. #include "av_player.h"
  4. #include "vframe.h"
  5. #include <QtConcurrent>
  6. #include <QApplication>
  7. #include <QDebug>
  8. #include <QElapsedTimer>
  9. AVPlayerWidget::AVPlayerWidget(QWidget *parent)
  10. : QWidget{parent}
  11. , m_player(new AVPlayer)
  12. , m_openglWidget(new AVOpenGLWidget(this))
  13. , m_isPlaying(false)
  14. , m_isPaused(false)
  15. , m_isLoading(false)
  16. , m_playWatcher(new QFutureWatcher<bool>(this))
  17. , m_loadingLabel(nullptr)
  18. , m_loadingProgress(nullptr)
  19. , m_loadingMovie(nullptr)
  20. {
  21. // 设置尺寸策略,确保能够正确填充空间
  22. setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  23. setupUI();
  24. connectSignals();
  25. // 设置默认音量
  26. m_player->setVolume(50);
  27. m_volumeSlider->setValue(50);
  28. // 设置默认测试URL
  29. m_urlEdit->setText("rtmp://106.55.186.74:1935/stream/V1/");
  30. }
  31. AVPlayerWidget::~AVPlayerWidget()
  32. {
  33. if (m_player) {
  34. m_player->clearPlayer();
  35. }
  36. }
  37. void AVPlayerWidget::setupUI()
  38. {
  39. m_mainLayout = new QVBoxLayout(this);
  40. m_mainLayout->setContentsMargins(5, 5, 5, 5); // 减少主布局边距
  41. m_mainLayout->setSpacing(5); // 减少组件间距
  42. // 添加OpenGL视频渲染组件
  43. m_mainLayout->addWidget(m_openglWidget, 1);
  44. // 创建加载状态UI组件(初始隐藏)
  45. m_loadingLabel = new QLabel("正在连接...", this);
  46. m_loadingLabel->setAlignment(Qt::AlignCenter);
  47. m_loadingLabel->setStyleSheet("QLabel { background-color: rgba(0, 0, 0, 180); color: white; font-size: 16px; padding: 10px; border-radius: 5px; }");
  48. m_loadingLabel->hide();
  49. m_loadingProgress = new QProgressBar(this);
  50. m_loadingProgress->setRange(0, 0); // 无限进度条
  51. m_loadingProgress->setStyleSheet("QProgressBar { border: 2px solid grey; border-radius: 5px; text-align: center; } QProgressBar::chunk { background-color: #05B8CC; width: 20px; }");
  52. m_loadingProgress->hide();
  53. // 创建控制面板容器
  54. QWidget *controlWidget = new QWidget(this);
  55. controlWidget->setFixedHeight(60); // 设置控制面板固定高度
  56. controlWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
  57. m_controlLayout = new QHBoxLayout(controlWidget);
  58. m_controlLayout->setContentsMargins(5, 5, 5, 5); // 减少控制面板内边距
  59. m_controlLayout->setSpacing(5); // 减少控件间距
  60. // URL输入框
  61. m_urlEdit = new QLineEdit(this);
  62. m_urlEdit->setPlaceholderText("输入视频文件路径或URL");
  63. m_urlEdit->setFixedHeight(30); // 固定高度适应控制面板
  64. m_urlEdit->setFont(QFont("Arial", 9));
  65. m_controlLayout->addWidget(m_urlEdit, 2);
  66. // 测试播放按钮
  67. m_testPlayButton = new QPushButton("测试播放", this);
  68. m_testPlayButton->setFixedSize(70, 30); // 固定尺寸
  69. m_testPlayButton->setFont(QFont("Arial", 9, QFont::Bold));
  70. m_controlLayout->addWidget(m_testPlayButton);
  71. // 播放控制按钮
  72. m_playButton = new QPushButton("播放", this);
  73. m_playButton->setFixedSize(50, 30); // 固定尺寸
  74. m_playButton->setFont(QFont("Arial", 9, QFont::Bold));
  75. m_pauseButton = new QPushButton("暂停", this);
  76. m_pauseButton->setFixedSize(50, 30); // 固定尺寸
  77. m_pauseButton->setFont(QFont("Arial", 9, QFont::Bold));
  78. m_stopButton = new QPushButton("停止", this);
  79. m_stopButton->setFixedSize(50, 30); // 固定尺寸
  80. m_stopButton->setFont(QFont("Arial", 9, QFont::Bold));
  81. m_controlLayout->addWidget(m_playButton);
  82. m_controlLayout->addWidget(m_pauseButton);
  83. m_controlLayout->addWidget(m_stopButton);
  84. // 音量控制
  85. QLabel *volumeLabel = new QLabel("音量:", this);
  86. volumeLabel->setFont(QFont("Arial", 9));
  87. m_volumeSlider = new QSlider(Qt::Horizontal, this);
  88. m_volumeSlider->setRange(0, 100);
  89. m_volumeSlider->setValue(50);
  90. m_volumeSlider->setFixedSize(100, 20); // 固定尺寸
  91. m_controlLayout->addWidget(volumeLabel);
  92. m_controlLayout->addWidget(m_volumeSlider);
  93. // 时间显示
  94. m_timeLabel = new QLabel("00:00 / 00:00", this);
  95. m_timeLabel->setFont(QFont("Arial", 9));
  96. m_timeLabel->setFixedWidth(80);
  97. m_controlLayout->addWidget(m_timeLabel);
  98. m_mainLayout->addWidget(controlWidget, 0); // 拉伸因子为0,不占用额外空间
  99. // 设置初始状态
  100. m_pauseButton->setEnabled(false);
  101. m_stopButton->setEnabled(false);
  102. controlWidget->hide();
  103. }
  104. void AVPlayerWidget::connectSignals()
  105. {
  106. // 连接AVPlayer信号
  107. connect(m_player, &AVPlayer::frameChanged, this, &AVPlayerWidget::onFrameChanged, Qt::QueuedConnection);
  108. connect(m_player, &AVPlayer::AVDurationChanged, this, &AVPlayerWidget::durationChangedSlot);
  109. connect(m_player, &AVPlayer::AVPtsChanged, this, &AVPlayerWidget::ptsChangedSlot);
  110. connect(m_player, &AVPlayer::AVTerminate, this, &AVPlayerWidget::terminateSlot, Qt::QueuedConnection);
  111. // 连接UI控件信号
  112. connect(m_playButton, &QPushButton::clicked, this, &AVPlayerWidget::onPlayButtonClicked);
  113. connect(m_pauseButton, &QPushButton::clicked, this, &AVPlayerWidget::onPauseButtonClicked);
  114. connect(m_stopButton, &QPushButton::clicked, this, &AVPlayerWidget::onStopButtonClicked);
  115. connect(m_testPlayButton, &QPushButton::clicked, this, &AVPlayerWidget::onTestPlayButtonClicked);
  116. connect(m_volumeSlider, &QSlider::valueChanged, this, &AVPlayerWidget::onVolumeChanged);
  117. // 连接异步播放信号
  118. connect(m_playWatcher, &QFutureWatcher<bool>::finished, this, &AVPlayerWidget::onAsyncPlayFinished);
  119. }
  120. void AVPlayerWidget::play(const QString &url)
  121. {
  122. if (!m_player->play(url)) {
  123. // 提示反馈?
  124. return;
  125. }
  126. m_isPlaying = true;
  127. m_isPaused = false;
  128. m_playButton->setEnabled(false);
  129. m_pauseButton->setEnabled(true);
  130. m_stopButton->setEnabled(true);
  131. emit playStateChanged(true);
  132. }
  133. void AVPlayerWidget::stop()
  134. {
  135. m_player->clearPlayer();
  136. m_isPlaying = false;
  137. m_isPaused = false;
  138. m_playButton->setEnabled(true);
  139. m_pauseButton->setEnabled(false);
  140. m_stopButton->setEnabled(false);
  141. m_openglWidget->clearFrame();
  142. emit playStateChanged(false);
  143. }
  144. void AVPlayerWidget::pause()
  145. {
  146. if (m_isPlaying && !m_isPaused) {
  147. m_player->pause(true);
  148. m_isPaused = true;
  149. m_playButton->setText("继续");
  150. m_playButton->setEnabled(true);
  151. m_pauseButton->setEnabled(false);
  152. }
  153. }
  154. void AVPlayerWidget::resume()
  155. {
  156. if (m_isPlaying && m_isPaused) {
  157. m_player->pause(false);
  158. m_isPaused = false;
  159. m_playButton->setText("播放");
  160. m_playButton->setEnabled(false);
  161. m_pauseButton->setEnabled(true);
  162. }
  163. }
  164. void AVPlayerWidget::onPlayButtonClicked()
  165. {
  166. if (m_isPaused) {
  167. resume();
  168. } else {
  169. QString url = m_urlEdit->text().trimmed();
  170. if (!url.isEmpty()) {
  171. play(url);
  172. }
  173. }
  174. }
  175. void AVPlayerWidget::onPauseButtonClicked()
  176. {
  177. pause();
  178. }
  179. void AVPlayerWidget::onStopButtonClicked()
  180. {
  181. stop();
  182. }
  183. void AVPlayerWidget::onTestPlayButtonClicked()
  184. {
  185. // 使用默认测试视频
  186. QString testUrl = "C:/Users/zhuizhu/Videos/2.mp4";
  187. m_urlEdit->setText(testUrl);
  188. play(testUrl);
  189. }
  190. void AVPlayerWidget::onVolumeChanged(int volume)
  191. {
  192. m_player->setVolume(volume);
  193. }
  194. void AVPlayerWidget::onFrameChanged(QSharedPointer<VideoFrame> frame)
  195. {
  196. m_openglWidget->onShowYUV(frame);
  197. // 注意:这里不要释放avFrame,因为Render方法会处理
  198. }
  199. void AVPlayerWidget::ptsChangedSlot(unsigned int pts)
  200. {
  201. // 更新时间显示
  202. int seconds = pts / 1000;
  203. int minutes = seconds / 60;
  204. seconds = seconds % 60;
  205. QString timeStr = QString("%1:%2").arg(minutes, 2, 10, QChar('0')).arg(seconds, 2, 10, QChar('0'));
  206. // 这里可以更新时间标签的当前时间部分
  207. }
  208. void AVPlayerWidget::durationChangedSlot(unsigned int duration)
  209. {
  210. // 更新总时长显示
  211. int seconds = duration / 1000;
  212. int minutes = seconds / 60;
  213. seconds = seconds % 60;
  214. QString durationStr = QString("%1:%2").arg(minutes, 2, 10, QChar('0')).arg(seconds, 2, 10, QChar('0'));
  215. m_timeLabel->setText(QString("00:00 / %1").arg(durationStr));
  216. }
  217. void AVPlayerWidget::terminateSlot()
  218. {
  219. stop();
  220. }
  221. // 兼容旧接口实现
  222. void AVPlayerWidget::stopPlay()
  223. {
  224. stop();
  225. }
  226. void AVPlayerWidget::setPlayRoomId(const QString &id)
  227. {
  228. // 以当前URL为基准,去掉最后一节后拼接房间ID,避免硬编码服务器地址
  229. QString base = m_urlEdit->text().trimmed();
  230. if (base.isEmpty()) {
  231. base = "rtmp://106.55.186.74:1935/stream/V1/";
  232. }
  233. // rtmp://106.55.186.74:1935/stream/V1/0198da41-cdb6-78e3-879d-2ea32d58f73f
  234. int lastSlash = base.lastIndexOf('/');
  235. QString streamBase = lastSlash > 0 ? base.left(lastSlash) : base;
  236. QString url = streamBase + "/" + id;
  237. qDebug() << "setPlayRoomId" << url;
  238. m_urlEdit->setText(url);
  239. }
  240. void AVPlayerWidget::startPlay()
  241. {
  242. const QString url = m_urlEdit->text().trimmed();
  243. if (!url.isEmpty()) {
  244. qDebug() << "startPlay" << url;
  245. playAsync(url); // 使用异步播放
  246. }
  247. }
  248. void AVPlayerWidget::playAsync(const QString &url)
  249. {
  250. if (m_isLoading) {
  251. qDebug() << "Already loading, ignoring new play request";
  252. return;
  253. }
  254. m_pendingUrl = url;
  255. m_isLoading = true;
  256. // 显示加载状态
  257. showLoadingUI();
  258. emit playLoadingStarted();
  259. // 在后台线程执行播放逻辑
  260. QFuture<bool> future = QtConcurrent::run([this, url]() -> bool {
  261. try {
  262. // 创建临时播放器用于测试连接
  263. AVPlayer testPlayer;
  264. // 设置超时时间(10秒)
  265. QElapsedTimer timer;
  266. timer.start();
  267. const int timeoutMs = 10000;
  268. bool success = testPlayer.play(url);
  269. // 检查是否超时
  270. if (timer.elapsed() > timeoutMs) {
  271. qDebug() << "Connection timeout after" << timeoutMs << "ms";
  272. testPlayer.clearPlayer();
  273. return false;
  274. }
  275. if (!success) {
  276. qDebug() << "Failed to connect to stream:" << url;
  277. return false;
  278. }
  279. // 连接成功,清理测试播放器
  280. testPlayer.clearPlayer();
  281. return true;
  282. } catch (const std::exception &e) {
  283. qDebug() << "Exception during async play:" << e.what();
  284. return false;
  285. } catch (...) {
  286. qDebug() << "Unknown exception during async play";
  287. return false;
  288. }
  289. });
  290. m_playWatcher->setFuture(future);
  291. }
  292. void AVPlayerWidget::startPlayAsync()
  293. {
  294. const QString url = m_urlEdit->text().trimmed();
  295. if (!url.isEmpty()) {
  296. playAsync(url);
  297. }
  298. }
  299. void AVPlayerWidget::onAsyncPlayFinished()
  300. {
  301. m_isLoading = false;
  302. hideLoadingUI();
  303. bool success = m_playWatcher->result();
  304. if (success) {
  305. // 连接成功,使用主线程播放器开始播放
  306. if (!m_player->play(m_pendingUrl)) {
  307. QString errorMsg = "播放失败:无法初始化播放器";
  308. qDebug() << errorMsg;
  309. emit playError(errorMsg);
  310. return;
  311. }
  312. m_isPlaying = true;
  313. m_isPaused = false;
  314. m_playButton->setEnabled(false);
  315. m_pauseButton->setEnabled(true);
  316. m_stopButton->setEnabled(true);
  317. emit playStateChanged(true);
  318. emit playLoadingFinished();
  319. qDebug() << "Successfully started playing:" << m_pendingUrl;
  320. } else {
  321. QString errorMsg;
  322. if (m_pendingUrl.startsWith("rtmp://")) {
  323. errorMsg = "RTMP连接失败:请检查流地址是否正确,网络是否畅通";
  324. } else if (m_pendingUrl.startsWith("http://") || m_pendingUrl.startsWith("https://")) {
  325. errorMsg = "HTTP流连接失败:请检查网络连接和流地址";
  326. } else {
  327. errorMsg = "连接失败:不支持的协议或无效的流地址";
  328. }
  329. qDebug() << "Play failed for URL:" << m_pendingUrl;
  330. emit playError(errorMsg);
  331. }
  332. }
  333. void AVPlayerWidget::onAsyncPlayError(const QString &error)
  334. {
  335. m_isLoading = false;
  336. hideLoadingUI();
  337. emit playError(error);
  338. }
  339. void AVPlayerWidget::showLoadingUI()
  340. {
  341. if (m_loadingLabel && m_loadingProgress) {
  342. // 计算居中位置
  343. QRect rect = m_openglWidget->geometry();
  344. int labelWidth = 200;
  345. int labelHeight = 50;
  346. int progressWidth = 300;
  347. int progressHeight = 20;
  348. m_loadingLabel->setGeometry(
  349. rect.x() + (rect.width() - labelWidth) / 2,
  350. rect.y() + (rect.height() - labelHeight) / 2 - 30,
  351. labelWidth, labelHeight
  352. );
  353. m_loadingProgress->setGeometry(
  354. rect.x() + (rect.width() - progressWidth) / 2,
  355. rect.y() + (rect.height() - progressHeight) / 2 + 30,
  356. progressWidth, progressHeight
  357. );
  358. m_loadingLabel->show();
  359. m_loadingProgress->show();
  360. m_loadingLabel->raise();
  361. m_loadingProgress->raise();
  362. }
  363. }
  364. void AVPlayerWidget::hideLoadingUI()
  365. {
  366. if (m_loadingLabel && m_loadingProgress) {
  367. m_loadingLabel->hide();
  368. m_loadingProgress->hide();
  369. }
  370. }