Ver código fonte

V0.4.2
1、继续做打开视频并初始化ffmpeg的操作

Apple 7 meses atrás
pai
commit
29e1e91c80

+ 174 - 1
demo/VideoPlayer/VideoPlayer/DecodeVedio.cpp

@@ -91,8 +91,10 @@ void DecodeVedio::startDecodeVedio()
         SPDLOG_WARN("未初始化FFMPEG...");
         return;
     }
+    m_isRunning = true;
+    decodeUsingCPU();
     /* 开启定时器,进入槽函数,就进入了新的线程 */
-    m_startThread.start(0);
+    // m_startThread.start(0);
 }
 
 /* 停止解码视频,退出工作函数,线程未停止 */
@@ -483,6 +485,8 @@ void DecodeVedio::unInitFFmpeg()
 
 
 
+
+
 /* 取消ffmpeg初始化函数 */
 void DecodeVedio::freeFFmpeg()
 {
@@ -662,6 +666,174 @@ void DecodeVedio::decodeVedio()
     m_threadStopped = true;
 }
 
+
+/* 打开视频,同时初始化解码器) */
+void DecodeVedio::openVedio(const QString& fileName)
+{
+    if(fileName.isEmpty())
+    {
+        SPDLOG_WARN("文件名为空...");
+        return;
+    }
+    m_fileName = fileName;
+    if(m_initFFmpeg)
+    {
+        freeFFmpeg();
+    }
+
+    /* 清空队列 */
+    if(m_ringQueue.QueueSize() > 0)
+    {
+        for(int i = 0; i < m_ringQueue.QueueSize(); i++)
+        {
+            auto image = m_ringQueue.deQueueBlock();
+            if (image)
+            {
+                delete image;
+            }
+        }
+        m_ringQueue.clearQueue();
+    }
+    m_ringQueue.setQueueSize(30);
+
+    SPDLOG_DEBUG("开始初始化FFMPEG");
+    AVDictionary* dict = nullptr;
+    av_dict_set(&dict, "rtsp_transport", "tcp", 0);      // 设置rtsp流使用tcp打开,如果打开失败错误信息为【Error number -135 occurred】可以切换(UDP、tcp、udp_multicast、http),比如vlc推流就需要使用udp打开
+    av_dict_set(&dict, "max_delay", "3", 0);             // 设置最大复用或解复用延迟(以微秒为单位)。当通过【UDP】 接收数据时,解复用器尝试重新排序接收到的数据包(因为它们可能无序到达,或者数据包可能完全丢失)。这可以通过将最大解复用延迟设置为零(通过max_delayAVFormatContext 字段)来禁用。
+    av_dict_set(&dict, "timeout", "1000000", 0);         // 以微秒为单位设置套接字 TCP I/O 超时,如果等待时间过短,也可能会还没连接就返回了。
+
+    /************ 存储文件格式信息 ************/
+    /* 打开文件,读取视频文件的头信息,放在第一个参数的结构体中 */
+    int ret = avformat_open_input(  &m_pFormatContext,                  /* 解封装上下文 */
+                                    m_fileName.toStdString().c_str(),   /* 打开视频地址 */
+                                    nullptr,                            /* 这个参数强制使用特定的输入格式,可以设置为null */
+                                    nullptr);                           /* 参数设置 */
+    if(ret != 0)
+    {
+        SPDLOG_WARN("打开视频文件错误,错误代码:{}",ret);
+        freeAll();
+        return;
+    }
+    /* 释放参数 */
+    if(dict != nullptr)
+    {
+        av_dict_free(&dict);
+    }
+
+    /************ 找到视频流 ************/
+    /* 检查视频容器内部的流信息,将所有流存储到了pFormatContext->streams中
+     * 查找到视频流,并获取视频时长相关的信息 */
+    ret = 0;
+    ret = avformat_find_stream_info(m_pFormatContext, nullptr);
+    if(ret < 0)
+    {
+        SPDLOG_WARN("获取视频流错误,错误代码:{}",ret);
+        freeAll();
+        return;
+    }
+    m_duration = m_pFormatContext->duration;
+    m_currentPos = 0;
+    m_startPos = m_pFormatContext->start_time;
+    // SPDLOG_DEBUG("开始时间:{} 时长:{}",m_startPos, m_duration);
+    
+    /* 一个调试函数,将流信息输出到控制台 */
+    av_dump_format(m_pFormatContext, 0, m_fileName.toStdString().c_str(), 0);
+
+    /* 找到视频流 */
+    m_videoStream = av_find_best_stream(m_pFormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
+    if(m_videoStream < 0)
+    {
+        SPDLOG_WARN("没有找到视频流");
+        freeAll();
+        return;
+    }
+    SPDLOG_INFO("找到视频流");
+    /* 获取视频流的编解码信息,主要是分辨率等信息 */
+    AVStream *pStream = m_pFormatContext->streams[m_videoStream];   /* 获取视频流 */
+    AVCodecParameters* pCodecParams = pStream->codecpar;            /* 获取视频流的编解码信息 */
+    SPDLOG_INFO("获取视频流参数成功!");
+    /* 获取视频相关信息 */
+    m_width = m_srcWidth = pCodecParams->width;
+    m_height = m_srcHeight = pCodecParams->height;
+    m_fps = rationalToDouble(&pStream->avg_frame_rate);
+    m_totalFrame = m_pFormatContext->streams[m_videoStream]->nb_frames;
+
+
+    /************ 查找并设置解码器 ************/
+    /* 找到解码器 */
+    const AVCodec* pCodec = avcodec_find_decoder(pCodecParams->codec_id);
+    if(pCodec == nullptr)
+    {
+        SPDLOG_WARN("没有找到解码器");
+        freeAll();
+        return;
+    }
+    SPDLOG_INFO("找到解码器 :{}",pCodec->name);
+    /* 获取视频信息的上下文,先分配空间,后面记得释放空间 */
+    m_pCodecCtx = avcodec_alloc_context3(pCodec);
+    /* 将视频流中的编码器参数拷贝下来,这个函数不是线程安全的 */
+    if(avcodec_parameters_to_context(m_pCodecCtx, pCodecParams) != 0)
+    {
+        SPDLOG_WARN("复制上下文错误");
+        freeAll();
+        return;
+    }
+    SPDLOG_INFO("复制上下文成功!");
+
+    /*************** 初始化硬件解码器 ******************/
+
+    /* 打开解码器,(初始化解码器上下文,如果调用了avcodec_alloc_context3,第二个参数可以设置为nullptr) */
+    if(avcodec_open2(m_pCodecCtx, nullptr, nullptr) < 0)
+    {
+        SPDLOG_ERROR("打开解码器错误");
+        return;
+    }
+    SPDLOG_TRACE("打开编码器成功!");
+
+    /******** 创建数据包(解码数据) ********/
+    m_packet = av_packet_alloc();
+    // av_init_packet(m_packet);
+    av_new_packet(m_packet, m_pCodecCtx->width * m_pCodecCtx->height);
+
+    /********* 创建两个pFrame,一个存放原始数据,一个存放转换后的RGB数据 **********/
+    m_pFrame = av_frame_alloc();
+    if(m_pFrame == nullptr)
+    {
+        SPDLOG_ERROR("创建pFrame错误");
+        return;
+    }
+    m_pFrameRGB = av_frame_alloc();
+    if(m_pFrameRGB == nullptr)
+    {
+        SPDLOG_ERROR("创建pFrameRGB错误");
+        return;
+    }
+    
+    int numBytes = 0;
+    /* 初始化pFrameRGB */
+    numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGBA, m_pCodecCtx->width, m_pCodecCtx->height, 1);
+    m_buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t));
+    
+    /* 这个函数的实际作用是将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);
+
+    /********** 创建一个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_initFFmpeg = true;
+    SPDLOG_INFO("FFMPEG初始化完成!");
+    /* 这里不能释放,否则会出问题 */
+    // avcodec_parameters_free(&pCodecCtxOrig);
+}
+
+
 /**
  * @brief 软解码线程,使用CPU解码,使用环境队列存储解码的数据
  * 
@@ -758,6 +930,7 @@ void DecodeVedio::decodeUsingCPU()
             pFrameTemp = m_pFrameHW;
             /* 将数据从GPU拷贝到内存中 */
             
