Browse Source

V0.4.5
1、基本完成了视频的播放和基本信息获取
2、视频跳转还未完成

Apple 4 months ago
parent
commit
c666c9599f

+ 61 - 217
demo/VideoPlayer/VideoPlayer/DecodeVedio.cpp

@@ -2,8 +2,6 @@
 #include "spdlog/spdlog.h"
 #include "FmtLog/fmtlog.h"
 
-
-
 #include <QThread>
 
 extern "C"
@@ -19,15 +17,14 @@ DecodeVedio::DecodeVedio(QThread* thread, QObject* parent) : QObject(parent) , m
 {
     /* 在连接之前调用,移动到新的线程 */
     this->moveToThread(thread);
-    m_startThread.setSingleShot(true);
-    connect(&m_startThread, &QTimer::timeout, this, &DecodeVedio::do_startDecodeVedio);
+    connect(this, &DecodeVedio::signal_startDecode, this, &DecodeVedio::do_startDecodeVedio);
     thread->start();
     getHWDecoder();
 }
 
 DecodeVedio::~DecodeVedio()
 {
-    exitThread();
+    stopDecodeVedio();
     if(m_thread != nullptr)
     {
         if(m_thread->isRunning())
@@ -37,20 +34,6 @@ DecodeVedio::~DecodeVedio()
         }
     }
 
-    if(!m_queueImage.isEmpty())
-    {
-        int size = m_queueImage.count();
-        for(int i = 0; i < size; i++)
-        {
-            auto image = m_queueImage.dequeue();
-            if (image)
-            {
-                delete image;
-            }
-            
-        }
-        
-    }
 }
 
 /* 获取硬件解码器 */
@@ -96,8 +79,8 @@ void DecodeVedio::startDecodeVedio()
     }
     m_isRunning = true;
     // decodeUsingCPU();
-    /* 开启定时器,进入槽函数,就进入了线程 */
-    m_startThread.start(0);
+    /* 发送信号,开启新线程 */
+    emit signal_startDecode();
 }
 
 /* 停止解码视频,退出工作函数,线程未停止 */
@@ -107,18 +90,17 @@ void DecodeVedio::stopDecodeVedio()
     {
         return;
     }
-    
-    m_isRunning = false;
-    m_pauseDecode = false;
+    exitThread();
+
     /* 唤醒阻塞住的解码线程 */
-    m_condQueueNoFull.wakeAll();
     // /* 等待线程执行结束 */
-    while(!m_threadStopped)
+    while(m_threadState)
     {
         /* 睡眠10ms */
-        std::this_thread::sleep_for(std::chrono::milliseconds(10));
+        std::this_thread::sleep_for(std::chrono::milliseconds(5));
     }
-    
+    freeAll();
+    m_isRunning = false;
 }
 
 /**
@@ -130,64 +112,6 @@ void DecodeVedio::stopDecodeVedio()
 void DecodeVedio::setCurrentPos(quint64 pos)
 {
     
-    /* 暂停解码 */
-    m_pauseDecode = true;
-    // SPDLOG_DEBUG("setCurrentPos threadID:{}",QThread::currentThreadId());
-    while (m_decodeStatus) {
-        /* 需要唤醒阻塞住的线程 */
-        m_condQueueNoFull.wakeAll();
-        std::this_thread::sleep_for(std::chrono::milliseconds(1));
-    }
-    m_isSeek = true;
-    /* 这个是int64最大的正数值 */
-    if(pos > 9223372036854775807)
-    {
-        pos = 0;
-    }
-    /* 去掉队列中已有的图片数目对应的时长,单位是us */
-    qint64 queueNumFPS = m_queueImage.count() * ( 1000.0f / m_fps ) * 1000 ;
-    qint64 target_pos = m_startPos + pos - queueNumFPS;
-    qint64 end_pos = m_startPos + m_duration;
-    /* 检查是否超过视频大小 */
-    if(target_pos > end_pos)
-    {
-        /* 跳转到最后 */
-        target_pos = end_pos;
-        // SPDLOG_DEBUG("现在的位置:{} 时长:{} 跳转到最后30帧",target_pos, m_duration);
-    }
-    if(target_pos < m_startPos)
-    {
-        target_pos = m_startPos;
-        // SPDLOG_DEBUG("现在的位置:{} 时长:{} 跳转到开始",target_pos, m_startPos);
-    }
-    m_currentPos = target_pos;
-    SPDLOG_DEBUG("设置播放位置:{}",target_pos);
-    /* 第二个参数设置为-1,表示所有流都跳转 */
-    int ret = av_seek_frame(m_pFormatContext, -1, target_pos, AVSEEK_FLAG_BACKWARD);
-    /* 清空缓存 */
-    avcodec_flush_buffers(m_pCodecContext);
-    if(ret < 0)
-    {
-        SPDLOG_WARN("跳转失败...");
-    }
-    /* 清空队列 */
-    m_mutexQueue.lock();
-    int size = m_queueImage.count();
-    for(int i = 0; i < size; i++)
-    {
-        auto image = m_queueImage.dequeue();
-        if (image)
-        {
-            delete image;
-        }
-            
-    }
-    m_mutexQueue.unlock();
-    m_condQueueNoFull.wakeAll();
-    SPDLOG_INFO("跳转完成 , queue count {}", m_queueImage.count());
-    /* 继续解码 */
-    m_pauseDecode = false;
-    // SPDLOG_DEBUG("继续解码... {}", m_pauseDecode.load());
 }
 
 /* 获取当前播放位置 */
