/rtsp.py
Python | 175 lines | 166 code | 8 blank | 1 comment | 5 complexity | a18b6ed9c2aaada11a02cc00f28b3544 MD5 | raw file
- #!/usr/bin/env python3
- import logging
- import time
- import glob
- import struct
- import sys
- import os
- import re
- import live555
- import threading
- from datetime import datetime
- from shared import common
- if common.check_prereqs() == False:
- print("Prerequisite check failed: {}".format(common.get_prereqs_msg()))
- exit(-1)
- if len(sys.argv) != 7:
- print("ERROR : usage: rtsp.py chunk_path jpg_path url deviceid initial_timeout frame_timeout")
- exit(-1)
- chunk_path = sys.argv[1]
- jpg_path = sys.argv[2]
- url = sys.argv[3]
- deviceid = sys.argv[4]
- initial_timeout = int(sys.argv[5])
- frame_timeout = int(sys.argv[6])
- logger = logging.getLogger("rtsp")
- logging.basicConfig(filename="/tmp/{}.log".format(deviceid),level=logging.DEBUG)
- basepath = os.path.dirname(os.path.abspath(__file__))
- fixedurl = re.sub(r'rtsp:\/\/.*@', 'rtsp://........@', url, flags=re.IGNORECASE)
- logger.info("Starting recording from {} to {} for {}".format(fixedurl, chunk_path,deviceid))
- class createFrameThread(threading.Thread):
- def __init__(self, video, jpg, thumb, mp4):
- threading.Thread.__init__(self)
- self.video = video
- self.jpg = jpg
- self.thumb = thumb
- self.mp4 = mp4
- def run(self):
- global logger
- try:
- p2 = common.subprocess_chunk_to_mp4(basepath, self.video, self.mp4, "")
- p = common.subprocess_chunk_to_jpg(basepath, self.video, self.jpg, "")
- p.wait()
- p = common.subprocess_resize_jpg(self.jpg, self.thumb, "scale=320:-1")
- p.wait()
- p2.wait()
- os.remove(self.video)
- if os.path.isfile(self.mp4) and os.stat(self.mp4).st_size < 4096:
- os.remove(self.mp4)
- except BaseException as e:
- logger.error("CREATE FRAME THREAD - ", e)
- except:
- logger.error("CREATE FRAME THREAD - Something bad happend")
- def createFrame(video, jpg, thumb, mp4):
- if video is None or len(video) == 0:
- return
- t = createFrameThread(video, jpg, thumb, mp4)
- t.start()
- return t
- def record(chunk_path, jpg_path, url, deviceid, initial_timeout, frame_timeout):
- global logger
- now = datetime.now()
- chunk_filename = common.getChunkFilenameBy(chunk_path, deviceid, now)
- jpg_filename = common.getJPGFilenameBy(jpg_path, deviceid, now)
- thumb_filename = common.getThumbnailFilenameBy(jpg_path, deviceid, now)
- mp4_filename = common.getMP4FilenameBy(chunk_path, deviceid, now)
- state = {
- 'lastfilename': "",
- 'lastjpgfilename': "",
- 'lastthumbfilename': "",
- 'lastmp4filename': "",
- 'fOut': open(chunk_filename, 'wb'),
- 'filename': chunk_filename,
- 'jpgfilename': jpg_filename,
- 'thumbfilename': thumb_filename,
- 'mp4filename': mp4_filename,
- 'last_update': now,
- 'first_update': True
- }
- # Cleanup and convert old chunk files for this camera into mp4's if done
- oldchunkfiles = glob.glob(os.path.join(chunk_path,"*/{}.*.*.chunk".format(deviceid)))
- if chunk_filename in oldchunkfiles: oldchunkfiles.remove(chunk_filename)
- for oldchunkfile in oldchunkfiles:
- logger.info("Upgrading {}".format(oldchunkfile))
- date_object = datetime.strptime(os.path.basename(oldchunkfile), "{}.%Y%m%d.%H%M.chunk".format(deviceid))
- old_jpg_filename = common.getJPGFilenameBy(jpg_path, deviceid, date_object)
- old_thumb_filename = common.getThumbnailFilenameBy(jpg_path, deviceid, date_object)
- old_mp4_filename = common.getMP4FilenameBy(chunk_path, deviceid, date_object)
- createFrame(oldchunkfile, old_jpg_filename, old_thumb_filename, old_mp4_filename).join()
-
- def oneFrame(codecName, bytes, sec, usec, durUSec, ctx):
- ctx['last_update'] = datetime.now()
- delta = datetime.now() - datetime(1970, 1, 1)
- frame_time = int(delta.total_seconds()*1000000)
- ctx['first_update'] = False
- F = int(bytes[0] & 128)
- NRI = int((bytes[0] & (64+32) ) / 32)
- TYPE = int((bytes[0] & (16+8+4+2+1) ))
- if TYPE==7:
- now = datetime.now()
- chunk_filename = common.getChunkFilenameBy(chunk_path, deviceid, now)
- jpg_filename = common.getJPGFilenameBy(jpg_path, deviceid, now)
- thumb_filename = common.getThumbnailFilenameBy(jpg_path, deviceid, now)
- mp4_filename = common.getMP4FilenameBy(chunk_path, deviceid, now)
- ctx['filename'] = chunk_filename
- ctx['jpgfilename'] = jpg_filename
- ctx['thumbfilename'] = thumb_filename
- ctx['mp4filename'] = mp4_filename
- if ctx['filename'] != ctx['lastfilename']:
- ctx['fOut'].close()
- createFrame(ctx['lastfilename'], ctx['lastjpgfilename'], ctx['lastthumbfilename'], ctx['lastmp4filename'])
- logger.info('INFO : opening {}'.format(ctx['filename']))
- ctx['fOut'] = open(ctx['filename'], 'wb')
- ctx['lastfilename'] = ctx['filename']
- ctx['lastjpgfilename'] = ctx['jpgfilename']
- ctx['lastthumbfilename'] = ctx['thumbfilename']
- ctx['lastmp4filename'] = ctx['mp4filename']
- ctx['fOut'].write(struct.pack('!BQQ', 0, frame_time, len(bytes)))
- ctx['fOut'].write(bytes)
- ctx['fOut'].flush()
- return ctx
- temp_oneFrame = lambda codecName, bytes, sec, usec, durUSec: oneFrame(codecName, bytes, sec, usec, durUSec, state)
- # Starts pulling frames from the URL, with the provided callback:
- useTCP = False
- live555.startRTSP(url, temp_oneFrame, useTCP)
- # Run Live555's event loop in a background thread:
- t = threading.Thread(target=live555.runEventLoop, args=())
- t.setDaemon(True)
- t.start()
- while True:
- delta = datetime.now() - state['last_update']
- time_since_update = delta.total_seconds()
- if state['first_update'] and time_since_update >= initial_timeout:
- logger.warn("Took longer than {} seconds for first frame".format(initial_timeout))
- break;
- if state['first_update'] == False and time_since_update >= frame_timeout:
- logger.warn("Delay of over {} seconds for data".format(frame_timeout))
- break;
- time.sleep(1)
- # Tell Live555's event loop to stop:
- live555.stopEventLoop()
- # Wait for the background thread to finish:
- t.join()
- record(chunk_path, jpg_path, url, deviceid, initial_timeout, frame_timeout)