Parcourir la source

V0.4.6
1、基本完成了VideoPlayer的播放类
2、在多次快速点击快进的时候可能会闪退

Apple il y a 4 mois
Parent
commit
1689ec700b

+ 130 - 69
demo/VideoPlayer/VideoPlayer/DecodeVedio.cpp

@@ -68,7 +68,7 @@ void DecodeVedio::getHWDecoder()
 /* 开始解码视频 */
 void DecodeVedio::startDecodeVedio()
 {
-    if(m_isRunning)
+    if(m_threadRuning)
     {
         return;
     }
@@ -77,7 +77,7 @@ void DecodeVedio::startDecodeVedio()
         SPDLOG_WARN("未初始化FFMPEG...");
         return;
     }
-    m_isRunning = true;
+    m_threadRuning = true;
     // decodeUsingCPU();
     /* 发送信号,开启新线程 */
     emit signal_startDecode();
@@ -86,7 +86,7 @@ void DecodeVedio::startDecodeVedio()
 /* 停止解码视频,退出工作函数,线程未停止 */
 void DecodeVedio::stopDecodeVedio()
 {
-    if(!m_isRunning)
+    if(!m_threadRuning)
     {
         return;
     }
@@ -100,32 +100,65 @@ void DecodeVedio::stopDecodeVedio()
         std::this_thread::sleep_for(std::chrono::milliseconds(5));
     }
     freeAll();
-    m_isRunning = false;
+    m_threadRuning = false;
 }
 
 /**
- * @brief 设置当前播放位置,单位是
+ * @brief 设置当前播放位置,单位是
  *        这里需要去掉队列中已有的图片数目对应的时长
  * 
- * @param pos 
+ * @param pos 要跳转的位置,范围从0~duration
  */
-void DecodeVedio::setCurrentPos(quint64 pos)
+void DecodeVedio::setCurrentPos(qint64 pos)
 {
-    
+    m_isSeek = true;
+    /* 先暂停解码 */
+    pauseDecode();
+    SPDLOG_DEBUG("跳转到:{}ms",pos);
+    /*在环形队列中有已解码的视频帧数,需要去掉已有的帧数所占的时间 */
+    pos = pos - m_queueImage.QueueSize() * (1000 / m_fps);
+    if(pos < 0) {
+        pos = 0;
+    }
+    if(pos > m_duration) {
+        pos = m_duration;
+    }
+    pos = pos + m_startPos;
+    qint64 targetPos = qRound64((double)pos / (1000 * rationalToDouble(&m_pFormatContext->streams[m_videoStream]->time_base)));
+    /* 开始跳转,这里设置标志为AVSEEK_FLAG_BACKWARD,跳转到目标位置的前一个关键帧中,然后开始解码,直到到达目标位置为止 */
+    int ret = av_seek_frame(m_pFormatContext, m_videoStream, targetPos, AVSEEK_FLAG_BACKWARD);
+    if(ret < 0)
+    {
+        SPDLOG_ERROR("跳转失败!");
+    }
+    m_targetPos = pos;
+    /* 刷新解码器缓冲区 */
+    m_flushDecoder.store(true);
+    /* 清空环形队列中的视频 */
+    QImage* image = 0;
+    for(int i = 0; i < m_queueImage.QueueSize(); i++)
+    {
+        image = nullptr;
+        m_queueImage.front_pop_NoBlock(image);
+        if(image != nullptr)
+        {
+            delete image;
+        }
+    }
+    /* 继续解码 */
+    continueDecode();
 }
 
-/* 获取当前播放位置 */
+/* 获取当前播放位置,单位ms */
 qint64 DecodeVedio::getCurrentPos()
 {
-    
-    return m_currentPos - m_startPos;
+    return m_pts.load() - m_startPos;
 }
 
 /* 获取视频时长 */
 qint64 DecodeVedio::getDuration()
 {
     return m_duration;
-
 }
 
 /**
@@ -136,7 +169,7 @@ qint64 DecodeVedio::getDuration()
 QImage* DecodeVedio::getOneImage()
 {
     QImage* image = nullptr;
-    if(!m_ringQueue.front_pop_NoBlock(image))
+    if(!m_queueImage.front_pop_NoBlock(image))
     {
         return nullptr;
     }
@@ -146,39 +179,11 @@ QImage* DecodeVedio::getOneImage()
 /* 获取一帧图像,直到有图像为止 */
 QImage* DecodeVedio::getOneImageUntilHave()
 {
-    QImage* image = m_ringQueue.front_pop();
+    QImage* image = m_queueImage.front_pop();
 
     return image;
 }
 
