|
@@ -61,8 +61,13 @@ AVTool::MediaInfo* Decoder::detectMediaInfo(const QString& url)
|
|
|
|
|
|
|
|
int videoIndex = av_find_best_stream(fmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
|
|
int videoIndex = av_find_best_stream(fmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
|
|
|
if (videoIndex < 0) {
|
|
if (videoIndex < 0) {
|
|
|
- qDebug() << "no video stream!";
|
|
|
|
|
- return Q_NULLPTR;
|
|
|
|
|
|
|
+ // 仅音频:返回时长和占位图
|
|
|
|
|
+ AVTool::MediaInfo* info = new AVTool::MediaInfo;
|
|
|
|
|
+ info->duration = duration;
|
|
|
|
|
+ info->tipImg = QImage(300, 300, QImage::Format_RGB888);
|
|
|
|
|
+ info->tipImg.fill(Qt::black);
|
|
|
|
|
+ avformat_close_input(&fmtCtx);
|
|
|
|
|
+ return info;
|
|
|
}
|
|
}
|
|
|
//视频解码初始化
|
|
//视频解码初始化
|
|
|
AVCodecParameters* videoCodecPar = fmtCtx->streams[videoIndex]->codecpar;
|
|
AVCodecParameters* videoCodecPar = fmtCtx->streams[videoIndex]->codecpar;
|
|
@@ -252,91 +257,102 @@ int Decoder::decode(const QString& url)
|
|
|
AVRational q = {1, AV_TIME_BASE};
|
|
AVRational q = {1, AV_TIME_BASE};
|
|
|
m_duration = (uint32_t) (m_fmtCtx->duration * av_q2d(q));
|
|
m_duration = (uint32_t) (m_fmtCtx->duration * av_q2d(q));
|
|
|
av_dict_free(&formatOpts);
|
|
av_dict_free(&formatOpts);
|
|
|
- //qDebug()<<QString("duration: %1:%2").arg(m_duration/60).arg(m_duration%60)<<endl;
|
|
|
|
|
|
|
|
|
|
|
|
+ // 查找音频/视频流,允许缺失
|
|
|
m_audioIndex = av_find_best_stream(m_fmtCtx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);
|
|
m_audioIndex = av_find_best_stream(m_fmtCtx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);
|
|
|
if (m_audioIndex < 0) {
|
|
if (m_audioIndex < 0) {
|
|
|
qDebug() << "no audio stream!";
|
|
qDebug() << "no audio stream!";
|
|
|
- return 0;
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
m_videoIndex = av_find_best_stream(m_fmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
|
|
m_videoIndex = av_find_best_stream(m_fmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
|
|
|
if (m_videoIndex < 0) {
|
|
if (m_videoIndex < 0) {
|
|
|
qDebug() << "no video stream!";
|
|
qDebug() << "no video stream!";
|
|
|
- return 0;
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- //音频解码初始化
|
|
|
|
|
- AVCodecParameters* audioCodecPar = m_fmtCtx->streams[m_audioIndex]->codecpar;
|
|
|
|
|
- if (!audioCodecPar) {
|
|
|
|
|
- qDebug() << "audio par is nullptr!";
|
|
|
|
|
|
|
+ if (m_audioIndex < 0 && m_videoIndex < 0) {
|
|
|
|
|
+ qDebug() << "no audio and video stream!";
|
|
|
return 0;
|
|
return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- m_audioPktDecoder.codecCtx = avcodec_alloc_context3(nullptr);
|
|
|
|
|
|
|
+ // 音频解码初始化(可选)
|
|
|
|
|
+ if (m_audioIndex >= 0) {
|
|
|
|
|
+ AVCodecParameters* audioCodecPar = m_fmtCtx->streams[m_audioIndex]->codecpar;
|
|
|
|
|
+ if (!audioCodecPar) {
|
|
|
|
|
+ qDebug() << "audio par is nullptr!";
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- ret = avcodec_parameters_to_context(m_audioPktDecoder.codecCtx, audioCodecPar);
|
|
|
|
|
- if (ret < 0) {
|
|
|
|
|
- av_strerror(ret, m_errBuf, sizeof(m_errBuf));
|
|
|
|
|
- qDebug() << "error info_avcodec_parameters_to_context:" << m_errBuf;
|
|
|
|
|
- return 0;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ m_audioPktDecoder.codecCtx = avcodec_alloc_context3(nullptr);
|
|
|
|
|
|
|
|
- const AVCodec* audioCodec = avcodec_find_decoder(m_audioPktDecoder.codecCtx->codec_id);
|
|
|
|
|
- if (!audioCodec) {
|
|
|
|
|
- qDebug() << "avcodec_find_decoder failed!";
|
|
|
|
|
- return 0;
|
|
|
|
|
- }
|
|
|
|
|
- m_audioPktDecoder.codecCtx->codec_id = audioCodec->id;
|
|
|
|
|
|
|
+ ret = avcodec_parameters_to_context(m_audioPktDecoder.codecCtx, audioCodecPar);
|
|
|
|
|
+ if (ret < 0) {
|
|
|
|
|
+ av_strerror(ret, m_errBuf, sizeof(m_errBuf));
|
|
|
|
|
+ qDebug() << "error info_avcodec_parameters_to_context:" << m_errBuf;
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- ret = avcodec_open2(m_audioPktDecoder.codecCtx, audioCodec, nullptr);
|
|
|
|
|
- if (ret < 0) {
|
|
|
|
|
- av_strerror(ret, m_errBuf, sizeof(m_errBuf));
|
|
|
|
|
- qDebug() << "error info_avcodec_open2:" << m_errBuf;
|
|
|
|
|
- return 0;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const AVCodec* audioCodec = avcodec_find_decoder(m_audioPktDecoder.codecCtx->codec_id);
|
|
|
|
|
+ if (!audioCodec) {
|
|
|
|
|
+ qDebug() << "avcodec_find_decoder failed!";
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ m_audioPktDecoder.codecCtx->codec_id = audioCodec->id;
|
|
|
|
|
|
|
|
- //视频解码初始化
|
|
|
|
|
- AVCodecParameters* videoCodecPar = m_fmtCtx->streams[m_videoIndex]->codecpar;
|
|
|
|
|
- if (!videoCodecPar) {
|
|
|
|
|
- qDebug() << "videocodecpar is nullptr!";
|
|
|
|
|
- return 0;
|
|
|
|
|
|
|
+ ret = avcodec_open2(m_audioPktDecoder.codecCtx, audioCodec, nullptr);
|
|
|
|
|
+ if (ret < 0) {
|
|
|
|
|
+ av_strerror(ret, m_errBuf, sizeof(m_errBuf));
|
|
|
|
|
+ qDebug() << "error info_avcodec_open2:" << m_errBuf;
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- m_videoPktDecoder.codecCtx = avcodec_alloc_context3(nullptr);
|
|
|
|
|
|
|
+ // 视频解码初始化(可选)
|
|
|
|
|
+ if (m_videoIndex >= 0) {
|
|
|
|
|
+ AVCodecParameters* videoCodecPar = m_fmtCtx->streams[m_videoIndex]->codecpar;
|
|
|
|
|
+ if (!videoCodecPar) {
|
|
|
|
|
+ qDebug() << "videocodecpar is nullptr!";
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- ret = avcodec_parameters_to_context(m_videoPktDecoder.codecCtx, videoCodecPar);
|
|
|
|
|
- if (ret < 0) {
|
|
|
|
|
- av_strerror(ret, m_errBuf, sizeof(m_errBuf));
|
|
|
|
|
- qDebug() << "error info_avcodec_parameters_to_context:" << m_errBuf;
|
|
|
|
|
- return 0;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ m_videoPktDecoder.codecCtx = avcodec_alloc_context3(nullptr);
|
|
|
|
|
|
|
|
- const AVCodec* videoCodec = avcodec_find_decoder(m_videoPktDecoder.codecCtx->codec_id);
|
|
|
|
|
- if (!videoCodec) {
|
|
|
|
|
- qDebug() << "avcodec_find_decoder failed!";
|
|
|
|
|
- return 0;
|
|
|
|
|
- }
|
|
|
|
|
- m_videoPktDecoder.codecCtx->codec_id = videoCodec->id;
|
|
|
|
|
|
|
+ ret = avcodec_parameters_to_context(m_videoPktDecoder.codecCtx, videoCodecPar);
|
|
|
|
|
+ if (ret < 0) {
|
|
|
|
|
+ av_strerror(ret, m_errBuf, sizeof(m_errBuf));
|
|
|
|
|
+ qDebug() << "error info_avcodec_parameters_to_context:" << m_errBuf;
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- ret = avcodec_open2(m_videoPktDecoder.codecCtx, videoCodec, nullptr);
|
|
|
|
|
- if (ret < 0) {
|
|
|
|
|
- av_strerror(ret, m_errBuf, sizeof(m_errBuf));
|
|
|
|
|
- qDebug() << "error info_avcodec_open2:" << m_errBuf;
|
|
|
|
|
- return 0;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const AVCodec* videoCodec = avcodec_find_decoder(m_videoPktDecoder.codecCtx->codec_id);
|
|
|
|
|
+ if (!videoCodec) {
|
|
|
|
|
+ qDebug() << "avcodec_find_decoder failed!";
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ m_videoPktDecoder.codecCtx->codec_id = videoCodec->id;
|
|
|
|
|
|
|
|
- //记录视频帧率
|
|
|
|
|
- m_vidFrameRate = av_guess_frame_rate(m_fmtCtx, m_fmtCtx->streams[m_videoIndex], nullptr);
|
|
|
|
|
|
|
+ ret = avcodec_open2(m_videoPktDecoder.codecCtx, videoCodec, nullptr);
|
|
|
|
|
+ if (ret < 0) {
|
|
|
|
|
+ av_strerror(ret, m_errBuf, sizeof(m_errBuf));
|
|
|
|
|
+ qDebug() << "error info_avcodec_open2:" << m_errBuf;
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //记录视频帧率
|
|
|
|
|
+ m_vidFrameRate = av_guess_frame_rate(m_fmtCtx, m_fmtCtx->streams[m_videoIndex], nullptr);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
setInitVal();
|
|
setInitVal();
|
|
|
|
|
|
|
|
ThreadPool::addTask(std::bind(&Decoder::demux, this, std::placeholders::_1),
|
|
ThreadPool::addTask(std::bind(&Decoder::demux, this, std::placeholders::_1),
|
|
|
std::make_shared<int>(1));
|
|
std::make_shared<int>(1));
|
|
|
- ThreadPool::addTask(std::bind(&Decoder::audioDecode, this, std::placeholders::_1),
|
|
|
|
|
- std::make_shared<int>(2));
|
|
|
|
|
- ThreadPool::addTask(std::bind(&Decoder::videoDecode, this, std::placeholders::_1),
|
|
|
|
|
- std::make_shared<int>(3));
|
|
|
|
|
|
|
+ if (m_audioIndex >= 0) {
|
|
|
|
|
+ ThreadPool::addTask(std::bind(&Decoder::audioDecode, this, std::placeholders::_1),
|
|
|
|
|
+ std::make_shared<int>(2));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (m_videoIndex >= 0) {
|
|
|
|
|
+ ThreadPool::addTask(std::bind(&Decoder::videoDecode, this, std::placeholders::_1),
|
|
|
|
|
+ std::make_shared<int>(3));
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
return 1;
|
|
return 1;
|
|
|
}
|
|
}
|
|
@@ -472,8 +488,15 @@ void Decoder::demux(std::shared_ptr<void> par)
|
|
|
av_packet_free(&pkt);
|
|
av_packet_free(&pkt);
|
|
|
//是读到文件末尾退出的才清空,强制退出不重复此操作
|
|
//是读到文件末尾退出的才清空,强制退出不重复此操作
|
|
|
if (!m_exit) {
|
|
if (!m_exit) {
|
|
|
- while (m_audioFrameQueue.size)
|
|
|
|
|
- QThread::msleep(50);
|
|
|
|
|
|
|
+ // 根据实际存在的流等待对应帧队列清空
|
|
|
|
|
+ if (m_audioIndex >= 0) {
|
|
|
|
|
+ while (m_audioFrameQueue.size)
|
|
|
|
|
+ QThread::msleep(50);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (m_videoIndex >= 0) {
|
|
|
|
|
+ while (m_videoFrameQueue.size)
|
|
|
|
|
+ QThread::msleep(50);
|
|
|
|
|
+ }
|
|
|
exit();
|
|
exit();
|
|
|
}
|
|
}
|
|
|
//qDebug() << "demuxthread exit";
|
|
//qDebug() << "demuxthread exit";
|