VCarContainer/engine/DataSourceEngine/VideoEngine.cpp

338 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "VideoEngine.h"
using namespace ai_matrix;
namespace
{
const int LOW_THRESHOLD = 128;
const int MAX_THRESHOLD = 4096;
const uint16_t DELAY_TIME = 10000;
}
VideoEngine::VideoEngine() {}
VideoEngine::~VideoEngine() {}
APP_ERROR VideoEngine::Init()
{
std::vector<DataSourceConfig> vecDataSourceConfig = Config::getins()->getAllDataSourceConfig();
if (vecDataSourceConfig.size() <= this->engineId_)
{
LogWarn << " -- " << engineName_ << "_" << engineId_ << " dataSource no set, Engine DeInit";
return APP_ERR_OK;
}
dataSourceConfig_ = vecDataSourceConfig.at(engineId_);
strPort0_ = engineName_ + "_" + std::to_string(engineId_) + "_0";
strPort1_ = engineName_ + "_" + std::to_string(engineId_) + "_1";
LogInfo << "engineId_:" << engineId_ << " VideoEngine Init ok";
return APP_ERR_OK;
}
APP_ERROR VideoEngine::DeInit()
{
ResetCamera();
LogInfo << "engineId_:" << engineId_ << " VideoEngine DeInit ok";
return APP_ERR_OK;
}
void VideoEngine::ResetCamera()
{
if (pFormatCtx_ != nullptr)
{
// clear th cache of the queue
avformat_close_input(&pFormatCtx_);
pFormatCtx_ = nullptr;
}
}
APP_ERROR VideoEngine::ConnectCamera()
{
pFormatCtx_ = CreateFormatContext(); // create context
if (pFormatCtx_ == nullptr)
{
LogError << "engineId_:" << engineId_ << " pFormatCtx_ null!";
return APP_ERR_COMM_FAILURE;
}
//0-代表输入
av_dump_format(pFormatCtx_, 0, dataSourceConfig_.strUrl.c_str(), 0);
// get stream infomation
int iRet = APP_ERR_OK;
iRet = GetStreamInfo();
if (iRet != APP_ERR_OK)
{
LogError << "engineId_:" << engineId_ << " Stream Info Check failed, iRet = " << iRet;
return APP_ERR_COMM_FAILURE;
}
return APP_ERR_OK;
}
APP_ERROR VideoEngine::GetStreamInfo()
{
if (pFormatCtx_ != nullptr)
{
iVideoStream_ = -1;
iAudioStream_ = -1;
//frameInfo_.iFrameId = 0; //帧号从0开始
for (unsigned int i = 0; i < pFormatCtx_->nb_streams; i++)
{
AVStream *inStream = pFormatCtx_->streams[i];
if (inStream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
iVideoStream_ = i;
frameInfo_.iHeight = inStream->codecpar->height;
frameInfo_.iWidth = inStream->codecpar->width;
//获取帧率,帧率的打印都在流中的两个成员.且应取平均帧率为先,为{x,0}或者{0,1}则取实时帧率
if (inStream->avg_frame_rate.den == 0 || (inStream->avg_frame_rate.num == 0 && inStream->avg_frame_rate.den == 1))
{
frameInfo_.iRate = inStream->r_frame_rate.num / inStream->r_frame_rate.den;
}
else
{
frameInfo_.iRate = inStream->avg_frame_rate.num / inStream->avg_frame_rate.den;
}
frameInfo_.iRate = frameInfo_.iRate == 0 ? 25 : frameInfo_.iRate;
LogDebug << "engineId_:" << engineId_ << " width:" << frameInfo_.iWidth << " height:" << frameInfo_.iHeight
<< " rate:" << frameInfo_.iRate << " iVideoStream_:" << iVideoStream_;
}
// else if (inStream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
// {
// iAudioStream_ = i;
// LogDebug << "engineId_:" << engineId_ << " iAudioStream_:" << iAudioStream_;
// }
}
if (iVideoStream_ == -1)
{
LogError << "engineId_:" << engineId_ << " Didn't find a video stream!";
return APP_ERR_COMM_FAILURE;
}
if (frameInfo_.iHeight < LOW_THRESHOLD || frameInfo_.iWidth < LOW_THRESHOLD ||
frameInfo_.iHeight > MAX_THRESHOLD || frameInfo_.iWidth > MAX_THRESHOLD)
{
LogError << "engineId_:" << engineId_ << " Size of frame is not supported in DVPP Video Decode!";
return APP_ERR_COMM_FAILURE;
}
pCodecParameters_ = pFormatCtx_->streams[iVideoStream_]->codecpar;
}
return APP_ERR_OK;
}
AVFormatContext *VideoEngine::CreateFormatContext()
{
// create message for stream pull
AVFormatContext *pFormatContext = nullptr;
AVDictionary *pOptions = nullptr;
// formatContext = avformat_alloc_context();
if (dataSourceConfig_.strUrl.find("rtsp:") != std::string::npos) // rtsp
{
//设置缓存大小,1080p可将值调大
av_dict_set(&pOptions, "buffer_size", "8192000", 0);
//以tcp方式打开,如果以udp方式打开将tcp替换为udp
av_dict_set(&pOptions, "rtsp_transport", "tcp", 0);
//设置超时断开连接时间,单位微秒,3000000表示3秒
av_dict_set(&pOptions, "stimeout", "3000000", 0);
//设置最大时延,单位微秒,1000000表示1秒
av_dict_set(&pOptions, "max_delay", "1000000", 0);
//自动开启线程数
av_dict_set(&pOptions, "threads", "auto", 0);
}
//av_register_all(); //注册所有支持的格式(这里一定注册这些,否则会因为协议解析问题报错!!!)
//avcodec_register_all(); //注册编解码器
//avformat_network_init(); //注册网格格式,如果为本地文件则可以去掉该代码
int iRet = avformat_open_input(&pFormatContext, dataSourceConfig_.strUrl.c_str(), nullptr, &pOptions);
if (nullptr != pOptions)
{
av_dict_free(&pOptions);
}
if (iRet != 0)
{
LogError << "engineId_:" << engineId_ << " Couldn't open input stream " << dataSourceConfig_.strUrl.c_str() << ", iRet=" << iRet;
return nullptr;
}
// pFormatContext->flags |= AVFMT_FLAG_NONBLOCK;
// pFormatContext->pb->flags |= AVIO_FLAG_NONBLOCK;
// av_dict_set(&pFormatContext->interrupt_callback.callback, "timeout", "3000", 0);
// iRet = avio_open2(&pFormatContext->pb, dataSourceConfig_.strUrl.c_str(), AVIO_FLAG_READ, NULL, NULL) < 0;
// {
// // 处理错误
// LogError << "engineId_:" << engineId_ << "avio_open2 iRet=" << iRet;
// return nullptr;
// }
iRet = avformat_find_stream_info(pFormatContext, nullptr);
if (iRet != 0)
{
LogError << "engineId_:" << engineId_ << " Couldn't find stream information, iRet = " << iRet;
return nullptr;
}
return pFormatContext;
}
//av_read_frame的中断回调函数
// int VideoEngine::InterruptCallback(void *pData)
// {
// TimeoutContext* pTimeOutCtx = (TimeoutContext*)pData;
// LogDebug << "InterruptCallback i64Timeout:" << pTimeOutCtx->i64Timeout;
// return std::chrono::duration_cast<std::chrono::milliseconds>(
// std::chrono::system_clock::now().time_since_epoch())
// .count() >= pTimeOutCtx->i64Timeout
// ? AVERROR_EXIT
// : 0;
// }
APP_ERROR VideoEngine::Process()
{
int iRet = APP_ERR_OK;
// Pull data cyclically
AVPacket pkt;
while (!isStop_)
{
//重连相机
if (bConnectFlag_)
{
iRet = ConnectCamera();
if (iRet == APP_ERR_OK)
{
LogInfo << "engineId_:" << engineId_ << " Start the stream......";
bConnectFlag_ = false;
}
else
{
// outputQueMap_[strPort1_]->push(std::static_pointer_cast<void>(std::make_shared<std::string>("摄像头连接失败!")));
ResetCamera();
bConnectFlag_ = true;
std::this_thread::sleep_for(std::chrono::seconds(3)); //3秒后重连
continue;
}
}
//设置av_read_frame中断函数 (中断函数中超过1s则中断处理)
// TimeoutContext timeoutCtx = { std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count() + 1000 };
// pFormatCtx_->interrupt_callback.callback = &VideoEngine::InterruptCallback;
// pFormatCtx_->interrupt_callback.opaque = &timeoutCtx;
av_init_packet(&pkt); //init pkt
iRet = av_read_frame(pFormatCtx_, &pkt); //需要一直读取,否则获取到的是历史数据
if (iRet != 0)
{
av_packet_unref(&pkt);
if (dataSourceConfig_.strUrl.find("rtsp:") != std::string::npos)
{
LogError << "engineId_:" << engineId_ << " Read frame failed, reconnect iRet:" << iRet;
//重连相机
ResetCamera();
bConnectFlag_ = true;
}
else
{
// LogWarn << "----- 视频播放完毕 -----";
// //重连相机
// ResetCamera();
// bConnectFlag_ = true;
std::this_thread::sleep_for(std::chrono::seconds(3)); //3秒
};
//组织数据
std::shared_ptr<ProcessData> pProcessData = std::make_shared<ProcessData>();
pProcessData->iDataSource = this->engineId_;
pProcessData->sourceFrameData.i64TimeStamp = TimeUtil::getins()->getCurrentTimeMillis(true);
pProcessData->bIsEnd = true;
//push端口0视频解码
iRet = outputQueMap_[strPort0_]->push(std::static_pointer_cast<void>(pProcessData), true);
if (iRet != APP_ERR_OK)
{
LogError << "数据推动失败,解码引擎关闭...";
}
continue;
}
if (pkt.stream_index == iVideoStream_) //只解码视频流
{
// LogDebug << "iRet:" << iRet << " pkt.size:" << pkt.size;
if (pkt.size <= 0)
{
LogError << "engineId_:" << engineId_ << " Invalid pkt.size: " << pkt.size;
av_packet_unref(&pkt);
continue;
}
if (dataSourceConfig_.strUrl.find(".mp4") != std::string::npos)
{
const char szStartCode[4] = {0, 0, 0, 1};
if (bIsAvc_ || memcmp(szStartCode, pkt.data, 4) != 0)
{ // is avc1 code, have no start code of H264
int iLen = 0;
uint8_t *p = pkt.data;
bIsAvc_ = true;
do
{ // add start_code for each NAL, one frame may have multi NALs.
iLen = ntohl(*((long *)p));
memcpy(p, szStartCode, 4);
p += 4;
p += iLen;
if (p >= pkt.data + pkt.size)
{
break;
}
} while (true);
}
}
void* pH264Buffer = nullptr;
pH264Buffer = new uint8_t[pkt.size];
memcpy(pH264Buffer, pkt.data, pkt.size);
//组织数据
std::shared_ptr<ProcessData> pProcessData = std::make_shared<ProcessData>();
pProcessData->iDataSource = this->engineId_;
pProcessData->pCodecParameters_ = this->pCodecParameters_;
pProcessData->dataSourceInfo = frameInfo_;
pProcessData->sourceFrameData.i64TimeStamp = TimeUtil::getins()->getCurrentTimeMillis(true);
pProcessData->sourceFrameData.iSize = pkt.size;
pProcessData->sourceFrameData.pData.reset(pH264Buffer, [](void* data){if(data) {delete[] data; data = nullptr;}}); //智能指针管理内存
//push端口0视频解码
iRet = outputQueMap_[strPort0_]->push(std::static_pointer_cast<void>(pProcessData), true);
if (iRet != APP_ERR_OK)
{
LogError << "数据推动失败,解码引擎关闭...";
}
}
else
{
// LogError << "engineId_:" << engineId_ << " stream err stream_index:" << pkt.stream_index;
}
av_packet_unref(&pkt); //unref
if (dataSourceConfig_.strUrl.find("rtsp:") == std::string::npos) // 如果不是rtsp定时发送
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000 / frameInfo_.iRate));
if (this->isStop_) return APP_ERR_OK;
}
}
return APP_ERR_OK;
}