cameraplayer.cpp 17 KB

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