@@ -204,12 +128,6 @@ qint64 DecodeVedio::getDuration()
 
 }
 
-/* 唤醒队列不满条件变量 */
-void DecodeVedio::wakeUpCondQueueNoEmpty()
-{
-    m_condQueueNoFull.wakeAll();    
-}
-
 /**
  * @brief 获取一帧图像,队列为空就返回nullptr,这个函数应该是运行在UI线程中的
  * @warning 传出这个指针后,队列就出队了,内存需要外面获取的实例释放
@@ -217,87 +135,22 @@ void DecodeVedio::wakeUpCondQueueNoEmpty()
  */
 QImage* DecodeVedio::getOneImage()
 {
-    QImage* image = m_ringQueue.deQueueBlock();
+    QImage* image = nullptr;
+    if(!m_ringQueue.front_pop_NoBlock(image))
+    {
+        return nullptr;
+    }
     return image;
 }
 
 /* 获取一帧图像,直到有图像为止 */
 QImage* DecodeVedio::getOneImageUntilHave()
 {
-    QImage* image = nullptr;
-    if(m_queueImage.count() == 0)
-    {
-        m_mutexQueue.lock();
-        m_condQueueNoEmpty.wait(&m_mutexQueue);
-        image = m_queueImage.dequeue();
-        m_mutexQueue.unlock();
-    }
+    QImage* image = m_ringQueue.front_pop();
 
     return image;
 }
 
-/**
- * @brief 获取每秒的帧数
- * 
- * @return int 正值表示帧数,-1表示未知,-2表示HEVC
-
- */
-int DecodeVedio::getFrameCount()
-{
-    AVFormatContext *pFormatCtx = NULL;
-    avformat_open_input(&pFormatCtx, m_fileName.toStdString().c_str(), NULL, NULL);
-    avformat_find_stream_info(pFormatCtx, NULL);
-    /* 找到视频流 */
-    int videoStreamIndex = -1;
-    for (int i = 0; i < pFormatCtx->nb_streams; i++) 
-    {
-        if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
-            videoStreamIndex = i;
-            break;
-        }
-    }
-    /* 获取视频格式是hevc还是h.264 */
-    enum AVCodecID codecID = pFormatCtx->streams[videoStreamIndex]->codecpar->codec_id;
-    if (codecID == AV_CODEC_ID_HEVC) 
-    {
-        SPDLOG_DEBUG("视频格式是HEVC");
-        m_fps = -2;
-        // AVPacket packet;
-        // int frameCount = 0;
-        // double timeBase = av_q2d(pFormatCtx->streams[videoStreamIndex]->time_base);
-        // double duration = 2.0;  // Duration in seconds
-
-        // while (av_read_frame(pFormatCtx, &packet) >= 0) 
-        // {
-        //     if (packet.stream_index == videoStreamIndex) 
-        //     {
-        //         double packetTime = packet.pts * timeBase;
-        //         if (packetTime <= duration) {
-        //             frameCount++;
-        //         } else {
-        //             break;
-        //         }
-        //     }
-        //     av_packet_unref(&packet);
-        // }
-        // fps = frameCount / duration;
-    }
-    else if(codecID == AV_CODEC_ID_H264)
-    {
-        SPDLOG_DEBUG("视频格式是H.264");
-        /* 获取到分子和分母,一除就是帧率 */
-        AVRational frameRate = pFormatCtx->streams[videoStreamIndex]->avg_frame_rate;
-        m_fps = frameRate.num*1.0 / frameRate.den;
-        // SPDLOG_DEBUG("分子:{} 分母:{} 帧率:{}",frameRate.num, frameRate.den, fps);
-    }else
-    {
-        m_fps = 30;
-    }
-
-    avformat_close_input(&pFormatCtx);
-
-    return m_fps;
-}
 /**
  * @brief 设置图像宽度和高度
  *        注意:目前不好用,建议使用Qt的图片缩放
@@ -352,8 +205,8 @@ void DecodeVedio::openVedio(const QString& fileName)
     {
         for(int i = 0; i < m_ringQueue.QueueSize(); i++)
         {
-            auto image = m_ringQueue.deQueueBlock();
-            if (image)
+            QImage* image = nullptr;
+            if (m_ringQueue.front_pop_NoBlock(image))
             {
                 delete image;
             }
@@ -397,7 +250,7 @@ void DecodeVedio::openVedio(const QString& fileName)
         freeAll();
         return;
     }
-    m_duration = m_pFormatContext->duration;
+    m_duration = m_pFormatContext->duration / (AV_TIME_BASE / 1000);  /* 获取视频时长,单位是毫秒 */
     m_currentPos = 0;
     m_startPos = m_pFormatContext->start_time;
     // SPDLOG_DEBUG("开始时间:{} 时长:{}",m_startPos, m_duration);