-/**
- * @brief 设置图像宽度和高度
- *        注意:目前不好用,建议使用Qt的图片缩放
- * 
- * @param width 
- * @param height 
- */
-// void DecodeVedio::setVideoSize(int width, int height)
-// {
-//     m_width = width;
-//     m_height = height;
-
-//     if(m_sws_ctx != nullptr)
-//     {
-//         /* 先暂停解码器 */
-//         pauseDecode();
-//         sws_freeContext(m_sws_ctx);
-//     }
-//     /* 初始化Sws Context,这是转换规则,转换成RGB */
-//     m_sws_ctx = sws_getContext( m_pCodecContext->width, m_pCodecContext->height, m_pCodecContext->pix_fmt,    /* 原图像大小和格式 */
-//                                 m_width, m_height, AV_PIX_FMT_RGB24,      /* 目标图像的大小和格式 */
-//                                 SWS_BICUBIC, 
-//                                 nullptr, 
-//                                 nullptr, 
-//                                 nullptr);
-// }
-
-
 /* 初始化硬件解码器 */
 void DecodeVedio::initHWDecoder(const AVCodec* codec)
 {
@@ -201,19 +206,19 @@ void DecodeVedio::openVedio(const QString& fileName)
     }
 
     /* 清空队列 */
-    if(m_ringQueue.QueueSize() > 0)
+    if(m_queueImage.QueueSize() > 0)
     {
-        for(int i = 0; i < m_ringQueue.QueueSize(); i++)
+        for(int i = 0; i < m_queueImage.QueueSize(); i++)
         {
             QImage* image = nullptr;
-            if (m_ringQueue.front_pop_NoBlock(image))
+            if (m_queueImage.front_pop_NoBlock(image))
             {
                 delete image;
             }
         }
-        m_ringQueue.clearQueue();
+        m_queueImage.clearQueue();
     }
-    m_ringQueue.setQueueSize(30);
+    m_queueImage.setQueueCapacity(30);
 
     SPDLOG_DEBUG("开始初始化FFMPEG");
     AVDictionary* dict = nullptr;
@@ -251,8 +256,7 @@ void DecodeVedio::openVedio(const QString& fileName)
         return;
     }
     m_duration = m_pFormatContext->duration / (AV_TIME_BASE / 1000);  /* 获取视频时长,单位是毫秒 */
-    m_currentPos = 0;
-    m_startPos = m_pFormatContext->start_time;
+    m_startPos = m_pFormatContext->start_time / (AV_TIME_BASE / 1000);  /* 获取视频开始时间,单位是毫秒 */
     // SPDLOG_DEBUG("开始时间:{} 时长:{}",m_startPos, m_duration);
     
     /* 一个调试函数,将流信息输出到控制台 */
@@ -276,7 +280,7 @@ void DecodeVedio::openVedio(const QString& fileName)
     m_srcSize.setHeight(pCodecParams->height);
     m_fps = rationalToDouble(&pStream->avg_frame_rate);
     m_totalFrame = m_pFormatContext->streams[m_videoStream]->nb_frames;
-
+    m_pts.store(0);
 
     /************ 查找并设置解码器 ************/
     /* 找到解码器 */
@@ -375,14 +379,23 @@ void DecodeVedio::threadDecodeUsingCPU()
     m_pauseDecode = false;
     m_decodeStatus = true;
     m_threadState = true;
-    
-    /* 设置环形队列的大小 */
-    m_ringQueue.setQueueSize(30);
 
     /* 开始解码 */
     SPDLOG_DEBUG("开始解码...");
