zhuizhu il y a 8 mois
Parent
commit
d9c1b02e5c

+ 1 - 1
AV/code/base/media_thread_base.h

@@ -150,4 +150,4 @@ std::unique_ptr<WorkerThread<WorkFunc>> makeWorkerThread(WorkFunc workFunc, int
 
 } // namespace av
 
-#endif // AV_BASE_THREAD_BASE_H
+#endif // AV_BASE_THREAD_BASE_H

+ 16 - 13
AV/code/player/player_core_v2.cpp

@@ -1202,8 +1202,8 @@ void PlayerCoreV2::readThreadFunc() {
         }
         
         // 检查队列是否接近容量上限 - 使用队列容量的90%作为警告阈值,但不丢弃包
-        bool videoQueueNearFull = m_videoPacketQueue && m_videoPacketQueue->size() > 1800; // 2000的90%
-        bool audioQueueNearFull = m_audioPacketQueue && m_audioPacketQueue->size() > 1800; // 2000的90%
+        bool videoQueueNearFull = m_videoPacketQueue && m_videoPacketQueue->size() > 800; 
+        bool audioQueueNearFull = m_audioPacketQueue && m_audioPacketQueue->size() > 800; 
         if (videoQueueNearFull || audioQueueNearFull) { 
             // 队列接近满,暂停一段时间让解码线程处理
             Logger::instance().warning("Packet queue is getting large: Video=" + 
@@ -1300,11 +1300,13 @@ void PlayerCoreV2::videoDecodeThreadFunc() {
             std::this_thread::sleep_for(std::chrono::milliseconds(10));
             continue;
         }
-        
+        Logger::instance().info("Video decode thread read Packet111");
         // 从视频包队列获取包
         AVPacket* packet = nullptr;
         while (!m_threadsShouldStop && !packet) {
-            packet = m_videoPacketQueue->pop();
+            Logger::instance().info("Video decode thread read Packet");
+            packet = m_videoPacketQueue->pop(100);
+            Logger::instance().infof("Video decode thread read Packet %d", packet);
             if (packet) {
                 // 检查是否是EOF标记
                 if (packet->stream_index == -1 && packet->data == nullptr) {
@@ -1333,12 +1335,12 @@ void PlayerCoreV2::videoDecodeThreadFunc() {
                 std::this_thread::sleep_for(std::chrono::milliseconds(5));
             }
         }
-        
+
         if (!packet) {
             Logger::instance().debug("Video decode thread: no more packets available");
             continue;
         }
-        
+
         packetCount++;
         Logger::instance().debug("Video decode thread got packet #" + std::to_string(packetCount) + 
                                 ", size=" + std::to_string(packet->size) + 
@@ -1382,12 +1384,11 @@ void PlayerCoreV2::videoDecodeThreadFunc() {
         } else {
             Logger::instance().warning("Video decode failed with error: " + std::to_string(static_cast<int>(decodeResult)));
         }
-        
-        // packet已经被packetPtr管理,不需要手动释放
     }
-    
-    Logger::instance().info("Video decode thread finished, packets processed: " + std::to_string(packetCount) + 
-                           ", frames decoded: " + std::to_string(frameCount));
+
+    Logger::instance().info("Video decode thread finished, packets processed: "
+                            + std::to_string(packetCount)
+                            + ", frames decoded: " + std::to_string(frameCount));
 }
 
 void PlayerCoreV2::audioDecodeThreadFunc() {
@@ -1404,7 +1405,7 @@ void PlayerCoreV2::audioDecodeThreadFunc() {
         // 从音频包队列获取包
         AVPacket* packet = nullptr;
         while (!m_threadsShouldStop && !packet) {
-            packet = m_audioPacketQueue->pop();
+            packet = m_audioPacketQueue->pop(100);
             if (packet) {
                 // 检查是否是EOF标记
                 if (packet->stream_index == -1 && packet->data == nullptr) {
@@ -1506,9 +1507,10 @@ void PlayerCoreV2::videoPlayThreadFunc() {
             if (m_threadsShouldStop) {
                 break;
             }
+            std::this_thread::sleep_for(std::chrono::milliseconds(10));
             continue;
         }
-        
+
         Logger::instance().debug("Video play thread got frame, pts=" + std::to_string(frame->pts));
 
         // 创建智能指针管理帧内存
@@ -1585,6 +1587,7 @@ void PlayerCoreV2::audioPlayThreadFunc() {
             if (m_threadsShouldStop) {
                 break;
             }
+            std::this_thread::sleep_for(std::chrono::milliseconds(10));
             continue;
         }
 

+ 2 - 2
AV/code/utils/utils_frame_queue.cpp

@@ -1,8 +1,8 @@
 #include "utils_frame_queue.h"
 #include "../base/logger.h"
 #include <algorithm>
-#include <shared_mutex>
 #include <deque>
+#include <shared_mutex>
 
 namespace av {
 namespace utils {
@@ -720,4 +720,4 @@ std::unique_ptr<MultiStreamFrameQueue> FrameQueueFactory::createMultiStreamQueue
 }
 
 } // namespace utils
-} // namespace av
+} // namespace av

+ 1 - 1
AV/code/utils/utils_packet_queue.cpp

@@ -1098,4 +1098,4 @@ std::unique_ptr<MultiStreamPacketQueue> PacketQueueFactory::createMultiStreamQue
 }
 
 } // namespace utils
-} // namespace av
+} // namespace av

+ 222 - 10
AV/test_player_with_ui.cpp

@@ -24,6 +24,9 @@
 #include <QTextEdit>
 #include <QSplitter>
 #include <QDebug>
+#include <QDateTime>
+#include <QFile>
+#include <QTextStream>
 
 using namespace av::player;
 using namespace av::utils;
@@ -111,7 +114,7 @@ public:
         
         // 设置窗口属性
         setWindowTitle("AV Player Test with UI");
-        resize(1200, 800);
+        resize(1280, 900); // 增加窗口大小以适应更多统计信息
         
         // 启动更新定时器
         m_updateTimer = new QTimer(this);
@@ -169,13 +172,12 @@ public slots:
     
     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);
-            }
+            // 我们不在这里更新进度条和时间标签,而是在updateUI中统一更新
+            // 这样可以避免UI更新过于频繁
         }
     }
     
