hehaoyang
ff66a2e0d6
1. 删除图片存储到本地的方式; 2. 取流方式由Opencv修改为FFmpeg方式; 3. 解码后的数据直接转为RK_FORMAT_YCbCr_422_SP格式发送给算法; 4. 视频裸流数据存储在内存中,保存30s; 5. 报警图片从报警录像视频中获取;
450 lines
15 KiB
C++
450 lines
15 KiB
C++
#include "HxVideoDevice.h"
|
|
#include "HxDataBase.h"
|
|
#include "HxUtils.h"
|
|
|
|
/* 算法报警处理函数 */
|
|
void algorithm_detection_callback(int nDataChannel, ObjectTrackEventResult *pObjectTrackEventResult, void *pPrivData)
|
|
{
|
|
Q_UNUSED(nDataChannel);
|
|
Q_UNUSED(pPrivData);
|
|
|
|
int algorithm_type = 0, channel = 0;
|
|
QDateTime current_time = QDateTime::currentDateTime();
|
|
|
|
if (nDataChannel == 0)
|
|
{
|
|
algorithm_type = ALGORITHM_TYPE_ADAS;
|
|
}
|
|
else if (nDataChannel == 2)
|
|
{
|
|
algorithm_type = ALGORITHM_TYPE_DSM;
|
|
}
|
|
else if (nDataChannel == 1 || /* BSD-右前 */
|
|
nDataChannel == 3 || /* BSD-右后 */
|
|
nDataChannel == 4 || /* BSD-左前 */
|
|
nDataChannel == 5 || /* BSD-左后 */
|
|
nDataChannel == 6 || /* BSD-前 */
|
|
nDataChannel == 7) /* BSD-后 */
|
|
{
|
|
algorithm_type = ALGORITHM_TYPE_BSD;
|
|
|
|
switch (nDataChannel)
|
|
{
|
|
case 6:
|
|
channel = 0;
|
|
break;
|
|
case 7:
|
|
channel = 1;
|
|
break;
|
|
case 4:
|
|
channel = 2;
|
|
break;
|
|
case 1:
|
|
channel = 3;
|
|
break;
|
|
case 5:
|
|
channel = 4;
|
|
break;
|
|
case 3:
|
|
channel = 5;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < EVENT_WARN_NUM; i++)
|
|
{
|
|
if ((pObjectTrackEventResult->nEventType >> i & 0x01) == 0x01)
|
|
{
|
|
HxTaskDispatch::alarm_upload_event(algorithm_type,
|
|
current_time,
|
|
channel,
|
|
i + 1,
|
|
pObjectTrackEventResult->nDangerLevel,
|
|
pObjectTrackEventResult->objInfo,
|
|
pObjectTrackEventResult->nObjectNumber,
|
|
pObjectTrackEventResult->tFaceLandMarks,
|
|
pObjectTrackEventResult->nFaceLandMarksNum,
|
|
pObjectTrackEventResult->nLeftLineType,
|
|
pObjectTrackEventResult->nRightLineType);
|
|
}
|
|
}
|
|
}
|
|
|
|
HxVideoDevice::HxVideoDevice(void)
|
|
{
|
|
m_detect_frame_buffer.u32Width = 1280;
|
|
m_detect_frame_buffer.u32Height = 720;
|
|
m_detect_frame_buffer.pu8VirAddr = (unsigned char *)calloc(m_detect_frame_buffer.u32Width * m_detect_frame_buffer.u32Height * 3, sizeof(unsigned char));
|
|
}
|
|
|
|
HxVideoDevice::~HxVideoDevice(void)
|
|
{
|
|
free(m_detect_frame_buffer.pu8VirAddr);
|
|
}
|
|
|
|
void HxVideoDevice::set(int type, QString address)
|
|
{
|
|
m_type = type;
|
|
m_address = address;
|
|
#if USE_ALGORITHM
|
|
auto result = MvSetAlgResultFuncCallback(m_type, algorithm_detection_callback, nullptr);
|
|
if (result != 0)
|
|
return;
|
|
#endif
|
|
|
|
start();
|
|
}
|
|
|
|
void HxVideoDevice::set(int type, QString address, BsdWarnRegion region)
|
|
{
|
|
Q_UNUSED(region);
|
|
|
|
m_type = type;
|
|
m_address = address;
|
|
m_detection_status = false;
|
|
|
|
#if USE_ALGORITHM
|
|
auto result = MvSetAlgResultFuncCallback(m_type, algorithm_detection_callback, nullptr);
|
|
if (result != 0)
|
|
return;
|
|
|
|
MvSetBsdWarnRegion(m_type, ®ion);
|
|
#endif
|
|
|
|
start();
|
|
}
|
|
|
|
void HxVideoDevice::set(bool status) { m_detection_status = status; }
|
|
|
|
bool HxVideoDevice::determine_alarm_detection_timestamp(int event_type)
|
|
{
|
|
if (!m_alarm_detection_timestamps.contains(event_type))
|
|
{
|
|
m_alarm_detection_timestamps.insert(event_type, QDateTime::currentDateTime());
|
|
return true;
|
|
}
|
|
|
|
return m_alarm_detection_timestamps[event_type].secsTo(QDateTime::currentDateTime()) > HxDataBase::alarm_protect_timestamp.at(event_type).toInt();
|
|
}
|
|
|
|
void HxVideoDevice::create_alarm_data(int event_type, QString filename)
|
|
{
|
|
/* 更新报警检测时间戳 */
|
|
m_alarm_detection_timestamps[event_type] = QDateTime::currentDateTime();
|
|
|
|
/* 如果创建队列中存在该文件, 则不在重复创建 */
|
|
if (m_records.contains(filename))
|
|
return;
|
|
|
|
/* 创建线程 */
|
|
QtConcurrent::run([=](QDateTime _alarm_timestamp, QString _filename)
|
|
{
|
|
Mat mat;
|
|
int frame_id = 0;
|
|
VideoCapture capture;
|
|
HxVideoWriter video_writer;
|
|
|
|
/* 添加文件名称, 表示正在创建 */
|
|
m_records_mutex.lock();
|
|
m_records.append(filename);
|
|
m_records_mutex.unlock();
|
|
|
|
sleep(HxDataBase::recording_prepend_time);
|
|
|
|
QQueue<HxVideoFrame> frames;
|
|
|
|
m_prerecorded_frames_mutex.lock();
|
|
for (auto it = m_prerecorded_frames.begin(); it != m_prerecorded_frames.end(); ++it) {
|
|
if(it->time > _alarm_timestamp.addSecs(-1 * (HxDataBase::recording_prepend_time + 1)) && it->time < _alarm_timestamp.addSecs(HxDataBase::recording_prepend_time + 1))
|
|
frames.enqueue(it->copy());
|
|
}
|
|
m_prerecorded_frames_mutex.unlock();
|
|
|
|
QString image_path = QString("%1/%2.jpg").arg(TEMPORARY_RECORD_DIRECTORY, _filename);
|
|
QString record_path = QString("%1/%2.avi").arg(TEMPORARY_RECORD_DIRECTORY, _filename);
|
|
QString _record_path = QString("%1/%2.mp4").arg(TEMPORARY_RECORD_DIRECTORY, _filename);
|
|
|
|
HxLog::append("recording", QString("%1 frames count=%2").arg(_record_path).arg(frames.count()));
|
|
|
|
while(!frames.isEmpty())
|
|
{
|
|
frame_id++;
|
|
|
|
auto frame = frames.dequeue();
|
|
|
|
/* 如果文件未被打开 */
|
|
if(!video_writer.open_status)
|
|
{
|
|
/* 查找关键帧 */
|
|
if (frame.packet->flags & AV_PKT_FLAG_KEY)
|
|
{
|
|
HxLog::append("recording", QString("%1 find %2 (AV_PKT_FLAG_KEY)").arg(_record_path).arg(frame_id));
|
|
|
|
/* 打开(创建)文件 */
|
|
video_writer.open(frame.ifmt_ctx, record_path);
|
|
if(!video_writer.open_status)
|
|
{
|
|
video_writer.close();
|
|
|
|
frame.free();
|
|
|
|
goto END;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 在打开的状态, 开始写入帧数据 */
|
|
if(video_writer.open_status)
|
|
{
|
|
video_writer.send(frame.packet);
|
|
}
|
|
|
|
frame.free();
|
|
|
|
msleep(10);
|
|
}
|
|
|
|
if(video_writer.open_status)
|
|
{
|
|
video_writer.close();
|
|
|
|
if(!QFile::exists(record_path))
|
|
goto END;
|
|
|
|
HxLog::append("recording", QString("%1 create success").arg(_record_path));
|
|
|
|
capture = VideoCapture(record_path.toUtf8().data());
|
|
|
|
if(capture.isOpened())
|
|
{
|
|
capture.set(CAP_PROP_POS_FRAMES, static_cast<int>(capture.get(CV_CAP_PROP_FRAME_COUNT)) / 2);
|
|
|
|
if(capture.read(mat))
|
|
{
|
|
vector <int> compression_params;
|
|
compression_params.push_back(IMWRITE_JPEG_QUALITY);
|
|
compression_params.push_back(90);
|
|
if(imwrite(image_path.toUtf8().data(), mat, compression_params))
|
|
HxTaskDispatch::enqueue_upload_file(_filename+".jpg");
|
|
}
|
|
|
|
capture.release();
|
|
}
|
|
|
|
QFile::rename(record_path, _record_path);
|
|
|
|
HxTaskDispatch::enqueue_upload_file(_filename+".mp4");
|
|
|
|
END:
|
|
while(!frames.isEmpty())
|
|
frames.dequeue().free();
|
|
|
|
/* 删除文件表示, 待验证 */
|
|
m_records_mutex.lock();
|
|
m_records.removeAll(_filename);
|
|
m_records_mutex.unlock();
|
|
} },
|
|
m_alarm_detection_timestamps[event_type], filename);
|
|
}
|
|
|
|
void HxVideoDevice::test(void)
|
|
{
|
|
ObjectTrackEventResult pObjectTrackEventResult;
|
|
pObjectTrackEventResult.nFrameId = 0;
|
|
pObjectTrackEventResult.nEventType = m_type == 0 ? (ObjectEventType)(EVENT_PCW | EVENT_HMW) : EVENT_YAWN;
|
|
pObjectTrackEventResult.nObjectNumber = 1;
|
|
pObjectTrackEventResult.objInfo[0].nDetectType = PEDESTRIAN_TYPE;
|
|
algorithm_detection_callback(0, &pObjectTrackEventResult, nullptr);
|
|
}
|
|
|
|
void HxVideoDevice::read_process(void)
|
|
{
|
|
while (true)
|
|
{
|
|
msleep(5000);
|
|
|
|
int error = 0;
|
|
|
|
AVDictionary *avdic = nullptr;
|
|
|
|
/* 减少卡顿或者花屏现象,相当于增加或扩大了缓冲区,给予编码和发送足够的时间 */
|
|
av_dict_set(&avdic, "buffer_size", "1024000", 0);
|
|
// /* 减少采集缓存 */
|
|
// av_dict_set(&avdic, "preset", "fast", 0);
|
|
// av_dict_set(&avdic, "tune", "zerolatency", 0);
|
|
// /* 减少音频采集sampels数量 */
|
|
// av_dict_set(&avdic, "audio_buffer_size","30", 0);
|
|
av_dict_set(&avdic, "stimeout", "500000", 0);
|
|
av_dict_set(&avdic, "max_delay", "500000", 0);
|
|
av_dict_set(&avdic, "rtsp_transport", "tcp", 0);
|
|
|
|
avformat_network_init();
|
|
|
|
ifmt_ctx = avformat_alloc_context();
|
|
|
|
error = avformat_open_input(&ifmt_ctx, m_address.toUtf8().data(), nullptr, &avdic);
|
|
|
|
av_dict_free(&avdic);
|
|
|
|
if (error != 0)
|
|
{
|
|
avformat_free_context(ifmt_ctx);
|
|
|
|
HxLog::append("videolivestream", QString("type=%1, avformat_open_input failed, errorcode=%2").arg(m_type).arg(error));
|
|
|
|
continue;
|
|
}
|
|
|
|
if (avformat_find_stream_info(ifmt_ctx, nullptr) < 0)
|
|
{
|
|
avformat_close_input(&ifmt_ctx);
|
|
|
|
avformat_free_context(ifmt_ctx);
|
|
|
|
HxLog::append("videolivestream", QString("type=%1, avformat_find_stream_info failed").arg(m_type));
|
|
|
|
return;
|
|
}
|
|
|
|
av_dump_format(ifmt_ctx, 0, m_address.toUtf8().data(), 0);
|
|
|
|
auto m_video_stream_index = -1;
|
|
for (uint i = 0; i < ifmt_ctx->nb_streams; i++)
|
|
{
|
|
if (ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
|
|
{
|
|
m_video_stream_index = static_cast<int>(i);
|
|
|
|
m_video_fps = ifmt_ctx->streams[i]->avg_frame_rate.num;
|
|
|
|
HxLog::append("videolivestream", QString("type=%1, video_fps=%2").arg(m_type).arg(m_video_fps));
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (m_video_stream_index == -1)
|
|
{
|
|
avformat_close_input(&ifmt_ctx);
|
|
avformat_free_context(ifmt_ctx);
|
|
|
|
HxLog::append("videolivestream", QString("type=%1, not found video stream").arg(m_type));
|
|
|
|
return;
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
AVPacket packet;
|
|
|
|
if (av_read_frame(ifmt_ctx, &packet) < 0)
|
|
break;
|
|
|
|
m_decoding_frames_mutex.lock();
|
|
m_decoding_frames.enqueue(HxVideoFrame(ifmt_ctx, &packet));
|
|
m_decoding_frames_mutex.unlock();
|
|
|
|
m_prerecorded_frames_mutex.lock();
|
|
m_prerecorded_frames.enqueue(HxVideoFrame(ifmt_ctx, &packet));
|
|
m_prerecorded_frames_mutex.unlock();
|
|
|
|
av_packet_unref(&packet);
|
|
|
|
msleep(1000 / m_video_fps);
|
|
}
|
|
|
|
if (ifmt_ctx != nullptr)
|
|
{
|
|
avformat_close_input(&ifmt_ctx);
|
|
avformat_free_context(ifmt_ctx);
|
|
ifmt_ctx = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
void HxVideoDevice::decoding_process(void)
|
|
{
|
|
Mat mat;
|
|
int m_frame_id = 0;
|
|
|
|
while (true)
|
|
{
|
|
msleep(10);
|
|
|
|
if (m_decoding_frames.isEmpty())
|
|
continue;
|
|
|
|
m_decoding_frames_mutex.lock();
|
|
auto frame = m_decoding_frames.dequeue();
|
|
m_decoding_frames_mutex.unlock();
|
|
|
|
if (!m_video_decoder.status)
|
|
{
|
|
#if USE_ALGORITHM
|
|
m_video_decoder.initialization();
|
|
#else
|
|
if (ifmt_ctx != nullptr)
|
|
m_video_decoder.initialization(ifmt_ctx);
|
|
#endif
|
|
if (!m_video_decoder.status)
|
|
{
|
|
HxLog::append("videolivestream", QString("type=%1, decoder initialization failed").arg(m_type));
|
|
|
|
goto frame_free;
|
|
}
|
|
}
|
|
|
|
/* 获取YUV数据 */
|
|
m_video_decoder.decode(frame.packet, &mat);
|
|
|
|
if (mat.data != nullptr)
|
|
{
|
|
/* 是否开始进行分析 */
|
|
if (m_detection_status)
|
|
{
|
|
/* 获取车速 */
|
|
auto car_info = HxTaskDispatch::get_car_info();
|
|
|
|
if (car_info->fVelocity >= 10)
|
|
{
|
|
m_detect_frame_buffer.nFrameId = m_frame_id; // 帧号
|
|
m_detect_frame_buffer.u64PTS = QDateTime::currentMSecsSinceEpoch(); // 时间戳(毫秒)
|
|
m_detect_frame_buffer.pu8VirAddr = mat.data;
|
|
|
|
#if USE_ALGORITHM
|
|
MvObjectEventDetect(this->m_type, &m_detect_frame_buffer, car_info);
|
|
#endif
|
|
m_frame_id++;
|
|
}
|
|
}
|
|
}
|
|
frame_free:
|
|
frame.free();
|
|
}
|
|
}
|
|
|
|
void HxVideoDevice::run()
|
|
{
|
|
/* 创建 视频读取线程 */
|
|
QtConcurrent::run(this, &HxVideoDevice::read_process);
|
|
|
|
/* 创建 录像 线程 */
|
|
QtConcurrent::run(this, &HxVideoDevice::decoding_process);
|
|
|
|
while (true)
|
|
{
|
|
/* 判断是否超过预录时长 */
|
|
if (!m_prerecorded_frames.isEmpty())
|
|
{
|
|
while (QDateTime::currentDateTime() > m_prerecorded_frames.first().time.addSecs(60))
|
|
{
|
|
m_prerecorded_frames_mutex.lock();
|
|
m_prerecorded_frames.dequeue().free();
|
|
m_prerecorded_frames_mutex.unlock();
|
|
}
|
|
}
|
|
|
|
msleep(1000);
|
|
}
|
|
}
|