-    while(m_isRunning)
+    while(m_threadRuning)
     {
+        /******** 判断是否在暂停状态 ********/
+        if(m_pauseDecode)
+        {
+            m_decodeState.store(DecodeState::DecodePause);
+            std::this_thread::sleep_for(std::chrono::milliseconds(2));
+        }
+        /* 刷新解码器缓冲区,清除掉里面残留的解码文件 */
+        if(m_flushDecoder.load())
+        {
+            avcodec_flush_buffers(m_pCodecContext);
+            m_flushDecoder.store(false);
+        }
         /******** 读取数据包 av_read_frame ********/
         int retRead = av_read_frame(m_pFormatContext, m_packet);
         if(retRead == AVERROR_EOF)
@@ -399,6 +412,7 @@ void DecodeVedio::threadDecodeUsingCPU()
         {
             if(m_packet->stream_index == m_videoStream)
             {
+                // SPDLOG_DEBUG("源pts:{}", m_packet->pts);
                 /*  pts 表示显示时间戳(Presentation Timestamp),它指示解码后的帧应该在什么时候显示。pts 是用于同步音视频的关键时间戳。
                     dts 表示解码时间戳(Decoding Timestamp),它指示解码器应该在什么时候解码这个数据包。dts 用于确保解码器按照正确的顺序解码数据包。 */
             #if 1
@@ -410,7 +424,7 @@ void DecodeVedio::threadDecodeUsingCPU()
                 m_currentFrame++;
                 // m_packet->pts = qRound64(m_currentFrame * (qreal(m_duration) / m_totalFrame));
             #endif
-
+                
                 /* 将数据传给解码器 */
                 int ret = avcodec_send_packet(m_pCodecContext, m_packet);
                 if(ret < 0)
@@ -426,9 +440,9 @@ void DecodeVedio::threadDecodeUsingCPU()
             }
         }
 
-        SPDLOG_DEBUG("读取到数据包packet,pts:{}",m_packet->pts);
+        // SPDLOG_DEBUG("读取到数据包packet,pts:{}",m_packet->pts);
         /* 解码packet包的内容,一个packet包内可能包含好多帧视频 */
-        while(m_isRunning)
+        while(m_threadRuning)
         {
             /* 读取出解码器返回的帧 avcodec_receive_frame */
             int ret = avcodec_receive_frame(m_pCodecContext, m_pFrameSRC);
@@ -455,8 +469,24 @@ void DecodeVedio::threadDecodeUsingCPU()
                     break;
                 }
             }
-            /* 解码成功,获取到帧数 */
+            /* 解码成功,获取当前时间,现在已经是准的时间了
+             * 如果在跳转状态,在这里判断是否到了目标位置 */
             m_pts = m_pFrameSRC->pts;
+            if(m_isSeek)
+            {
+                if(m_pts < m_targetPos)
+                {
+                    SPDLOG_DEBUG("目标位置:{} 当前位置:{}",m_targetPos, m_pts.load());
+                    av_frame_unref(m_pFrameSRC);
+                    continue;
+                }else {
+                    m_isSeek = false;
+                    m_targetPos = -1;
+                    SPDLOG_INFO("跳转结束。");
+                }
+            }
+            // SPDLOG_DEBUG("当前帧的pts:{}", m_pts.load());
+
             /* 转换解码后的帧格式,转换成RGBA格式,Qt可以识别 */
             if(m_sws_ctx == nullptr)
             {
@@ -494,11 +524,17 @@ void DecodeVedio::threadDecodeUsingCPU()
             {
                 /* 将数据拷贝到QImage中 */
                 auto image = new QImage(m_buffer, m_srcSize.width(), m_srcSize.height(), QImage::Format_RGBA8888);
-                m_ringQueue.push(image);
+                /* 如果队列满,线程会阻塞在这里 */
+                m_queueImage.push(image);
                 av_frame_unref(m_pFrameRGB);
                 // SPDLOG_DEBUG("一帧视频入队");
             }
             av_frame_unref(m_pFrameSRC);
+            /* 如果在跳转过程中,直接退出,防止一个packet中有多个视频帧,再次阻塞在上面 */
+            if(m_isSeek)
+            {
+                break;
+            }
         }
         av_packet_unref(m_packet);    /* 释放数据包,引用计数-1,为0时释放空间 */
         if(isEnd)
@@ -516,31 +552,56 @@ label_ThreadDecodeExit:
 /* 退出线程,将所有可能暂停线程运行的条件全部唤醒 */
 void DecodeVedio::exitThread()
 {
-    if(m_isRunning)
+    if(m_threadRuning)
     {
-        m_isRunning = false;
+        m_threadRuning = false;
     }
     m_pauseDecode = false;
     /* 先退出可能阻塞住的解码线程 */
-    m_ringQueue.exit();
+    m_queueImage.exit();
 }
 
 /* 暂停解码,会阻塞到线程暂停为止 */
 void DecodeVedio::pauseDecode()
 {
     m_pauseDecode = true;
+    /* 队列出队两张图片,防止解码线程阻塞到环形队列满上面 */
+    QImage* image = nullptr;
+    m_queueImage.front_pop_NoBlock(image);
+    if(image != nullptr)
+    {
+        delete image;
+        image = nullptr;
+    }
+    m_queueImage.front_pop_NoBlock(image);
+    if(image != nullptr)
+    {
+        delete image;
+        image = nullptr;
+    }
+    /* 等待线程状态变为暂停为止 */
+    while (m_decodeState.load() != DecodeState::DecodePause)
+    {
+        std::this_thread::sleep_for(std::chrono::microseconds(100));
+    }
+}
 