@@ -190,6 +192,9 @@ public slots:
             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)));
             }
             
             // 更新视频流复选框状态
@@ -230,6 +235,16 @@ public slots:
     
     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:
@@ -243,6 +258,98 @@ private slots:
         }
     }
     
+    void refreshStats() {
+        if (m_player) {
+            // 调用播放器的dumpStats方法输出详细统计信息到日志
+            m_player->dumpStats();
+            
+            // 强制更新UI显示
+            updateUI();
+            
+            // 显示提示信息
+            QMessageBox::information(this, "统计信息", "统计信息已刷新,详细信息已写入日志文件。");
+        }
+    }
+    
+    void exportStats() {
+        if (!m_player) return;
+        
+        // 获取保存文件名
+        QString fileName = QFileDialog::getSaveFileName(this,
+            "导出统计信息", "player_stats.txt",
+            "文本文件 (*.txt);;所有文件 (*.*)");
+            
+        if (fileName.isEmpty()) return;
+        
+        // 获取统计信息
+        auto stats = m_player->getStats();
+        auto mediaInfo = m_player->getMediaInfo();
+        auto debugInfo = QString::fromStdString(m_player->getDebugInfo());
+        
+        // 创建文件
+        QFile file(fileName);
+        if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
+            QMessageBox::critical(this, "错误", "无法创建文件: " + fileName);
+            return;
+        }
+        
+        QTextStream out(&file);
+        
+        // 写入时间戳
+        auto now = QDateTime::currentDateTime();
+        out << "播放器统计信息导出 - " << now.toString("yyyy-MM-dd HH:mm:ss") << "\n\n";
+        
+        // 写入媒体信息
+        out << "=== 媒体信息 ===\n";
+        out << "文件: " << QString::fromStdString(mediaInfo.filename) << "\n";
+        out << "时长: " << (mediaInfo.duration / 1000000.0) << " 秒\n";
+        if (mediaInfo.hasVideo) {
+            out << "视频: " << mediaInfo.width << "x" << mediaInfo.height << " @ " << mediaInfo.fps << " fps\n";
+        }
+        if (mediaInfo.hasAudio) {
+            out << "音频: " << mediaInfo.sampleRate << " Hz, " << mediaInfo.channels << " 通道\n";
+        }
+        out << "比特率: " << (mediaInfo.bitrate / 1000.0) << " kbps\n\n";
+        
+        // 写入播放统计
+        out << "=== 播放统计 ===\n";
+        out << "当前位置: " << (stats.currentTime / 1000000.0) << " 秒\n";
+        out << "总帧数: " << stats.totalFrames << "\n";
+        out << "丢帧数: " << stats.droppedFrames;
+        if (stats.totalFrames > 0) {
+            out << " (" << (stats.droppedFrames * 100.0 / stats.totalFrames) << "%)";
+        }
+        out << "\n";
+        out << "重复帧数: " << stats.duplicatedFrames << "\n";
+        out << "播放速度: " << stats.playbackSpeed << "x\n\n";
+        
+        // 写入队列状态
+        out << "=== 队列状态 ===\n";
+        out << "视频帧队列: " << stats.queuedVideoFrames << "\n";
+        out << "音频帧队列: " << stats.queuedAudioFrames << "\n";
+        out << "数据包队列: " << stats.queuedPackets << "\n\n";
+        
+        // 写入同步状态
+        out << "=== 同步状态 ===\n";
+        out << "当前同步误差: " << (stats.syncError * 1000) << " ms\n";
+        out << "平均同步误差: " << (stats.avgSyncError * 1000) << " ms\n";
+        out << "最大同步误差: " << (stats.maxSyncError * 1000) << " ms\n\n";
+        
+        // 写入性能统计
+        out << "=== 性能统计 ===\n";
+        out << "CPU使用率: " << stats.cpuUsage << "%\n";
+        out << "内存使用: " << stats.memoryUsage << " MB\n";
+        out << "比特率: " << stats.bitrate << " kbps\n\n";
+        
+        // 写入调试信息
+        out << "=== 调试信息 ===\n";
+        out << debugInfo << "\n";
+        
+        file.close();
+        
+        QMessageBox::information(this, "导出成功", "统计信息已成功导出到: " + fileName);
+    }
+    
     void playPause() {
         if (!m_player) return;
         
@@ -269,10 +376,30 @@ private slots:
             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;
@@ -281,16 +408,66 @@ private slots:
         }
     }
     
