Bladeren bron

V0.4.1
1、基本完成了使用CPU解码视频,但是目前无法运行,运行会段错误

Apple 4 maanden geleden
bovenliggende
commit
89926cf8fc

+ 2 - 2
CMakeLists.txt

@@ -144,8 +144,8 @@ file(GLOB GLOBAL_SRC
 # add_subdirectory(${CMAKE_SOURCE_DIR}/demo/http)
 # add_subdirectory(${CMAKE_SOURCE_DIR}/demo/threadPool)
 # add_subdirectory(${CMAKE_SOURCE_DIR}/demo/ftp)
-add_subdirectory(${CMAKE_SOURCE_DIR}/demo/OneThread)
+# add_subdirectory(${CMAKE_SOURCE_DIR}/demo/OneThread)
 # add_subdirectory(${CMAKE_SOURCE_DIR}/demo/timer)
 # add_subdirectory(${CMAKE_SOURCE_DIR}/demo/time)
-# add_subdirectory(${CMAKE_SOURCE_DIR}/demo/VideoPlayer)
+add_subdirectory(${CMAKE_SOURCE_DIR}/demo/VideoPlayer)
 

+ 157 - 26
demo/VideoPlayer/VideoPlayer/DecodeVedio.cpp

@@ -212,20 +212,21 @@ void DecodeVedio::wakeUpCondQueueNoEmpty()
  */
 QImage* DecodeVedio::getOneImage()
 {
-    if(m_queueImage.count() == 0)
-    {
-        // SPDLOG_TRACE("队列为空...");
-        return nullptr;
-    }
-    // SPDLOG_TRACE("******************************** 队列中图片个数:{} ",m_queueImage.count());
-    m_mutexQueue.lock();
-    auto image = m_queueImage.dequeue();
-    m_mutexQueue.unlock();
-    /* 唤醒可能阻塞住的解码线程,队列中的图片低于20之后再唤醒 */
-    if(m_queueImage.count() < 20)
-    {
-        m_condQueueNoFull.wakeAll();
-    }
+    // if(m_queueImage.count() == 0)
+    // {
+    //     // SPDLOG_TRACE("队列为空...");
+    //     return nullptr;
+    // }
+    // // SPDLOG_TRACE("******************************** 队列中图片个数:{} ",m_queueImage.count());
+    // m_mutexQueue.lock();
+    // auto image = m_queueImage.dequeue();
+    // m_mutexQueue.unlock();
+    // /* 唤醒可能阻塞住的解码线程,队列中的图片低于20之后再唤醒 */
+    // if(m_queueImage.count() < 20)
+    // {
+    //     m_condQueueNoFull.wakeAll();
+    // }
+    QImage* image = m_ringQueue.deQueueBlock();
     return image;
 }
 
@@ -452,7 +453,7 @@ void DecodeVedio::initFFmpeg(const QString& fileName)
     /* 获取视频相关信息 */
     m_width = m_srcWidth = m_pCodecCtx->width;
     m_height = m_srcHeight = m_pCodecCtx->height;
-    m_frameCount = m_pFormatContext->streams[m_videoStream]->nb_frames;
+    m_totalFrame = m_pFormatContext->streams[m_videoStream]->nb_frames;
     
     /* 这个函数的实际作用是将buffer设置给pFrameRGB作为原始数据的内存区域 */
     av_image_fill_arrays(m_pFrameRGB->data, m_pFrameRGB->linesize, m_buffer, AV_PIX_FMT_RGBA, m_pCodecCtx->width, m_pCodecCtx->height, 1);
@@ -460,12 +461,12 @@ void DecodeVedio::initFFmpeg(const QString& fileName)
     /********** 创建一个SwsContext结构体,主要为了格式转化的时候使用 ***********/
     
     /* 初始化Sws Context,这是转换规则,转换成RGB */
-    m_sws_ctx = sws_getContext( m_pCodecCtx->width, m_pCodecCtx->height, m_pCodecCtx->pix_fmt,    /* 原图像大小和格式 */
-                                m_srcWidth, m_srcHeight, AV_PIX_FMT_RGBA,      /* 目标图像的大小和格式 */
-                                SWS_BILINEAR,           /* 双线性 */
-                                nullptr, 
-                                nullptr, 
-                                nullptr);
+    // m_sws_ctx = sws_getContext( m_pCodecCtx->width, m_pCodecCtx->height, m_pCodecCtx->pix_fmt,    /* 原图像大小和格式 */
+    //                             m_srcWidth, m_srcHeight, AV_PIX_FMT_RGBA,      /* 目标图像的大小和格式 */
+    //                             SWS_BILINEAR,           /* 双线性 */
+    //                             nullptr, 
+    //                             nullptr, 
+    //                             nullptr);
 
     m_initFFmpeg = true;
     SPDLOG_INFO("FFMPEG初始化完成!");
@@ -715,14 +716,85 @@ void DecodeVedio::decodeUsingCPU()
         }
         else
         {
-        
+            if(packet->stream_index == m_videoStream)
+            {
+            #if 0
+                /* 计算当前帧时间,两种方法,第一种适用于所有场景,但是存在一定的误差 */
+                packet->pts = qRound64(packet->pts * (1000 * rationalToDouble(&m_pFormatContext->streams[m_videoStream]->time_base)));
+                packet->dts = qRound64(packet->dts * (1000 * rationalToDouble(&m_pFormatContext->streams[m_videoStream]->time_base)));
+            #else
+                /* 适用于本地视频,直接计算本地视频文件的每一帧时间 */
+                m_currentFrame++;
+                packet->pts = qRound64(m_currentFrame * (qreal(m_duration) / m_totalFrame));
+            #endif
+
+                /* 将数据传给解码器 */
+                int ret = avcodec_send_packet(m_pCodecCtx, packet);
+                if(ret < 0)
+                {
+                    SPDLOG_ERROR("发送数据包错误...");
+                    continue;
+                }
+            }
         }