+/* 继续解码 */
+void DecodeVedio::continueDecode()
+{
+    m_pauseDecode = false;
+    m_decodeState.store(DecodeState::DecodeRun);
 }
 
 /**
- * @brief          AVRational存储的是分子和分母,这里相除转换为double,用于计算帧率
+ * @brief AVRational存储的是分子和分母,这里相除转换为double,用于计算帧率
+ *        这个函数就等同于av_q2d()
  * @param rational
  * @return
  */
 qreal DecodeVedio::rationalToDouble(AVRational* rational)
 {
-    qreal frameRate = (rational->den == 0) ? 0 : (qreal(rational->num) / rational->den);
-    return frameRate;
+    return ( (rational->den == 0) ? 0 : (qreal(rational->num) / rational->den) );
 }
 
 
@@ -552,7 +613,7 @@ void DecodeVedio::do_startDecodeVedio()
     // {
     //     initFFmpeg();
     // }
-    m_isRunning = true;
+    m_threadRuning = true;
     m_pauseDecode = false;
     /* 进入解码,直到播放完成或者手动退出 */
     threadDecodeUsingCPU();
@@ -602,14 +663,14 @@ void DecodeVedio::freeAll()
         av_buffer_unref(&m_hw_device_ctx);
     }
 
-    for(int i = 0; i < m_ringQueue.QueueSize(); i++)
+    for(int i = 0; i < m_queueImage.QueueSize(); i++)
     {
         QImage* image = nullptr;
-        if (m_ringQueue.front_pop_NoBlock(image))
+        if (m_queueImage.front_pop_NoBlock(image))
         {
             delete image;
         }
     }
-    m_ringQueue.clearQueue();
+    m_queueImage.clearQueue();
 }
 

+ 33 - 17
demo/VideoPlayer/VideoPlayer/DecodeVedio.h

@@ -20,6 +20,16 @@ extern "C"
 // #include <libavutil/imgutils.h>
 }
 
+
+enum class DecodeState
+{
+    NONE = 0,
+    DecodeRun,          /* 解码运行中 */
+    DecodePause,        /* 暂停解码 */
+    DecodeSeek         /* 跳转中 */
+};
+
+
 /**
  * 使用方式:
  *      1. 初始化FFmpeg:initFFmpeg()
@@ -48,11 +58,11 @@ public:
     /* 停止解码视频,也是停止线程 */
     void stopDecodeVedio();
     /* 获取解码状态 */
-    bool isDecoding() { return m_isRunning; }
+    bool isDecoding() { return m_threadRuning; }
 
-    /* 设置当前播放位置 */
-    void setCurrentPos(quint64 pos);
-    /* 获取当前播放位置 */
+    /* 设置当前播放位置,单位ms */
+    void setCurrentPos(qint64 pos);
+    /* 获取当前播放位置,单位ms */
     qint64 getCurrentPos();
     /* 获取视频时长 */
     qint64 getDuration();
@@ -81,9 +91,11 @@ private:
     /* 软解码线程 */       
     void threadDecodeUsingCPU();                    
     /* 退出线程 */      
-    void exitThread();                         
-    /* 暂停解码 */     
-    void pauseDecode();                             
+    void exitThread();
+    /* 暂停解码 */
+    void pauseDecode();
+    /* 继续解码 */
+    void continueDecode();
     /* 将AVRational转换为double */
     qreal rationalToDouble(AVRational* rational);
     /* 释放所有资源 */
@@ -94,13 +106,16 @@ private slots:
 
 private:
     QThread* m_thread = nullptr;                    /* 解码线程 */
-    std::atomic_bool m_isRunning = false;           /* 解码线程是否运行 */
+    /* 线程状态 */
+    std::atomic_bool m_threadRuning = false;        /* 解码线程是运行标志 */
     std::atomic_bool m_initFFmpeg = false;          /* ffmpeg初始化标志 */
     std::atomic_bool m_threadState = false;         /* 线程运行状态 */
     std::atomic_bool m_pauseDecode = false;         /* 暂停解码 */
     std::atomic_bool m_decodeStatus = false;        /* 解码状态,这里主要是检测是否暂停解码 */
-    std::atomic_bool m_isSeek = false;              /* 是否跳转 */
-
+    std::atomic_bool m_isSeek = false;              /* 是否在跳转中 */
+    std::atomic_bool m_flushDecoder = false;        /* 刷新解码器 */
+    std::atomic<DecodeState> m_decodeState = DecodeState::NONE;
+    /* 视频解码相关变量信息 */
     QString m_fileName;                             /* 解码的视频文件名称 */
     AVFormatContext *m_pFormatContext = nullptr;    /* 格式上下文,贯穿全局 */
     AVCodecContext *m_pCodecContext = nullptr;      /* 解码器上下文 */