@@ -419,8 +272,8 @@ void DecodeVedio::openVedio(const QString& fileName)
     AVCodecParameters* pCodecParams = pStream->codecpar;            /* 获取视频流的编解码信息 */
     SPDLOG_INFO("获取视频流参数成功!");
     /* 获取视频相关信息 */
-    m_width = m_srcWidth = pCodecParams->width;
-    m_height = m_srcHeight = pCodecParams->height;
+    m_srcSize.setWidth(pCodecParams->width);
+    m_srcSize.setHeight(pCodecParams->height);
     m_fps = rationalToDouble(&pStream->avg_frame_rate);
     m_totalFrame = m_pFormatContext->streams[m_videoStream]->nb_frames;
 
@@ -434,7 +287,8 @@ void DecodeVedio::openVedio(const QString& fileName)
         freeAll();
         return;
     }
-    SPDLOG_INFO("找到解码器:{}",pCodec->name);
+    m_decoderName = pCodec->name;
+    // SPDLOG_INFO("找到解码器:{}",pCodec->name);
     /* 获取视频信息的上下文,先分配空间,后面记得释放空间 */
     m_pCodecContext = avcodec_alloc_context3(pCodec);
     /* 将视频流中的编码器参数拷贝下来,这个函数不是线程安全的 */
@@ -503,7 +357,7 @@ void DecodeVedio::openVedio(const QString& fileName)
 
     m_initFFmpeg = true;
     SPDLOG_INFO("FFMPEG初始化完成!");
-    SPDLOG_INFO("视频宽度:{} 高度:{} 帧率:{} 总时长:{} 总帧数:{}",m_width, m_height, m_fps, m_duration, m_totalFrame);
+    // SPDLOG_INFO("视频宽度:{} 高度:{} 帧率:{} 总时长:{} 总帧数:{}",m_srcSize.width(), m_srcSize.height(), m_fps, m_duration, m_totalFrame);
 }
 
 
@@ -511,7 +365,7 @@ void DecodeVedio::openVedio(const QString& fileName)
  * @brief 软解码线程,使用CPU解码,使用环境队列存储解码的数据
  * 
  */