-
-        /* 发送给解码器 avcodec_send_packet */
+        av_packet_unref(packet);    /* 释放数据包,引用计数-1,为0时释放空间 */
 
         /* 读取出解码器返回的帧 avcodec_receive_frame */
+        int ret = avcodec_receive_frame(m_pCodecCtx, pFrameSRC);
+        if(ret < 0)
+        {
+            av_frame_unref(pFrameSRC);
+            if(retRead < 0)
+            {
+                SPDLOG_INFO("读取到文件末尾...");
+                break;
+            }
+        }
 
+        AVFrame* pFrameTemp = pFrameSRC;
+        if(pFrameTemp->data[0] == nullptr)
+        {
+            /* 使用的是硬件解码器 */
+            pFrameTemp = m_pFrameHW;
+            /* 将数据从GPU拷贝到内存中 */
+            
+        }
+        m_pts = pFrameTemp->pts;
         /* 转换解码后的帧格式,转换成RGBA格式,Qt可以识别 */
+        if(m_sws_ctx == nullptr)
+        {
+            m_sws_ctx = sws_getCachedContext(m_sws_ctx, 
+                                            pFrameTemp->width, pFrameTemp->height,      /* 原图像大小和格式 */
+                                            m_pCodecCtx->pix_fmt,                       /* 输入图像的像素格式 */
+                                            m_width, m_height,                          /* 目标图像的大小 */
+                                            AV_PIX_FMT_RGBA,                            /* 目标图像的格式 */
+                                            SWS_BILINEAR,                               /* 图像缩放算法,双线性 */
+                                            nullptr,                                    /* 输入图像的滤波器信息,不需要传NULL */
+                                            nullptr,                                    /* 输出图像的滤波器信息,不需要传NULL */
+                                            nullptr);                                   /* 特定缩放算法需要的参数,不需要传NULL */
+            if(m_sws_ctx == nullptr)
+            {
+                SPDLOG_ERROR("创建SwsContext错误...");
+                av_packet_free(&packet);
+                av_frame_free(&pFrameSRC);
+                av_frame_free(&pFrameRGB);
+                break;
+            }
+        }
+        /* 转换成RGBA格式 */
+        sws_scale(  m_sws_ctx,              /* 缩放的上下文 */
+                    pFrameTemp->data,       /* 源图像数组 */
+                    pFrameTemp->linesize,   /* 包含源图像每个平面步幅的数组 */
+                    0,                      /* 开始位置 */
+                    pFrameTemp->height,     /* 函数 */
+                    pFrameRGB->data,        /* 目标图像数组 */
+                    pFrameRGB->linesize);   /* 目标图像行数 */
+        auto image = new QImage(m_pFrameRGB->data[0], m_width, m_height, QImage::Format_RGBA8888);
+        m_ringQueue.enQueueBlock(image);
+
+
+        av_frame_unref(pFrameRGB);
+        av_frame_unref(pFrameSRC);
     }
     /* 释放空间 */
     av_packet_free(&packet);
@@ -752,6 +824,17 @@ void DecodeVedio::pauseDecode()
     }
 }
 
+/**
+ * @brief          将AVRational转换为double,用于计算帧率
+ * @param rational
+ * @return
+ */
+qreal DecodeVedio::rationalToDouble(AVRational* rational)
+{
+    qreal frameRate = (rational->den == 0) ? 0 : (qreal(rational->num) / rational->den);
+    return frameRate;
+}
+
 
 /* 开启解码 */
 void DecodeVedio::do_startDecodeVedio()