+            break;
         }
         m_pts = pFrameTemp->pts;
         /* 转换解码后的帧格式,转换成RGBA格式,Qt可以识别 */

+ 5 - 2
demo/VideoPlayer/VideoPlayer/DecodeVedio.h

@@ -45,7 +45,10 @@ public:
     /* 取消ffmpeg初始化函数 */
     void unInitFFmpeg();
     /* 获取ffmpeg初始化状态 */
-    bool isInitFFmpeg() { return m_initFFmpeg; }     
+    bool isInitFFmpeg() { return m_initFFmpeg; }    
+
+    /* 打开视频,同时初始化解码器) */
+    void openVedio(const QString& fileName);
     
     /* 开始解码视频,开始前先设置视频名称 */
     void startDecodeVedio();
@@ -120,7 +123,7 @@ private:
     int m_fps;                                      /* 每秒的帧数 */
     quint64 m_currentFrame = 0;                     /* 当前已播放的帧数 */
     quint64 m_pts = 0;                              /* 当前帧显示时间 */
-    qint64 m_duration = 0;                          /* 视频时长 */
+    qint64 m_duration = 0;                          /* 视频时长,单位微秒 */
     qint64 m_currentPos = 0;                        /* 当前播放位置 */
     qint64 m_startPos = 0;                          /* 起始位置,后续操作都需要在这各基础上操作 */