|
@@ -1,17 +1,20 @@
|
|
|
#include "DecodeVedio.h"
|
|
|
+#include "FrameFormat.h"
|
|
|
#include "spdlog/spdlog.h"
|
|
|
-#include "FmtLog/fmtlog.h"
|
|
|
|
|
|
#include <QThread>
|
|
|
|
|
|
extern "C"
|
|
|
{
|
|
|
-#include <libavcodec/avcodec.h>
|
|
|
-#include <libavformat/avformat.h>
|
|
|
+// #include <libavcodec/avcodec.h>
|
|
|
+// #include <libavformat/avformat.h>
|
|
|
#include <libswscale/swscale.h>
|
|
|
#include <libavutil/imgutils.h>
|
|
|
}
|
|
|
|
|
|
+#include "OpenGLWidgetAPI.h"
|
|
|
+
|
|
|
+
|
|
|
/*=================================================================================================
|
|
|
* @brief FFmpeg获取GPU硬件解码帧格式的回调函数
|
|
|
===================================================================================================*/
|
|
@@ -136,7 +139,7 @@ void DecodeVedio::setCurrentPos(qint64 pos)
|
|
|
|
|
|
SPDLOG_DEBUG("跳转到:{}ms",pos);
|
|
|
/*在环形队列中有已解码的视频帧数,需要去掉已有的帧数所占的时间 */
|
|
|
- pos = pos - m_queueImage.QueueSize() * (1000 / m_fps);
|
|
|
+ pos = pos - m_queueFrame.QueueSize() * (1000 / m_fps);
|
|
|
if(pos < 0) {
|
|
|
pos = 0;
|
|
|
}
|
|
@@ -156,16 +159,7 @@ void DecodeVedio::setCurrentPos(qint64 pos)
|
|
|
m_flushDecoder.store(true);
|
|
|
/* 清空环形队列中的视频 */
|
|
|
SPDLOG_DEBUG("清空环形队列中的视频。");
|
|
|
- QImage* image = 0;
|
|
|
- while (m_queueImage.QueueSize() > 0)
|
|
|
- {
|
|
|
- image = nullptr;
|
|
|
- m_queueImage.front_pop_NoBlock(image);
|
|
|
- if(image != nullptr)
|
|
|
- {
|
|
|
- delete image;
|
|
|
- }
|
|
|
- }
|
|
|
+ clearQueueFrame();
|
|
|
|
|
|
/* 继续解码 */
|
|
|
continueDecode();
|
|
@@ -188,18 +182,18 @@ qint64 DecodeVedio::getDuration()
|
|
|
* @warning 传出这个指针后,队列就出队了,内存需要外面获取的实例释放
|
|
|
* @return QImage* 一帧图像的指针
|
|
|
*/
|
|
|
-QImage* DecodeVedio::getOneImage()
|
|
|
+FrameBase* DecodeVedio::getOneImage()
|
|
|
{
|
|
|
if(!m_threadRuning)
|
|
|
{
|
|
|
return nullptr;
|
|
|
}
|
|
|
- QImage* image = nullptr;
|
|
|
- if(!m_queueImage.front_pop_NoBlock(image))
|
|
|
- {
|
|
|
- return nullptr;
|
|
|
- }
|
|
|
- return image;
|
|
|
+ // FrameBase* frame = nullptr;
|
|
|
+ // if(!m_queueFrame.front_pop_NoBlock(frame))
|
|
|
+ // {
|
|
|
+ // return nullptr;
|
|
|
+ // }
|
|
|
+ return m_queueFrame.front_pop_NoBlock();
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -208,7 +202,7 @@ QImage* DecodeVedio::getOneImage()
|
|
|
* @param timeOut 设为-1是一直等待,设置正整数是等待的时间,单位ms
|
|
|
* @return QImage*
|
|
|
*/
|
|
|
-QImage* DecodeVedio::getOneImageUntilHave(int timeOut)
|
|
|
+FrameBase* DecodeVedio::getOneImageUntilHave(int timeOut)
|
|
|
{
|
|
|
if(!m_threadRuning)
|
|
|
{
|
|
@@ -216,15 +210,15 @@ QImage* DecodeVedio::getOneImageUntilHave(int timeOut)
|
|
|
}
|
|
|
if(timeOut < 0)
|
|
|
{
|
|
|
- QImage* image = m_queueImage.front_pop();
|
|
|
- return image;
|
|
|
+ auto frame = m_queueFrame.front_pop();
|
|
|
+ return frame;
|
|
|
}
|
|
|
+ FrameBase* frame = nullptr;
|
|
|
for(int i = 0; i < timeOut; i++)
|
|
|
{
|
|
|
- QImage* image = nullptr;
|
|
|
- if(m_queueImage.front_pop_NoBlock(image))
|
|
|
+ if(m_queueFrame.front_pop_NoBlock(frame))
|
|
|
{
|
|
|
- return image;
|
|
|
+ return frame;
|
|
|
}
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
|
}
|
|
@@ -248,6 +242,28 @@ void DecodeVedio::findHWDecoder(QStringList& listDecoderName)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/* 清空环形队列 */
|
|
|
+void DecodeVedio::clearQueueFrame()
|
|
|
+{
|
|
|
+ while(m_queueFrame.QueueSize() > 0)
|
|
|
+ {
|
|
|
+ FrameBase* frame = nullptr;
|
|
|
+ m_queueFrame.front_pop_NoBlock(frame);
|
|
|
+ deleteOneFrame(frame);
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/* 删除一个图片 */
|
|
|
+void DecodeVedio::deleteOneImage()
|
|
|
+{
|
|
|
+ if(m_queueFrame.QueueSize() > 0)
|
|
|
+ {
|
|
|
+ auto frame = m_queueFrame.front_pop_NoBlock();
|
|
|
+ deleteOneFrame(frame);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
/* 获取硬件解码器 */
|
|
|
void DecodeVedio::findHWDecoder()
|
|
@@ -271,6 +287,8 @@ void DecodeVedio::findHWDecoder()
|
|
|
}else {
|
|
|
m_supportHWDecoder = true;
|
|
|
}
|
|
|
+
|
|
|
+ m_supportHWDecoder = false;
|
|
|
}
|
|
|
|
|
|
|
|
@@ -357,19 +375,9 @@ bool DecodeVedio::openVedio(const QString& fileName)
|
|
|
}
|
|
|
|
|
|
/* 清空队列 */
|
|
|
- if(m_queueImage.QueueSize() > 0)
|
|
|
- {
|
|
|
- for(int i = 0; i < m_queueImage.QueueSize(); i++)
|
|
|
- {
|
|
|
- QImage* image = nullptr;
|
|
|
- if (m_queueImage.front_pop_NoBlock(image))
|
|
|
- {
|
|
|
- delete image;
|
|
|
- }
|
|
|
- }
|
|
|
- m_queueImage.clearQueue();
|
|
|
- }
|
|
|
- m_queueImage.setQueueCapacity(30);
|
|
|
+ clearQueueFrame();
|
|
|
+ m_queueFrame.setQueueCapacity(30);
|
|
|
+ m_queueFrame.setDefaultValue(nullptr);
|
|
|
|
|
|
SPDLOG_DEBUG("开始初始化FFMPEG");
|
|
|
/* 设置网络缓冲区大小和错误恢复选项 */
|
|
@@ -549,9 +557,9 @@ void DecodeVedio::threadDecodeUsingCPU()
|
|
|
{
|
|
|
/******** 初始化局部变量 ********/
|
|
|
bool isEnd = false;
|
|
|
- int ret = 0;
|
|
|
- int retFrame = 0;
|
|
|
- int retPacket = 0;
|
|
|
+ // int ret = 0;
|
|
|
+ // int retFrame = 0;
|
|
|
+ // int retPacket = 0;
|
|
|
m_pauseDecode = false;
|
|
|
m_decodeStatus = true;
|
|
|
m_decodeState.store(DecodeState::DecodeRun);
|
|
@@ -664,48 +672,52 @@ void DecodeVedio::threadDecodeUsingCPU()
|
|
|
}
|
|
|
// SPDLOG_DEBUG("当前帧的pts:{}", m_pts.load());
|
|
|
|
|
|
- /* 转换解码后的帧格式,转换成RGBA格式,Qt可以识别 */
|
|
|
- if(m_sws_ctx == nullptr)
|
|
|
- {
|
|
|
- /* 选择在这里创建,为了兼容硬件解码,硬件解码出来的格式可能和解码器传出的不一样 */
|
|
|
- m_sws_ctx = sws_getCachedContext(m_sws_ctx,
|
|
|
- m_pFrameSRC->width, m_pFrameSRC->height, /* 原图像大小和格式 */
|
|
|
- m_pCodecContext->pix_fmt, /* 输入图像的像素格式 */
|
|
|
- m_srcSize.width(), m_srcSize.height(), /* 目标图像的大小 */
|
|
|
- AV_PIX_FMT_RGBA, /* 目标图像的格式 */
|
|
|
- SWS_BILINEAR, /* 图像缩放算法,双线性 */
|
|
|
- nullptr, /* 输入图像的滤波器信息,不需要传NULL */
|
|
|
- nullptr, /* 输出图像的滤波器信息,不需要传NULL */
|
|
|
- nullptr); /* 特定缩放算法需要的参数,不需要传NULL */
|
|
|
- if(m_sws_ctx == nullptr)
|
|
|
- {
|
|
|
- SPDLOG_ERROR("创建SwsContext错误...");
|
|
|
- goto label_ThreadDecodeExit;
|
|
|
- }
|
|
|
- SPDLOG_INFO("创建SwsContext成功...");
|
|
|
- }
|
|
|
- /* 转换成RGBA格式 */
|
|
|
- // uint8_t* data[1] = { m_buffer };
|
|
|
- int lines[4];
|
|
|
- /* 使用像素格式pix_fmt和宽度填充图像的平面线条的大小(一行大小?) */
|
|
|
- av_image_fill_linesizes(lines, AV_PIX_FMT_RGBA, m_pFrameSRC->width);
|
|
|
-
|
|
|
- sws_scale( m_sws_ctx, /* 缩放的上下文 */
|
|
|
- m_pFrameSRC->data, /* 源图像数组 */
|
|
|
- m_pFrameSRC->linesize, /* 包含源图像每个平面步幅的数组 */
|
|
|
- 0, /* 开始位置 */
|
|
|
- m_pFrameSRC->height, /* 行数 */
|
|
|
- &m_buffer, /* 目标图像数组 */
|
|
|
- lines); /* 目标图像行数 */
|
|
|
- if(m_buffer != nullptr)
|
|
|
- {
|
|
|
- /* 将数据拷贝到QImage中 */
|
|
|
- auto image = new QImage(m_buffer, m_srcSize.width(), m_srcSize.height(), QImage::Format_RGBA8888);
|
|
|
- /* 如果队列满,线程会阻塞在这里 */
|
|
|
- m_queueImage.push(image);
|
|
|
- // av_frame_unref(m_pFrameRGB);
|
|
|
- // SPDLOG_DEBUG("一帧视频入队");
|
|
|
- }
|
|
|
+ // /* 转换解码后的帧格式,转换成RGBA格式,Qt可以识别 */
|
|
|
+ // if(m_sws_ctx == nullptr)
|
|
|
+ // {
|
|
|
+ // /* 选择在这里创建,为了兼容硬件解码,硬件解码出来的格式可能和解码器传出的不一样 */
|
|
|
+ // m_sws_ctx = sws_getCachedContext(m_sws_ctx,
|
|
|
+ // m_pFrameSRC->width, m_pFrameSRC->height, /* 原图像大小和格式 */
|
|
|
+ // m_pCodecContext->pix_fmt, /* 输入图像的像素格式 */
|
|
|
+ // m_srcSize.width(), m_srcSize.height(), /* 目标图像的大小 */
|
|
|
+ // AV_PIX_FMT_RGBA, /* 目标图像的格式 */
|
|
|
+ // SWS_BILINEAR, /* 图像缩放算法,双线性 */
|
|
|
+ // nullptr, /* 输入图像的滤波器信息,不需要传NULL */
|
|
|
+ // nullptr, /* 输出图像的滤波器信息,不需要传NULL */
|
|
|
+ // nullptr); /* 特定缩放算法需要的参数,不需要传NULL */
|
|
|
+ // if(m_sws_ctx == nullptr)
|
|
|
+ // {
|
|
|
+ // SPDLOG_ERROR("创建SwsContext错误...");
|
|
|
+ // goto label_ThreadDecodeExit;
|
|
|
+ // }
|
|
|
+ // SPDLOG_INFO("创建SwsContext成功...");
|
|
|
+ // }
|
|
|
+ // /* 转换成RGBA格式 */
|
|
|
+ // // uint8_t* data[1] = { m_buffer };
|
|
|
+ // int lines[4];
|
|
|
+ // /* 使用像素格式pix_fmt和宽度填充图像的平面线条的大小(一行大小?) */
|
|
|
+ // av_image_fill_linesizes(lines, AV_PIX_FMT_RGBA, m_pFrameSRC->width);
|
|
|
+
|
|
|
+ // sws_scale( m_sws_ctx, /* 转换上下文 */
|
|
|
+ // m_pFrameSRC->data, /* 源图像数据指针数组 */
|
|
|
+ // m_pFrameSRC->linesize, /* 源图像每行字节数数组 */
|
|
|
+ // 0, /* 源图像起始行(通常为0) */
|
|
|
+ // m_pFrameSRC->height, /* 处理的行数(通常为图像高度) */
|
|
|
+ // &m_buffer, /* 目标图像数据指针数组(packed格式只用[0]) */
|
|
|
+ // lines); /* 目标图像每行字节数数组 */
|
|
|
+ // if(m_buffer != nullptr)
|
|
|
+ // {
|
|
|
+ // /* 将数据拷贝到QImage中 */
|
|
|
+ // auto image = new QImage(m_buffer, m_srcSize.width(), m_srcSize.height(), QImage::Format_RGBA8888);
|
|
|
+ // /* 如果队列满,线程会阻塞在这里 */
|
|
|
+ // m_queueRGBA.push(image);
|
|
|
+ // // av_frame_unref(m_pFrameRGB);
|
|
|
+ // // SPDLOG_DEBUG("一帧视频入队");
|
|
|
+ // }
|
|
|
+
|
|
|
+ /* 对生成的图像进行格式转换,并添加到环形队列中 */
|
|
|
+ convertImageFormatAndPushToQueue(m_pFrameSRC);
|
|
|
+
|
|
|
av_frame_unref(m_pFrameSRC);
|
|
|
/* 如果在跳转过程中,直接退出,防止一个packet中有多个视频帧,再次阻塞在上面 */
|
|
|
if(m_isSeek)
|
|
@@ -744,7 +756,7 @@ void DecodeVedio::exitThread()
|
|
|
m_decodeState.store(DecodeState::DecodeRun);
|
|
|
m_pauseDecode = false;
|
|
|
/* 先退出可能阻塞住的解码线程 */
|
|
|
- m_queueImage.exit();
|
|
|
+ m_queueFrame.exit();
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -755,9 +767,9 @@ void DecodeVedio::threadDecodeUsingGPU()
|
|
|
{
|
|
|
/******** 初始化局部变量 ********/
|
|
|
bool isEnd = false;
|
|
|
- int ret = 0;
|
|
|
- int retFrame = 0;
|
|
|
- int retPacket = 0;
|
|
|
+ // int ret = 0;
|
|
|
+ // int retFrame = 0;
|
|
|
+ // int retPacket = 0;
|
|
|
m_pauseDecode = false;
|
|
|
m_decodeStatus = true;
|
|
|
m_decodeState.store(DecodeState::DecodeRun);
|
|
@@ -879,47 +891,49 @@ void DecodeVedio::threadDecodeUsingGPU()
|
|
|
// SPDLOG_DEBUG("当前帧的pts:{}", m_pts.load());
|
|
|
|
|
|
/* 转换解码后的帧格式,转换成RGBA格式,Qt可以识别 */
|
|
|
- if(m_sws_ctx == nullptr)
|
|
|
- {
|
|
|
- /* 选择在这里创建,为了兼容硬件解码,硬件解码出来的格式可能和解码器传出的不一样 */
|
|
|
- m_sws_ctx = sws_getCachedContext(m_sws_ctx,
|
|
|
- m_pFrameHW->width, m_pFrameHW->height, /* 原图像大小和格式 */
|
|
|
- m_pCodecContext->pix_fmt, /* 输入图像的像素格式 */
|
|
|
- m_srcSize.width(), m_srcSize.height(), /* 目标图像的大小 */
|
|
|
- AV_PIX_FMT_RGBA, /* 目标图像的格式 */
|
|
|
- SWS_BILINEAR, /* 图像缩放算法,双线性 */
|
|
|
- nullptr, /* 输入图像的滤波器信息,不需要传NULL */
|
|
|
- nullptr, /* 输出图像的滤波器信息,不需要传NULL */
|
|
|
- nullptr); /* 特定缩放算法需要的参数,不需要传NULL */
|
|
|
- if(m_sws_ctx == nullptr)
|
|
|
- {
|
|
|
- SPDLOG_ERROR("创建SwsContext错误...");
|
|
|
- goto label_ThreadDecodeExit;
|
|
|
- }
|
|
|
- SPDLOG_INFO("创建SwsContext成功...");
|
|
|
- }
|
|
|
- /* 转换成RGBA格式 */
|
|
|
- // uint8_t* data[1] = { m_buffer };
|
|
|
- int lines[4];
|
|
|
- /* 使用像素格式pix_fmt和宽度填充图像的平面线条的大小(一行大小?) */
|
|
|
- av_image_fill_linesizes(lines, AV_PIX_FMT_RGBA, m_pFrameHW->width);
|
|
|
-
|
|
|
- sws_scale( m_sws_ctx, /* 缩放的上下文 */
|
|
|
- m_pFrameHW->data, /* 源图像数组 */
|
|
|
- m_pFrameHW->linesize, /* 包含源图像每个平面步幅的数组 */
|
|
|
- 0, /* 开始位置 */
|
|
|
- m_pFrameHW->height, /* 行数 */
|
|
|
- &m_buffer, /* 目标图像数组 */
|
|
|
- lines); /* 目标图像行数 */
|
|
|
- if(m_buffer != nullptr)
|
|
|
- {
|
|
|
- /* 将数据拷贝到QImage中 */
|
|
|
- auto image = new QImage(m_buffer, m_srcSize.width(), m_srcSize.height(), QImage::Format_RGBA8888);
|
|
|
- /* 如果队列满,线程会阻塞在这里 */
|
|
|
- m_queueImage.push(image);
|
|
|
- // av_frame_unref(m_pFrameRGB);
|
|
|
- // SPDLOG_DEBUG("一帧视频入队");
|
|
|
- }
|
|
|
+ // if(m_sws_ctx == nullptr)
|
|
|
+ // {
|
|
|
+ // /* 选择在这里创建,为了兼容硬件解码,硬件解码出来的格式可能和解码器传出的不一样 */
|
|
|
+ // m_sws_ctx = sws_getCachedContext(m_sws_ctx,
|
|
|
+ // m_pFrameHW->width, m_pFrameHW->height, /* 原图像大小和格式 */
|
|
|
+ // m_pCodecContext->pix_fmt, /* 输入图像的像素格式 */
|
|
|
+ // m_srcSize.width(), m_srcSize.height(), /* 目标图像的大小 */
|
|
|
+ // AV_PIX_FMT_RGBA, /* 目标图像的格式 */
|
|
|
+ // SWS_BILINEAR, /* 图像缩放算法,双线性 */
|
|
|
+ // nullptr, /* 输入图像的滤波器信息,不需要传NULL */
|
|
|
+ // nullptr, /* 输出图像的滤波器信息,不需要传NULL */
|
|
|
+ // nullptr); /* 特定缩放算法需要的参数,不需要传NULL */
|
|
|
+ // if(m_sws_ctx == nullptr)
|
|
|
+ // {
|
|
|
+ // SPDLOG_ERROR("创建SwsContext错误...");
|
|
|
+ // goto label_ThreadDecodeExit;
|
|
|
+ // }
|
|
|
+ // SPDLOG_INFO("创建SwsContext成功...");
|
|
|
+ // }
|
|
|
+ // /* 转换成RGBA格式 */
|
|
|
+ // // uint8_t* data[1] = { m_buffer };
|
|
|
+ // int lines[4];
|
|
|
+ // /* 使用像素格式pix_fmt和宽度填充图像的平面线条的大小(一行大小?) */
|
|
|
+ // av_image_fill_linesizes(lines, AV_PIX_FMT_RGBA, m_pFrameHW->width);
|
|
|
+
|
|
|
+ // sws_scale( m_sws_ctx, /* 缩放的上下文 */
|
|
|
+ // m_pFrameHW->data, /* 源图像数组 */
|
|
|
+ // m_pFrameHW->linesize, /* 包含源图像每个平面步幅的数组 */
|
|
|
+ // 0, /* 开始位置 */
|
|
|
+ // m_pFrameHW->height, /* 行数 */
|
|
|
+ // &m_buffer, /* 目标图像数组 */
|
|
|
+ // lines); /* 目标图像行数 */
|
|
|
+ // if(m_buffer != nullptr)
|
|
|
+ // {
|
|
|
+ // /* 将数据拷贝到QImage中 */
|
|
|
+ // auto image = new QImage(m_buffer, m_srcSize.width(), m_srcSize.height(), QImage::Format_RGBA8888);
|
|
|
+ // /* 如果队列满,线程会阻塞在这里 */
|
|
|
+ // m_queueRGBA.push(image);
|
|
|
+ // // av_frame_unref(m_pFrameRGB);
|
|
|
+ // // SPDLOG_DEBUG("一帧视频入队");
|
|
|
+ // }
|
|
|
+ convertImageFormatAndPushToQueue(m_pFrameHW);
|
|
|
+
|
|
|
av_frame_unref(m_pFrameSRC);
|
|
|
/* 如果在跳转过程中,直接退出,防止一个packet中有多个视频帧,再次阻塞在上面 */
|
|
|
if(m_isSeek)
|
|
@@ -947,6 +961,94 @@ label_ThreadDecodeExit:
|
|
|
m_threadRuning = false;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+/* 对生成的图像进行格式转换,并添加到环形队列中 */
|
|
|
+void DecodeVedio::convertImageFormatAndPushToQueue(AVFrame* pFrame)
|
|
|
+{
|
|
|
+ /* 判断视频帧格式 */
|
|
|
+ if(pFrame->format == AV_PIX_FMT_YUV420P)
|
|
|
+ {
|
|
|
+ m_frameFormat = EFrameFormat::Frame_YUV420P;
|
|
|
+ /* 是yuv420p格式, */
|
|
|
+ Frame_YUV420P* frame = new Frame_YUV420P();
|
|
|
+ convertYUV420PToImage(pFrame, frame->frameData);
|
|
|
+ m_queueFrame.push(frame);
|
|
|
+ } else
|
|
|
+ {
|
|
|
+ /* 不是420p,转换成RGBA格式 */
|
|
|
+ m_frameFormat = EFrameFormat::Frame_RGBA8888;
|
|
|
+ convertToRGBA(pFrame);
|
|
|
+ if(m_buffer != nullptr)
|
|
|
+ {
|
|
|
+ Frame_RGBA8888* frame = new Frame_RGBA8888();
|
|
|
+ /* 将数据拷贝到QImage中 */
|
|
|
+ frame->frameData = new QImage(m_buffer, m_srcSize.width(), m_srcSize.height(), QImage::Format_RGBA8888);
|
|
|
+ /* 如果队列满,线程会阻塞在这里 */
|
|
|
+ m_queueFrame.push(frame);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* 将yuv420p的uint8_t数组缓存转换成Image_YUV420P结构体 */
|
|
|
+void DecodeVedio::convertYUV420PToImage(AVFrame* pFrame, Image_YUV420P* image)
|
|
|
+{
|
|
|
+ if(pFrame == nullptr || image == nullptr)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if(pFrame->data[0] == nullptr || pFrame->data[1] == nullptr || pFrame->data[2] == nullptr)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ image->width = pFrame->width;
|
|
|
+ image->height = pFrame->height;
|
|
|
+ uint32_t ySzie = pFrame->width * pFrame->height; /* Y分量的大小 */
|
|
|
+ image->yData.append(reinterpret_cast<const char*>(pFrame->data[0]), ySzie);
|
|
|
+ image->uData.append(reinterpret_cast<const char*>(pFrame->data[1]), ySzie / 4);
|
|
|
+ image->vData.append(reinterpret_cast<const char*>(pFrame->data[2]), ySzie / 4);
|
|
|
+}
|
|
|
+
|
|
|
+/* 转换成RGBA格式 */
|
|
|
+void DecodeVedio::convertToRGBA(AVFrame* pFrame)
|
|
|
+{
|
|
|
+ /* 转换解码后的帧格式,转换成RGBA格式,Qt可以识别 */
|
|
|
+ if(m_sws_ctx == nullptr)
|
|
|
+ {
|
|
|
+ /* 选择在这里创建,为了兼容硬件解码,硬件解码出来的格式可能和解码器传出的不一样 */
|
|
|
+ m_sws_ctx = sws_getCachedContext(m_sws_ctx,
|
|
|
+ pFrame->width, pFrame->height, /* 原图像大小和格式 */
|
|
|
+ m_pCodecContext->pix_fmt, /* 输入图像的像素格式 */
|
|
|
+ m_srcSize.width(), m_srcSize.height(), /* 目标图像的大小 */
|
|
|
+ AV_PIX_FMT_RGBA, /* 目标图像的格式 */
|
|
|
+ SWS_BILINEAR, /* 图像缩放算法,双线性 */
|
|
|
+ nullptr, /* 输入图像的滤波器信息,不需要传NULL */
|
|
|
+ nullptr, /* 输出图像的滤波器信息,不需要传NULL */
|
|
|
+ nullptr); /* 特定缩放算法需要的参数,不需要传NULL */
|
|
|
+ if(m_sws_ctx == nullptr)
|
|
|
+ {
|
|
|
+ SPDLOG_ERROR("创建SwsContext错误...");
|
|
|
+ /* 释放空间 */
|
|
|
+ av_packet_free(&m_packet);
|
|
|
+ m_decodeState.store(DecodeState::DecodeExit);
|
|
|
+ m_threadRuning = false;
|
|
|
+ }
|
|
|
+ SPDLOG_INFO("创建SwsContext成功...");
|
|
|
+ }
|
|
|
+ /* 转换成RGBA格式 */
|
|
|
+ // uint8_t* data[1] = { m_buffer };
|
|
|
+ int lines[4];
|
|
|
+ /* 使用像素格式pix_fmt和宽度填充图像的平面线条的大小(一行大小?) */
|
|
|
+ av_image_fill_linesizes(lines, AV_PIX_FMT_RGBA, pFrame->width);
|
|
|
+ sws_scale( m_sws_ctx, /* 转换上下文 */
|
|
|
+ pFrame->data, /* 源图像数据指针数组 */
|
|
|
+ pFrame->linesize, /* 源图像每行字节数数组 */
|
|
|
+ 0, /* 源图像起始行(通常为0) */
|
|
|
+ pFrame->height, /* 处理的行数(通常为图像高度) */
|
|
|
+ &m_buffer, /* 目标图像数据指针数组(packed格式只用[0]) */
|
|
|
+ lines); /* 目标图像每行字节数数组 */
|
|
|
+}
|
|
|
+
|
|
|
/* 暂停解码,会阻塞到线程暂停为止 */
|
|
|
void DecodeVedio::pauseDecode()
|
|
|
{
|
|
@@ -963,19 +1065,8 @@ void DecodeVedio::pauseDecode()
|
|
|
m_decodeState.store(DecodeState::DecodeRun);
|
|
|
m_pauseDecode = true;
|
|
|
/* 队列出队两张图片,防止解码线程阻塞到环形队列满上面 */
|
|
|
- QImage* image = nullptr;
|
|
|
- m_queueImage.front_pop_NoBlock(image);
|
|
|
- if(image != nullptr)
|
|
|
- {
|
|
|
- delete image;
|
|
|
- image = nullptr;
|
|
|
- }
|
|
|
- m_queueImage.front_pop_NoBlock(image);
|
|
|
- if(image != nullptr)
|
|
|
- {
|
|
|
- delete image;
|
|
|
- image = nullptr;
|
|
|
- }
|
|
|
+ deleteOneImage();
|
|
|
+ deleteOneImage();
|
|
|
/* 等待线程状态变为暂停为止 */
|
|
|
while (m_decodeState.load() != DecodeState::DecodePause)
|
|
|
{
|
|
@@ -1013,7 +1104,16 @@ void DecodeVedio::do_startDecodeVedio()
|
|
|
m_threadRuning = true;
|
|
|
m_pauseDecode = false;
|
|
|
/* 进入解码,直到播放完成或者手动退出 */
|
|
|
- threadDecodeUsingCPU();
|
|
|
+ if(m_supportHWDecoder)
|
|
|
+ {
|
|
|
+ SPDLOG_TRACE("使用硬件解码器进行解码...");
|
|
|
+ threadDecodeUsingGPU();
|
|
|
+ }else
|
|
|
+ {
|
|
|
+ SPDLOG_INFO("使用CPU进行解码...");
|
|
|
+ threadDecodeUsingCPU();
|
|
|
+ }
|
|
|
+
|
|
|
SPDLOG_TRACE("Decode解码结束。");
|
|
|
|
|
|
}
|
|
@@ -1060,15 +1160,8 @@ void DecodeVedio::freeAll()
|
|
|
av_buffer_unref(&m_hw_device_ctx);
|
|
|
}
|
|
|
|
|
|
- for(int i = 0; i < m_queueImage.QueueSize(); i++)
|
|
|
- {
|
|
|
- QImage* image = nullptr;
|
|
|
- if (m_queueImage.front_pop_NoBlock(image))
|
|
|
- {
|
|
|
- delete image;
|
|
|
- }
|
|
|
- }
|
|
|
- m_queueImage.clearQueue();
|
|
|
+ clearQueueFrame();
|
|
|
+ m_queueFrame.clearQueue();
|
|
|
}
|
|
|
|
|
|
|