@@ -765,7 +848,55 @@ void DecodeVedio::do_startDecodeVedio()
     m_threadStopped = false;
     m_pauseDecode = false;
     /* 进入解码,直到播放完成或者手动退出 */
-    decodeVedio();
+    // decodeVedio();
+    decodeUsingCPU();
     SPDLOG_TRACE("Decode播放完成...");
 
-}
+}
+
+/* 释放所有资源 */
+void DecodeVedio::freeAll()
+{
+    if(m_sws_ctx)
+    {
+        sws_freeContext(m_sws_ctx);
+        m_sws_ctx = nullptr;
+    }
+    if(m_pFrameRGB)
+    {
+        av_frame_free(&m_pFrameRGB);
+    }
+    if(m_pFrame)
+    {
+        av_frame_free(&m_pFrame);
+    }
+    if(m_packet)
+    {
+        av_packet_free(&m_packet);
+    }
+    if(m_pCodecCtx)
+    {
+        avcodec_free_context(&m_pCodecCtx);
+    }
+    if(m_pFormatContext)
+    {
+        avformat_close_input(&m_pFormatContext);
+    }
+
+
+
+    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;
+            }
+        }
+    }
+
+}
+

+ 6 - 1
demo/VideoPlayer/VideoPlayer/DecodeVedio.h

@@ -85,6 +85,8 @@ private:
     void decodeUsingCPU();                          /* 软解码线程 */
     void exitThread();                              /* 退出线程 */
     void pauseDecode();                             /* 暂停解码 */
+    qreal rationalToDouble(AVRational* rational);   /* 将AVRational转换为double */
+    void freeAll();                                 /* 释放所有资源 */
 
 private slots:
     void do_startDecodeVedio();                     /* 开启解码 */
@@ -105,6 +107,7 @@ private:
     AVPacket* m_packet = nullptr;                   /* 存储解码前的数据,一个数据包 */
     AVFrame* m_pFrame = nullptr;                    /* 存储解码后的一帧数据原始视频编码 */
     AVFrame* m_pFrameRGB = nullptr;                 /* 存储解码后的一帧数据,RGB格式 */
+    AVFrame* m_pFrameHW = nullptr;                  /* 存储解码后的一帧数据,硬件解码 */
     struct SwsContext *m_sws_ctx = nullptr;         /* 视频转换上下文 */
     uint8_t *m_buffer = nullptr;                    /* 存储解码后的一帧数据,RGB格式 */
     int m_videoStream = -1;                         /* 记录视频流是第几个流 */
@@ -113,8 +116,10 @@ private:
     int m_srcHeight = 0;                            /* 图片原本高度 */
     int m_width = 0;                                /* 图像宽度 */
     int m_height = 0;                               /* 图像高度 */
-    int m_frameCount = 0;                           /* 帧数 */
+    int m_totalFrame = 0;                           /* 视频总帧数 */
     int m_fps;                                      /* 每秒的帧数 */
+    quint64 m_currentFrame = 0;                     /* 当前已播放的帧数 */
+    quint64 m_pts = 0;                              /* 当前帧显示时间 */
     qint64 m_duration = 0;                          /* 视频时长 */
     qint64 m_currentPos = 0;                        /* 当前播放位置 */
     qint64 m_startPos = 0;                          /* 起始位置,后续操作都需要在这各基础上操作 */

+ 5 - 5
demo/VideoPlayer/VideoPlayer/VideoPlayer.cpp

@@ -346,10 +346,10 @@ void VideoPlayer::paintEvent(QPaintEvent *event)
         // SPDLOG_TRACE("开始绘制画面...");
         /* 对图像进行缩放 */
         QImage image;
-        if(m_srcWidth != m_nowWidth || m_srcHeight != m_nowHeight)
-        {
-            image = m_image->scaled(m_nowWidth, m_nowHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
-        }
+        // if(m_srcWidth != m_nowWidth || m_srcHeight != m_nowHeight)
+        // {
+        //     image = m_image->scaled(m_nowWidth, m_nowHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+        // }
         QPainter painter(this);
         painter.drawImage(0, 0, image);
     }
@@ -434,7 +434,7 @@ void VideoPlayer::do_refreshUI()
             // SPDLOG_DEBUG("绘制画面...");
             update();
         }
-        m_decodeVedio->wakeUpCondQueueNoEmpty();
+        // m_decodeVedio->wakeUpCondQueueNoEmpty();
     }
 }