Переглянути джерело

fix: 丢帧严重的问题

zhuizhu 9 місяців тому
батько
коміт
c97bde8690

+ 18 - 5
AvRecorder/capturer/audio/audio_capturer.cpp

@@ -7,11 +7,9 @@
 #define DEFAULT_CHANNELS 1               // 默认音频通道数:1
 #define DEFAULT_AUDIO_PACKET_INTERVAL 10 // 默认音频包发送间隔:10ms
 
-bool AudioCapturer::Init(Type deviceType, CallBack callback, void* userInfo)
+bool AudioCapturer::Init(Type deviceType)
 {
     Stop();
-    _userInfo = userInfo;
-    _callback = callback;
     _deviceType = deviceType;
     __CheckBool(_CreateDeviceEnumerator(&_pDeviceEnumerator));
     __CheckBool(_CreateDevice(_pDeviceEnumerator, &_pDevice));
@@ -37,7 +35,6 @@ bool AudioCapturer::Start()
 {
     __CheckBool(_isInit);
     _loopFlag = true;
-    // 用于强制打开扬声器
     PlaySoundA("./rc/mute.wav", nullptr, SND_FILENAME | SND_ASYNC | SND_LOOP);
     _captureThread = new std::thread(
         [this] { _ThreadRun(_pAudioClient, _pAudioCaptureClient); });
@@ -179,7 +176,12 @@ bool AudioCapturer::_ThreadRun(IAudioClient* audio_client,
                 &dw_flag, nullptr, nullptr)));
 
             size_t size = (_formatex.Format.wBitsPerSample >> 3) * _formatex.Format.nChannels * num_frames_to_read;
-            _callback(p_audio_data, size, _userInfo);
+            {
+                std::lock_guard<std::mutex> lock(_bufferMutex);
+                size_t oldSize = _buffer.size();
+                _buffer.resize(oldSize + size);
+                memcpy(_buffer.data() + oldSize, p_audio_data, size);
+            }
             __CheckBool(SUCCEEDED(audio_capture_client->ReleaseBuffer(num_frames_to_read)));
         }
     }
@@ -187,3 +189,14 @@ bool AudioCapturer::_ThreadRun(IAudioClient* audio_client,
     audio_client->Stop();
     return true;
 }
+
+int AudioCapturer::readAudioData(char* buf, int maxLen)
+{
+    std::lock_guard<std::mutex> lock(_bufferMutex);
+    int toRead = std::min<int>(maxLen, _buffer.size());
+    if (toRead > 0) {
+        memcpy(buf, _buffer.data(), toRead);
+        _buffer.erase(_buffer.begin(), _buffer.begin() + toRead);
+    }
+    return toRead;
+}

+ 6 - 3
AvRecorder/capturer/audio/audio_capturer.h

@@ -10,20 +10,22 @@
 
 #include <memory>
 #include <thread>
