| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793 |
- #include <chrono>
- #include <string>
- #include <thread>
- #include <iomanip>
- #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 <libavutil/frame.h>
- #include <libavutil/imgutils.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>
- #include <QDateTime>
- #include <QFile>
- #include <QTextStream>
- #include <QMetaObject>
- 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<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);
- }
-
- // 新增的视频渲染回调函数
- void onVideoFrameReady(AVFrame* frameData) override
- {
- if (m_videoRenderer) {
- // 将 frameData 转换为 AVFrame* 并进行深度拷贝
- AVFrame* originalFrame = static_cast<AVFrame*>(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<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));
-
- // 我们不在这里更新进度条和时间标签,而是在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<int>(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<int>((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<PlayerCoreV2>(syncConfig);
-
- // 注意:不再直接设置视频渲染器,而是通过回调机制处理
- // m_player->setOpenGLVideoRenderer(m_videoRenderer); // 已废弃
-
- // 设置初始音量
- m_player->setVolume(m_volumeSlider->value() / 100.0);
-
- // 创建事件回调,传入视频渲染器
- m_callback = std::make_unique<UIPlayerCallback>(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<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;
- 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<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");
- qRegisterMetaType<AVFrame*>("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"
|