@@ -112,21 +127,22 @@ private:
     uint8_t *m_buffer = nullptr;                    /* 存储解码后的一帧数据,RGB格式 */
     int m_videoStream = -1;                         /* 记录视频流是第几个流 */
     AVBufferRef* m_hw_device_ctx = nullptr;         /* 对数据缓冲区的引用 */
-
     bool m_HWDecoder = false;                       /* 是否使用硬件解码 */
+    /* 视频相关信息 */
     QSize m_srcSize;                                /* 原始视频分辨率大小 */
     qint64 m_totalFrame = 0;                        /* 视频总帧数 */
-    int m_fps;                                      /* 每秒的帧数 */
-    qint64 m_currentFrame = 0;                      /* 当前已播放的帧数 */
-    qint64 m_pts = 0;                               /* 当前帧显示时间 */
+    int m_fps = 0;                                  /* 每秒的帧数 */
     qint64 m_duration = 0;                          /* 视频时长,单位毫秒 */
-    qint64 m_currentPos = 0;                        /* 当前播放位置 */
-    qint64 m_startPos = 0;                          /* 起始位置,后续操作都需要在这各基础上操作 */
+    qint64 m_startPos = 0;                          /* 开始播放的位置,摄像机视频的位置不是从0开始的,需要在初始化的时候取出这个值 */
+    std::atomic<qint64> m_pts = 0;                  /* 当前帧显示时间,也就是当前的进度时间 */
+    
+    qint64 m_targetPos = -1;                        /* 跳转的目标播放位置 */
+    qint64 m_currentFrame = 0;                      /* 当前已播放的帧数 */
     QString m_decoderName;                          /* 解码器名称 */
 
     QList<int> m_listHWDeviceType;                  /* 保存当前环境支持的硬件解码器 */
 
-    RingQueue<QImage*> m_ringQueue;                 /* 环形队列,存储生成的图像 */
+    RingQueue<QImage*> m_queueImage;                 /* 环形队列,存储生成的图像 */
 };
 
 

+ 120 - 63
demo/VideoPlayer/VideoPlayer/VideoPlayer.cpp