+#include <vector>
+#include <mutex>
 
 class AudioCapturer : public IAudioCapturer
 {
 public:
-    bool Init(Type deviceType, CallBack callback, void* userInfo = nullptr) override;
+    bool Init(Type deviceType) override;
     bool Start() override;
     void Stop() override;
 
     const AudioFormat& GetFormat() const override { return _format; }
+    int readAudioData(char* buf, int maxLen) override;
 
 private:
     mutable AudioFormat _format;
     bool _isInit = false;
-    CallBack _callback;
     Type _deviceType;
     IMMDeviceEnumerator* _pDeviceEnumerator = nullptr;
     IMMDevice* _pDevice = nullptr;
@@ -32,7 +34,8 @@ private:
     std::thread* _captureThread = nullptr;
     bool _loopFlag = false;
     WAVEFORMATEXTENSIBLE _formatex;
-    void* _userInfo = nullptr;
+    std::vector<char> _buffer;
+    std::mutex _bufferMutex;
 
     bool _CreateDeviceEnumerator(IMMDeviceEnumerator** enumerator);
     bool _CreateDevice(IMMDeviceEnumerator* enumerator, IMMDevice** device);

+ 25 - 79
AvRecorder/capturer/audio/audio_qt_capturer.cpp

@@ -140,8 +140,6 @@ qint64 AudioInfo::writeData(const char* data, qint64 len)
 QtAudioCapturer::QtAudioCapturer(QObject* parent)
     : QObject(parent)
     , m_deviceType(Microphone)
-    , m_callback(nullptr)
-    , m_userInfo(nullptr)
     , m_isRunning(false)
     , m_audioDevice(nullptr)
 {
@@ -157,14 +155,10 @@ QtAudioCapturer::~QtAudioCapturer()
     Stop();
 }
 
-bool QtAudioCapturer::Init(Type deviceType, CallBack callback, void* userInfo)
+bool QtAudioCapturer::Init(Type deviceType)
 {
     m_deviceType = deviceType;
-    m_callback = callback;
-    m_userInfo = userInfo;
-
     setupAudioFormat();
-
     if (m_deviceType == Microphone) {
         return initMicrophone();
     } else {
@@ -188,57 +182,15 @@ bool QtAudioCapturer::Start()
              << (m_deviceType == Microphone ? "麦克风" : "扬声器");
 
     if (m_deviceType == Microphone && m_audioInput) {
-        // 设置更合理的缓冲区大小
-        // int bufferSize = 8192;
-        // 或者使用计算值:m_qtAudioFormat.sampleRate() * m_qtAudioFormat.channelCount() * 2 / 5;
-        // m_audioInput->setBufferSize(bufferSize);
-        // qDebug() << "QtAudioCapturer::Start - 设置缓冲区大小:" << bufferSize;
-
-        // qDebug() << "QtAudioCapturer::Start - 缓冲区大小:" << m_audioInput->bufferSize();
-
-        // 设置更短的通知间隔,提高响应性
-        // m_audioInput->setNotifyInterval(20); // 20毫秒
-
-        // 启动音频输入
         m_audioDevice = m_audioInput->start();
         if (!m_audioDevice) {
             qWarning() << "QtAudioCapturer::Start - 启动音频输入失败";
             return false;
         }
-
-        // qDebug() << "QtAudioCapturer::Start - 麦克风启动成功,缓冲区大小:"
-        //          << m_audioInput->bufferSize();
-
-        // 连接信号
+        qDebug() << "QtAudioCapturer::Start - QAudioInput state:" << m_audioInput->state();
+        qDebug() << "QtAudioCapturer::Start - QAudioInput error:" << m_audioInput->error();
         connect(m_audioDevice, &QIODevice::readyRead, this, &QtAudioCapturer::handleReadyRead);
-
-        // 添加一个定时器,定期检查音频状态
-        QTimer* statusTimer = new QTimer(this);
-        connect(statusTimer, &QTimer::timeout, [this]() {
-            // qDebug() << "QtAudioCapturer::StatusCheck - 音频输入状态:" << m_audioInput->state()
-            //          << "错误:" << m_audioInput->error()
-            //          << "处理字节数:" << m_audioInput->processedUSecs()
-            //          << "可用字节数:" << (m_audioDevice ? m_audioDevice->bytesAvailable() : 0);
-
-            // 如果状态不是活动状态,尝试重新启动
-            if (m_audioInput->state() != QAudio::ActiveState) {
-                qDebug() << "QtAudioCapturer::StatusCheck - 尝试重新启动音频输入";
-                m_audioInput->stop();
-                m_audioDevice = m_audioInput->start();
-                if (m_audioDevice) {
-                    connect(m_audioDevice,
-                            &QIODevice::readyRead,
-                            this,
-                            &QtAudioCapturer::handleReadyRead);
-                }
-            }
-        });
-        statusTimer->start(2000); // 每2秒检查一次
-    } else if (m_deviceType == Speaker) {
-        // 系统声音捕获的实现
-        qDebug() << "QtAudioCapturer::Start - 尝试启动扬声器捕获";
     }
-
     m_isRunning = true;
     return true;
 }
@@ -272,42 +224,36 @@ const AudioFormat& QtAudioCapturer::GetFormat() const
 
 void QtAudioCapturer::handleReadyRead()
 {
-    // qDebug() << "handleReadyRead" << m_audioDevice << m_audioDevice->bytesAvailable();
+    //qDebug() << "handleReadyRead called, m_audioDevice:" << m_audioDevice;
     if (m_audioDevice) {
         QByteArray data = m_audioDevice->readAll();
-        // qDebug() << "QtAudioCapturer::handleReadyRead - 收到数据大小:" << data.size() << "字节";
-
-        // 检查数据是否有效
+        //qDebug() << "handleReadyRead: read data size:" << data.size();
         if (data.size() > 0) {
-            // 计算音量级别用于调试
-            // float volume = calculateVolume(data);
-            // qDebug() << "QtAudioCapturer::handleReadyRead - 计算的音量级别:" << volume;
-
-            // 确保回调被调用
-            if (m_callback) {
-                m_callback(data.data(), data.size(), m_userInfo);
-            } else {
-                qWarning() << "QtAudioCapturer::handleReadyRead - 回调函数为空";
-            }
+            QMutexLocker locker(&m_mutex);
+            m_dataBuffer.append(data);
+            //qDebug() << "Audio data appended, buffer size now:" << m_dataBuffer.size();
         }
+    }
+}
 
-        // 不再需要将数据写入缓冲区,因为已经直接处理了
-        // m_buffer.write(data);
-    } else {
-        // 减少日志输出频率,只在调试时启用
-        // qDebug() << "QtAudioCapturer::handleReadyRead - 没有可用数据";
+int QtAudioCapturer::readAudioData(char* buf, int maxLen)
+{
+    QMutexLocker locker(&m_mutex);
+    int toRead = qMin(maxLen, m_dataBuffer.size());
+    // qDebug() << "readAudioData called, buffer size:" << m_dataBuffer.size() << ", toRead:" << toRead;
+    if (toRead > 0) {
+        memcpy(buf, m_dataBuffer.constData(), toRead);
+        m_dataBuffer.remove(0, toRead);
     }
+    return toRead;
 }
 
 void QtAudioCapturer::processAudioData()
 {
     QByteArray data = m_buffer.buffer();
-    if (data.size() > 0 && m_callback) {
-        qDebug() << "QtAudioCapturer::processAudioData - 处理缓冲区数据,大小:" << data.size()
-                 << "字节";
-
-        // 如果没有在 handleReadyRead 中直接调用回调,则在这里调用
-        // m_callback(data.data(), data.size(), m_userInfo);
+    if (data.size() > 0) {
+        // qDebug() << "QtAudioCapturer::processAudioData - 处理缓冲区数据,大小:" << data.size()
+        //          << "字节";
 
         // 清空缓冲区
         m_buffer.buffer().clear();
@@ -329,9 +275,9 @@ void QtAudioCapturer::onAudioNotify()
             buffer.resize(len);
 
             // 调用回调函数
-            if (m_callback) {
-                m_callback(buffer.data(), buffer.size(), m_userInfo);
-            }
+            // if (m_callback) {
+            //     m_callback(buffer.data(), buffer.size(), m_userInfo);
+            // }
         }
     }
 }

+ 5 - 4
AvRecorder/capturer/audio/audio_qt_capturer.h

@@ -7,6 +7,7 @@
 #include <QObject>
 #include <QTimer>
 #include <memory>
+#include <QMutex>
 
 #include "iaudiocapturer.h"
 
@@ -41,10 +42,11 @@ public:
     ~QtAudioCapturer() override;
 
     // IAudioCapturer接口实现
-    bool Init(Type deviceType, CallBack callback, void* userInfo = nullptr) override;
+    bool Init(Type deviceType) override;
     bool Start() override;
     void Stop() override;
     const AudioFormat& GetFormat() const override;
+    int readAudioData(char* buf, int maxLen) override;
 
 private slots:
     void handleReadyRead();
@@ -59,8 +61,6 @@ private:
 
 private:
     Type m_deviceType;
-    CallBack m_callback;
-    void* m_userInfo;
     AudioFormat m_audioFormat;
     bool m_isRunning;
 
@@ -72,7 +72,8 @@ private:
     QIODevice* m_audioDevice;
     QBuffer m_buffer;
     QTimer m_processTimer;
-
+    QByteArray m_dataBuffer;
+    QMutex m_mutex;
     static constexpr int PROCESS_INTERVAL_MS = 20; // 处理间隔,20ms
 };
 

+ 3 - 10
AvRecorder/capturer/audio/iaudiocapturer.h

@@ -38,24 +38,17 @@ class IAudioCapturer
 public:
     enum Type { Microphone, Speaker };
 
-    using CallBack = std::function<void(void* data, size_t size, void* userInfo)>;
-
     virtual ~IAudioCapturer() = default;
 
-    virtual bool Init(Type deviceType, CallBack callback, void* userInfo = nullptr) = 0;
+    virtual bool Init(Type deviceType) = 0;
     virtual bool Start() = 0;
     virtual void Stop() = 0;
 
     // 获取音频格式
     virtual const AudioFormat& GetFormat() const = 0;
 
-    // // 获取音频格式信息
-    // virtual int GetSampleRate() const = 0;
-    // virtual int GetChannels() const = 0;
-    // virtual int GetBitsPerSample() const = 0;
-
-    // // 用于兼容现有代码的格式获取方法
-    // virtual void* GetNativeFormat() const = 0;
+    // 主动拉取音频数据,返回实际读取字节数
+    virtual int readAudioData(char* buf, int maxLen) = 0;
 };
 
 #endif // IAUDIOCAPTURER_H

