|
|
|
|
#include "decodestream.h"
|
|
|
|
|
|
|
|
|
|
DecodeStream::DecodeStream(QObject *parent) : QObject{parent} {}
|
|
|
|
|
|
|
|
|
|
bool DecodeStream::init(AVPacketQueueManager *queueManager,
|
|
|
|
|
AVFormatContext *formatContext, int videoIndex) {
|
|
|
|
|
// QMutexLocker locker(&m_mutex);
|
|
|
|
|
// m_packetsQueue = queue;
|
|
|
|
|
m_queueManager = queueManager;
|
|
|
|
|
m_formatContext = formatContext;
|
|
|
|
|
m_videoIndex = videoIndex;
|
|
|
|
|
|
|
|
|
|
return initDecoder(formatContext, videoIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 视频解码线程任务
|
|
|
|
|
void DecodeStream::startDecode() {
|
|
|
|
|
qDebug() << "deocdeStreamThreadID:" << QThread::currentThreadId();
|
|
|
|
|
m_start = initObject();
|
|
|
|
|
if (!m_start) {
|
|
|
|
|
free();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
while (m_start) {
|
|
|
|
|
try {
|
|
|
|
|
AVPacket *inputPacket = m_queueManager->dequeueDecodePacket();
|
|
|
|
|
if (inputPacket) {
|
|
|
|
|
AVFrame *frame = decodePacket(inputPacket);
|
|
|
|
|
emit repaintSignal(frame);
|
|
|
|
|
av_packet_unref(inputPacket);
|
|
|
|
|
av_packet_free(&inputPacket);
|
|
|
|
|
inputPacket = nullptr;
|
|
|
|
|
} else {
|
|
|
|
|
// QThread::usleep(1000);
|
|
|
|
|
// av_usleep(1000);
|
|
|
|
|
}
|
|
|
|
|
} catch (...) {
|
|
|
|
|
}
|
|
|
|
|
av_usleep(1000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free();
|
|
|
|
|
qDebug() << "Decoding Thread End!";
|
|
|
|
|
emit sendErrorMessageSignal("视频解码结束!",
|
|
|
|
|
NotificationType::NOTIFICATION_SUCCESS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DecodeStream::close() {
|
|
|
|
|
// QMutexLocker locker(&m_mutex);
|
|
|
|
|
m_start = false;
|
|
|
|
|
qDebug() << "decode Stream close!" << m_start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DecodeStream::initObject() {
|
|
|
|
|
// 分配AVFrame并将其字段设置为默认值。
|
|
|
|
|
m_frame = av_frame_alloc();
|
|
|
|
|
if (!m_frame) {
|
|
|
|
|
qWarning() << "av_frame_alloc() Error!";
|
|
|
|
|
free();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DecodeStream::initDecoder(AVFormatContext *inputFormatContext,
|
|
|
|
|
int videoIndex) {
|
|
|
|
|
if (!inputFormatContext) return false;
|
|
|
|
|
AVStream *videoStream =
|
|
|
|
|
inputFormatContext->streams[videoIndex]; // 通过查询到的索引获取视频流
|
|
|
|
|
|
|
|
|
|
// 获取视频图像分辨率(AVStream中的AVCodecContext在新版本中弃用,改为使用AVCodecParameters)
|
|
|
|
|
// m_size.setWidth(videoStream->codecpar->width);
|
|
|
|
|
// m_size.setHeight(videoStream->codecpar->height);
|
|
|
|
|
// m_frameRate = rationalToDouble(&videoStream->avg_frame_rate); //
|
|
|
|
|
// 视频帧率
|
|
|
|
|
AVCodecParameters *videoCodecPara = videoStream->codecpar;
|
|
|
|
|
// 通过解码器ID获取视频解码器(新版本返回值必须使用const)
|
|
|
|
|
const AVCodec *codec = avcodec_find_decoder(videoCodecPara->codec_id);
|
|
|
|
|
if (!codec) {
|
|
|
|
|
printf("Cannot find valid decode codec.\n");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 分配AVCodecContext并将其字段设置为默认值。
|
|
|
|
|
m_codecContext = avcodec_alloc_context3(codec);
|
|
|
|
|
if (!m_codecContext) {
|
|
|
|
|
#if PRINT_LOG
|
|
|
|
|
qWarning() << "创建视频解码器上下文失败!";
|
|
|
|
|
#endif
|
|
|
|
|
free();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 使用视频流的codecpar为解码器上下文赋值
|
|
|
|
|
int ret = avcodec_parameters_to_context(m_codecContext, videoCodecPara);
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
showError(ret);
|
|
|
|
|
free();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_codecContext->lowres = codec->max_lowres;
|
|
|
|
|
m_codecContext->flags2 |=
|
|
|
|
|
AV_CODEC_FLAG2_FAST; // 允许不符合规范的加速技巧。
|
|
|
|
|
// m_codecContext->thread_count = 4; // 使用8线程解码
|
|
|
|
|
// 设置解码器容错选项,忽略丢失的帧或参数
|
|
|
|
|
// m_codecContext->err_recognition = AV_EF_IGNORE_ERR;
|
|
|
|
|
// m_codecContext->flags |= AV_CODEC_FLAG2_CHUNKS;
|
|
|
|
|
|
|
|
|
|
// 初始化解码器上下文,如果之前avcodec_alloc_context3传入了解码器,这里设置NULL就可以
|
|
|
|
|
ret = avcodec_open2(m_codecContext, codec, nullptr);
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
showError(ret);
|
|
|
|
|
free();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AVFrame *DecodeStream::decodePacket(AVPacket *inputPacket) {
|
|
|
|
|
if (!isValidAVPacket(inputPacket)) return nullptr;
|
|
|
|
|
// 将读取到的原始数据包传入解码器
|
|
|
|
|
int ret = avcodec_send_packet(m_codecContext, inputPacket);
|
|
|
|
|
if (ret >= 0) {
|
|
|
|
|
while ((ret = avcodec_receive_frame(m_codecContext, m_frame)) >= 0) {
|
|
|
|
|
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
|
|
|
|
|
showError(ret);
|
|
|
|
|
qWarning() << "decoding file end\n";
|
|
|
|
|
break;
|
|
|
|
|
} else if (ret < 0) {
|
|
|
|
|
showError(ret);
|
|
|
|
|
qWarning() << "Error during decoding\n";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!isValidAVFrame(m_frame)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_frameTemp = m_frame;
|
|
|
|
|
return m_frameTemp;
|
|
|
|
|
// QThread::msleep(1);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
qDebug() << "avcodec_send_packet error:" << ret << "\n";
|
|
|
|
|
if (ret == AVERROR(EAGAIN)) {
|
|
|
|
|
qDebug() << "AVERROR(EAGAIN)";
|
|
|
|
|
av_usleep(10000);
|
|
|
|
|
}
|
|
|
|
|
if (ret == AVERROR_EOF) {
|
|
|
|
|
qDebug() << " AVERROR_EOF";
|
|
|
|
|
avcodec_flush_buffers(m_codecContext);
|
|
|
|
|
av_usleep(10000);
|
|
|
|
|
}
|
|
|
|
|
showError(ret);
|
|
|
|
|
}
|
|
|
|
|
av_frame_unref(m_frame);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DecodeStream::free() {
|
|
|
|
|
if (m_codecContext) {
|
|
|
|
|
avcodec_free_context(&m_codecContext);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_frame) {
|
|
|
|
|
av_frame_free(&m_frame);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DecodeStream::isValidAVFrame(AVFrame *frame) {
|
|
|
|
|
// 空指针检查
|
|
|
|
|
if (!frame) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查像素格式是否有效
|
|
|
|
|
if (frame->format == AV_PIX_FMT_NONE) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查数据指针是否有效
|
|
|
|
|
if (!frame->data[0]) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查宽高是否有效
|
|
|
|
|
if (frame->width <= 0 || frame->height <= 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查行大小是否有效
|
|
|
|
|
if (frame->linesize[0] <= 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果需要,添加更多判断条件,例如时间戳或关键帧检查
|
|
|
|
|
if (frame->pts == AV_NOPTS_VALUE) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true; // 如果所有条件都通过,则认为 AVFrame 有效
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DecodeStream::isValidAVPacket(AVPacket *pkt) {
|
|
|
|
|
if (pkt == nullptr) {
|
|
|
|
|
qDebug() << "Invalid AVPacket: packet pointer is null.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// 检查数据指针和大小
|
|
|
|
|
if (pkt->data == nullptr || pkt->size <= 0) {
|
|
|
|
|
qDebug() << "Invalid AVPacket: data is null or size is non-positive.\n";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查时间戳
|
|
|
|
|
if (pkt->pts == AV_NOPTS_VALUE || pkt->dts == AV_NOPTS_VALUE) {
|
|
|
|
|
qDebug() << "Invalid AVPacket: pts or dts is AV_NOPTS_VALUE.\n";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查流索引(如果是多流)
|
|
|
|
|
if (pkt->stream_index < 0) {
|
|
|
|
|
qDebug() << "Invalid AVPacket: stream_index is invalid.\n";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查是否是有效的关键帧(可选)
|
|
|
|
|
if (pkt->flags & AV_PKT_FLAG_KEY) {
|
|
|
|
|
// 是关键帧
|
|
|
|
|
// qDebug() << "This is a keyframe.\n";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|