|
@@ -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可以识别 */
|