+ 34 - 8
AvRecorder/muxer/av_muxer.cpp

@@ -1,6 +1,8 @@
 
 #include "av_muxer.h"
 
+#include <QDebug>
+
 bool AvMuxer::Open(std::string_view filePath, std::string_view format)
 {
     Close();
@@ -58,7 +60,6 @@ int AvMuxer::AddAudioStream(const Encoder<MediaType::AUDIO>::Param& param)
 
 bool AvMuxer::Write(AVFrame* frame, int streamIndex, bool isEnd)
 {
-    // 此函数不能被多个流同时调用
     std::lock_guard<std::mutex> lk(_mtx);
     __CheckBool(_infos.size() > streamIndex);
     auto&& info = _infos[streamIndex];
@@ -70,19 +71,43 @@ bool AvMuxer::Write(AVFrame* frame, int streamIndex, bool isEnd)
         frame = nullptr;
     }
     __CheckBool(info.encoder);
-    // 检测流之间时间是不是差的太多,如果差的太多,直接弃掉数据多的流数据
-    if (!_CheckTime(double(info.pts) / info.fps)) {
-        info.isEncodeOverload = true;
-        return false;
+
+    // 只在有多个活跃流且音频流有数据时才做同步检查
+    int activeStreamCount = 0;
+    int audioStreamIdx = -1;
+    for (int i = 0; i < _infos.size(); ++i) {
+        if (!_infos[i].isEnd && _infos[i].pts > 0) {
+            ++activeStreamCount;
+            if (_infos[i].type == MediaType::AUDIO) audioStreamIdx = i;
+        }
+    }
+    // 只在视频流超前音频流时丢帧,音频流超前时不阻塞视频帧写入
+    if (activeStreamCount > 1 && audioStreamIdx != -1) {
+        double curTime = double(info.pts) / info.fps;
+        double audioTime = double(_infos[audioStreamIdx].pts) / _infos[audioStreamIdx].fps;
+        if (info.type == MediaType::VIDEO && curTime - audioTime > 0.7) {
+            // 视频流超前音频流太多才丢帧
+            return false;
+        }
     }
     info.isEncodeOverload = false;
-    __CheckBool(info.encoder->PushFrame(frame, isEnd, info.pts));
+    if (!info.encoder->PushFrame(frame, isEnd, info.pts)) {
+        return false;
+    }
+    // 调试输出音视频流的pts
+    // if (info.type == MediaType::AUDIO) {
+    //     qDebug() << "AUDIO PTS:" << info.pts;
+    // } else if (info.type == MediaType::VIDEO) {
+    //     qDebug() << "VIDEO PTS:" << info.pts;
+    // }
     info.pts += info.type == MediaType::AUDIO ? info.encoder->GetCtx()->frame_size : 1; // 更新 pts
     AVPacket* packet = nullptr;
     while ((packet = info.encoder->Encode())) {
         av_packet_rescale_ts(packet, info.encoder->GetCtx()->time_base, info.stream->time_base);
         packet->stream_index = info.stream->index;
-        __CheckBool(av_interleaved_write_frame(_fmtCtx, packet) >= 0);
+        if (av_interleaved_write_frame(_fmtCtx, packet) < 0) {
+            return false;
+        }
     }
     info.encoder->AfterEncode();
     return true;
@@ -94,7 +119,8 @@ bool AvMuxer::_CheckTime(double time)
     for (int idx = 1; idx < _infos.size(); ++idx) {
         minTime = std::min(double(_infos[idx].pts) / _infos[idx].fps, minTime);
     }
-    if (time - minTime > 0.1) { // 说明相差的太多了,下一帧不能再送往编码器
+    if (time - minTime > 0.3) { // 放宽到0.3秒
+        qDebug() << "丢帧: 当前流时间:" << time << "最慢流时间:" << minTime << "差值:" << (time - minTime);
         return false;
     }
     return true;

+ 26 - 34
AvRecorder/recorder/audio_recorder.cpp

@@ -1,6 +1,7 @@
 #include "audio_recorder.h"
 
 #include "capturer/audio/audio_qt_capturer.h"
+#include "qdebug.h"
 
 AudioRecorder::AudioRecorder() {}
 
@@ -16,6 +17,7 @@ bool AudioRecorder::Open(const std::vector<AudioCapturer::Type>& deviceTypes,
                          const uint32_t bitsPerSample,
                          const AVSampleFormat format)
 {
+    qDebug() << "AudioRecorder::Open called, deviceTypes size:" << deviceTypes.size();
     Close();
     Info mixInfo;
     mixInfo.mixer = &_mixer;
@@ -33,7 +35,7 @@ bool AudioRecorder::Open(const std::vector<AudioCapturer::Type>& deviceTypes,
     // 初始化每个音频捕获器
     for (int index = 0; index < deviceTypes.size(); ++index) {
         auto capturer = m_audioCapturers[index];
-        if (!capturer->Init(deviceTypes[index], _Callback, &(_infos[index]))) {
+        if (!capturer->Init(deviceTypes[index])) {
             continue;
         }
         auto&& format = capturer->GetFormat();
@@ -49,12 +51,9 @@ bool AudioRecorder::Open(const std::vector<AudioCapturer::Type>& deviceTypes,
     __CheckBool(_mixer.SetOutFrameSize(1024));
 
     // 启动所有成功初始化的音频捕获器
-    for (int index = 0; index < deviceTypes.size(); ++index) {
-        if (_mixer.GetInputInfo(index) != nullptr) {
-            __CheckBool(m_audioCapturers[index]->Start());
-        }
+    for (auto capturer : m_audioCapturers) {
+        capturer->Start();
     }
-
     return true;
 }
 
@@ -95,43 +94,36 @@ bool AudioRecorder::LoadMuxer(AvMuxer& muxer)
 bool AudioRecorder::StartRecord()
 {
     _isRecord = true;
+    m_audioTimer.Start(AUDIO_PULL_INTERVAL_MS, [this] {
+        this->PullAndProcessAudio();
+    });
     return true;
 }
 
 void AudioRecorder::StopRecord()
 {
     _isRecord = false;
+    m_audioTimer.Stop();
 }
 
-void AudioRecorder::_Callback(void* data, size_t size, void* userInfo)
+// 新增:主动拉取音频数据的接口
+void AudioRecorder::PullAndProcessAudio()
 {
-    if (!data || size == 0 || !userInfo) {
-        return;
-    }
-
-    Info* info = static_cast<Info*>(userInfo);
-    if (!info->mixer || !info->isRecord) {
-        return;
-    }
-
-    // 添加调试输出
-    // qDebug() << "AudioRecorder: 收到音频数据,大小:" << size << "字节,混音索引:" << info->mixIndex;
-
-    /* auto inputInfo =*/info->mixer->GetInputInfo(info->mixIndex);
-    auto frame = info->mixer->Convert(info->mixIndex, (uint8_t*) data, size);
-    if (frame == nullptr) {
-        return;
-    }
-    if (*(info->isRecord)) {
-        __CheckNo(info->streamIndex && *(info->streamIndex) != -1);
-        int frameSize = info->muxer->GetCodecCtx(*info->streamIndex)->frame_size;
-        if (info->mixer->GetOutFrameSize() != frameSize) {
-            __DebugPrint("Change frame size from %d to %d",
-                         info->mixer->GetOutFrameSize(),
-                         frameSize);
-            info->mixer->SetOutFrameSize(frameSize);
-            return;
+    for (int index = 0; index < m_audioCapturers.size(); ++index) {
+        // 每次多次拉取小块数据,提升音频帧写入频率
+        while (true) {
+            char buf[1024];
+            int bytes = m_audioCapturers[index]->readAudioData(buf, sizeof(buf));
+            if (bytes <= 0) break;
+            auto frame = _mixer.Convert(index, (uint8_t*)buf, bytes);
+            if (frame && _isRecord && _streamIndex != -1) {
+                int frameSize = _mixer.GetOutFrameSize();
+                if (_mixer.GetOutFrameSize() != frameSize) {
+                    _mixer.SetOutFrameSize(frameSize);
+                    continue;
+                }
+                _infos[index].muxer->Write(frame, _streamIndex);
+            }
         }
-        __CheckNo(info->muxer->Write(frame, *(info->streamIndex)));
     }
 }

+ 4 - 0
AvRecorder/recorder/audio_recorder.h

@@ -4,6 +4,7 @@
 #include "capturer/audio/audio_capturer.h"
 #include "encoder/audio_mixer.h"
 #include "muxer/av_muxer.h"
+#include "basic/timer.h"
 
 class AudioRecorder
 {
@@ -30,6 +31,7 @@ public:
     bool StartRecord();
     void StopRecord();
     void Close();
+    void PullAndProcessAudio(); // 新增:主动拉取音频数据
     auto GetCaptureInfo(int mixIndex) { return _mixer.GetInputInfo(mixIndex); }
     void SetVolumeScale(float scale, int mixIndex);
 
@@ -40,6 +42,8 @@ private:
     bool _isRecord = false;
     int _streamIndex;
     Encoder<MediaType::AUDIO>::Param _param;
+    Timer m_audioTimer; // 新增高精度定时器
+    static constexpr int AUDIO_PULL_INTERVAL_MS = 10;
     static void _Callback(void* data, size_t size, void* userInfo);
     AVSampleFormat _GetAVSampleFormat(int wBitsPerSample)
     {

+ 12 - 3
AvRecorder/recorder/video_recorder.cpp

@@ -82,11 +82,13 @@ bool VideoRecorder::StartRecord()
 {
     _totalPts = 0;
     _lossPts = 0;
+    _lossHistory.clear();
     _muxTimer.Start(_param.fps, [this] {
         ++_totalPts;
-        if (!_muxer->Write(_encodeFrame, _streamIndex)) {
-            ++_lossPts;
-        }
+        bool loss = !_muxer->Write(_encodeFrame, _streamIndex);
+        if (loss) ++_lossPts;
+        _lossHistory.push_back(loss);
+        if (_lossHistory.size() > LOSS_WINDOW) _lossHistory.pop_front();
     });
     _isRecord = true;
     return true;
@@ -97,6 +99,13 @@ void VideoRecorder::StopRecord()
     _muxTimer.Stop();
 }
 
+double VideoRecorder::GetLossRate()
+{
+    if (_lossHistory.size() < LOSS_WINDOW) return -1.0; // 统计中
+    int lossCount = std::count(_lossHistory.begin(), _lossHistory.end(), true);
+    return double(lossCount) / _lossHistory.size();
+}
+
 void VideoRecorder::Close()
 {
     StopRecord();

+ 6 - 1
AvRecorder/recorder/video_recorder.h

@@ -4,6 +4,7 @@
 #include "basic/timer.h"
 #include "capturer/video/video_capturer.h"
 #include "muxer/av_muxer.h"
+#include <deque>
 // #include <condition_variable>
 // #include <queue>
 
@@ -23,7 +24,8 @@ public:
         _capturer.SetDrawCursor(isDraw);
     }
     bool IsCaptureOverload() const { return _captureTimer.IsOverload(); }
-    double GetLossRate() { return _lossPts == 0 ? 0 : (double)_lossPts / _totalPts; }
+    // 返回滑动窗口丢帧率,窗口未满时返回-1
+    double GetLossRate();
 
 private:
     bool _Open(Encoder<MediaType::VIDEO>::Param& param);
@@ -39,5 +41,8 @@ private:
     std::mutex _renderMtx;
     uint64_t _totalPts = 0;
     uint64_t _lossPts = 0;
+    // 滑动窗口丢帧统计
+    static constexpr int LOSS_WINDOW = 100;
+    std::deque<bool> _lossHistory;
 };
 #endif

+ 8 - 4
AvRecorder/ui/av_recorder.cpp

@@ -232,10 +232,14 @@ void AvRecorder::initConnect()
                                             .arg(minute, 2, 10, QChar('0'))
                                             .arg(sec, 2, 10, QChar('0')));
             auto lossRate = m_videoRecorder.GetLossRate();
-            int num = lossRate * 10000;
-            m_videolossRate->setText(QString("丢帧率: %1.%2%")
-                                         .arg(num / 100, 2, 10, QChar('0'))
-                                         .arg(num % 100, 2, 10, QChar('0')));
+            if (lossRate < 0) {
+                m_videolossRate->setText("丢帧率: 统计中");
+            } else {
+                int num = lossRate * 10000;
+                m_videolossRate->setText(QString("丢帧率: %1.%2%")
+                                             .arg(num / 100, 2, 10, QChar('0'))
+                                             .arg(num % 100, 2, 10, QChar('0')));
+            }
         } else if (m_captureTimeLabel->text() != "00:00:00") {
             m_captureTimeLabel->setText("00:00:00");
         }