#include #include #include #include #include "code/base/logger.h" #include "code/player/player_core_v2.h" #include "../../AvRecorder/ui/opengl_video_widget.h" #include "qobject.h" extern "C" { #include #include } #include #include #include #include #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, OpenGLVideoWidget* videoRenderer) : m_parent(parent), m_videoRenderer(videoRenderer) {} 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); } // 新增的视频渲染回调函数 void onVideoFrameReady(AVFrame* frameData) override { if (m_videoRenderer) { // 将 frameData 转换为 AVFrame* 并进行深度拷贝 AVFrame* originalFrame = static_cast(frameData); AVFrame* copiedFrame = av_frame_clone(originalFrame); if (copiedFrame) { // 使用 QueuedConnection 避免死锁 // 当播放器暂停时,如果使用 BlockingQueuedConnection 可能导致死锁 // 因为暂停操作会等待播放线程停止,而播放线程可能正在等待UI线程响应 bool renderResult = false; QMetaObject::invokeMethod(m_videoRenderer, "Render", Qt::QueuedConnection, Q_ARG(AVFrame*, copiedFrame)); // 注意:使用QueuedConnection时,copiedFrame的释放需要在Render方法内部处理 // 这里不能立即释放,因为调用是异步的 // OpenGLVideoWidget::Render方法需要负责释放传入的AVFrame* } } } void onVideoRendererInitRequired(int width, int height) override { if (m_videoRenderer) { Logger::instance().info("[EVENT] Video renderer init required: " + std::to_string(width) + "x" + std::to_string(height)); // QMetaObject::invokeMethod(m_videoRenderer, "Open", Qt::QueuedConnection, // Q_ARG(int, width), // Q_ARG(int, height), // Q_ARG(int, pixelFormat), // Q_ARG(double, fps)); } } void onVideoRendererCloseRequired() override { if (m_videoRenderer) { Logger::instance().info("[EVENT] Video renderer close required"); // QMetaObject::invokeMethod(m_videoRenderer, "Close", Qt::QueuedConnection); } } private: QWidget* m_parent; OpenGLVideoWidget* m_videoRenderer; }; // 主播放器窗口类 class PlayerWindow : public QWidget { Q_OBJECT public: PlayerWindow(QWidget* parent = nullptr) : QWidget(parent) { setupUI(); setupPlayer(); connectSignals(); // 设置窗口属性 setWindowTitle("AV Player Test with UI"); resize(1280, 900); // 增加窗口大小以适应更多统计信息 // 启动更新定时器 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)); // 我们不在这里更新进度条和时间标签,而是在updateUI中统一更新 // 这样可以避免UI更新过于频繁 } } 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); // 更新时间标签显示总时长 m_timeLabel->setText(QString("00:00:00 / %1").arg(formatTime(info.duration))); } // 更新视频流复选框状态 m_videoStreamCheckBox->setEnabled(info.hasVideo); if (!info.hasVideo) { m_videoStreamCheckBox->setChecked(false); } if (info.hasVideo) { infoText += QString("视频: %1x%2 @ %3fps\n") .arg(info.width).arg(info.height).arg(info.fps, 0, 'f', 2); } // 更新音频流复选框状态 m_audioStreamCheckBox->setEnabled(info.hasAudio); if (!info.hasAudio) { m_audioStreamCheckBox->setChecked(false); } 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("播放"); // 重置进度条和时间显示 m_progressSlider->setValue(0); // 如果有持续时间,显示0/总时长;否则显示0/0 if (m_duration > 0) { m_timeLabel->setText(QString("00:00:00 / %1").arg(formatTime(m_duration))); } else { m_timeLabel->setText("00:00:00 / 00:00:00"); } } 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 refreshStats() { if (m_player) { // 调用播放器的dumpStats方法输出详细统计信息到日志 m_player->dumpStats(); // 强制更新UI显示 updateUI(); // 显示提示信息 QMessageBox::information(this, "统计信息", "统计信息已刷新,详细信息已写入日志文件。"); } } 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 // 这样可以让seek操作有足够时间完成初始化和缓冲 QTimer::singleShot(500, this, [this]() { m_seeking = false; }); return; // 不要立即设置m_seeking为false } m_seeking = false; } void onProgressSliderMoved(int value) { if (m_player && m_duration > 0) { // 计算预览位置 int64_t previewPosition = (m_duration * value) / 100; // 更新时间标签显示预览时间 QString timeText = QString("%1 / %2") .arg(formatTime(previewPosition)) .arg(formatTime(m_duration)); m_timeLabel->setText(timeText); } } void onVolumeChanged(int value) { if (m_player) { double volume = value / 100.0; m_player->setVolume(volume); m_volumeLabel->setText(QString("音量: %1%").arg(value)); } } // 将微秒转换为时间字符串 (HH:MM:SS) QString formatTime(int64_t timeUs) { int totalSeconds = static_cast(timeUs / 1000000); int hours = totalSeconds / 3600; int minutes = (totalSeconds % 3600) / 60; int seconds = totalSeconds % 60; return QString("%1:%2:%3") .arg(hours, 2, 10, QChar('0')) .arg(minutes, 2, 10, QChar('0')) .arg(seconds, 2, 10, QChar('0')); } void updateUI() { if (m_player) { // 更新统计信息 auto stats = m_player->getStats(); // 基本播放统计 QString statsText = QString("帧数: %1\n丢帧: %2 (%3%)\n重复帧: %4\n速度: %5x") .arg(stats.totalFrames) .arg(stats.droppedFrames) .arg(stats.totalFrames > 0 ? (stats.droppedFrames * 100.0 / stats.totalFrames) : 0, 0, 'f', 1) .arg(stats.duplicatedFrames) .arg(stats.playbackSpeed, 0, 'f', 2); // 队列状态 statsText += QString("\n\n队列状态:\n视频帧: %1\n音频帧: %2\n数据包: %3") .arg(stats.queuedVideoFrames) .arg(stats.queuedAudioFrames) .arg(stats.queuedPackets); // 同步统计 - 获取更详细的同步信息 QString syncText = "\n\n同步状态:\n当前误差: %1 ms\n平均误差: %2 ms\n最大误差: %3 ms"; // 如果播放器有同步器,获取详细的时钟信息 if (m_player) { auto debugInfo = QString::fromStdString(m_player->getDebugInfo()); // 解析调试信息中的时钟数据 QStringList lines = debugInfo.split('\n'); QString audioClockStr = "N/A", videoClockStr = "N/A", externalClockStr = "N/A", masterClockStr = "N/A"; QString masterTypeStr = "N/A", syncStrategyStr = "N/A"; for (const QString& line : lines) { if (line.trimmed().startsWith("Audio:")) { QStringList parts = line.split(":"); if (parts.size() >= 2) { bool ok; double value = parts[1].trimmed().toDouble(&ok); if (ok) { audioClockStr = QString::number(value, 'f', 3) + "s"; } } } else if (line.trimmed().startsWith("Video:")) { QStringList parts = line.split(":"); if (parts.size() >= 2) { bool ok; double value = parts[1].trimmed().toDouble(&ok); if (ok) { videoClockStr = QString::number(value, 'f', 3) + "s"; } } } else if (line.trimmed().startsWith("External:")) { QStringList parts = line.split(":"); if (parts.size() >= 2) { bool ok; double value = parts[1].trimmed().toDouble(&ok); if (ok) { externalClockStr = QString::number(value, 'f', 3) + "s"; } } } else if (line.trimmed().startsWith("Master:")) { QStringList parts = line.split(":"); if (parts.size() >= 2) { bool ok; double value = parts[1].trimmed().toDouble(&ok); if (ok) { masterClockStr = QString::number(value, 'f', 3) + "s"; } } } else if (line.contains("Master Clock Type:")) { QStringList parts = line.split(":"); if (parts.size() >= 2) { masterTypeStr = parts[1].trimmed(); } } else if (line.contains("Sync Strategy:")) { QStringList parts = line.split(":"); if (parts.size() >= 2) { syncStrategyStr = parts[1].trimmed(); } } } statsText += debugInfo; syncText = QString("\n\n同步状态:\n当前误差: %1 ms\n平均误差: %2 ms\n最大误差: %3 " "ms\n\n时钟信息:\n音频时钟: %4\n视频时钟: %5\n外部时钟: " "%6\n主时钟: %7\n\n同步配置:\n主时钟类型: %8\n同步策略: %9") .arg(stats.syncError * 1000, 0, 'f', 1) .arg(stats.avgSyncError * 1000, 0, 'f', 1) .arg(stats.maxSyncError * 1000, 0, 'f', 1) .arg(audioClockStr) .arg(videoClockStr) .arg(externalClockStr) .arg(masterClockStr) .arg(masterTypeStr) .arg(syncStrategyStr); } else { syncText = syncText.arg(stats.syncError * 1000, 0, 'f', 1) .arg(stats.avgSyncError * 1000, 0, 'f', 1) .arg(stats.maxSyncError * 1000, 0, 'f', 1); } statsText += syncText; // 性能统计 // statsText += QString("\n\n性能:\nCPU: %1%\n内存: %2 MB\n比特率: %3 kbps") // .arg(stats.cpuUsage, 0, 'f', 1) // .arg(stats.memoryUsage, 0, 'f', 1) // .arg(stats.bitrate, 0, 'f', 0); m_statsLabel->setText(statsText); // 更新时间显示和进度条 if (!m_seeking && m_duration > 0) { int64_t currentPosition = m_player->getCurrentTime(); // 更新进度条 int progress = static_cast((currentPosition * 100) / m_duration); m_progressSlider->setValue(progress); // 更新时间标签 QString timeText = QString("%1 / %2") .arg(formatTime(currentPosition)) .arg(formatTime(m_duration)); m_timeLabel->setText(timeText); } } } void onVideoStreamToggled(bool enabled) { if (m_player) { m_player->enableVideoStream(enabled); Logger::instance().info(QString("视频流%1").arg(enabled ? "启用" : "禁用").toStdString()); } } void onAudioStreamToggled(bool enabled) { if (m_player) { m_player->enableAudioStream(enabled); Logger::instance().info(QString("音频流%1").arg(enabled ? "启用" : "禁用").toStdString()); } } 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); // 进度条和时间显示 auto* progressLayout = new QHBoxLayout; m_progressSlider = new QSlider(Qt::Horizontal); m_progressSlider->setRange(0, 100); m_timeLabel = new QLabel("00:00:00 / 00:00:00"); progressLayout->addWidget(m_progressSlider); progressLayout->addWidget(m_timeLabel); leftLayout->addLayout(progressLayout); // 音量控制 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); // 流控制 auto* streamControlLayout = new QHBoxLayout; m_videoStreamCheckBox = new QCheckBox("启用视频"); m_audioStreamCheckBox = new QCheckBox("启用音频"); m_videoStreamCheckBox->setChecked(true); m_audioStreamCheckBox->setChecked(true); streamControlLayout->addWidget(m_videoStreamCheckBox); streamControlLayout->addWidget(m_audioStreamCheckBox); streamControlLayout->addStretch(); leftLayout->addLayout(streamControlLayout); mainLayout->addWidget(leftWidget, 2); // 右侧信息面板 auto* rightWidget = new QWidget; auto* rightLayout = new QVBoxLayout(rightWidget); rightWidget->setMinimumWidth(300); rightWidget->setMaximumWidth(350); // 状态信息 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); // 添加刷新和导出按钮 auto* refreshLayout = new QHBoxLayout(); m_refreshStatsButton = new QPushButton("刷新统计"); refreshLayout->addWidget(m_refreshStatsButton); refreshLayout->addStretch(); statsLayout->addLayout(refreshLayout); m_statsLabel = new QLabel("帧数: 0\n丢帧: 0 (0.0%)\n重复帧: 0\n速度: 1.0x\n\n队列状态:\n视频帧: 0\n音频帧: 0\n数据包: 0\n\n同步状态:\n当前误差: 0.0 ms\n平均误差: 0.0 ms\n最大误差: 0.0 ms\n\n时钟信息:\n音频时钟: N/A\n视频时钟: N/A\n外部时钟: N/A\n主时钟: N/A\n\n同步配置:\n主时钟类型: N/A\n同步策略: N/A\n\n性能:\nCPU: 0.0%\n内存: 0.0 MB\n比特率: 0 kbps"); m_statsLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop); m_statsLabel->setWordWrap(true); m_statsLabel->setMinimumHeight(450); // 增加最小高度以显示更多同步信息 statsLayout->addWidget(m_statsLabel); rightLayout->addWidget(statsGroup); rightLayout->addStretch(); mainLayout->addWidget(rightWidget); } void setupPlayer() { // 创建同步配置 SyncConfigV2 syncConfig; syncConfig.strategy = SyncStrategy::VIDEO_MASTER; syncConfig.syncThresholdMin = 0.040; syncConfig.syncThresholdMax = 0.100; syncConfig.frameDropThreshold = 0.100; syncConfig.frameDupThreshold = 0.100; syncConfig.audioDiffThreshold = 0.100; syncConfig.audioDiffAvgCoef = 0.01; syncConfig.audioDiffAvgCount = 20; syncConfig.clockSpeedMin = 0.9; syncConfig.clockSpeedMax = 1.1; syncConfig.clockSpeedStep = 0.001; syncConfig.minFramesForSync = 2; syncConfig.maxFramesForSync = 10; syncConfig.noSyncThreshold = 10.0; // 创建播放器实例 m_player = std::make_unique(syncConfig); // 注意:不再直接设置视频渲染器,而是通过回调机制处理 // m_player->setOpenGLVideoRenderer(m_videoRenderer); // 已废弃 // 设置初始音量 m_player->setVolume(m_volumeSlider->value() / 100.0); // 创建事件回调,传入视频渲染器 m_callback = std::make_unique(this, m_videoRenderer); 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_progressSlider, &QSlider::sliderMoved, this, &PlayerWindow::onProgressSliderMoved); // 添加进度条点击跳转功能 connect(m_progressSlider, &QSlider::valueChanged, [this](int value) { if (m_seeking && m_player && m_duration > 0) { onProgressSliderMoved(value); } }); connect(m_volumeSlider, &QSlider::valueChanged, this, &PlayerWindow::onVolumeChanged); // 连接流控制复选框信号 connect(m_videoStreamCheckBox, &QCheckBox::toggled, this, &PlayerWindow::onVideoStreamToggled); connect(m_audioStreamCheckBox, &QCheckBox::toggled, this, &PlayerWindow::onAudioStreamToggled); // 连接统计按钮信号 connect(m_refreshStatsButton, &QPushButton::clicked, this, &PlayerWindow::refreshStats); } void loadFile(const std::string& filename) { if (!m_player) return; Logger::instance().info("Loading file: " + filename); // 在打开文件前,根据复选框状态设置流启用状态 m_player->enableVideoStream(m_videoStreamCheckBox->isChecked()); m_player->enableAudioStream(m_audioStreamCheckBox->isChecked()); 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; QPushButton* m_refreshStatsButton; QSlider* m_progressSlider; QSlider* m_volumeSlider; QLabel* m_stateLabel; QLabel* m_positionLabel; QLabel* m_infoLabel; QLabel* m_statsLabel; QLabel* m_volumeLabel; QLabel* m_timeLabel; QCheckBox* m_videoStreamCheckBox; QCheckBox* m_audioStreamCheckBox; 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"); qRegisterMetaType("AVFrame*"); // 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"