PageRenderTime 88ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/rtsp.py

https://gitlab.com/antarpreets/pynvr
Python | 175 lines | 166 code | 8 blank | 1 comment | 5 complexity | a18b6ed9c2aaada11a02cc00f28b3544 MD5 | raw file
  1. #!/usr/bin/env python3
  2. import logging
  3. import time
  4. import glob
  5. import struct
  6. import sys
  7. import os
  8. import re
  9. import live555
  10. import threading
  11. from datetime import datetime
  12. from shared import common
  13. if common.check_prereqs() == False:
  14. print("Prerequisite check failed: {}".format(common.get_prereqs_msg()))
  15. exit(-1)
  16. if len(sys.argv) != 7:
  17. print("ERROR : usage: rtsp.py chunk_path jpg_path url deviceid initial_timeout frame_timeout")
  18. exit(-1)
  19. chunk_path = sys.argv[1]
  20. jpg_path = sys.argv[2]
  21. url = sys.argv[3]
  22. deviceid = sys.argv[4]
  23. initial_timeout = int(sys.argv[5])
  24. frame_timeout = int(sys.argv[6])
  25. logger = logging.getLogger("rtsp")
  26. logging.basicConfig(filename="/tmp/{}.log".format(deviceid),level=logging.DEBUG)
  27. basepath = os.path.dirname(os.path.abspath(__file__))
  28. fixedurl = re.sub(r'rtsp:\/\/.*@', 'rtsp://........@', url, flags=re.IGNORECASE)
  29. logger.info("Starting recording from {} to {} for {}".format(fixedurl, chunk_path,deviceid))
  30. class createFrameThread(threading.Thread):
  31. def __init__(self, video, jpg, thumb, mp4):
  32. threading.Thread.__init__(self)
  33. self.video = video
  34. self.jpg = jpg
  35. self.thumb = thumb
  36. self.mp4 = mp4
  37. def run(self):
  38. global logger
  39. try:
  40. p2 = common.subprocess_chunk_to_mp4(basepath, self.video, self.mp4, "")
  41. p = common.subprocess_chunk_to_jpg(basepath, self.video, self.jpg, "")
  42. p.wait()
  43. p = common.subprocess_resize_jpg(self.jpg, self.thumb, "scale=320:-1")
  44. p.wait()
  45. p2.wait()
  46. os.remove(self.video)
  47. if os.path.isfile(self.mp4) and os.stat(self.mp4).st_size < 4096:
  48. os.remove(self.mp4)
  49. except BaseException as e:
  50. logger.error("CREATE FRAME THREAD - ", e)
  51. except:
  52. logger.error("CREATE FRAME THREAD - Something bad happend")
  53. def createFrame(video, jpg, thumb, mp4):
  54. if video is None or len(video) == 0:
  55. return
  56. t = createFrameThread(video, jpg, thumb, mp4)
  57. t.start()
  58. return t
  59. def record(chunk_path, jpg_path, url, deviceid, initial_timeout, frame_timeout):
  60. global logger
  61. now = datetime.now()
  62. chunk_filename = common.getChunkFilenameBy(chunk_path, deviceid, now)
  63. jpg_filename = common.getJPGFilenameBy(jpg_path, deviceid, now)
  64. thumb_filename = common.getThumbnailFilenameBy(jpg_path, deviceid, now)
  65. mp4_filename = common.getMP4FilenameBy(chunk_path, deviceid, now)
  66. state = {
  67. 'lastfilename': "",
  68. 'lastjpgfilename': "",
  69. 'lastthumbfilename': "",
  70. 'lastmp4filename': "",
  71. 'fOut': open(chunk_filename, 'wb'),
  72. 'filename': chunk_filename,
  73. 'jpgfilename': jpg_filename,
  74. 'thumbfilename': thumb_filename,
  75. 'mp4filename': mp4_filename,
  76. 'last_update': now,
  77. 'first_update': True
  78. }
  79. # Cleanup and convert old chunk files for this camera into mp4's if done
  80. oldchunkfiles = glob.glob(os.path.join(chunk_path,"*/{}.*.*.chunk".format(deviceid)))
  81. if chunk_filename in oldchunkfiles: oldchunkfiles.remove(chunk_filename)
  82. for oldchunkfile in oldchunkfiles:
  83. logger.info("Upgrading {}".format(oldchunkfile))
  84. date_object = datetime.strptime(os.path.basename(oldchunkfile), "{}.%Y%m%d.%H%M.chunk".format(deviceid))
  85. old_jpg_filename = common.getJPGFilenameBy(jpg_path, deviceid, date_object)
  86. old_thumb_filename = common.getThumbnailFilenameBy(jpg_path, deviceid, date_object)
  87. old_mp4_filename = common.getMP4FilenameBy(chunk_path, deviceid, date_object)
  88. createFrame(oldchunkfile, old_jpg_filename, old_thumb_filename, old_mp4_filename).join()
  89. def oneFrame(codecName, bytes, sec, usec, durUSec, ctx):
  90. ctx['last_update'] = datetime.now()
  91. delta = datetime.now() - datetime(1970, 1, 1)
  92. frame_time = int(delta.total_seconds()*1000000)
  93. ctx['first_update'] = False
  94. F = int(bytes[0] & 128)
  95. NRI = int((bytes[0] & (64+32) ) / 32)
  96. TYPE = int((bytes[0] & (16+8+4+2+1) ))
  97. if TYPE==7:
  98. now = datetime.now()
  99. chunk_filename = common.getChunkFilenameBy(chunk_path, deviceid, now)
  100. jpg_filename = common.getJPGFilenameBy(jpg_path, deviceid, now)
  101. thumb_filename = common.getThumbnailFilenameBy(jpg_path, deviceid, now)
  102. mp4_filename = common.getMP4FilenameBy(chunk_path, deviceid, now)
  103. ctx['filename'] = chunk_filename
  104. ctx['jpgfilename'] = jpg_filename
  105. ctx['thumbfilename'] = thumb_filename
  106. ctx['mp4filename'] = mp4_filename
  107. if ctx['filename'] != ctx['lastfilename']:
  108. ctx['fOut'].close()
  109. createFrame(ctx['lastfilename'], ctx['lastjpgfilename'], ctx['lastthumbfilename'], ctx['lastmp4filename'])
  110. logger.info('INFO : opening {}'.format(ctx['filename']))
  111. ctx['fOut'] = open(ctx['filename'], 'wb')
  112. ctx['lastfilename'] = ctx['filename']
  113. ctx['lastjpgfilename'] = ctx['jpgfilename']
  114. ctx['lastthumbfilename'] = ctx['thumbfilename']
  115. ctx['lastmp4filename'] = ctx['mp4filename']
  116. ctx['fOut'].write(struct.pack('!BQQ', 0, frame_time, len(bytes)))
  117. ctx['fOut'].write(bytes)
  118. ctx['fOut'].flush()
  119. return ctx
  120. temp_oneFrame = lambda codecName, bytes, sec, usec, durUSec: oneFrame(codecName, bytes, sec, usec, durUSec, state)
  121. # Starts pulling frames from the URL, with the provided callback:
  122. useTCP = False
  123. live555.startRTSP(url, temp_oneFrame, useTCP)
  124. # Run Live555's event loop in a background thread:
  125. t = threading.Thread(target=live555.runEventLoop, args=())
  126. t.setDaemon(True)
  127. t.start()
  128. while True:
  129. delta = datetime.now() - state['last_update']
  130. time_since_update = delta.total_seconds()
  131. if state['first_update'] and time_since_update >= initial_timeout:
  132. logger.warn("Took longer than {} seconds for first frame".format(initial_timeout))
  133. break;
  134. if state['first_update'] == False and time_since_update >= frame_timeout:
  135. logger.warn("Delay of over {} seconds for data".format(frame_timeout))
  136. break;
  137. time.sleep(1)
  138. # Tell Live555's event loop to stop:
  139. live555.stopEventLoop()
  140. # Wait for the background thread to finish:
  141. t.join()
  142. record(chunk_path, jpg_path, url, deviceid, initial_timeout, frame_timeout)