| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- #include <chrono>
- #include <string>
- #include <thread>
- #include <iomanip>
- #include "code/base/logger.h"
- #include "code/player/player_core_v2.h"
- #include <QApplication>
- #include <QWidget>
- #include <QVBoxLayout>
- #include <QHBoxLayout>
- #include <QPushButton>
- #include <QLabel>
- #include <QSlider>
- #include <QFileDialog>
- #include <QMessageBox>
- #include <QTimer>
- #include <QProgressBar>
- #include <QGroupBox>
- #include <QSpinBox>
- #include <QCheckBox>
- #include <QComboBox>
- #include <QTextEdit>
- #include <QSplitter>
- #include <QDebug>
- 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<int>(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<PlayerState>(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<int>((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<PlayerCoreV2>(syncConfig);
-
- // 设置视频渲染器
- m_player->setOpenGLVideoRenderer(m_videoRenderer);
-
- // 创建事件回调
- m_callback = std::make_unique<UIPlayerCallback>(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<int>(result));
- QMessageBox::critical(this, "文件打开失败", error);
- Logger::instance().error("Failed to open file: " + std::to_string(static_cast<int>(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<PlayerCoreV2> m_player;
- std::unique_ptr<UIPlayerCallback> 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"
|