#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 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(capture.get(CV_CAP_PROP_FRAME_COUNT)) / 2); if(capture.read(mat)) { vector 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(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); } }