def next(self): while True: self.get_playlist() if self.clip_nodes is None: self.eof_handling( 'No valid playlist:\n{}'.format(self.json_file), True, 30) yield self.src_cmd + self.filtergraph continue self.begin = self.init_time # loop through all clips in playlist and get correct clip in time for index, node in enumerate(self.clip_nodes["program"]): self.get_clip_in_out(node) # first time we end up here if self.first and \ self.last_time < self.begin + self.out - self.seek: self.peperation_task(index, node) self.first = False break elif self.last_time < self.begin: if index + 1 == len(self.clip_nodes["program"]): self.last = True else: self.last = False self.peperation_task(index, node) break self.begin += self.out - self.seek else: if stdin_args.loop: self.check_for_next_playlist() self.init_time = self.last_time + 1 self.src_cmd = None elif not _playlist.length and not stdin_args.loop: # when we reach playlist end, stop script messenger.info('Playlist reached end!') self.eof_handling('Playlist reached end!', True) #return None elif self.begin == self.init_time: # no clip was played, generate dummy self.eof_handling('Playlist is empty!', False) else: # playlist is not long enough, play filler self.eof_handling('Playlist is not long enough!', True) if self.src_cmd is not None: yield self.src_cmd + self.filtergraph
def peperation_task(self, index, node): # call functions in order to prepare source and filter self.src = node["source"] self.probe.load(self.src) self.get_input() self.get_category(index, node) self.set_filtergraph() self.check_for_next_playlist() if _playout.on_track_change: command = _playout.on_track_change + " '" + json.dumps(node) + "'" messenger.info(command) os.system(command)
def get_playlist(self): if stdin_args.playlist: self.json_file = stdin_args.playlist else: year, month, day = self.list_date.split('-') self.json_file = os.path.join( _playlist.path, year, month, self.list_date + '.json') if '://' in self.json_file: self.json_file = self.json_file.replace('\\', '/') try: req = request.urlopen(self.json_file, timeout=1, context=ssl._create_unverified_context()) b_time = req.headers['last-modified'] temp_time = time.strptime(b_time, "%a, %d %b %Y %H:%M:%S %Z") mod_time = time.mktime(temp_time) if mod_time > self.last_mod_time: self.clip_nodes = valid_json(req) self.last_mod_time = mod_time messenger.info('Open: ' + self.json_file) validate_thread(self.clip_nodes) except (request.URLError, socket.timeout): self.eof_handling('Get playlist from url failed!', False) elif os.path.isfile(self.json_file): # check last modification from playlist mod_time = os.path.getmtime(self.json_file) if mod_time > self.last_mod_time: with open(self.json_file, 'r', encoding='utf-8') as f: self.clip_nodes = valid_json(f) self.last_mod_time = mod_time messenger.info('Open: ' + self.json_file) validate_thread(self.clip_nodes) else: # when we have no playlist for the current day, # then we generate a black clip # and calculate the seek in time, for when the playlist comes back self.eof_handling('Playlist not exist:', False)
def output(): """ this output is for streaming to a target address, like rtmp, rtp, svt, etc. """ year = get_date(False).split('-')[0] overlay = [] ff_pre_settings = [ '-pix_fmt', 'yuv420p', '-r', str(_pre.fps), '-c:v', 'mpeg2video', '-intra', '-b:v', '{}k'.format( _pre.v_bitrate), '-minrate', '{}k'.format(_pre.v_bitrate), '-maxrate', '{}k'.format(_pre.v_bitrate), '-bufsize', '{}k'.format( _pre.v_bufsize) ] + pre_audio_codec() + ['-f', 'mpegts', '-'] if _text.add_text and not _text.over_pre: messenger.info('Using drawtext node, listening on address: {}'.format( _text.address)) overlay = [ '-vf', "null,zmq=b=tcp\\\\://'{}',drawtext=text='':fontfile='{}'".format( _text.address.replace(':', '\\:'), _text.fontfile) ] try: _ff.encoder = Popen([ 'ffmpeg', '-v', _log.ff_level.lower(), '-hide_banner', '-nostats', '-re', '-thread_queue_size', '256', '-i', 'pipe:0' ] + overlay + [ '-metadata', 'service_name=' + _playout.name, '-metadata', 'service_provider=' + _playout.provider, '-metadata', 'year={}'.format(year) ] + _playout.ffmpeg_param + _playout.stream_output, stdin=PIPE, stderr=PIPE) enc_err_thread = Thread(target=ffmpeg_stderr_reader, args=(_ff.encoder.stderr, False)) enc_err_thread.daemon = True enc_err_thread.start() if _playlist.mode and not stdin_args.folder: watcher = None get_source = GetSourceFromPlaylist() else: messenger.info('Start folder mode') media = MediaStore() watcher = MediaWatcher(media) get_source = GetSourceFromFolder(media) try: for src_cmd in get_source.next(): messenger.debug('src_cmd: "{}"'.format(src_cmd)) if src_cmd[0] == '-i': current_file = src_cmd[1] else: current_file = src_cmd[3] _current.clip = current_file messenger.info('Play: "{}"'.format(current_file)) with Popen([ 'ffmpeg', '-v', _log.ff_level.lower(), '-hide_banner', '-nostats' ] + src_cmd + ff_pre_settings, stdout=PIPE, stderr=PIPE) as _ff.decoder: dec_err_thread = Thread(target=ffmpeg_stderr_reader, args=(_ff.decoder.stderr, True)) dec_err_thread.daemon = True dec_err_thread.start() while True: buf = _ff.decoder.stdout.read(COPY_BUFSIZE) if not buf: break _ff.encoder.stdin.write(buf) except BrokenPipeError: messenger.error('Broken Pipe!') terminate_processes(watcher) except SystemExit: messenger.info('Got close command') terminate_processes(watcher) except KeyboardInterrupt: messenger.warning('Program terminated') terminate_processes(watcher) # close encoder when nothing is to do anymore if _ff.encoder.poll() is None: _ff.encoder.terminate() finally: if _ff.encoder.poll() is None: _ff.encoder.terminate() _ff.encoder.wait()
def output(): """ this output is for playing on desktop with ffplay """ overlay = [] ff_pre_settings = [ '-pix_fmt', 'yuv420p', '-r', str(_pre.fps), '-c:v', 'mpeg2video', '-intra', '-b:v', f'{_pre.v_bitrate}k', '-minrate', f'{_pre.v_bitrate}k', '-maxrate', f'{_pre.v_bitrate}k', '-bufsize', f'{_pre.v_bufsize}k' ] + pre_audio_codec() + ['-f', 'mpegts', '-'] if _text.add_text and not _text.over_pre: messenger.info( f'Using drawtext node, listening on address: {_text.address}') overlay = [ '-vf', "null,zmq=b=tcp\\\\://'{}',drawtext=text='':fontfile='{}'".format( _text.address.replace(':', '\\:'), _text.fontfile) ] try: enc_cmd = ['ffplay', '-hide_banner', '-nostats', '-i', 'pipe:0' ] + overlay messenger.debug(f'Encoder CMD: "{" ".join(enc_cmd)}"') _ff.encoder = Popen(enc_cmd, stderr=PIPE, stdin=PIPE, stdout=None) enc_err_thread = Thread(target=ffmpeg_stderr_reader, args=(_ff.encoder.stderr, False)) enc_err_thread.daemon = True enc_err_thread.start() if _playlist.mode and not stdin_args.folder: watcher = None get_source = GetSourceFromPlaylist() else: messenger.info('Start folder mode') media = MediaStore() watcher = MediaWatcher(media) get_source = GetSourceFromFolder(media) try: for node in get_source.next(): if watcher is not None: watcher.current_clip = node.get('source') messenger.info(f'Play for {node["out"] - node["seek"]:.2f} ' f'seconds: {node.get("source")}') dec_cmd = [ 'ffmpeg', '-v', _log.ff_level.lower(), '-hide_banner', '-nostats' ] + node['src_cmd'] + node['filter'] + ff_pre_settings messenger.debug(f'Decoder CMD: "{" ".join(dec_cmd)}"') with Popen(dec_cmd, stdout=PIPE, stderr=PIPE) as _ff.decoder: dec_err_thread = Thread(target=ffmpeg_stderr_reader, args=(_ff.decoder.stderr, True)) dec_err_thread.daemon = True dec_err_thread.start() while True: buf = _ff.decoder.stdout.read(COPY_BUFSIZE) if not buf: break _ff.encoder.stdin.write(buf) except BrokenPipeError: messenger.error('Broken Pipe!') terminate_processes(watcher) except SystemExit: messenger.info('Got close command') terminate_processes(watcher) except KeyboardInterrupt: messenger.warning('Program terminated') terminate_processes(watcher) # close encoder when nothing is to do anymore if _ff.encoder.poll() is None: _ff.encoder.terminate() finally: if _ff.encoder.poll() is None: _ff.encoder.terminate() _ff.encoder.wait()
def output(): """ this output is hls output, no pre-process is needed. """ year = get_date(False).split('-')[0] sync_op.realtime = True try: if playlist.mode and not stdin_args.folder: watcher = None get_source = GetSourceFromPlaylist() else: messenger.info('Start folder mode') media = MediaStore() watcher = MediaWatcher(media) get_source = GetSourceFromFolder(media) try: for node in get_source.next(): if watcher is not None: watcher.current_clip = node.get('source') messenger.info(f'Play: {node.get("source")}') cmd = [ 'ffmpeg', '-v', log.ff_level.lower(), '-hide_banner', '-nostats' ] + node['src_cmd'] + node['filter'] + [ '-metadata', 'service_name=' + playout.name, '-metadata', 'service_provider=' + playout.provider, '-metadata', 'year={}'.format(year) ] + playout.ffmpeg_param + playout.hls_output messenger.debug(f'Encoder CMD: "{" ".join(cmd)}"') ff_proc.encoder = Popen(cmd, stdin=PIPE, stderr=PIPE) stderr_reader_thread = Thread(target=ffmpeg_stderr_reader, args=(ff_proc.encoder.stderr, False)) stderr_reader_thread.daemon = True stderr_reader_thread.start() stderr_reader_thread.join() ts_cleaning_thread = Thread(target=clean_ts) ts_cleaning_thread.daemon = True ts_cleaning_thread.start() except BrokenPipeError: messenger.error('Broken Pipe!') terminate_processes(watcher) except SystemExit: messenger.info('Got close command') terminate_processes(watcher) except KeyboardInterrupt: messenger.warning('Program terminated') terminate_processes(watcher) # close encoder when nothing is to do anymore if ff_proc.encoder.poll() is None: ff_proc.encoder.terminate() finally: if ff_proc.encoder.poll() is None: ff_proc.encoder.terminate() ff_proc.encoder.wait()
def output(): """ this output is hls output, no preprocess is needed. """ year = get_date(False).split('-')[0] try: if _playlist.mode and not stdin_args.folder: watcher = None get_source = GetSourceFromPlaylist() else: messenger.info('Start folder mode') media = MediaStore() watcher = MediaWatcher(media) get_source = GetSourceFromFolder(media) try: for src_cmd in get_source.next(): messenger.debug('src_cmd: "{}"'.format(src_cmd)) if src_cmd[0] == '-i': current_file = src_cmd[1] else: current_file = src_cmd[3] messenger.info('Play: "{}"'.format(current_file)) cmd = [ 'ffmpeg', '-v', _log.ff_level.lower(), '-hide_banner', '-nostats' ] + src_cmd + [ '-metadata', 'service_name=' + _playout.name, '-metadata', 'service_provider=' + _playout.provider, '-metadata', 'year={}'.format(year) ] + _playout.ffmpeg_param + _playout.hls_output _ff.encoder = Popen(cmd, stdin=PIPE, stderr=PIPE) stderr_reader_thread = Thread(target=ffmpeg_stderr_reader, args=(_ff.encoder.stderr, False)) stderr_reader_thread.daemon = True stderr_reader_thread.start() stderr_reader_thread.join() ts_cleaning_thread = Thread(target=clean_ts) ts_cleaning_thread.daemon = True ts_cleaning_thread.start() except BrokenPipeError: messenger.error('Broken Pipe!') terminate_processes(watcher) except SystemExit: messenger.info('Got close command') terminate_processes(watcher) except KeyboardInterrupt: messenger.warning('Program terminated') terminate_processes(watcher) # close encoder when nothing is to do anymore if _ff.encoder.poll() is None: _ff.encoder.terminate() finally: if _ff.encoder.poll() is None: _ff.encoder.terminate() _ff.encoder.wait()
def output(): """ this output is for streaming to a target address, like rtmp, rtp, svt, etc. """ year = get_date(False).split('-')[0] overlay = [] ff_pre_settings = [ '-pix_fmt', 'yuv420p', '-r', str(pre.fps), '-c:v', 'mpeg2video', '-intra', '-b:v', f'{pre.v_bitrate}k', '-minrate', f'{pre.v_bitrate}k', '-maxrate', f'{pre.v_bitrate}k', '-bufsize', f'{pre.v_bufsize}k' ] + pre_audio_codec() + ['-f', 'mpegts', '-'] if lower_third.add_text and not lower_third.over_pre: messenger.info( f'Using drawtext node, listening on address: {lower_third.address}' ) overlay = [ '-vf', "null,zmq=b=tcp\\\\://'{}',drawtext=text='':fontfile='{}'".format( lower_third.address.replace(':', '\\:'), lower_third.fontfile) ] try: enc_cmd = [ 'ffmpeg', '-v', log.ff_level.lower(), '-hide_banner', '-nostats', '-re', '-thread_queue_size', '160', '-i', 'pipe:0' ] + overlay + [ '-metadata', 'service_name=' + playout.name, '-metadata', 'service_provider=' + playout.provider, '-metadata', f'year={year}' ] + playout.ffmpeg_param + playout.stream_output messenger.debug(f'Encoder CMD: "{" ".join(enc_cmd)}"') ff_proc.encoder = Popen(enc_cmd, stdin=PIPE, stderr=PIPE) enc_err_thread = Thread(target=ffmpeg_stderr_reader, args=(ff_proc.encoder.stderr, False)) enc_err_thread.daemon = True enc_err_thread.start() if playlist.mode and not stdin_args.folder: watcher = None get_source = GetSourceFromPlaylist() else: messenger.info('Start folder mode') media = MediaStore() watcher = MediaWatcher(media) get_source = GetSourceFromFolder(media) try: for node in get_source.next(): if watcher is not None: watcher.current_clip = node.get('source') messenger.info(f'Play: {node.get("source")}') dec_cmd = [ 'ffmpeg', '-v', log.ff_level.lower(), '-hide_banner', '-nostats' ] + node['src_cmd'] + node['filter'] + ff_pre_settings messenger.debug(f'Decoder CMD: "{" ".join(dec_cmd)}"') with Popen(dec_cmd, stdout=PIPE, stderr=PIPE) as ff_proc.decoder: dec_err_thread = Thread(target=ffmpeg_stderr_reader, args=(ff_proc.decoder.stderr, True)) dec_err_thread.daemon = True dec_err_thread.start() while True: buf = ff_proc.decoder.stdout.read(COPY_BUFSIZE) if not buf: break ff_proc.encoder.stdin.write(buf) except BrokenPipeError: messenger.error('Broken Pipe!') terminate_processes(watcher) except SystemExit: messenger.info('Got close command') terminate_processes(watcher) except KeyboardInterrupt: messenger.warning('Program terminated') terminate_processes(watcher) # close encoder when nothing is to do anymore if ff_proc.encoder.poll() is None: ff_proc.encoder.terminate() finally: if ff_proc.encoder.poll() is None: ff_proc.encoder.terminate() ff_proc.encoder.wait()