-void DecodeVedio::decodeUsingCPU()
+void DecodeVedio::threadDecodeUsingCPU()
 {
     /******** 初始化局部变量 ********/
     bool isEnd = false;
@@ -520,6 +374,7 @@ void DecodeVedio::decodeUsingCPU()
     int retPacket = 0;
     m_pauseDecode = false;
     m_decodeStatus = true;
+    m_threadState = true;
     
     /* 设置环形队列的大小 */
     m_ringQueue.setQueueSize(30);
@@ -544,6 +399,8 @@ void DecodeVedio::decodeUsingCPU()
         {
             if(m_packet->stream_index == m_videoStream)
             {
+                /*  pts 表示显示时间戳(Presentation Timestamp),它指示解码后的帧应该在什么时候显示。pts 是用于同步音视频的关键时间戳。
+                    dts 表示解码时间戳(Decoding Timestamp),它指示解码器应该在什么时候解码这个数据包。dts 用于确保解码器按照正确的顺序解码数据包。 */
             #if 1
                 /* 计算当前帧时间,两种方法,第一种适用于所有场景,但是存在一定的误差 */
                 m_packet->pts = qRound64(m_packet->pts * (1000 * rationalToDouble(&m_pFormatContext->streams[m_videoStream]->time_base)));
@@ -563,7 +420,7 @@ void DecodeVedio::decodeUsingCPU()
                     continue;
                 }
             }else {
-                SPDLOG_INFO("不是视频流。");
+                // SPDLOG_INFO("不是视频流。");
                 av_packet_unref(m_packet);
                 continue;
             }
@@ -585,7 +442,7 @@ void DecodeVedio::decodeUsingCPU()
             {
                 /* packet中的内容无法解码成一帧,需要更多的输入包,可能已经取出了几帧,也肯能就不够一帧
                  * 这时候就需要往解码器送数据包了 */
-                SPDLOG_WARN("packet无法解码成一帧,需要更多的输入包");
+                // SPDLOG_WARN("packet无法解码成一帧,需要更多的输入包");
                 av_frame_unref(m_pFrameSRC);
                 break;
             }
@@ -605,9 +462,9 @@ void DecodeVedio::decodeUsingCPU()
             {
                 /* 选择在这里创建,为了兼容硬件解码,硬件解码出来的格式可能和解码器传出的不一样 */
                 m_sws_ctx = sws_getCachedContext(m_sws_ctx, 
-                                                m_pFrameSRC->width, m_pFrameSRC->height,      /* 原图像大小和格式 */
-                                                m_pCodecContext->pix_fmt,                       /* 输入图像的像素格式 */
-                                                m_width, m_height,                          /* 目标图像的大小 */
+                                                m_pFrameSRC->width, m_pFrameSRC->height,    /* 原图像大小和格式 */
+                                                m_pCodecContext->pix_fmt,                   /* 输入图像的像素格式 */
+                                                m_srcSize.width(), m_srcSize.height(),      /* 目标图像的大小 */
                                                 AV_PIX_FMT_RGBA,                            /* 目标图像的格式 */
                                                 SWS_BILINEAR,                               /* 图像缩放算法,双线性 */
                                                 nullptr,                                    /* 输入图像的滤波器信息,不需要传NULL */
@@ -616,10 +473,7 @@ void DecodeVedio::decodeUsingCPU()
                 if(m_sws_ctx == nullptr)
                 {
                     SPDLOG_ERROR("创建SwsContext错误...");
-                    av_packet_free(&m_packet);
-                    av_frame_free(&m_pFrameSRC);
-                    av_frame_free(&m_pFrameRGB);
-                    return;
+                    goto label_ThreadDecodeExit;
                 }
                 SPDLOG_INFO("创建SwsContext成功...");
             }
@@ -639,10 +493,10 @@ void DecodeVedio::decodeUsingCPU()
             if(m_buffer != nullptr)
             {
                 /* 将数据拷贝到QImage中 */
-                auto image = new QImage(m_pFrameRGB->data[0], m_width, m_height, QImage::Format_RGBA8888);
-                m_ringQueue.enQueueBlock(image);
+                auto image = new QImage(m_buffer, m_srcSize.width(), m_srcSize.height(), QImage::Format_RGBA8888);
+                m_ringQueue.push(image);
                 av_frame_unref(m_pFrameRGB);
-                SPDLOG_DEBUG("一帧视频入队");
+                // SPDLOG_DEBUG("一帧视频入队");
             }
             av_frame_unref(m_pFrameSRC);
         }
@@ -651,21 +505,12 @@ void DecodeVedio::decodeUsingCPU()
         {
             break;
         }
-
-        // AVFrame* pFrameTemp = m_pFrameSRC;
-        // if(m_pFrameSRC->data[0] == nullptr)
-        // {
-        //     /* 使用的是硬件解码器 */
-        //     pFrameTemp = m_pFrameHW;
-        //     /* 将数据从GPU拷贝到内存中 */
-            
-        //     break;
-        // }
     }
+label_ThreadDecodeExit:
     /* 释放空间 */
     av_packet_free(&m_packet);
