#include "avplayerwidget.h" #include "avopenglwidget.h" #include "av_player.h" #include "vframe.h" AVPlayerWidget::AVPlayerWidget(QWidget *parent) : QWidget{parent} , m_player(new AVPlayer) , m_openglWidget(new AVOpenGLWidget(this)) , m_isPlaying(false) , m_isPaused(false) { setupUI(); connectSignals(); // 设置默认音量 m_player->setVolume(50); m_volumeSlider->setValue(50); // 设置默认测试URL m_urlEdit->setText("rtmp://127.0.0.1:1935/stream/V1/stream"); } AVPlayerWidget::~AVPlayerWidget() { if (m_player) { m_player->clearPlayer(); } } void AVPlayerWidget::setupUI() { m_mainLayout = new QVBoxLayout(this); // 添加OpenGL视频渲染组件 m_mainLayout->addWidget(m_openglWidget, 1); // 创建控制面板 m_controlLayout = new QHBoxLayout(); // URL输入框 m_urlEdit = new QLineEdit(this); m_urlEdit->setPlaceholderText("输入视频文件路径或URL"); m_controlLayout->addWidget(m_urlEdit, 2); // 测试播放按钮 m_testPlayButton = new QPushButton("测试播放", this); m_controlLayout->addWidget(m_testPlayButton); // 播放控制按钮 m_playButton = new QPushButton("播放", this); m_pauseButton = new QPushButton("暂停", this); m_stopButton = new QPushButton("停止", this); m_controlLayout->addWidget(m_playButton); m_controlLayout->addWidget(m_pauseButton); m_controlLayout->addWidget(m_stopButton); // 音量控制 QLabel *volumeLabel = new QLabel("音量:", this); m_volumeSlider = new QSlider(Qt::Horizontal, this); m_volumeSlider->setRange(0, 100); m_volumeSlider->setValue(50); m_volumeSlider->setMaximumWidth(100); m_controlLayout->addWidget(volumeLabel); m_controlLayout->addWidget(m_volumeSlider); // 时间显示 m_timeLabel = new QLabel("00:00 / 00:00", this); m_controlLayout->addWidget(m_timeLabel); m_mainLayout->addLayout(m_controlLayout); // 设置初始状态 m_pauseButton->setEnabled(false); m_stopButton->setEnabled(false); } void AVPlayerWidget::connectSignals() { // 连接AVPlayer信号 connect(m_player, &AVPlayer::frameChanged, this, &AVPlayerWidget::onFrameChanged, Qt::QueuedConnection); connect(m_player, &AVPlayer::AVDurationChanged, this, &AVPlayerWidget::durationChangedSlot); connect(m_player, &AVPlayer::AVPtsChanged, this, &AVPlayerWidget::ptsChangedSlot); connect(m_player, &AVPlayer::AVTerminate, this, &AVPlayerWidget::terminateSlot, Qt::QueuedConnection); // 连接UI控件信号 connect(m_playButton, &QPushButton::clicked, this, &AVPlayerWidget::onPlayButtonClicked); connect(m_pauseButton, &QPushButton::clicked, this, &AVPlayerWidget::onPauseButtonClicked); connect(m_stopButton, &QPushButton::clicked, this, &AVPlayerWidget::onStopButtonClicked); connect(m_testPlayButton, &QPushButton::clicked, this, &AVPlayerWidget::onTestPlayButtonClicked); connect(m_volumeSlider, &QSlider::valueChanged, this, &AVPlayerWidget::onVolumeChanged); } void AVPlayerWidget::play(const QString &url) { if (!m_player->play(url)) { // 提示反馈? return; } m_isPlaying = true; m_isPaused = false; m_playButton->setEnabled(false); m_pauseButton->setEnabled(true); m_stopButton->setEnabled(true); emit playStateChanged(true); } void AVPlayerWidget::stop() { m_player->clearPlayer(); m_isPlaying = false; m_isPaused = false; m_playButton->setEnabled(true); m_pauseButton->setEnabled(false); m_stopButton->setEnabled(false); m_openglWidget->clearFrame(); emit playStateChanged(false); } void AVPlayerWidget::pause() { if (m_isPlaying && !m_isPaused) { m_player->pause(true); m_isPaused = true; m_playButton->setText("继续"); m_playButton->setEnabled(true); m_pauseButton->setEnabled(false); } } void AVPlayerWidget::resume() { if (m_isPlaying && m_isPaused) { m_player->pause(false); m_isPaused = false; m_playButton->setText("播放"); m_playButton->setEnabled(false); m_pauseButton->setEnabled(true); } } void AVPlayerWidget::onPlayButtonClicked() { if (m_isPaused) { resume(); } else { QString url = m_urlEdit->text().trimmed(); if (!url.isEmpty()) { play(url); } } } void AVPlayerWidget::onPauseButtonClicked() { pause(); } void AVPlayerWidget::onStopButtonClicked() { stop(); } void AVPlayerWidget::onTestPlayButtonClicked() { // 使用默认测试视频 QString testUrl = "C:/Users/zhuizhu/Videos/2.mp4"; m_urlEdit->setText(testUrl); play(testUrl); } void AVPlayerWidget::onVolumeChanged(int volume) { m_player->setVolume(volume); } void AVPlayerWidget::onFrameChanged(QSharedPointer frame) { m_openglWidget->onShowYUV(frame); // 注意:这里不要释放avFrame,因为Render方法会处理 } void AVPlayerWidget::ptsChangedSlot(unsigned int pts) { // 更新时间显示 int seconds = pts / 1000; int minutes = seconds / 60; seconds = seconds % 60; QString timeStr = QString("%1:%2").arg(minutes, 2, 10, QChar('0')).arg(seconds, 2, 10, QChar('0')); // 这里可以更新时间标签的当前时间部分 } void AVPlayerWidget::durationChangedSlot(unsigned int duration) { // 更新总时长显示 int seconds = duration / 1000; int minutes = seconds / 60; seconds = seconds % 60; QString durationStr = QString("%1:%2").arg(minutes, 2, 10, QChar('0')).arg(seconds, 2, 10, QChar('0')); m_timeLabel->setText(QString("00:00 / %1").arg(durationStr)); } void AVPlayerWidget::terminateSlot() { stop(); } // 兼容旧接口实现 void AVPlayerWidget::stopPlay() { stop(); } void AVPlayerWidget::setPlayRoomId(const QString &id) { // 以当前URL为基准,去掉最后一节后拼接房间ID,避免硬编码服务器地址 QString base = m_urlEdit->text().trimmed(); if (base.isEmpty()) { base = "rtmp://127.0.0.1:1935/stream/V1/stream"; } int lastSlash = base.lastIndexOf('/'); QString streamBase = lastSlash > 0 ? base.left(lastSlash) : base; QString url = streamBase + "/" + id; m_urlEdit->setText(url); } void AVPlayerWidget::startPlay() { const QString url = m_urlEdit->text().trimmed(); if (!url.isEmpty()) { play(url); } }