#include #include #include #include #include "code/base/logger.h" #include "code/player/player_core_v2.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace av::player; using namespace av::utils; // 播放器事件回调类 class UIPlayerCallback : public PlayerEventCallback { public: UIPlayerCallback(QWidget* parent) : m_parent(parent) {} void onStateChanged(PlayerState newState) override { std::string stateStr; switch (newState) { case PlayerState::Idle: stateStr = "Idle"; break; case PlayerState::Opening: stateStr = "Opening"; break; case PlayerState::Stopped: stateStr = "Stopped"; break; case PlayerState::Playing: stateStr = "Playing"; break; case PlayerState::Paused: stateStr = "Paused"; break; case PlayerState::Seeking: stateStr = "Seeking"; break; case PlayerState::Error: stateStr = "Error"; break; } Logger::instance().info("[EVENT] State changed to: " + stateStr); // 发送信号到UI线程 QMetaObject::invokeMethod(m_parent, "onPlayerStateChanged", Qt::QueuedConnection, Q_ARG(int, static_cast(newState))); } void onPositionChanged(int64_t position) override { // 发送位置更新到UI线程 QMetaObject::invokeMethod(m_parent, "onPlayerPositionChanged", Qt::QueuedConnection, Q_ARG(qint64, position)); } void onMediaInfoChanged(const MediaInfo& info) override { Logger::instance().info("[EVENT] Media info changed:"); Logger::instance().info(" File: " + info.filename); Logger::instance().info(" Duration: " + std::to_string(info.duration / 1000000.0) + "s"); Logger::instance().info(" Has Video: " + std::string(info.hasVideo ? "Yes" : "No")); Logger::instance().info(" Has Audio: " + std::string(info.hasAudio ? "Yes" : "No")); if (info.hasVideo) { Logger::instance().info(" Video: " + std::to_string(info.width) + "x" + std::to_string(info.height) + " @ " + std::to_string(info.fps) + " fps"); } if (info.hasAudio) { Logger::instance().info(" Audio: " + std::to_string(info.sampleRate) + " Hz, " + std::to_string(info.channels) + " channels"); } // 发送媒体信息到UI线程 QMetaObject::invokeMethod(m_parent, "onMediaInfoChanged", Qt::QueuedConnection); } void onErrorOccurred(const std::string& error) override { Logger::instance().error("[ERROR] " + error); QMetaObject::invokeMethod(m_parent, "onPlayerError", Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(error))); } void onFrameDropped(int64_t totalDropped) override { Logger::instance().warning("[WARNING] Frame dropped, total: " + std::to_string(totalDropped)); } void onSyncError(double error, const std::string& reason) override { Logger::instance().warning("[WARNING] Sync error: " + std::to_string(error * 1000) + "ms, reason: " + reason); } void onEndOfFile() override { Logger::instance().info("[EVENT] End of file reached"); QMetaObject::invokeMethod(m_parent, "onPlayerEndOfFile", Qt::QueuedConnection); } private: QWidget* m_parent; }; // 主播放器窗口类 class PlayerWindow : public QWidget { Q_OBJECT public: PlayerWindow(QWidget* parent = nullptr) : QWidget(parent) { setupUI(); setupPlayer(); connectSignals(); // 设置窗口属性 setWindowTitle("AV Player Test with UI"); resize(1200, 800); // 启动更新定时器 m_updateTimer = new QTimer(this); connect(m_updateTimer, &QTimer::timeout, this, &PlayerWindow::updateUI); m_updateTimer->start(100); // 100ms更新一次 m_player->openFile("C:/Users/zhuizhu/Videos/2.mp4"); // playPause(); } ~PlayerWindow() { if (m_player) { m_player->stop(); } } public slots: void onPlayerStateChanged(int state) { PlayerState playerState = static_cast(state); switch (playerState) { case PlayerState::Idle: m_stateLabel->setText("状态: 空闲"); m_playButton->setText("播放"); m_playButton->setEnabled(false); break; case PlayerState::Opening: m_stateLabel->setText("状态: 打开中..."); m_playButton->setEnabled(false); break; case PlayerState::Stopped: m_stateLabel->setText("状态: 已停止"); m_playButton->setText("播放"); m_playButton->setEnabled(true); break; case PlayerState::Playing: m_stateLabel->setText("状态: 播放中"); m_playButton->setText("暂停"); m_playButton->setEnabled(true); break; case PlayerState::Paused: m_stateLabel->setText("状态: 已暂停"); m_playButton->setText("播放"); m_playButton->setEnabled(true); break; case PlayerState::Seeking: m_stateLabel->setText("状态: 跳转中..."); break; case PlayerState::Error: m_stateLabel->setText("状态: 错误"); m_playButton->setEnabled(false); break; } } void onPlayerPositionChanged(qint64 position) { if (!m_seeking) { double seconds = position / 1000000.0; m_positionLabel->setText(QString("位置: %1s").arg(seconds, 0, 'f', 2)); if (m_duration > 0) { int progress = static_cast((position * 100) / m_duration); m_progressSlider->setValue(progress); } } } void onMediaInfoChanged() { if (m_player) { auto info = m_player->getMediaInfo(); m_duration = info.duration; QString infoText = QString("文件: %1\n") .arg(QString::fromStdString(info.filename)); if (info.duration > 0) { double durationSec = info.duration / 1000000.0; infoText += QString("时长: %1s\n").arg(durationSec, 0, 'f', 2); } if (info.hasVideo) { infoText += QString("视频: %1x%2 @ %3fps\n") .arg(info.width).arg(info.height).arg(info.fps, 0, 'f', 2); // 初始化视频渲染器 // if (m_videoRenderer && !m_videoRenderer->isInitialized()) { // m_videoRenderer->initialize(info.width, info.height, AV_PIX_FMT_YUV420P, info.fps); // } } if (info.hasAudio) { infoText += QString("音频: %1Hz, %2ch\n") .arg(info.sampleRate).arg(info.channels); } m_infoLabel->setText(infoText); m_playButton->setEnabled(true); } } void onPlayerError(const QString& error) { QMessageBox::critical(this, "播放器错误", error); } void onPlayerEndOfFile() { m_playButton->setText("播放"); } private slots: void openFile() { QString fileName = QFileDialog::getOpenFileName(this, "选择媒体文件", "", "媒体文件 (*.mp4 *.avi *.mkv *.mov *.wmv *.flv *.webm *.mp3 *.wav *.aac *.flac);;所有文件 (*.*)"); if (!fileName.isEmpty()) { loadFile(fileName.toStdString()); } } void playPause() { if (!m_player) return; auto state = m_player->getState(); if (state == PlayerState::Playing) { m_player->pause(); } else if (state == PlayerState::Paused || state == PlayerState::Stopped) { m_player->play(); } } void stop() { if (m_player) { m_player->stop(); } } void onProgressSliderPressed() { m_seeking = true; } void onProgressSliderReleased() { if (m_player && m_duration > 0) { int value = m_progressSlider->value(); int64_t position = (m_duration * value) / 100; m_player->seek(position); } m_seeking = false; } void onVolumeChanged(int value) { if (m_player) { double volume = value / 100.0; m_player->setVolume(volume); m_volumeLabel->setText(QString("音量: %1%").arg(value)); } } void updateUI() { if (m_player) { // 更新统计信息 auto stats = m_player->getStats(); QString statsText = QString("帧数: %1\n丢帧: %2\n重复帧: %3\n速度: %4x") .arg(stats.totalFrames) .arg(stats.droppedFrames) .arg(stats.duplicatedFrames) .arg(stats.playbackSpeed, 0, 'f', 2); m_statsLabel->setText(statsText); } } private: void setupUI() { auto* mainLayout = new QHBoxLayout(this); // 左侧视频区域 auto* leftWidget = new QWidget; auto* leftLayout = new QVBoxLayout(leftWidget); // 视频渲染器 m_videoRenderer = new OpenGLVideoWidget; m_videoRenderer->setMinimumSize(640, 480); leftLayout->addWidget(m_videoRenderer, 1); // 控制按钮 auto* controlLayout = new QHBoxLayout; m_openButton = new QPushButton("打开文件"); m_playButton = new QPushButton("播放"); m_stopButton = new QPushButton("停止"); m_playButton->setEnabled(false); controlLayout->addWidget(m_openButton); controlLayout->addWidget(m_playButton); controlLayout->addWidget(m_stopButton); controlLayout->addStretch(); leftLayout->addLayout(controlLayout); // 进度条 m_progressSlider = new QSlider(Qt::Horizontal); m_progressSlider->setRange(0, 100); leftLayout->addWidget(m_progressSlider); // 音量控制 auto* volumeLayout = new QHBoxLayout; volumeLayout->addWidget(new QLabel("音量:")); m_volumeSlider = new QSlider(Qt::Horizontal); m_volumeSlider->setRange(0, 100); m_volumeSlider->setValue(100); m_volumeSlider->setMaximumWidth(200); m_volumeLabel = new QLabel("音量: 100%"); volumeLayout->addWidget(m_volumeSlider); volumeLayout->addWidget(m_volumeLabel); volumeLayout->addStretch(); leftLayout->addLayout(volumeLayout); mainLayout->addWidget(leftWidget, 2); // 右侧信息面板 auto* rightWidget = new QWidget; auto* rightLayout = new QVBoxLayout(rightWidget); rightWidget->setMaximumWidth(300); // 状态信息 auto* statusGroup = new QGroupBox("状态信息"); auto* statusLayout = new QVBoxLayout(statusGroup); m_stateLabel = new QLabel("状态: 空闲"); m_positionLabel = new QLabel("位置: 0s"); statusLayout->addWidget(m_stateLabel); statusLayout->addWidget(m_positionLabel); rightLayout->addWidget(statusGroup); // 媒体信息 auto* infoGroup = new QGroupBox("媒体信息"); auto* infoLayout = new QVBoxLayout(infoGroup); m_infoLabel = new QLabel("未加载文件"); m_infoLabel->setWordWrap(true); infoLayout->addWidget(m_infoLabel); rightLayout->addWidget(infoGroup); // 统计信息 auto* statsGroup = new QGroupBox("播放统计"); auto* statsLayout = new QVBoxLayout(statsGroup); m_statsLabel = new QLabel("帧数: 0\n丢帧: 0\n重复帧: 0\n速度: 1.0x"); statsLayout->addWidget(m_statsLabel); rightLayout->addWidget(statsGroup); rightLayout->addStretch(); mainLayout->addWidget(rightWidget); } void setupPlayer() { // 创建同步配置 SyncConfigV2 syncConfig; syncConfig.strategy = SyncStrategy::VIDEO_MASTER; syncConfig.audioSyncThreshold = 0.040; syncConfig.videoSyncThreshold = 0.020; syncConfig.maxSyncError = 0.200; syncConfig.clockUpdateInterval = 10; syncConfig.smoothingWindow = 10; syncConfig.enableAdaptiveSync = true; syncConfig.enableFrameDrop = true; syncConfig.enableFrameDuplicate = true; syncConfig.enablePrediction = true; syncConfig.enableErrorRecovery = true; // 创建播放器实例 m_player = std::make_unique(syncConfig); // 设置视频渲染器 m_player->setOpenGLVideoRenderer(m_videoRenderer); // 创建事件回调 m_callback = std::make_unique(this); m_player->setEventCallback(m_callback.get()); m_duration = 0; m_seeking = false; } void connectSignals() { connect(m_openButton, &QPushButton::clicked, this, &PlayerWindow::openFile); connect(m_playButton, &QPushButton::clicked, this, &PlayerWindow::playPause); connect(m_stopButton, &QPushButton::clicked, this, &PlayerWindow::stop); connect(m_progressSlider, &QSlider::sliderPressed, this, &PlayerWindow::onProgressSliderPressed); connect(m_progressSlider, &QSlider::sliderReleased, this, &PlayerWindow::onProgressSliderReleased); connect(m_volumeSlider, &QSlider::valueChanged, this, &PlayerWindow::onVolumeChanged); } void loadFile(const std::string& filename) { if (!m_player) return; Logger::instance().info("Loading file: " + filename); auto result = m_player->openFile(filename); if (result != ErrorCode::SUCCESS) { QString error = QString("无法打开文件: %1\n错误代码: %2") .arg(QString::fromStdString(filename)) .arg(static_cast(result)); QMessageBox::critical(this, "文件打开失败", error); Logger::instance().error("Failed to open file: " + std::to_string(static_cast(result))); } } private: // UI组件 OpenGLVideoWidget* m_videoRenderer; QPushButton* m_openButton; QPushButton* m_playButton; QPushButton* m_stopButton; QSlider* m_progressSlider; QSlider* m_volumeSlider; QLabel* m_stateLabel; QLabel* m_positionLabel; QLabel* m_infoLabel; QLabel* m_statsLabel; QLabel* m_volumeLabel; QTimer* m_updateTimer; // 播放器相关 std::unique_ptr m_player; std::unique_ptr m_callback; int64_t m_duration; bool m_seeking; }; int main(int argc, char* argv[]) { QApplication app(argc, argv); // 初始化日志系统 Logger::instance().initialize("test.log", LogLevel::DEBUG, false, false); Logger::instance().info("PlayerCoreV2 Example Started"); try { // 创建播放器窗口 PlayerWindow window; window.show(); // 如果有命令行参数,自动加载文件 // if (argc > 1) { // QTimer::singleShot(500, [&window, argv]() { // QMetaObject::invokeMethod(&window, "loadFile", Qt::QueuedConnection, // Q_ARG(std::string, std::string(argv[1]))); // }); // } else { // // 默认加载测试文件 // QTimer::singleShot(500, [&window]() { // std::string testFile = "C:/Users/zhuizhu/Videos/2.mp4"; // QMetaObject::invokeMethod(&window, "loadFile", Qt::QueuedConnection, // Q_ARG(std::string, testFile)); // }); // } return app.exec(); } catch (const std::exception& e) { Logger::instance().error("Exception: " + std::string(e.what())); QMessageBox::critical(nullptr, "错误", QString("发生异常: %1").arg(e.what())); return 1; } catch (...) { Logger::instance().error("Unknown exception occurred"); QMessageBox::critical(nullptr, "错误", "发生未知异常"); return 1; } } #include "test_player_with_ui.moc"