-    av_free(m_pFrameSRC);
-    av_free(m_pFrameRGB);
+
+    m_threadState = false;
 }
 
 /* 退出线程,将所有可能暂停线程运行的条件全部唤醒 */
@@ -676,22 +521,19 @@ void DecodeVedio::exitThread()
         m_isRunning = false;
     }
     m_pauseDecode = false;
-    m_condQueueNoFull.wakeAll();
+    /* 先退出可能阻塞住的解码线程 */
+    m_ringQueue.exit();
 }
 
 /* 暂停解码,会阻塞到线程暂停为止 */
 void DecodeVedio::pauseDecode()
 {
     m_pauseDecode = true;
-    while (m_decodeStatus) {
-        /* 需要唤醒阻塞住的线程 */
-        m_condQueueNoFull.wakeAll();
-        std::this_thread::sleep_for(std::chrono::milliseconds(1));
-    }
+
 }
 
 /**
- * @brief          AVRational转换为double,用于计算帧率
+ * @brief          AVRational存储的是分子和分母,这里相除转换为double,用于计算帧率
  * @param rational
  * @return
  */
@@ -705,17 +547,16 @@ qreal DecodeVedio::rationalToDouble(AVRational* rational)
 /* 开启解码 */
 void DecodeVedio::do_startDecodeVedio()
 {
-    SPDLOG_DEBUG("线程ID:{}",QThread::currentThreadId());
+    SPDLOG_DEBUG("解码线程ID:{}",QThread::currentThreadId());
     // if(!m_initFFmpeg)
     // {
     //     initFFmpeg();
     // }
     m_isRunning = true;
-    m_threadStopped = false;
     m_pauseDecode = false;
     /* 进入解码,直到播放完成或者手动退出 */
-    decodeUsingCPU();
-    SPDLOG_TRACE("Decode播放完成...");
+    threadDecodeUsingCPU();
+    SPDLOG_TRACE("Decode解码结束。");
 
 }
 
@@ -751,21 +592,24 @@ void DecodeVedio::freeAll()
     {
         avformat_close_input(&m_pFormatContext);
     }
+    if(m_buffer)
+    {
+        av_free(m_buffer);
+        m_buffer = nullptr;
+    }
+    if(m_hw_device_ctx)
+    {
+        av_buffer_unref(&m_hw_device_ctx);
+    }
 
-
-
-    if(!m_queueImage.isEmpty())
+    for(int i = 0; i < m_ringQueue.QueueSize(); i++)
     {
-        int size = m_queueImage.count();
-        for(int i = 0; i < size; i++)
+        QImage* image = nullptr;
+        if (m_ringQueue.front_pop_NoBlock(image))
         {
-            auto image = m_queueImage.dequeue();
-            if (image)
-            {
-                delete image;
-            }
+            delete image;
         }
     }
-
+    m_ringQueue.clearQueue();
 }
 

+ 20 - 35
demo/VideoPlayer/VideoPlayer/DecodeVedio.h

@@ -39,52 +39,47 @@ public:
 
     /* 获取硬件解码器 */
     void getHWDecoder();
-    // void setFileName(const QString& fileName) { m_fileName = fileName; }
-
-    /* 获取ffmpeg初始化状态 */
-    bool isInitFFmpeg() { return m_initFFmpeg; }    
 
     /* 打开视频,同时初始化解码器) */
     void openVedio(const QString& fileName);
     
-    /* 开始解码视频,开始前先设置视频名称 */
+    /* 开始解码视频,开始前先打开视频文件 */
     void startDecodeVedio();
     /* 停止解码视频,也是停止线程 */
     void stopDecodeVedio();
+    /* 获取解码状态 */
+    bool isDecoding() { return m_isRunning; }
+
     /* 设置当前播放位置 */
     void setCurrentPos(quint64 pos);
     /* 获取当前播放位置 */
     qint64 getCurrentPos();
     /* 获取视频时长 */
     qint64 getDuration();
-    /* 获取解码线程是否运行 */
-    bool isRunning() { return m_isRunning; }
+    
 
-    /* 唤醒队列非空条件变量 */
-    void wakeUpCondQueueNoEmpty();
     /* 获取一帧图像 */                  
     QImage* getOneImage();
     /* 获取一帧图像,直到有图像为止 */                          
     QImage* getOneImageUntilHave();                 
 
     /* 获取帧数 */