@@ -13,9 +13,9 @@
 
 VideoPlayer::VideoPlayer(QWidget *parent) : QWidget(parent)
 {
-    /* 初始化解码线程 */
-    m_threadDecode = new QThread(this);
-    m_decodeVedio = new DecodeVedio(m_threadDecode);
+    // /* 初始化解码线程 */
+    // m_threadDecode = new QThread(this);
+    // m_decodeVedio = new DecodeVedio(m_threadDecode);
 
     m_semRefresh = new QSemaphore(0);
 
@@ -24,7 +24,7 @@ VideoPlayer::VideoPlayer(QWidget *parent) : QWidget(parent)
     m_timerRefreshUI.setTimerType(Qt::PreciseTimer);
     connect(&m_timerRefreshUI, &QTimer::timeout, this, &VideoPlayer::do_refreshUI);
 
-    connect(m_decodeVedio, &DecodeVedio::signal_oneImage, this, &VideoPlayer::do_refreshOneUI);
+    connect(this, &VideoPlayer::signal_refreshImage, this, &VideoPlayer::do_refreshSamImage);
     connect(m_decodeVedio, &DecodeVedio::signal_playCompleted, this, &VideoPlayer::do_playCompleted);
     SPDLOG_TRACE("UI线程ID:{}", QThread::currentThreadId());
 
@@ -55,6 +55,13 @@ void VideoPlayer::openPlayVedio(const QString& fileName)
     if(m_isOpenFile)
     {
         m_isOpenFile = false;
+        stop();
+    }
+    if(m_decodeVedio == nullptr)
+    {
+        /* 初始化解码线程 */
+        m_threadDecode = new QThread(this);
+        m_decodeVedio = new DecodeVedio(m_threadDecode);
     }
     if(m_decodeVedio->isDecoding())
     {
@@ -81,6 +88,7 @@ void VideoPlayer::openPlayVedio(const QString& fileName)
     int ms = m_duration % 1000;
     SPDLOG_INFO("视频分辨率:{}x{} 帧数:{}", m_srcWidth, m_srcHeight, m_fps);
     SPDLOG_INFO("时长:{}h:{}m:{}.{}s 总时长:{}ms", hh, mm, ss, ms, m_duration);
+
     /* 设置视频宽和高的最小大小 */
     this->setMinimumSize(160,90);
     /* 开启定时器刷新 */
@@ -92,8 +100,7 @@ void VideoPlayer::openPlayVedio(const QString& fileName)
     /* 开启解码,手动刷新第一帧 */
     m_decodeVedio->startDecodeVedio();
     m_semRefresh->release(2);
-    // m_timerRefreshUI.setSingleShot(true);
-    // m_timerRefreshUI.start(500);
+    emit signal_refreshImage();
 }
 
 /* 播放视频 */
@@ -101,7 +108,7 @@ bool VideoPlayer::play()
 {
     if(!m_isOpenFile)
     {
-        SPDLOG_ERROR("文件名为空");
+        SPDLOG_ERROR("未打开视频文件!");
         return false;
     }
     if(m_playStatus)
@@ -119,9 +126,15 @@ bool VideoPlayer::play()
     return true;
 }
 
+
 /* 暂停播放 */
 void VideoPlayer::pause()
 {
+    if(!m_isOpenFile)
+    {
+        SPDLOG_ERROR("未打开视频文件!");
+        return;
+    }
     if(!m_playStatus)
     {
         return;
@@ -133,65 +146,115 @@ void VideoPlayer::pause()
 /* 停止播放,停止后停止解码,将时间等复位到开始时间 */
 void VideoPlayer::stop()
 {
+    if(!m_isOpenFile)
+    {
+        SPDLOG_ERROR("未打开视频文件!");
+        return;
+    }
     SPDLOG_DEBUG("...停止播放...");
-    // m_fileName = QString();
+    m_fileName = QString();
     if(m_timerRefreshUI.isActive())
     {
         m_timerRefreshUI.stop();
     }
     // SPDLOG_DEBUG("...停止解码...");
+    /* 删除解码器 */
+    delete m_decodeVedio;
+    m_decodeVedio = nullptr;
+    delete m_threadDecode;
+    m_threadDecode = nullptr;
     
-    /* 重新设置播放视频 */
-    openPlayVedio(m_fileName);
-
-    /* 绘制黑帧 */
-    // SPDLOG_DEBUG("绘制黑帧");
-    // m_image = new QImage(m_nowWidth, m_nowHeight, QImage::Format_RGB32);
-    // m_image->fill(Qt::black);
-    // update();
     m_playStatus = false;
-    // isSetVedioFile = false;
+    m_isOpenFile = false;
+    /* 绘制黑帧 */
+    SPDLOG_DEBUG("绘制黑帧");
+    m_image = new QImage(m_nowWidth, m_nowHeight, QImage::Format_RGB32);
+    m_image->fill(Qt::black);
+    update();
+    
 }
 
 /* 后退,单位ms */
-void VideoPlayer::backward(quint64 ms)
+void VideoPlayer::backward(qint64 ms)
 {
+    if(!m_isOpenFile)
+    {
+        SPDLOG_ERROR("未打开视频文件!");
+        return;
+    }
     /* 获取当前位置 */
     qint64 pos = m_decodeVedio->getCurrentPos();
-    pos = pos - ( ms * 1000 );
+    pos = pos - ms;
     if(pos < 0)
     {
         pos = 0;
     }
 
     setCurrentPos(pos);
+    /* 如果是暂停状态,就刷新两帧显示跳转结果 */
+    if(!m_playStatus)
+    {
+        m_semRefresh->release(2);
+        emit signal_refreshImage();
+    }
 }
 
 /* 前进,单位ms */
-void VideoPlayer::forward(quint64 ms)
+void VideoPlayer::forward(qint64 ms)
 {
+    if(!m_isOpenFile)
+    {
+        SPDLOG_ERROR("未打开视频文件!");
+        return;
+    }
     /* 获取当前位置 */
     qint64 pos = m_decodeVedio->getCurrentPos();
-    pos = pos + ( ms * 1000 );
-
+    SPDLOG_DEBUG("pos:{} ms:{}", pos, ms);
+    pos = pos + ms;
+    
     setCurrentPos(pos);
+    /* 如果是暂停状态,就刷新两帧显示跳转结果 */
+    if(!m_playStatus)
+    {
+        m_semRefresh->release(2);
+        emit signal_refreshImage();
+    }
 }
 
 /* 获取视频时长 */
 qint64 VideoPlayer::getDuration()
 {
+    if(!m_isOpenFile)
+    {
+        SPDLOG_ERROR("未打开视频文件!");
+        return -1;
+    }
     return m_decodeVedio->getDuration();
 }
 
 /* 获取当前播放位置 */
 qint64 VideoPlayer::getCurrentPos()
 {
+    if(!m_isOpenFile)
+    {
+        SPDLOG_ERROR("未打开视频文件!");
+        return -1;
+    }
     return m_decodeVedio->getCurrentPos();
 }
 
-/* 设置当前播放位置,单位us */
-void VideoPlayer::setCurrentPos(quint64 pos)
+/* 设置当前播放位置,单位ms */
+void VideoPlayer::setCurrentPos(qint64 pos)
 {
+    if(!m_isOpenFile)
+    {
+        SPDLOG_ERROR("未打开视频文件!");
+        return;
+    }
+    if(pos < 0)
+    {
+        pos = 0;
+    }
     /* 先停止播放 */
     bool temp = m_playStatus;
     if(m_playStatus)
@@ -208,13 +271,13 @@ void VideoPlayer::setCurrentPos(quint64 pos)
         m_playStatus = true;
     }else
     {
-        /* 刷新5张照片,防止第一张是跳转前的时间段 */
-        m_semRefresh->release(5);
+        /* 刷新2张照片 */
+        m_semRefresh->release(2);
     }
 }
 
 /* 设置播放视频大小 */
-void VideoPlayer::setPlayVedioSize(int width,int height)
+void VideoPlayer::setPlayWidgetSize(int width,int height)
 {
     /* 对宽和高就行缩放,保持比例,同时将其居中放置
      * 先计算出比例,和16/9相对比
@@ -272,7 +335,7 @@ void VideoPlayer::setPlayWidget(QWidget* widget)
     layout->setSpacing(0);
     widget->setLayout(layout);
     /* 设置窗口大小 */
-    setPlayVedioSize(widget->width(), widget->height());
+    setPlayWidgetSize(widget->width(), widget->height());
 }
 
 /* 列出当前环境支持的硬件解码器 */
@@ -351,9 +414,6 @@ void VideoPlayer::resizeEvent(QResizeEvent *event)
     m_nowWidth = event->size().width();
     m_nowHeight = event->size().height();
 
-    /* 传递给解码器 */
-    // m_decodeVedio->setVideoSize(m_nowWidth, m_nowHeight);
-
     QWidget::resizeEvent(event);
 }
 
@@ -374,10 +434,11 @@ void VideoPlayer::refreshOneUIUntilHave()
         
         if(m_image)
         {
-            // if(m_srcWidth != m_nowWidth || m_srcHeight != m_nowHeight)
-            // {
-            //     *m_image = m_image->scaled(m_nowWidth, m_nowHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
-            // }
+            if(m_image->isNull())
+            {
+                SPDLOG_WARN("取出的图片为空...");
+                return;
+            }
             // SPDLOG_DEBUG("绘制画面...");
             update();
         }
@@ -421,10 +482,6 @@ void VideoPlayer::do_refreshUI()
                 SPDLOG_WARN("取出的图片为空...");
                 return;
             }
