|
@@ -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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|