PlayBackDemo.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. # coding=utf-8
  2. import sys
  3. import os
  4. from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox
  5. from PyQt5.QtCore import Qt, QDate, QThread, pyqtSignal
  6. from ctypes import *
  7. from PlayBackUI import Ui_MainWindow
  8. from NetSDK.NetSDK import NetClient
  9. from NetSDK.SDK_Enum import EM_USEDEV_MODE, EM_QUERY_RECORD_TYPE, EM_LOGIN_SPAC_CAP_TYPE
  10. from NetSDK.SDK_Struct import NET_TIME, NET_RECORDFILE_INFO, NET_IN_PLAY_BACK_BY_TIME_INFO, NET_OUT_PLAY_BACK_BY_TIME_INFO, \
  11. C_LLONG, C_DWORD, C_LDWORD, NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY, NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY, CB_FUNCTYPE, sys_platform
  12. from NetSDK.SDK_Callback import fDisConnect, fHaveReConnect
  13. if sys_platform == 'windows':
  14. import tkinter as tk
  15. from tkinter import filedialog
  16. # 继承QThread
  17. class Mythread(QThread):
  18. # 定义信号,定义参数为int, int类型
  19. breakSignal = pyqtSignal(int, int)
  20. def __init__(self, parent=None, dwTotalSize = 1, dwDownLoadSize = 0):
  21. super().__init__(parent)
  22. self.dwTotalSize = dwTotalSize
  23. self.dwDownLoadSize = dwDownLoadSize
  24. def run(self):
  25. self.breakSignal.emit(self.dwTotalSize, self.dwDownLoadSize)
  26. def update_data(self, total_size, download_size):
  27. self.breakSignal.emit(total_size, download_size)
  28. global wnd
  29. @CB_FUNCTYPE(None, C_LLONG, C_DWORD, C_DWORD, C_LDWORD)
  30. def DownLoadPosCallBack(lLoginID, pchDVRIP, nDVRPort, dwUser):
  31. pass
  32. @CB_FUNCTYPE(c_int, C_LLONG, C_DWORD, POINTER(c_ubyte), C_DWORD, C_LDWORD)
  33. def DownLoadDataCallBack(lPlayHandle, dwDataType, pBuffer, dwBufSize, dwUser):
  34. # buf_data = cast(pBuffer, POINTER(c_ubyte * dwBufSize)).contents
  35. # with open('./buffer.dav', 'ab+') as buf_file:
  36. # buf_file.write(buf_data)
  37. return 1
  38. @CB_FUNCTYPE(None, C_LLONG, C_DWORD, C_DWORD, c_int, NET_RECORDFILE_INFO, C_LDWORD)
  39. def TimeDownLoadPosCallBack(lPlayHandle, dwTotalSize, dwDownLoadSize, index, recordfileinfo, dwUser):
  40. try:
  41. wnd.update_download_progress_thread(dwTotalSize, dwDownLoadSize)
  42. except Exception as e:
  43. print(e)
  44. class MyMainWindow(QMainWindow, Ui_MainWindow):
  45. def __init__(self, parent=None):
  46. super(MyMainWindow, self).__init__(parent)
  47. self.setupUi(self)
  48. # 界面初始化
  49. self._init_ui()
  50. # NetSDK用到的相关变量和回调
  51. self.loginID = C_LLONG()
  52. self.playbackID = C_LLONG()
  53. self.downloadID = C_LLONG()
  54. self.m_DisConnectCallBack = fDisConnect(self.DisConnectCallBack)
  55. self.m_ReConnectCallBack = fHaveReConnect(self.ReConnectCallBack)
  56. self.thread = Mythread()
  57. self.thread.breakSignal.connect(self.update_download_progress)
  58. self.thread.start()
  59. # 获取NetSDK对象并初始化
  60. self.sdk = NetClient()
  61. self.sdk.InitEx(self.m_DisConnectCallBack)
  62. self.sdk.SetAutoReconnect(self.m_ReConnectCallBack)
  63. # demo内需要用到的变量
  64. self.pause_state = False
  65. self.record_count = 0
  66. self.record_infos = NET_RECORDFILE_INFO * 5000
  67. # 初始化界面
  68. def _init_ui(self):
  69. self.Login_pushButton.setText('登录(Login)')
  70. self.PlayBack_pushbutton.setText('回放(PlayBack)')
  71. self.PlayBack_pushbutton.setEnabled(False)
  72. self.IP_lineEdit.setText('172.23.12.231')
  73. self.Port_lineEdit.setText('37777')
  74. self.Name_lineEdit.setText('admin')
  75. self.Pwd_lineEdit.setText('admin123')
  76. self.setWindowFlag(Qt.WindowMinimizeButtonHint)
  77. self.setWindowFlag(Qt.WindowCloseButtonHint)
  78. self.setFixedSize(self.width(), self.height())
  79. self.Login_pushButton.clicked.connect(self.login_btn_onclick)
  80. self.PlayBack_pushbutton.clicked.connect(self.playback_btn_onclick)
  81. self.Pause_pushbutton.clicked.connect(self.pause_btn_onclick)
  82. self.Download_pushButton.clicked.connect(self.download_btn_onclick)
  83. self.SelectDate_calendarWidget.selectionChanged.connect(self.selectdate_calendar_onselectionChanged)
  84. self.StreamTyp_comboBox.currentIndexChanged.connect(self.stream_comboBox_oncurrentIndexChanged)
  85. def login_btn_onclick(self):
  86. if not self.loginID:
  87. ip = self.IP_lineEdit.text()
  88. port = int(self.Port_lineEdit.text())
  89. username = self.Name_lineEdit.text()
  90. password = self.Pwd_lineEdit.text()
  91. stuInParam = NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY()
  92. stuInParam.dwSize = sizeof(NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY)
  93. stuInParam.szIP = ip.encode()
  94. stuInParam.nPort = port
  95. stuInParam.szUserName = username.encode()
  96. stuInParam.szPassword = password.encode()
  97. stuInParam.emSpecCap = EM_LOGIN_SPAC_CAP_TYPE.TCP
  98. stuInParam.pCapParam = None
  99. stuOutParam = NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY()
  100. stuOutParam.dwSize = sizeof(NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY)
  101. self.loginID, device_info, error_msg = self.sdk.LoginWithHighLevelSecurity(stuInParam, stuOutParam)
  102. if self.loginID != 0:
  103. self.setWindowTitle('回放(PlayBack)-在线(OnLine)')
  104. self.Login_pushButton.setText('登出(Logout)')
  105. self.Download_pushButton.setEnabled(True)
  106. self.Channel_comboBox.setEnabled(True)
  107. self.StreamTyp_comboBox.setEnabled(True)
  108. self.SelectDate_calendarWidget.setEnabled(True)
  109. self.SelectDate_calendarWidget.setSelectedDate(QDate.currentDate())
  110. self.set_stream_type(0)
  111. for i in range(int(device_info.nChanNum)):
  112. self.Channel_comboBox.addItem(str(i))
  113. else:
  114. QMessageBox.about(self, '提示(prompt)', error_msg)
  115. else:
  116. if self.playbackID:
  117. self.sdk.StopPlayBack(self.playbackID)
  118. self.playbackID = 0
  119. if self.downloadID:
  120. self.sdk.StopDownload(self.downloadID)
  121. self.downloadID = 0
  122. result = self.sdk.Logout(self.loginID)
  123. if result:
  124. self.setWindowTitle("回放(PlayBack)-离线(OffLine)")
  125. self.Login_pushButton.setText("登录(Login)")
  126. self.loginID = 0
  127. self.StreamTyp_comboBox.setEnabled(False)
  128. self.PlayBack_pushbutton.setEnabled(False)
  129. self.Pause_pushbutton.setEnabled(False)
  130. self.Download_pushButton.setEnabled(False)
  131. self.Channel_comboBox.setEnabled(False)
  132. self.StreamTyp_comboBox.setEnabled(False)
  133. self.SelectDate_calendarWidget.setEnabled(False)
  134. self.exist_radioButton.setChecked(False)
  135. self.PlayBack_pushbutton.setText("回放(PlayBack)")
  136. self.Pause_pushbutton.setText("暂停(Pause)")
  137. self.Download_pushButton.setText("下载(download)")
  138. self.PlayBackWnd.repaint()
  139. self.Channel_comboBox.clear()
  140. self.Download_progressBar.setValue(0)
  141. self.thread.update_data(1, 0)
  142. def stream_comboBox_oncurrentIndexChanged(self):
  143. stream_type = self.StreamTyp_comboBox.currentIndex()
  144. self.set_stream_type(stream_type)
  145. def selectdate_calendar_onselectionChanged(self):
  146. if not self.loginID:
  147. return
  148. if self.playbackID:
  149. return
  150. self.exist_radioButton.setChecked(False)
  151. self.PlayBack_pushbutton.setEnabled(False)
  152. date = QDate(self.SelectDate_calendarWidget.selectedDate())
  153. startTime = NET_TIME()
  154. startTime.dwYear = date.year()
  155. startTime.dwMonth = date.month()
  156. startTime.dwDay = date.day()
  157. startTime.dwHour = 0
  158. startTime.dwMinute = 0
  159. startTime.dwSecond = 0
  160. endTime = NET_TIME()
  161. endTime.dwYear = date.year()
  162. endTime.dwMonth = date.month()
  163. endTime.dwDay = date.day()
  164. endTime.dwHour = 23
  165. endTime.dwMinute = 59
  166. endTime.dwSecond = 59
  167. result, fileCount, self.record_infos = self.query_file(startTime, endTime)
  168. if not result:
  169. QMessageBox.about(self, '提示(prompt)', self.sdk.GetLastErrorMessage())
  170. return 0
  171. if fileCount > 0:
  172. self.record_count = fileCount
  173. self.exist_radioButton.setChecked(True)
  174. self.PlayBack_pushbutton.setEnabled(True)
  175. def playback_btn_onclick(self):
  176. if not self.playbackID:
  177. start_time = self.record_infos[0].starttime
  178. if self.record_count >= 2:
  179. end_time = self.record_infos[self.record_count - 2].endtime # 最后一个录像,也就是self.record_infos[self.record_count-1]的endtime可能是0:0:0,所以取倒数第二个的结束时间
  180. else:
  181. end_time = self.record_infos[0].endtime
  182. inParam = NET_IN_PLAY_BACK_BY_TIME_INFO()
  183. inParam.hWnd = c_long(self.PlayBackWnd.winId())
  184. inParam.cbDownLoadPos = DownLoadPosCallBack
  185. inParam.dwPosUser = 0
  186. inParam.fDownLoadDataCallBack = DownLoadDataCallBack
  187. inParam.dwDataUser = 0
  188. inParam.nPlayDirection = 0
  189. inParam.nWaittime = 5000
  190. inParam.stStartTime.dwYear = start_time.dwYear
  191. inParam.stStartTime.dwMonth = start_time.dwMonth
  192. inParam.stStartTime.dwDay = start_time.dwDay
  193. inParam.stStartTime.dwHour = start_time.dwHour
  194. inParam.stStartTime.dwMinute = start_time.dwMinute
  195. inParam.stStartTime.dwSecond = start_time.dwSecond
  196. inParam.stStopTime.dwYear = end_time.dwYear
  197. inParam.stStopTime.dwMonth = end_time.dwMonth
  198. inParam.stStopTime.dwDay = end_time.dwDay
  199. inParam.stStopTime.dwHour = end_time.dwHour
  200. inParam.stStopTime.dwMinute = end_time.dwMinute
  201. inParam.stStopTime.dwSecond = end_time.dwSecond
  202. outParam = NET_OUT_PLAY_BACK_BY_TIME_INFO()
  203. nchannel = self.Channel_comboBox.currentIndex()
  204. self.playbackID = self.sdk.PlayBackByTimeEx2(self.loginID, nchannel, inParam, outParam)
  205. if self.playbackID != 0:
  206. self.PlayBack_pushbutton.setText("停止(Stop)")
  207. self.Pause_pushbutton.setEnabled(True)
  208. self.Channel_comboBox.setEnabled(False)
  209. self.StreamTyp_comboBox.setEnabled(False)
  210. self.SelectDate_calendarWidget.setEnabled(False)
  211. self.Channel_comboBox.repaint()
  212. self.StreamTyp_comboBox.repaint()
  213. self.PlayBackWnd.repaint()
  214. else:
  215. QMessageBox.about(self, '提示(prompt)', self.sdk.GetLastErrorMessage())
  216. else:
  217. result = self.sdk.StopPlayBack(self.playbackID)
  218. if result:
  219. self.PlayBack_pushbutton.setText("回放(PlayBack)")
  220. self.playbackID = 0
  221. self.PlayBackWnd.repaint()
  222. self.Pause_pushbutton.setText("暂停(Pause)")
  223. self.Pause_pushbutton.setEnabled(False)
  224. self.Channel_comboBox.setEnabled(True)
  225. self.StreamTyp_comboBox.setEnabled(True)
  226. self.SelectDate_calendarWidget.setEnabled(True)
  227. else:
  228. QMessageBox.about(self, '提示(prompt)', self.sdk.GetLastErrorMessage())
  229. def pause_btn_onclick(self):
  230. if self.playbackID:
  231. self.pause_state = not self.pause_state
  232. result = self.sdk.PausePlayBack(self.playbackID, self.pause_state)
  233. if not result:
  234. QMessageBox.about(self, '提示(prompt)', self.sdk.GetLastErrorMessage())
  235. return
  236. if self.pause_state:
  237. self.Pause_pushbutton.setText("恢复(Resume)")
  238. else:
  239. self.Pause_pushbutton.setText("暂停(Pause)")
  240. else:
  241. pass
  242. def download_btn_onclick(self):
  243. if not self.downloadID:
  244. if sys_platform == 'windows':
  245. application_window = tk.Tk()
  246. application_window.withdraw()
  247. # 设置文件对话框会显示的文件类型
  248. save_filetypes = [('data', '.dav')]
  249. # 请求选择一个用以保存的文件
  250. save_file_name = filedialog.asksaveasfilename(parent=application_window,
  251. initialdir=os.getcwd(),
  252. title="Please select a file name for saving:",
  253. filetypes=save_filetypes)
  254. else:
  255. save_file_name = os.path.dirname(__file__) + 'data.dav'
  256. stream_type = self.StreamTyp_comboBox.currentIndex()
  257. self.set_stream_type(stream_type)
  258. start_date = self.Start_dateTimeEdit.date()
  259. start_time = self.Start_dateTimeEdit.time()
  260. startDateTime = NET_TIME()
  261. startDateTime.dwYear = start_date.year()
  262. startDateTime.dwMonth = start_date.month()
  263. startDateTime.dwDay = start_date.day()
  264. startDateTime.dwHour = start_time.hour()
  265. startDateTime.dwMinute = start_time.minute()
  266. startDateTime.dwSecond = start_time.second()
  267. end_date = self.End_dateTimeEdit.date()
  268. end_time = self.End_dateTimeEdit.time()
  269. enddateTime = NET_TIME()
  270. enddateTime.dwYear = end_date.year()
  271. enddateTime.dwMonth = end_date.month()
  272. enddateTime.dwDay = end_date.day()
  273. enddateTime.dwHour = end_time.hour()
  274. enddateTime.dwMinute = end_time.minute()
  275. enddateTime.dwSecond = end_time.second()
  276. nchannel = self.Channel_comboBox.currentIndex()
  277. self.downloadID = self.sdk.DownloadByTimeEx(self.loginID, nchannel, int(EM_QUERY_RECORD_TYPE.ALL),
  278. startDateTime, enddateTime, save_file_name,
  279. TimeDownLoadPosCallBack, 0,
  280. DownLoadDataCallBack, 0)
  281. if self.downloadID:
  282. self.Download_pushButton.setText("停止(Stop)")
  283. else:
  284. QMessageBox.about(self, '提示(prompt)', self.sdk.GetLastErrorMessage())
  285. else:
  286. result = self.sdk.StopDownload(self.downloadID)
  287. if result:
  288. self.downloadID = 0
  289. self.Download_pushButton.setText("下载(download)")
  290. self.Download_progressBar.setValue(0)
  291. self.thread.update_data(1, 0)
  292. else:
  293. QMessageBox.about(self, '提示(prompt)', self.sdk.GetLastErrorMessage())
  294. def set_stream_type(self, stream_type):
  295. # set stream type;设置码流类型
  296. stream_type = c_int(stream_type)
  297. result = self.sdk.SetDeviceMode(self.loginID, int(EM_USEDEV_MODE.RECORD_STREAM_TYPE), stream_type)
  298. if not result:
  299. QMessageBox.about(self, '提示(prompt)', self.sdk.GetLastErrorMessage())
  300. return 0, 0, None
  301. def query_file(self, startTime, endTime):
  302. # query record file 查询录像文件
  303. result, fileCount, infos = self.sdk.QueryRecordFile(self.loginID, 0, int(EM_QUERY_RECORD_TYPE.ALL), startTime,
  304. endTime, None, 5000, False)
  305. if not result:
  306. QMessageBox.about(self, '提示(prompt)', self.sdk.GetLastErrorMessage())
  307. return 0, 0, None
  308. return result, fileCount, infos
  309. def update_download_progress_thread(self, totalsize, downloadsize):
  310. try:
  311. self.thread.update_data(totalsize, downloadsize)
  312. except Exception as e:
  313. print(e)
  314. def update_download_progress(self, total_size, download_size):
  315. try:
  316. if download_size == -1:
  317. self.downloadID = 0
  318. self.Download_progressBar.setValue(0)
  319. self.sdk.StopDownload(self.downloadID)
  320. self.Download_pushButton.setText("下载(download)")
  321. QMessageBox.about(self, '提示(prompt)', "Download End(下载结束)!")
  322. elif download_size == -2:
  323. self.downloadID = 0
  324. self.Download_progressBar.setValue(0)
  325. self.Download_pushButton.setText("下载(download)")
  326. QMessageBox.about(self, '提示(prompt)', "Download Failed(下载失败)!")
  327. else:
  328. if download_size >= total_size:
  329. self.Download_progressBar.setValue(100)
  330. else:
  331. percentage = int(download_size * 100 / total_size)
  332. self.Download_progressBar.setValue(percentage)
  333. except Exception as e:
  334. print(e)
  335. # 实现断线回调函数功能
  336. def DisConnectCallBack(self, lLoginID, pchDVRIP, nDVRPort, dwUser):
  337. self.setWindowTitle("回放(PlayBack)-离线(OffLine)")
  338. # 实现断线重连回调函数功能
  339. def ReConnectCallBack(self, lLoginID, pchDVRIP, nDVRPort, dwUser):
  340. self.setWindowTitle('回放(PlayBack)-在线(OnLine)')
  341. # 关闭主窗口时清理资源
  342. def closeEvent(self, event):
  343. event.accept()
  344. if self.loginID:
  345. self.sdk.Logout(self.loginID)
  346. self.sdk.Cleanup()
  347. if __name__ == '__main__':
  348. app = QApplication(sys.argv)
  349. my_wnd = MyMainWindow()
  350. wnd = my_wnd
  351. my_wnd.show()
  352. sys.exit(app.exec_())