-            // if(m_srcWidth != m_nowWidth || m_srcHeight != m_nowHeight)
-            // {
-            //     *m_image = m_image->scaled(m_nowWidth, m_nowHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
-            // }
             // SPDLOG_DEBUG("绘制画面...");
             update();
         }
@@ -434,32 +491,32 @@ void VideoPlayer::do_refreshUI()
 
 
 /* 通过信号刷新第一张图片 */
-void VideoPlayer::do_refreshOneUI()
+void VideoPlayer::do_refreshSamImage()
 {
-    if(!m_semRefresh->tryAcquire(1))
-    {
-        return;
-    }
-    /* 取出第一张 */
-    if(m_decodeVedio != nullptr)
+    while(m_semRefresh->tryAcquire(1))
     {
-        // SPDLOG_DEBUG("取出一帧图片...");
-        /* 删除上一帧图片 */
-        if(m_image != nullptr)
-        {
-            delete m_image;
-            m_image = nullptr;
-        }
-        m_image = m_decodeVedio->getOneImage();
-        
-        if(m_image)
+        /* 取出第一张 */
+        if(m_decodeVedio != nullptr)
         {
-            // if(m_srcWidth != m_nowWidth || m_srcHeight != m_nowHeight)
-            // {
-            //     *m_image = m_image->scaled(m_nowWidth, m_nowHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
-            // }
-            SPDLOG_DEBUG("绘制预览画面...");
-            update();
+            // SPDLOG_DEBUG("取出一帧图片...");
+            /* 删除上一帧图片 */
+            if(m_image != nullptr)
+            {
+                delete m_image;
+                m_image = nullptr;
+            }
+            m_image = m_decodeVedio->getOneImageUntilHave();
+            
+            if(m_image)
+            {
+                if(m_image->isNull())
+                {
+                    SPDLOG_WARN("取出的图片为空...");
+                    return;
+                }
+                SPDLOG_DEBUG("绘制预览画面。");
+                update();
+            }
         }
     }
 }