-    int getFrameCount();                            
+    int getFPS() const { return m_fps; }
     /* 获取图像宽度 */
-    int getSrcVideoWidth() const {return m_srcWidth;}
-    /* 获取图像高度 */
-    int getSrcVideoHeight() const {return m_srcHeight;}
-    // /* 设置图像宽度和高度 */
-    // void setVideoSize(int width,int height);
+    QSize getSrcVideoSize() const {return m_srcSize; }
+    /* 获取解码器名称(编码格式) */
+    QString getDecoderName() const { return m_decoderName; }
 
 signals:
     void signal_oneImage();                         /* 一帧图像信号 */
     void signal_playCompleted();                    /* 播放完成信号 */
+    void signal_startDecode();                      /* 开始解码信号 */
 private:
     /* 初始化硬件解码器 */
     void initHWDecoder(const AVCodec* codec);
                 
     /* 软解码线程 */       
-    void decodeUsingCPU();                    
+    void threadDecodeUsingCPU();                    
     /* 退出线程 */      
     void exitThread();                         
     /* 暂停解码 */     
@@ -92,7 +87,7 @@ private:
     /* 将AVRational转换为double */
     qreal rationalToDouble(AVRational* rational);
     /* 释放所有资源 */
-    void freeAll();                                 
+    void freeAll();
 
 private slots:
     void do_startDecodeVedio();                     /* 开启解码 */
@@ -100,9 +95,8 @@ private slots:
 private:
     QThread* m_thread = nullptr;                    /* 解码线程 */
     std::atomic_bool m_isRunning = false;           /* 解码线程是否运行 */
-    QTimer m_startThread;                           /* 开启线程的定时器 */
     std::atomic_bool m_initFFmpeg = false;          /* ffmpeg初始化标志 */
-    std::atomic_bool m_threadStopped = false;       /* 停止线程 */
+    std::atomic_bool m_threadState = false;         /* 线程运行状态 */
     std::atomic_bool m_pauseDecode = false;         /* 暂停解码 */
     std::atomic_bool m_decodeStatus = false;        /* 解码状态,这里主要是检测是否暂停解码 */
     std::atomic_bool m_isSeek = false;              /* 是否跳转 */
@@ -117,29 +111,20 @@ private:
     struct SwsContext *m_sws_ctx = nullptr;         /* 视频转换上下文 */
     uint8_t *m_buffer = nullptr;                    /* 存储解码后的一帧数据,RGB格式 */
     int m_videoStream = -1;                         /* 记录视频流是第几个流 */
+    AVBufferRef* m_hw_device_ctx = nullptr;         /* 对数据缓冲区的引用 */
 
     bool m_HWDecoder = false;                       /* 是否使用硬件解码 */
-    int m_srcWidth = 0;                             /* 图片原本宽度 */
-    int m_srcHeight = 0;                            /* 图片原本高度 */
-    int m_width = 0;                                /* 图像宽度 */
-    int m_height = 0;                               /* 图像高度 */
-    int m_totalFrame = 0;                           /* 视频总帧数 */
+    QSize m_srcSize;                                /* 原始视频分辨率大小 */
+    qint64 m_totalFrame = 0;                        /* 视频总帧数 */
     int m_fps;                                      /* 每秒的帧数 */
-    quint64 m_currentFrame = 0;                     /* 当前已播放的帧数 */
-    quint64 m_pts = 0;                              /* 当前帧显示时间 */
-    qint64 m_duration = 0;                          /* 视频时长,单位秒 */
+    qint64 m_currentFrame = 0;                      /* 当前已播放的帧数 */
+    qint64 m_pts = 0;                               /* 当前帧显示时间 */
+    qint64 m_duration = 0;                          /* 视频时长,单位秒 */
     qint64 m_currentPos = 0;                        /* 当前播放位置 */
     qint64 m_startPos = 0;                          /* 起始位置,后续操作都需要在这各基础上操作 */
-
-    // QImage *m_image = nullptr;
-    int m_queueMaxNum = 10;                         /* 这里不是环形队列,最大设置为30帧图像 */
-    QQueue<QImage*> m_queueImage;                   /* 视频帧队列 */
-    QMutex m_mutexQueue;                            /* 队列互斥锁 */
-    QWaitCondition m_condQueueNoFull;               /* 队列不满,可以入队 */
-    QWaitCondition m_condQueueNoEmpty;              /* 队列非空,可以读取 */
+    QString m_decoderName;                          /* 解码器名称 */
 
     QList<int> m_listHWDeviceType;                  /* 保存当前环境支持的硬件解码器 */
