def unittest(): import sys, time logging.basicConfig( stream=sys.stdout, level=logging.DEBUG, format="%(asctime)s %(message)s" ) sid="test" ig = ImageGenerator(sid) si_topic = config.StreamInputTopic(sid) img_topic = config.ImageTopic(sid) def image_ready(filename=""): logging.info("++++++++++++ image ready ++++++++++++") signals.subscribe(img_topic,image_ready) stop_topic = config.StreamStopTopic(sid) ig.start() f = open(os.environ['HOME']+"/oceans.mp4") while True: time.sleep(0.25) chunk = f.read(32767) if not chunk or len(chunk) == 0: break #ig.feed(chunk) signals.publish(si_topic,chunk) signals.publish(stop_topic)
def run(self): # run worker thread that oversees the ffmpeg process, prmorning the # task of restarting the stream if need and be and routing video # traffic. try: self._run() except BaseException: logging.error(traceback.format_exc()) signals.publish(self.sf_topic, { 'error': 'SystemError', 'desc': 'Exception thrown check error log' }) if self.proc and not self.proc.poll(): self.proc.kill() self.proc.communicate(b'')
def _inactivity_fault(self): """ Test to see if traffic was received from the output pipe conf.maxInActvity seconds ago. """ if time.time() > (self.last_output_time + self.conf['maxInActvity']): self.state = config.STREAM_STATE_FAULT signals.publish( self.sf_topic, { 'error': 'BadInput', 'desc': 'No input received after %d seconds' % self.conf['maxInActvity'] }) return True return False
def feed(self, data): """ Feed data to ffmpeg, allow it to generate an image, send the filename of the image if it is a new image and at the minimum interval. """ logging.debug("writing chunk to pipe") retval = self.proc.poll() if retval: logging.error("imageGenerate: ffmpeg crash") return os.write(self.vin_w,data) if os.access(self.imgfile,os.F_OK): mtime = os.stat(self.imgfile) logging.info("mtime = %s" % str(mtime)) if not self.last_mtime or mtime != self.last_mtime: signals.publish(self.img_topic, self.imgfile) self.last_mtime = mtime
def _line_proc(self, line): logging.debug("line = %s" % line) # process stats # We're looking for this: # 'frame= 138 fps= 11 q=31.0 PSNR=Y:30.88 U:36.31 V:37.12 *:32.10 size= 882kB' t = line.split() if len(t) > 0 and t[0] == 'frame=': def translate(text): text = text.replace('kB', ' * 1000') text = text.replace('kbits/s', ' * 1000') text = text.replace('M', ' * 1000000') return eval(text) def psnr(t): for tt in t: if tt.startswith('*:'): return float(tt[2:]) return 0 def bitrate(line): pos = line.find('bitrate=') if pos > -1: pos += len('bitrate=') return line[pos:].split()[0] pos = line.find('size=') if pos > -1: return line[pos:].split()[1] return "0" logging.debug(psnr) input_stats = { 'fps': t[3], # signal to noise ratio, in example: *:(32.10) 'psnr': psnr(t), 'bitrate': translate(bitrate(line)) } logging.debug("received stats: %s" % str(input_stats)) # route stats to whoever is listening signals.publish(self.ss_topic, input_stats)
def _while_ffmpeg_running(self): """ launch ffmpeg route output traffic to stream input topic parse """ if self.conf['url'] == 'testsrc': cmdargs = (config.FFMPEG, self.vout_w) cmd = _isfmt_testsrc % cmdargs elif self.conf['url'].startswith('http:'): cmdargs = (config.FFMPEG, self.conf['url'], self.vout_w) cmd = _isfmt_http % cmdargs else: cmdargs = (config.FFMPEG, self.conf['url'], self.vout_w) cmd = _isfmt % cmdargs logging.debug(cmd) self.proc = subprocess.Popen(shlex.split(cmd), shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) plist = select.poll() plist.register(self.cmd_r, select.POLLIN) plist.register(self.vout_r, select.POLLIN) plist.register(self.proc.stdout.fileno(), select.POLLIN) plist.register(self.proc.stderr.fileno(), select.POLLIN) # initialize reference time to now self.last_output_time = time.time() self.state = config.STREAM_STATE_STARTING nb_stdout = nonblockingReadline(self.proc.stdout) nb_stderr = nonblockingReadline(self.proc.stderr) # while ffmpeg is running, theer has been output activity within a configurable # amount of time and stream state is not fault. while not self.proc.poll() and not self._inactivity_fault( ) and self.state in (config.STREAM_STATE_STARTING, config.STREAM_STATE_PLAYING): logging.debug("get next io activity") line, video_chunk = None, None for (fd, evt) in plist.poll(2000): # select on 3 sources if not (evt & select.POLLIN): self.state = config.STREAM_STATE_FAULT signals.publish(self.sf_topic, { 'error': 'SystemError', 'desc': 'Pipe I/O error' }) break if fd == self.cmd_r: self.state = config.STREAM_STATE_STOPPED break elif fd == self.proc.stdout.fileno(): logging.debug("stdout line") line = nb_stdout.readline() elif fd == self.proc.stderr.fileno(): logging.debug("stdout error") line = nb_stderr.readline() else: video_chunk = os.read(self.vout_r, 0xFFFF) if line: self._line_proc(line) if video_chunk: self._route_video(video_chunk) # shutdown ffmpeg and what for process to die so we # don't leave defunct process. logging.warning("existing ffmpeg loop state=%" % self.state) self.proc.kill() self.proc.communicate(b'') self.proc = None
def _route_video(self, video_chunk): logging.debug("_route_video: %d bytes of video received" % len(video_chunk)) signals.publish(self.si_topic, video_chunk) self.state = config.STREAM_STATE_PLAYING self.last_output_time = time.time()
def setup(self): signals.publish(config.STREAM_CREATE_TOPIC, self)