@@ -467,7 +524,7 @@ void VideoPlayer::do_refreshOneUI()
 /* 播放完成 */
 void VideoPlayer::do_playCompleted()
 {
-    SPDLOG_INFO("Video 播放完成...");
+    SPDLOG_INFO("视频播放完成。");
     m_timerRefreshUI.stop();
     m_playStatus = false;
     // if(m_funcPlayCB != nullptr)

+ 10 - 6
demo/VideoPlayer/VideoPlayer/VideoPlayer.h

@@ -20,18 +20,22 @@ public:
     void pause();                                   /* 暂停播放 */
     void stop();                                    /* 停止播放 */
     
-    void backward(quint64 ms);                      /* 后退,单位ms */
-    void forward(quint64 ms);                       /* 前进,单位ms */
+    void backward(qint64 ms);                       /* 后退,单位ms */
+    void forward(qint64 ms);                        /* 前进,单位ms */
     bool getPlayStatus() { return m_playStatus; }   /* 获取播放状态 */
     qint64 getDuration();                           /* 获取视频时长 */
     qint64 getCurrentPos();                         /* 获取当前播放位置 */
-    void setCurrentPos(quint64 pos);                /* 设置当前播放位置 */
-    void setPlayVedioSize(int width,int height);    /* 设置播放视频大小 */
+    void setCurrentPos(qint64 pos);                 /* 设置当前播放位置 */
+    void setPlayWidgetSize(int width,int height);   /* 设置播放视频窗口的大小 */
     void setPlayWidget(QWidget* widget);            /* 设置播放窗口 */
 
     static void ListHWDecoder();                    /* 列出当前环境支持的硬件解码器 */
 
     // void setPlayCallBack(std::function<Play_CallBack> playCallBack,void* context);  /* 设置播放回调函数 */
+signals:
+    void signal_playCompleted();                    /* 播放完成信号 */
+    void signal_refreshImage();                     /* 刷新图片信号 */
+
 protected:
     void paintEvent(QPaintEvent *event) override;
     void resizeEvent(QResizeEvent *event) override;
@@ -41,7 +45,7 @@ protected:
 
 private slots:
     void do_refreshUI();                            /* 取出画面,刷新UI */
-    void do_refreshOneUI();                         /* 通过信号刷新第一张图片 */
+    void do_refreshSamImage();                      /* 通过信号刷新图片 */
     void do_playCompleted();                        /* 播放完成 */
 
 private:
@@ -59,7 +63,7 @@ private:
     QThread* m_threadDecode = nullptr;              /* 解码器所在的线程 */
     QImage* m_image = nullptr;                      /* 画面 */
     bool m_playStatus = false;                      /* 是否正在播放 */
-    bool m_isOpenFile = false;                      /* 是否打开了视频文件 */
+    bool m_isOpenFile = false;                      /* 是否打开了视频文件,未打开视频文件也就是未初始化解码线程 */
     QSemaphore* m_semRefresh = nullptr;             /* 刷新信号量 */
 
     // std::function<Play_CallBack> m_funcPlayCB = nullptr;  /* 播放回调函数 */

+ 0 - 1
demo/VideoPlayer/demo/VideoPlayer1.h

@@ -50,7 +50,6 @@ private:
     QThread* m_threadDecode = nullptr;              /* 解码器所在的线程 */
     QImage* m_image = nullptr;                      /* 画面 */
     bool m_playStatus = false;                      /* 是否正在播放 */
-    bool isSetVedioFile = false;                    /* 是否设置了视频文件 */
     QSemaphore* m_semRefresh = nullptr;             /* 刷新信号量 */
 
     // std::function<Play_CallBack> m_funcPlayCB = nullptr;  /* 播放回调函数 */

+ 4 - 4
demo/VideoPlayer/widget.cpp

@@ -66,25 +66,25 @@ void Widget::on_pBtn_play_clicked()
 void Widget::on_pBtn_pause_clicked()
 {
     SPDLOG_INFO("点击了“暂停”按钮");
-    // m_videoPlayer->pause();
+    m_videoPlayer->pause();
 }
 
 void Widget::on_pBtn_stop_clicked()
 {
     SPDLOG_INFO("点击了“停止”按钮");
-    // m_videoPlayer->stop();
+    m_videoPlayer->stop();
 }
 
 void Widget::on_pBtn_backward_clicked()
 {
     SPDLOG_INFO("点击了“后退”按钮");
-    // m_videoPlayer->backward(10000);
+    m_videoPlayer->backward(10000);
 }
 
 void Widget::on_pBtn_forward_clicked()
 {
     SPDLOG_INFO("点击了“前进”按钮");
-    // m_videoPlayer->forward(10000);
+    m_videoPlayer->forward(10000);
 }