+    // 将微秒转换为时间字符串 (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\n重复帧: %3\n速度: %4x")
+            
+            // 基本播放统计
+            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);
+            
+            // 同步统计
+            statsText += QString("\n\n同步状态:\n当前误差: %1 ms\n平均误差: %2 ms\n最大误差: %3 ms")
+                         .arg(stats.syncError * 1000, 0, 'f', 1)
+                         .arg(stats.avgSyncError * 1000, 0, 'f', 1)
+                         .arg(stats.maxSyncError * 1000, 0, 'f', 1);
+            
+            // 性能统计
+            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);
+            }
         }
     }
     
@@ -335,10 +512,15 @@ private:
         
         leftLayout->addLayout(controlLayout);
         
-        // 进度条
+        // 进度条和时间显示
+        auto* progressLayout = new QHBoxLayout;
         m_progressSlider = new QSlider(Qt::Horizontal);
         m_progressSlider->setRange(0, 100);
-        leftLayout->addWidget(m_progressSlider);
+        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;
@@ -369,7 +551,8 @@ private:
         // 右侧信息面板
         auto* rightWidget = new QWidget;
         auto* rightLayout = new QVBoxLayout(rightWidget);
-        rightWidget->setMaximumWidth(300);
+        rightWidget->setMinimumWidth(300);
+        rightWidget->setMaximumWidth(350);
         
         // 状态信息
         auto* statusGroup = new QGroupBox("状态信息");
@@ -391,7 +574,20 @@ private:
         // 统计信息
         auto* statsGroup = new QGroupBox("播放统计");
         auto* statsLayout = new QVBoxLayout(statsGroup);
-        m_statsLabel = new QLabel("帧数: 0\n丢帧: 0\n重复帧: 0\n速度: 1.0x");
+        
+        // 添加刷新和导出按钮
+        auto* refreshLayout = new QHBoxLayout();
+        m_refreshStatsButton = new QPushButton("刷新统计");
+        m_exportStatsButton = new QPushButton("导出统计");
+        refreshLayout->addWidget(m_refreshStatsButton);
+        refreshLayout->addWidget(m_exportStatsButton);
+        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性能:\nCPU: 0.0%\n内存: 0.0 MB\n比特率: 0 kbps");
+        m_statsLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);
+        m_statsLabel->setWordWrap(true);
+        m_statsLabel->setMinimumHeight(300); // 设置最小高度以显示所有统计信息
         statsLayout->addWidget(m_statsLabel);
         rightLayout->addWidget(statsGroup);
         
@@ -439,14 +635,27 @@ private:
         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);
+        connect(m_exportStatsButton, &QPushButton::clicked, this, &PlayerWindow::exportStats);
     }
     
     void loadFile(const std::string& filename) {
@@ -474,6 +683,8 @@ private:
     QPushButton* m_openButton;
     QPushButton* m_playButton;
     QPushButton* m_stopButton;
+    QPushButton* m_refreshStatsButton;
+    QPushButton* m_exportStatsButton;
     QSlider* m_progressSlider;
     QSlider* m_volumeSlider;
     QLabel* m_stateLabel;
@@ -481,6 +692,7 @@ private:
     QLabel* m_infoLabel;
     QLabel* m_statsLabel;
     QLabel* m_volumeLabel;
+    QLabel* m_timeLabel;
     QCheckBox* m_videoStreamCheckBox;
     QCheckBox* m_audioStreamCheckBox;
     QTimer* m_updateTimer;