cameraplayer.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. #include "cameraplayer.h"
  2. #include <QThread>
  3. #include <QDebug>
  4. #include "LHQLogAPI.h"
  5. CameraPlayer::CameraPlayer(QObject *parent) : QObject(parent)
  6. {
  7. // qDebug() << "主线程ID:" << QThread::currentThreadId();
  8. m_yuvQueue = new RingQueue<Image_YUV420*>(10);
  9. m_yuvQueue->setDefaultValue(nullptr);
  10. m_player = new PlayerGLWidget();
  11. /* 连接信号和槽 */
  12. connect(&m_frameTimer, &QTimer::timeout, this, &CameraPlayer::do_updateFrame);
  13. }
  14. CameraPlayer::~CameraPlayer()
  15. {
  16. stopRealPlay();
  17. /* 退出登录 */
  18. NET_DVR_Logout(m_loginID);
  19. /* 释放SDK资源,重复清除资源会怎么样 */
  20. NET_DVR_Cleanup();
  21. }
  22. /* 设置摄像机信息 */
  23. bool CameraPlayer::initCamera(QString cameraIP, int cameraPort, QString cameraUser, QString cameraPwd)
  24. {
  25. /* 初始化海康摄像头SDK */
  26. if(!NET_DVR_Init())
  27. {
  28. LH_WRITE_ERROR("NET_DVR_Init 失败!");
  29. return false;
  30. }
  31. /* 设置超时时间 */
  32. NET_DVR_SetConnectTime(3000,3);
  33. NET_DVR_SetReconnect();
  34. /* 设置异常消息回调函数 */
  35. #ifdef Q_OS_LINUX
  36. NET_DVR_SetExceptionCallBack_V30(0, nullptr, exceptionCallBack, nullptr);
  37. #endif
  38. /* 用户登录,同步登陆 */
  39. NET_DVR_USER_LOGIN_INFO loginInfo = {0};
  40. /* 返回的设备信息,包括序列号、设备类型、通道号等 */
  41. NET_DVR_DEVICEINFO_V40 deviceInfo = {0};
  42. strncpy(loginInfo.sDeviceAddress, cameraIP.toStdString().c_str(), cameraIP.count());
  43. strncpy(loginInfo.sUserName, cameraUser.toStdString().c_str(), cameraUser.count());
  44. strncpy(loginInfo.sPassword, cameraPwd.toStdString().c_str(), cameraPwd.count());
  45. loginInfo.wPort = cameraPort; /* 端口 */
  46. loginInfo.bUseAsynLogin = 0; /* 0是同步登陆, 1是异步登录 */
  47. LH_WRITE_LOG_DEBUG(QString("摄像机登录信息: %1:%2:%3:%4")
  48. .arg(loginInfo.sDeviceAddress)
  49. .arg(loginInfo.wPort)
  50. .arg(loginInfo.sUserName)
  51. .arg(loginInfo.sPassword));
  52. /* 这里是同步登陆,返回值就是设备操作ID,异步登录需要在回调函数中取出返回的ID */
  53. m_loginID = NET_DVR_Login_V40(&loginInfo, &deviceInfo);
  54. if(m_loginID < 0)
  55. {
  56. LH_WRITE_ERROR(QString("登录摄像机 %1:%2 失败,错误代码: %3").arg(cameraIP).arg(cameraPort).arg(NET_DVR_GetLastError()));
  57. NET_DVR_Cleanup(); /* 需不需要在这里清除资源? */
  58. return false;
  59. }
  60. /* 取出通道号 */
  61. m_camInfo.AChannelNum = 0;
  62. m_camInfo.AChannelStart = 0;
  63. m_camInfo.DChannelNum = 0;
  64. m_camInfo.DChannelStart = 0;
  65. m_camInfo.AChannelNum = deviceInfo.struDeviceV30.byChanNum; /* 模拟通道个数 */
  66. m_camInfo.AChannelStart = deviceInfo.struDeviceV30.byStartChan; /* 模拟通道的起始通道号 */
  67. unsigned char h8 = deviceInfo.struDeviceV30.byIPChanNum; /* 数字通道的最大个数需要组合起来 */
  68. unsigned char l8 = deviceInfo.struDeviceV30.byHighDChanNum;
  69. m_camInfo.DChannelNum = h8 * 256 + l8;
  70. m_camInfo.DChannelStart = deviceInfo.struDeviceV30.byStartDChan;/* 数字通道起始号 */
  71. LH_WRITE_LOG_DEBUG(QString("最大模拟通道号数:%1,起始号:%2").arg(m_camInfo.AChannelNum).arg(m_camInfo.AChannelStart));
  72. LH_WRITE_LOG_DEBUG(QString("最大数字通道号数:%1,起始号:%2").arg(m_camInfo.DChannelNum).arg(m_camInfo.DChannelStart));
  73. LH_WRITE_LOG("****** HK SDK初始化完成! ******");
  74. return true;
  75. }
  76. /**
  77. * @brief 实时播放监控,视频流在回调函数中
  78. *
  79. * @param Channel
  80. * @return true
  81. * @return false
  82. */
  83. bool CameraPlayer::realPlay(int channel)
  84. {
  85. /* 启动预览并设置回调数据流 */
  86. NET_DVR_PREVIEWINFO playInfo = {0};
  87. playInfo.hPlayWnd = 0; //需要SDK解码时句柄设为有效值,仅取流不解码时可设为空
  88. playInfo.lChannel = channel; //预览通道号
  89. playInfo.dwStreamType = 0; //0-主码流,1-子码流,2-码流3,3-码流4,以此类推
  90. playInfo.dwLinkMode = 0; //0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4-RTP/RTSP,5-RSTP/HTTP
  91. playInfo.bBlocked = 1; //0- 非阻塞取流,1- 阻塞取流
  92. /* 设置回调函数 */
  93. m_realPlayHandle = NET_DVR_RealPlay_V40(m_loginID, &playInfo, realDataCallBack, this);
  94. if (m_realPlayHandle < 0)
  95. {
  96. LH_WRITE_ERROR(QString("NET_DVR_RealPlay_V40()调用错误,错误代码:%1").arg(NET_DVR_GetLastError()));
  97. return false;
  98. }
  99. /* 设置回调函数 */
  100. // NET_DVR_SetStandardDataCallBack(m_realPlayHandle, realDataCallBackStandard, 0);
  101. // NET_DVR_SetRealDataCallBackEx(m_realPlayHandle, realDataCallBack, nullptr);
  102. return true;
  103. }
  104. /* 关闭预览 */
  105. void CameraPlayer::stopRealPlay()
  106. {
  107. //关闭预览
  108. NET_DVR_StopRealPlay(m_realPlayHandle);
  109. LH_WRITE_LOG("实时获取数据结束");
  110. }
  111. /* 开始播放预览 */
  112. void CameraPlayer::startPlay()
  113. {
  114. if(m_frameRate == 0)
  115. {
  116. LH_WRITE_ERROR("帧率为0,无法播放");
  117. return;
  118. }
  119. /* 开启定时器 */
  120. m_frameTimer.setTimerType(Qt::PreciseTimer);
  121. m_frameTimer.setInterval(1000 / m_frameRate);
  122. m_frameTimer.start();
  123. }
  124. /* 设置播放窗口父指针 */
  125. void CameraPlayer::setPlayerParent(QWidget* playWnd)
  126. {
  127. m_player->setParent(playWnd);
  128. }
  129. /* 设置播放窗口大小 */
  130. void CameraPlayer::setPlayWndSize(int width, int height)
  131. {
  132. m_player->resize(width, height);
  133. }
  134. /* 更新一帧数据 */
  135. void CameraPlayer::do_updateFrame()
  136. {
  137. /* 获取一帧 */
  138. if(m_yuvQueue->isEmpty())
  139. {
  140. LH_WRITE_LOG("环形队列为空,无法更新一帧数据");
  141. return;
  142. }
  143. /* 以非阻塞的方式先获取一帧数据,获取成功后再出队 */
  144. auto one = m_yuvQueue->front_pop_NoBlock();
  145. if(one != nullptr)
  146. {
  147. m_player->updateFrame(*one);
  148. delete one;
  149. }
  150. }
  151. /**
  152. * @brief 异常回调函数
  153. *
  154. * @param type 异常类型,目前就知道以下几个
  155. * @param userID
  156. * @param handle
  157. * @param user
  158. */
  159. void CameraPlayer::exceptionCallBack(unsigned int type, int userID,int handle,void* user)
  160. {
  161. /* 异常代码是16进制的 */
  162. LH_WRITE_ERROR(QString("调用了异常回调函数,异常代码: %1").arg(type));
  163. switch(type)
  164. {
  165. /* 报警上传时网络异常 */
  166. case EXCEPTION_ALARM:
  167. LH_WRITE_ERROR("报警上传时网络异常!!!");
  168. //TODO: 关闭报警上传
  169. break;
  170. /* 网络预览时异常 */
  171. case EXCEPTION_PREVIEW:
  172. LH_WRITE_ERROR("网络预览时网络异常!!!");
  173. //TODO: 关闭网络预览
  174. break;
  175. /* 预览时重连 */
  176. case EXCEPTION_RECONNECT:
  177. break;
  178. case EXCEPTION_PLAYBACK:
  179. LH_WRITE_ERROR("回放异常!");
  180. break;
  181. default:
  182. break;
  183. }
  184. }
  185. /**
  186. * @brief 实时预览回调函数,没有解码的数据流,这函数会运行在子线程中
  187. *
  188. * @param realHandle 操作句柄
  189. * @param dataType 数据类型
  190. * @param pBuffer 数据指针
  191. * @param bufSize 数据大小
  192. * @param user
  193. */
  194. void CameraPlayer::realDataCallBack(LONG realHandle, DWORD dataType, BYTE *pBuffer,DWORD bufSize,void* user)
  195. {
  196. // LH_WRITE_LOG_DEBUG(QString("realDataCallBack接收到了数据,数据大小为:%1").arg(bufSize));
  197. // LH_WRITE_LOG_DEBUG(QString("realDataCallBack所在的线程: %1").arg(QThread::currentThreadId()));
  198. // qDebug() << "realDataCallBack所在的线程:" << QThread::currentThreadId();
  199. /* 转换传入的指针,是这个类本身 */
  200. auto cameraPlayer = static_cast<CameraPlayer*>(user);
  201. LONG playPort = cameraPlayer->m_playPort;
  202. PlayM4_GetPort(&realHandle);
  203. switch (dataType)
  204. {
  205. /* 系统头数据 */
  206. case NET_DVR_SYSHEAD:
  207. LH_WRITE_LOG_DEBUG("系统头数据");
  208. /* 获取播放库未使用的通道号,最多貌似可以有500个 */
  209. if(!PlayM4_GetPort(&playPort))
  210. {
  211. LH_WRITE_ERROR("获取播放库未使用的通道号失败");
  212. break;
  213. }
  214. /* 打开流 */
  215. if(bufSize > 0)
  216. {
  217. cameraPlayer->m_playPort = playPort;
  218. /* 最后一个参数是 设置播放器中存放数据流的缓冲区大小,不能太大也不能太小 */
  219. if(!PlayM4_OpenStream(playPort, pBuffer, bufSize, 1024 * 1024))
  220. {
  221. auto ret = PlayM4_GetLastError(playPort);
  222. LH_WRITE_ERROR(QString("打开流失败,错误代码: %1").arg(ret));
  223. break;
  224. }
  225. /* 设置解码回调函数 */
  226. bool ret2 = PlayM4_SetDecCallBackMend(playPort, DecCallBack, cameraPlayer);
  227. if(!ret2)
  228. {
  229. auto ret3 = PlayM4_GetLastError(playPort);
  230. LH_WRITE_ERROR(QString("设置解码回调函数失败,错误代码: %1").arg(ret3));
  231. break;
  232. }
  233. /* 打开视频解码,不传入播放窗口 */
  234. if(!PlayM4_Play(playPort, 0))
  235. {
  236. auto ret4 = PlayM4_GetLastError(playPort);
  237. LH_WRITE_ERROR(QString("打开视频解码失败,错误代码: %1").arg(ret4));
  238. break;
  239. }
  240. /* 打开音频解码,需要码流是复合流 */
  241. if(!PlayM4_PlaySound(playPort))
  242. {
  243. auto ret5 = PlayM4_GetLastError(playPort);
  244. LH_WRITE_ERROR(QString("打开音频解码失败,错误代码: %1").arg(ret5));
  245. break;
  246. }
  247. }else {
  248. LH_WRITE_ERROR("系统头数据大小为0");
  249. }
  250. break;
  251. /* 视频流数据(包括复合流和音视频分开的视频流数据) */
  252. case NET_DVR_STREAMDATA:
  253. // LH_WRITE_LOG_DEBUG("音视频复合流数据");
  254. /* 解码数据 */
  255. if(bufSize > 0 && playPort != -1)
  256. {
  257. auto ret = PlayM4_InputData(playPort, pBuffer, bufSize);
  258. if(!ret)
  259. {
  260. LH_WRITE_ERROR("解码数据失败");
  261. break;
  262. }
  263. }
  264. break;
  265. /* 其他数据 */
  266. case NET_DVR_AUDIOSTREAMDATA:
  267. LH_WRITE_LOG_DEBUG("音频流数据");
  268. break;
  269. case NET_DVR_PRIVATE_DATA:
  270. LH_WRITE_LOG_DEBUG("私有数据");
  271. break;
  272. default:
  273. LH_WRITE_LOG_DEBUG("其他数据");
  274. break;
  275. }
  276. }
  277. /* 标准数据流的预览回调函数 */
  278. void CameraPlayer::realDataCallBackStandard(LONG realHandle, DWORD dataType, BYTE *pBuffer,DWORD bufSize,DWORD user)
  279. {
  280. LH_WRITE_LOG_DEBUG(QString("realDataCallBackStandard接收到了数据,数据大小为:%1").arg(bufSize));
  281. switch(dataType)
  282. {
  283. case NET_DVR_SYSHEAD:
  284. LH_WRITE_LOG("系统头数据");
  285. break;
  286. case NET_DVR_STREAMDATA:
  287. LH_WRITE_LOG("音视频流复合数据");
  288. break;
  289. case NET_DVR_STD_VIDEODATA:
  290. LH_WRITE_LOG("标准视频流数据");
  291. break;
  292. case NET_DVR_STD_AUDIODATA:
  293. LH_WRITE_LOG("标准音频流数据");
  294. break;
  295. default:
  296. LH_WRITE_LOG("其他数据");
  297. break;
  298. }
  299. }
  300. /**
  301. * @brief 解码回调函数,这个函数会运行在一个新的子线程中
  302. *
  303. * @param nPort 播放端口号
  304. * @param pBuf 需要解码的数据
  305. * @param nSize 数据大小
  306. * @param pFrameInfo 流格式
  307. * @param nUser 用户数据指针
  308. * @param nReserved2
  309. */
  310. void CameraPlayer::DecCallBack(int nPort,char * pBuf,int nSize,FRAME_INFO * pFrameInfo, void* nUser,int nReserved2)
  311. {
  312. auto player = static_cast<CameraPlayer*>(nUser);
  313. // LH_WRITE_LOG(QString("解码回调函数,解码通道号: %1 %2").arg(nPort).arg(player->m_playPort));
  314. // qDebug() << "DecCallBack所在的线程:" << QThread::currentThreadId();
  315. /* 获取视频帧率,如果是音频,就是采样率 */
  316. int frameRate = pFrameInfo->nFrameRate;
  317. /* 视频数据是 T_YV12 音频数据是 T_AUDIO16 */
  318. if(pFrameInfo->nType == T_YV12)
  319. {
  320. /* 获取视频帧率 */
  321. if(player->m_frameRate == 0)
  322. {
  323. player->m_frameRate = frameRate;
  324. }
  325. int width = pFrameInfo->nWidth;
  326. int height = pFrameInfo->nHeight;
  327. LH_WRITE_LOG_DEBUG(QString("视频宽高: %1 x %2, 视频帧率: %3").arg(width).arg(height).arg(frameRate));
  328. /* 截图标志位,保存图片 */
  329. /* 转换成yuv,保存到环形队列中 */
  330. Image_YUV420* image = new Image_YUV420();
  331. player->YV12ToYUV420((unsigned char*)pBuf, *image, width, height);
  332. /* 判断环形队列是否满了,满了就出队一个 */
  333. if(player->m_yuvQueue->isFull())
  334. {
  335. LH_WRITE_LOG_DEBUG("环形队列满了,出队一个");
  336. auto one = player->m_yuvQueue->front_pop();
  337. delete one;
  338. }
  339. player->m_yuvQueue->push_NoBlock(image);
  340. }
  341. else if(pFrameInfo->nType == T_AUDIO16)
  342. {
  343. // LH_WRITE_LOG("音频数据");
  344. } else
  345. {
  346. LH_WRITE_LOG("其他数据");
  347. }
  348. }
  349. /* YV12转RGB888 */
  350. void CameraPlayer::YV12ToRGB888(unsigned char *pYV12Data, unsigned char *pRGB24Data, int width, int height)
  351. {
  352. int ySize = width * height;
  353. int uvSize = ySize / 4;
  354. const uint8_t* yData = pYV12Data;
  355. const uint8_t* vData = pYV12Data + ySize;
  356. const uint8_t* uData = pYV12Data + ySize + uvSize;
  357. for (int y = 0; y < height; y++) {
  358. for (int x = 0; x < width; x++) {
  359. int yIndex = y * width + x;
  360. int uvIndex = (y / 2) * (width / 2) + (x / 2);
  361. uint8_t Y = yData[yIndex];
  362. uint8_t U = uData[uvIndex];
  363. uint8_t V = vData[uvIndex];
  364. // YUV 转 RGB
  365. int R = Y + 1.403 * (V - 128);
  366. int G = Y - 0.344 * (U - 128) - 0.714 * (V - 128);
  367. int B = Y + 1.770 * (U - 128);
  368. // 限制范围 [0, 255]
  369. R = std::min(std::max(R, 0), 255);
  370. G = std::min(std::max(G, 0), 255);
  371. B = std::min(std::max(B, 0), 255);
  372. // 存储 RGB 数据
  373. int rgbIndex = yIndex * 3;
  374. pRGB24Data[rgbIndex] = static_cast<uint8_t>(R);
  375. pRGB24Data[rgbIndex + 1] = static_cast<uint8_t>(G);
  376. pRGB24Data[rgbIndex + 2] = static_cast<uint8_t>(B);
  377. }
  378. }
  379. }
  380. /* YV12转YUV420 */
  381. // void CameraPlayer::YV12ToYUV420(unsigned char *pYV12Data, Image_YUV420& yuvData, int width, int height)
  382. // {
  383. // int y_size = width * height;
  384. // int uv_size = y_size / 4;
  385. // // 分配 YUV 数据的空间
  386. // yuvData.yData.resize(y_size);
  387. // yuvData.uData.resize(uv_size);
  388. // yuvData.vData.resize(uv_size);
  389. // // 提取 Y、V、U 分量
  390. // const uint8_t* y = vecYUV.data();
  391. // const uint8_t* v = vecYUV.data() + y_size;
  392. // const uint8_t* u = vecYUV.data() + y_size + uv_size;
  393. // // 复制 Y 分量
  394. // std::memcpy(vecYUV.data(), y, y_size);
  395. // // 复制 U 分量
  396. // uint8_t* yuv_u = vecYUV.data() + y_size;
  397. // for (int i = 0; i < uv_size; ++i) {
  398. // yuv_u[i] = u[i];
  399. // }
  400. // // 复制 V 分量
  401. // uint8_t* yuv_v = vecYUV.data() + y_size + uv_size;
  402. // for (int i = 0; i < uv_size; ++i) {
  403. // yuv_v[i] = v[i];
  404. // }
  405. // }
  406. void CameraPlayer::YV12ToYUV420(unsigned char *pYV12Data, Image_YUV420& yuvData, int width, int height)
  407. {
  408. int y_size = width * height;
  409. int uv_size = y_size / 4;
  410. // 分配 YUV 数据的空间
  411. yuvData.yData.resize(y_size);
  412. yuvData.uData.resize(uv_size);
  413. yuvData.vData.resize(uv_size);
  414. // 提取 Y、V、U 分量
  415. const uint8_t* y = pYV12Data;
  416. const uint8_t* v = pYV12Data + y_size;
  417. const uint8_t* u = pYV12Data + y_size + uv_size;
  418. // 复制 Y 分量
  419. // std::memcpy(yuvData.yData.data(), y, y_size);
  420. yuvData.yData = QByteArray((const char*)y, y_size);
  421. // 复制 U 分量
  422. // std::memcpy(yuvData.uData.data(), u, uv_size);
  423. yuvData.uData.append((const char*)u, uv_size);
  424. // 复制 V 分量
  425. // std::memcpy(yuvData.vData.data(), v, uv_size);
  426. yuvData.vData.append((const char*)v, uv_size);
  427. /* 设置宽和高 */
  428. yuvData.width = width;
  429. yuvData.height = height;
  430. }