#include "IPlayerCore.h"
#include "FFmpegHelper.h"
#include "ffmpeg_dxva2.h"
#include <GlobalConfig.h>
#include <GlobalSignalSlot.h>
#include <util.h>
#include <ErrorCode.h>
#include <QDebug>
#include <QDateTime>
#include <QAudioOutput>
#include <QThreadPool>
#include <QEventLoop>
#include <QProcess>
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QTimer>
#include <QMap>
#include <QFuture>
#include <QtConcurrent>
#define IS_SET_FPS_WHEN_ERROR 1 ///< 当无法解析帧率时, 是否使用自定义的数值
#define FPS_CUSTOM 25 ///< 当无法解析帧率时使用的自定义数值
//AVPixelFormat GetHwFormat(AVCodecContext *s, const AVPixelFormat *pix_fmts)
//{
// //InputStream* ist = (InputStream*)s->opaque;
// IPlayerCore* iPlayerCore = (IPlayerCore*)s->opaque;
// if (nullptr == iPlayerCore)
// {
// return AV_PIX_FMT_NONE;
// }
// InputStream *ist = iPlayerCore->m_inputStream;
// if (nullptr == ist)
// {
// return AV_PIX_FMT_NONE;
// }
// ist->active_hwaccel_id = HWACCEL_DXVA2;
// ist->hwaccel_pix_fmt = AV_PIX_FMT_DXVA2_VLD;
// return ist->hwaccel_pix_fmt;
//}
AVPixelFormat GetHwFormat(AVCodecContext* s, const AVPixelFormat* pix_fmts) {
InputStream* ist = (InputStream*)s->opaque;
ist->active_hwaccel_id = HWACCEL_DXVA2;
ist->hwaccel_pix_fmt = AV_PIX_FMT_DXVA2_VLD;
return ist->hwaccel_pix_fmt;
}
IPlayerCore::IPlayerCore(QWidget *wid)
: QThread(Q_NULLPTR)
, m_mutex(QMutex::Recursive)
{
FFmpegHelper::init_ffmpeg();
init();
m_decoder = new DecodeThread(this);
if (wid)
{
m_render = new RenderThread(wid, 40);
connect(m_render, &RenderThread::draw, this, &IPlayerCore::draw);
m_render->setFFmpeg(this);
}
moveToThread(this);
start();
}
IPlayerCore::~IPlayerCore()
{
//qDebug() << "~IPlayerCore() 1";
if(m_decoder)
{
delete m_decoder;
m_decoder = Q_NULLPTR;
}
if(m_render)
{
delete m_render;
m_render = Q_NULLPTR;
}
m_mutex_output.lock();
qDeleteAll(m_outputs);
m_outputs.clear();
m_mutex_output.unlock();
m_destructed = true;
qDebug() << "~IPlayerCore() 2";
}
void IPlayerCore::init()
{
if (m_vDecodeCtx)
{
m_vDecodeCtx->opaque = nullptr;
m_vDecodeCtx->get_buffer2 = nullptr;
m_vDecodeCtx->thread_safe_callbacks = 0;
m_vDecodeCtx->get_format = nullptr;
av_freep(&m_vDecodeCtx->hwaccel_context);
avcodec_free_context(&m_vDecodeCtx);
m_vDecodeCtx = nullptr;
}
if (m_pAVFormatCtx)
{
avformat_close_input(&m_pAVFormatCtx); // 关闭媒体
m_pAVFormatCtx = Q_NULLPTR;
}
if (m_swsCtx)
{
sws_freeContext(m_swsCtx);
m_swsCtx = nullptr;
}
{
//QMutexLocker lock(&m_decodeFrameMtx);
if (m_pYuv)
{
av_frame_free(&m_pYuv); // 关闭时释放解码后的视频帧空间
m_pYuv = Q_NULLPTR;
}
}
#ifdef Enable_D3dRender
#ifdef Enable_Hardcode
// NV12
#else
if (m_frameBuf)
{
delete[] m_frameBuf;
m_frameBuf = nullptr;
m_frameSize = 0;
}
#endif
#endif
// 数据初始化
m_fps = 0;
m_isEof = false;
m_videoStream = -1;
m_size = QSize(0,0);
m_isOpened = false;
}
void IPlayerCore::open(const QString &path)
{
if (doOpen(path))
{
emit begin(Q_NULLPTR);
}
}
void IPlayerCore::pause(bool isPause)
{
if (m_render) m_render->pause(isPause);
}
bool IPlayerCore::doOpen(const QString &path)
{
QTime t = QTime::currentTime();
if (m_isOpened)
{
doClose(); // 打开前先关闭清理
}
if(path.isEmpty())
{
return false;
}
m_path = path;
AVDictionary* options = Q_NULLPTR;
if (m_path.startsWith("rtsp"))
{
av_dict_set(&options, "stimeout", "10000000", 0); // 10秒,太小的话avformat_open_input会返回Operation not permitted
av_dict_set(&options, "max_delay", "100000", 0); // 100毫秒
if (CONFIG.isRtspOverTcp())
{
av_dict_set(&options, "rtsp_transport", "tcp", 0);
}
else
{
av_dict_set(&options, "rtsp_transport", "udp", 0);
// UDP协议需要增大buffer_size,不然播放会出现花屏,10M,TCP应该不需要设置
av_dict_set(&options, "buffer_size", "10240000", 0);
}
}
AVInputFormat *iformat = Q_NULLPTR;
QByteArray burl = m_path.toUtf8();
int re = 0;
//QTime t2 = QTime::currentTime();
QMutexLocker locker(&m_mutex);
QString errMsg = util::logRtspError(m_path);
re = avformat_open_input(&m_pAVFormatCtx, burl.constData(), iformat, &options); // 打开解封装器
if (re != 0)
{
QString err = parseError(re);
qWarning() << "Open" << util::logRtspUrl(path) << "Fail:" << err;
//if ("Error number -138 occurred" == err)
//{
errMsg.append("\n网络问题:连接摄像头超时");
qInfo() << errMsg;
emit IpcParamsError(errMsg);
//}
doClose();
return false;
}
//qDebug() << "avformat_open_input time: " << t2.elapsed();
if (m_pAVFormatCtx->duration <= 0) // 如果读取到的媒体信息有误
{
// 读取文件信息(有些媒体文件在打开时读不到,需要使用此函数)
//QTime t3 = QTime::currentTime();
if (1 && CONFIG.reduceAnalyzeTime())
{
// 启用这两个选项会获取不到fps
m_pAVFormatCtx->probesize = 1024;
m_pAVFormatCtx->max_analyze_duration = 1000;
}
re = avformat_find_stream_info(m_pAVFormatCtx, Q_NULLPTR);
if (re < 0)
{
qWarning() << "Get Media Info Error:" << parseError(re);
doClose();
return false;
}
//qDebug() << "avformat_find_stream_info time: " << t3.elapsed();
}
// 打开解码器
int ret = 0;
QString vcodecName, acodecName;
int64_t videoBitrate;
for (int i = 0 ; i < m_pAVFormatCtx->nb_streams; ++i)
{
AVStream* stream = m_pAVFormatCtx->streams[i];
AVCodecParameters* codecpar = stream->codecpar;
if (codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
AVCodecID codecId = codecpar->codec_id;
AVCodec* decoder = avcodec_find_decoder(codecId);
if (!decoder)
{
qWarning("Video Codec Not Found![%d]", codecId);
doClose();
return false;
}
//从视频流中拷贝参数到codecCtx
m_vDecodeCtx = avcodec_alloc_context3(decoder);
if ((ret = avcodec_parameters_to_context(m_vDecodeCtx, codecpar)) < 0)
{
qDebug() << "Video avcodec_parameters_to_context failed,error code: " << ret;
doClose();
return false;
}
m_vDecodeCtx->flags |= AV_CODEC_FLAG_LOW_DELAY;
// 尝试显示缩略图
m_vDecodeCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
if (m_hwDecode)
{
if (NULL == m_windowHandle)
{
qDebug() << QStringLiteral("Window handle NUll, check code");
doClose();
return false;
}
// dxva初始化需要,demo是(1920x1088)
m_vDecodeCtx->coded_width = m_vDecodeCtx->width;
m_vDecodeCtx->coded_height = m_vDecodeCtx->height;
//m_vDecodeCtx->thread_count = 1; // Multithreading is apparently not compatible with hardware decoding
//m_inputStream = new InputStream();
//m_inputStream->hwaccel_id = HWACCEL_AUTO;
//m_inputStream->active_hwaccel_id = HWACCEL_AUTO;
//m_inputStream->hwac