CreateRecordFileThread.h 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. #ifndef _CREATERECORDFILETHREAD_H_
  2. #define _CREATERECORDFILETHREAD_H_
  3. #include "BaseRecordThread.h"
  4. #include "AudioData.h"
  5. #include "GlobalVariable.h"
  6. #include "RingQueue.hpp"
  7. #include "RingQueueManualMutex.hpp"
  8. #include <atomic>
  9. #include <mutex>
  10. #include <condition_variable>
  11. #include <QFile>
  12. #include <QDir>
  13. #include <QDateTime>
  14. /**
  15. 同一个对比项中多个一致性报警时,主通道的录音可能开始录音时间不同,
  16. 会有多个录音同时存在
  17. */
  18. struct AlarmKey_t
  19. {
  20. int compareItemID; /* 对比项ID */
  21. int roadNum; /* 对比通道编号 */
  22. EAlarmType alarmType; /* 报警类型 */
  23. QDateTime startTime; /* 报警开始时间 */
  24. AlarmKey_t(int id, int num, EAlarmType type, const QDateTime& time)
  25. : compareItemID(id), roadNum(num), alarmType(type), startTime(time) {}
  26. bool operator<(const AlarmKey_t& other) const
  27. {
  28. if(compareItemID != other.compareItemID) {
  29. return compareItemID < other.compareItemID;
  30. }
  31. if(roadNum != other.roadNum) {
  32. return roadNum < other.roadNum;
  33. }
  34. if(alarmType != other.alarmType) {
  35. return alarmType < other.alarmType;
  36. }
  37. return startTime < other.startTime;
  38. }
  39. };
  40. /**
  41. 报警文件队列的值
  42. */
  43. struct AlarmValue_t
  44. {
  45. QString fileName; /* 报警文件名 */
  46. QString fileNameEnd; /* 报警文件结束名,只有在报警结束时才有值 */
  47. eRecordState state; /* 录音状态 */
  48. QDateTime startTime; /* 录音开始时间 */
  49. QDateTime endTime; /* 录音结束时间 */
  50. int alarmStartPos = 0; /* 录音开始位置,单位:秒,报警问价的开始位置不一定是报警的开始位置 */
  51. /* wav文件信息 */
  52. WavHeader wavHeader; /* wav文件头信息 */
  53. int writtenSize = 0; /* 已经写入的大小,单位:字节 */
  54. /* 对比项和通道信息*/
  55. EAlarmType alarmType; /* 报警类型 */
  56. int numConpareItemID; /* 对比项ID */
  57. QString strCompareItemName; /* 对比项名称 */
  58. CompareItemRoadInfo_t itemRoadInfo; /* 对比项通道信息 */
  59. AlarmValue_t() : state(eRecordState::eRS_Init) {}
  60. AlarmValue_t(const QString& name, eRecordState s, const QDateTime& start, const QDateTime& end)
  61. : fileName(name), state(s), startTime(start), endTime(end) {}
  62. };
  63. /**
  64. 这个线程类记录一个长的录音文件,1小时一个文件
  65. 功能1:记录一个长的录音文件,1小时一个文件
  66. 1、如果是刚开始录音,到下一个整点记录一个文件,后续都是满1小时一个文件
  67. 2、数据先放到缓冲区中,满1分钟写一次文件
  68. 功能2:
  69. 1、录制报警文件
  70. 2、由其他线程传来报警参数,开启录制,报警结束后,结束录制
  71. 3、由于静音、过载、反相、一致性报警、噪音报警条件是和对比项相关联的,因此这里可能回同时存在多个录音,
  72. 根据对比项ID和对比项通道以及报警类型来区分
  73. 4、报警文件的命名规则:Alarm_CompareItemID_RoadNum_AlarmType_yyyyMMdd_hhmmss-yyyyMMdd_hhmmss.wav
  74. 例如:Alarm_1_1_Silent_20250101_120000-20250101_120100.wav
  75. 文件夹路径格式:
  76. 1、长录音文件夹: Application/ACAServerData/Record/yyyy-MM-dd/SoundCardID-RoadNum/Record_yyyy-MM-dd-hh-mm-ss-yyyy-MM-dd-hh-mm-ss.wav
  77. 例如:Application/2025-01-01/SoundCard1-1/Record_2025-01-01-12-00-00-2025-01-01-12-01-00.wav
  78. 2、报警录音文件夹:Application/ACAServerData/AlarmWav/yyyy-MM-dd/CompareItemID-id/Alarm_RoadNum_AlarmType_yyyyMMdd_hhmmss-yyyyMMdd_hhmmss.wav
  79. 例如:Application/2025-01-01/1-1/Alarm_1_1_Silent_20250101_120000-20250101_120100.wav
  80. */
  81. class CreateRecordFileThread : public BaseRecordThread
  82. {
  83. public:
  84. CreateRecordFileThread(RecordThreadInfo_t& threadInfo);
  85. ~CreateRecordFileThread() override;
  86. /* 设置数据 */
  87. bool setData(const AudioSrcData& srcData) override;
  88. /* 开始录制长文件 */
  89. bool startRecordLongFile(const OneCompareItemRoadInfo_t& compareItemRoadInfo);
  90. /* 停止录制长文件 */
  91. bool stopRecordLongFile(const OneCompareItemRoadInfo_t& compareItemRoadInfo);
  92. /* 开启录制报警信息 */
  93. bool startRecordAlarmFile(const AlarmInfo_t& alarmInfo);
  94. /* 停止录制,alarmInfo既是传入参数,也是传出参数,传出文件路径和开始位置 */
  95. bool stopRecordAlarmFile(AlarmInfo_t& alarmInfo);
  96. private:
  97. /* 执行任务函数 */
  98. void task() override;
  99. /* 初始化一些数据 */
  100. bool initData() override;
  101. /* 清理数据 */
  102. void clearData() override;
  103. /*===============================================================================*/
  104. /* 写入长记录文件 */
  105. bool writeLongRecordFile();
  106. /* 写入数据 */
  107. bool writeRecordFileData(const std::list<AudioSrcData*>& dataList, bool isNewFile, bool isRecordCompleted = false);
  108. /* 结束文件录制 */
  109. bool endRecordFile();
  110. /* 设置今日目录 */
  111. inline bool setTodayPath(bool isNewFile);
  112. /* 打开文件 */
  113. bool openFile(QFile& wavFile, bool isNewFile);
  114. /* 写入音频数据到文件 */
  115. bool writeAudioDataToFile(const AudioSrcData& audioData, const QString& fileName);
  116. /* 生成文件名 */
  117. inline QString generateFileName(const QDateTime& startTime, const QDateTime& endTime) const;
  118. /* 判断是否过了整点 */
  119. // inline bool isOneHourPassed();
  120. inline bool isOneHourPassed(const std::list<AudioSrcData*>& dataList);
  121. /* 更新文件信息到数据库 */
  122. void updateRecordFileInfoToDB(bool isNewFile, bool isRecordCompleted);
  123. /*===============================================================================*/
  124. /* 写入报警文件 */
  125. void writeAlarmFile();
  126. /* 创建新的文件 */
  127. void createNewAlarmFile(AlarmValue_t& value, const std::list<AudioSrcData*>& dataList);
  128. /* 写入新文件数据 */
  129. void writeNewAlarmFileData(std::list<AudioSrcData*> newList);
  130. /* 写入已经创建的报警文件数据 */
  131. void writeExistingAlarmFileData(std::list<AudioSrcData*> newList);
  132. /* 生成报警文件名 */
  133. inline QString generateAlarmFileName(const AlarmValue_t& value, bool isNewFile);
  134. /* 设置今日报警文件夹 */
  135. bool setTodayAlarmPath();
  136. /* 根据报警类型的枚举获取字符 */
  137. QString getAlarmTypeString(EAlarmType type) const;
  138. private:
  139. /* 使用条件变量进行线程睡眠,是为了防止报警时间很短,还未写入报警文件,报警就结束了,导致没有报警文件的问题
  140. 使用条件变量,一有报警即会创建文件写入 */
  141. std::condition_variable m_cond_var; /* 条件变量,用于线程间同步 */
  142. std::mutex m_mutexCondVar; /* 条件变量的互斥锁 */
  143. RingQueueManualMutex<AudioSrcData*> m_ringQueue; /* 环形队列,存储报警文件数据 */
  144. QDateTime m_alarmWrittenTime; /* 报警文件中环形队列已写入文件的时间 */
  145. /* ------------------------- 录音文件 ------------------------- */
  146. /* 对比项通道信息列表,记录当前对比项的通道信息,如果没有对比项信息就停止录音 */
  147. std::mutex m_mutexCompareItemRoadInfo;
  148. QList<OneCompareItemRoadInfo_t> m_listCompareItemRoadInfo;
  149. std::atomic_bool m_isRequireRecord = false; /* 是否需要录音 */
  150. // int32_t m_writeCriticalSize = 0; /* 写入文件的临界大小,单位:字节,缓存超过这个大小就写入文件 */
  151. // int32_t m_oneHourSize = 0; /* 一小时的音频数据大小 */
  152. QDir m_todayDir; /* 今日目录 */
  153. QDate m_todayDateRecord; /* 今日日期,记录长文件用 */
  154. QString m_wavFileName; /* 当前录音的wav文件名 */
  155. int m_openFileErrorSize = 0; /* 打开文件错误次数 */
  156. WavHeader m_wavHeader; /* 当前wav文件头信息 */
  157. int64_t m_writtenSize = 0; /* 已经写入的数据大小 */
  158. QDateTime m_writtenStartTime; /* 已经写入数据的开始时间点 */
  159. QDateTime m_writtenNowTime; /* 已经写入数据的最后时间点 */
  160. int m_numNewRecordSeconds = 0; /* 新的录音文件的秒数,新加入的录音文件的秒数,每次写完后清零 */
  161. /* ------------------------- 报警文件 ------------------------- */
  162. /* 报警文件名,key是报警信息,value是文件信息 */
  163. std::mutex m_mutexAlarmFile;
  164. std::map<AlarmKey_t, AlarmValue_t> m_mapAlarmFile;
  165. QDir m_yesterdayDir; /* 昨日目录,用来给还未录制完成的报警文件使用的 */
  166. QDir m_todayDirAlarm; /* 今日报警目录,这个目录只到日期,里面的子文件夹是对比项相关的 */
  167. QDate m_todayDateAlarm; /* 今日日期,记录报警文件用 */
  168. int m_numNewAlarmSeconds = 0; /* 新的报警文件的秒数,新加入的报警文件的秒数,每次写完后清零 */
  169. };
  170. #endif // _CREATERECORDFILETHREAD_H_