-    AVBufferRef* m_hw_device_ctx = nullptr;         /* 对数据缓冲区的引用 */
 
     RingQueue<QImage*> m_ringQueue;                 /* 环形队列,存储生成的图像 */
 };

+ 29 - 32
demo/VideoPlayer/VideoPlayer/VideoPlayer.cpp

@@ -11,7 +11,6 @@
 #include "FmtLog/fmtlog.h"
 
 
-
 VideoPlayer::VideoPlayer(QWidget *parent) : QWidget(parent)
 {
     /* 初始化解码线程 */
@@ -28,6 +27,7 @@ VideoPlayer::VideoPlayer(QWidget *parent) : QWidget(parent)
     connect(m_decodeVedio, &DecodeVedio::signal_oneImage, this, &VideoPlayer::do_refreshOneUI);
     connect(m_decodeVedio, &DecodeVedio::signal_playCompleted, this, &VideoPlayer::do_playCompleted);
     SPDLOG_TRACE("UI线程ID:{}", QThread::currentThreadId());
+
 }
 
 VideoPlayer::~VideoPlayer()
@@ -50,15 +50,15 @@ VideoPlayer::~VideoPlayer()
  * 
  * @param fileName 
  */
-void VideoPlayer::setPlayVedio(const QString& fileName)
+void VideoPlayer::openPlayVedio(const QString& fileName)
 {
-    if(isSetVedioFile)
+    if(m_isOpenFile)
     {
-        isSetVedioFile = false;
+        m_isOpenFile = false;
     }
-    if(m_decodeVedio->isInitFFmpeg())
+    if(m_decodeVedio->isDecoding())
     {
-        // m_decodeVedio->unInitFFmpeg();
+        m_decodeVedio->stopDecodeVedio();
     }
     if(fileName.isEmpty())
     {
@@ -66,34 +66,28 @@ void VideoPlayer::setPlayVedio(const QString& fileName)
         return;
     }
     m_fileName = fileName;
-    isSetVedioFile = true;
+    m_isOpenFile = true;
 
     m_decodeVedio->openVedio(fileName);
-    // m_decodeVedio->setVideoSize(this->width(), this->height());
     /* 获取原始视频信息 */
-    m_srcWidth = m_decodeVedio->getSrcVideoWidth();
-    m_srcHeight = m_decodeVedio->getSrcVideoHeight();
-    m_frameCount = m_decodeVedio->getFrameCount();
-    SPDLOG_DEBUG("视频宽:{} 高:{} 帧数:{}", m_srcWidth, m_srcHeight, m_frameCount);
+    m_srcWidth = m_decodeVedio->getSrcVideoSize().width();
+    m_srcHeight = m_decodeVedio->getSrcVideoSize().height();
+    m_fps = m_decodeVedio->getFPS();
+    m_duration = m_decodeVedio->getDuration();
+    SPDLOG_INFO("视频编码格式:{}", m_decodeVedio->getDecoderName().toStdString());
+    int hh = m_duration / 3600000;
+    int mm = (m_duration % 3600000) / 60000;
+    int ss = (m_duration % 60000) / 1000;
+    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);
     /* 开启定时器刷新 */
-    if(m_frameCount < 0)
-    {
-        /* HEVC帧率,获取不到,就按照24帧来刷新 */
-        if(m_frameCount == -2)
-        {
-            m_frameCount = 24;
-        }
-        else {
-            m_frameCount = 25;
-        }
-    }
-    else if(m_frameCount == 0)
+    if(m_fps <= 0)
     {
-        m_frameCount = 25;
+        m_fps = 25;
     }
-    SPDLOG_INFO("帧率:{}", m_frameCount);
 
     /* 开启解码,手动刷新第一帧 */
     m_decodeVedio->startDecodeVedio();
@@ -105,7 +99,7 @@ void VideoPlayer::setPlayVedio(const QString& fileName)
 /* 播放视频 */
 bool VideoPlayer::play()
 {
-    if(!isSetVedioFile)
+    if(!m_isOpenFile)
     {
         SPDLOG_ERROR("文件名为空");
         return false;
@@ -117,7 +111,7 @@ bool VideoPlayer::play()
     
     /* 设置刷新时间 */
     m_timerRefreshUI.setSingleShot(false);
-    m_interval = qRound64(1000.0 / m_frameCount);
+    m_interval = qRound64(1000.0 / m_fps);
     SPDLOG_TRACE("刷新UI的定时间隔:{}",m_interval);
     m_timerRefreshUI.start(m_interval);
     m_playStatus = true;
@@ -148,7 +142,7 @@ void VideoPlayer::stop()
     // SPDLOG_DEBUG("...停止解码...");
     
     /* 重新设置播放视频 */
-    setPlayVedio(m_fileName);
+    openPlayVedio(m_fileName);
 
     /* 绘制黑帧 */
     // SPDLOG_DEBUG("绘制黑帧");
@@ -338,7 +332,7 @@ void VideoPlayer::ListHWDecoder()
 
 void VideoPlayer::paintEvent(QPaintEvent *event)
 {
-    if(m_image)
+    if(m_image != nullptr)
     {
         // SPDLOG_TRACE("开始绘制画面...");
         /* 对图像进行缩放 */
@@ -387,7 +381,6 @@ void VideoPlayer::refreshOneUIUntilHave()
             // SPDLOG_DEBUG("绘制画面...");
             update();
         }
-        m_decodeVedio->wakeUpCondQueueNoEmpty();
     }
 }
 
@@ -423,6 +416,11 @@ void VideoPlayer::do_refreshUI()
         
         if(m_image)
         {
+            if(m_image->isNull())
+            {
+                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);
@@ -463,7 +461,6 @@ void VideoPlayer::do_refreshOneUI()
             SPDLOG_DEBUG("绘制预览画面...");
             update();
         }
-        m_decodeVedio->wakeUpCondQueueNoEmpty();
     }
 }
 

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

@@ -15,10 +15,11 @@ public:
     explicit VideoPlayer(QWidget *parent = nullptr);
     ~VideoPlayer();
 
-    void setPlayVedio(const QString& fileName);     /* 设置播放视频 */
+    void openPlayVedio(const QString& fileName);    /* 打开播放视频 */
     bool play();                                    /* 播放视频 */
     void pause();                                   /* 暂停播放 */
     void stop();                                    /* 停止播放 */
+    
     void backward(quint64 ms);                      /* 后退,单位ms */
     void forward(quint64 ms);                       /* 前进,单位ms */
     bool getPlayStatus() { return m_playStatus; }   /* 获取播放状态 */
@@ -50,14 +51,15 @@ private:
     int m_srcHeight = 0;
     int m_nowWidth = 0;                             /* 现在大小 */
     int m_nowHeight = 0;
-    int m_frameCount = 0;                           /* 帧数 */
-    int m_interval = 0;                             /* 间隔 */
+    int m_fps = 0;                                  /* 帧数 */
+    int m_interval = 0;                             /* 定时器定时间隔,帧率的倒数,单位ms */
+    qint64 m_duration = 0;                          /* 时长,单位ms */
 
     DecodeVedio* m_decodeVedio = nullptr;
     QThread* m_threadDecode = nullptr;              /* 解码器所在的线程 */
     QImage* m_image = nullptr;                      /* 画面 */
     bool m_playStatus = false;                      /* 是否正在播放 */
-    bool isSetVedioFile = false;                    /* 是否设置了视频文件 */
+    bool m_isOpenFile = false;                      /* 是否打开了视频文件 */
     QSemaphore* m_semRefresh = nullptr;             /* 刷新信号量 */
 
     // std::function<Play_CallBack> m_funcPlayCB = nullptr;  /* 播放回调函数 */

+ 3 - 2
demo/VideoPlayer/widget.cpp

@@ -17,7 +17,8 @@ Widget::Widget(QWidget *parent)
     ui->setupUi(this);
 
     m_videoPlayer = std::make_shared<VideoPlayer>();
-    m_videoPlayer->setParent(ui->widget_display);
+    m_videoPlayer->setPlayWidget(ui->widget_display);
+    
 
     // m_videoPlayer1 = std::make_shared<VideoPlayer1>();
     // m_videoPlayer1->setParent(ui->widget_display);
@@ -50,7 +51,7 @@ void Widget::on_pBtn_openVideo_clicked()
         }
     }
 
-    m_videoPlayer->setPlayVedio(ui->lineEdit->text());
+    m_videoPlayer->openPlayVedio(ui->lineEdit->text());
     // m_videoPlayer1->setPlayVedio(ui->lineEdit->text());
     
 }