def __init__(self, video_url, log_uuid, show_loading_screen): self.__logger = Logger().set_namespace(self.__class__.__name__) if log_uuid: Logger.set_uuid(log_uuid) else: Logger.set_uuid(Logger.make_uuid()) self.__config_loader = ConfigLoader() self.__video_url = video_url self.__show_loading_screen = show_loading_screen # Store the PGIDs separately, because attempting to get the PGID later via `os.getpgid` can # raise `ProcessLookupError: [Errno 3] No such process` if the process is no longer running self.__video_broadcast_proc_pgid = None self.__download_and_convert_video_proc_pgid = None # Metadata about the video we are using, such as title, resolution, file extension, etc # Access should go through self.get_video_info() to populate it lazily self.__video_info = None # Bind multicast traffic to eth0. Otherwise it might send over wlan0 -- multicast doesn't work well over wifi. # `|| true` to avoid 'RTNETLINK answers: File exists' if the route has already been added. (subprocess.check_output( f"sudo ip route add {MulticastHelper.ADDRESS}/32 dev eth0 || true", shell=True, executable='/usr/bin/bash', stderr=subprocess.STDOUT)) self.__control_message_helper = ControlMessageHelper( ).setup_for_broadcaster() self.__do_housekeeping(for_end_of_video=False) self.__register_signal_handlers()
def __play_screensaver(self): log_uuid = 'SCREENSAVER__' + Logger.make_uuid() Logger.set_uuid(log_uuid) # choose random screensaver video to play screensavers_config = self.__config_loader.get_raw_config( )['screensavers'] if self.__config_loader.is_any_receiver_dual_video_output(): options = screensavers_config['720p'] else: options = screensavers_config['1080p'] screensaver_data = random.choice(list(options.values())) path = DirectoryUtils().root_dir + '/' + screensaver_data['video_path'] self.__logger.info("Starting broadcast of screensaver...") self.__do_broadcast(path, log_uuid)
def __stop_loading_screen_playback_if_playing(self, reset_log_uuid): if not self.__is_loading_screen_playback_in_progress: return if self.__loading_screen_pgid: self.__logger.info("Killing loading_screen proc (if it's still running)...") try: os.killpg(self.__loading_screen_pgid, signal.SIGTERM) except Exception: # might raise: `ProcessLookupError: [Errno 3] No such process` pass if reset_log_uuid: Logger.set_uuid('') self.__is_loading_screen_playback_in_progress = False self.__loading_screen_crop_args = None self.__loading_screen_crop_args2 = None
def __show_loading_screen(self, ctrl_msg): ctrl_msg_content = ctrl_msg[ControlMessageHelper.CONTENT_KEY] Logger.set_uuid(ctrl_msg_content['log_uuid']) cmd, self.__loading_screen_crop_args, self.__loading_screen_crop_args2 = ( self.__receiver_command_builder.build_loading_screen_command_and_get_crop_args( self.__video_player_volume_pct, self.__display_mode, self.__display_mode2, ctrl_msg_content['loading_screen_data'] ) ) self.__logger.info(f"Showing loading screen with command: {cmd}") self.__is_loading_screen_playback_in_progress = True proc = subprocess.Popen( cmd, shell = True, executable = '/usr/bin/bash', start_new_session = True ) return proc
def __receive_and_play_video(self, ctrl_msg): ctrl_msg_content = ctrl_msg[ControlMessageHelper.CONTENT_KEY] Logger.set_uuid(ctrl_msg_content['log_uuid']) cmd, self.__video_crop_args, self.__video_crop_args2 = ( self.__receiver_command_builder.build_receive_and_play_video_command_and_get_crop_args( ctrl_msg_content['log_uuid'], ctrl_msg_content['video_width'], ctrl_msg_content['video_height'], self.__video_player_volume_pct, self.__display_mode, self.__display_mode2 ) ) self.__logger.info(f"Running receive_and_play_video command: {cmd}") self.__is_video_playback_in_progress = True proc = subprocess.Popen( cmd, shell = True, executable = '/usr/bin/bash', start_new_session = True ) return proc
def __play_playlist_item(self, playlist_item): if not self.__playlist.set_current_video( playlist_item["playlist_video_id"]): # Someone deleted the item from the queue in between getting the item and starting it. return log_uuid = Logger.make_uuid() Logger.set_uuid(log_uuid) self.__logger.info( f"Starting broadcast for playlist_video_id: {playlist_item['playlist_video_id']}" ) msg = { 'log_uuid': log_uuid, 'loading_screen_data': self.__choose_random_loading_screen() } self.__control_message_helper.send_msg( ControlMessageHelper.TYPE_SHOW_LOADING_SCREEN, msg) self.__do_broadcast(playlist_item['url'], log_uuid) self.__playlist_item = playlist_item
def __stop_video_playback_if_playing(self, stop_loading_screen_playback): if stop_loading_screen_playback: self.__stop_loading_screen_playback_if_playing(reset_log_uuid = False) if not self.__is_video_playback_in_progress: if stop_loading_screen_playback: Logger.set_uuid('') return if self.__receive_and_play_video_proc_pgid: self.__logger.info("Killing receive_and_play_video proc (if it's still running)...") try: os.killpg(self.__receive_and_play_video_proc_pgid, signal.SIGTERM) except Exception: # might raise: `ProcessLookupError: [Errno 3] No such process` pass Logger.set_uuid('') self.__is_video_playback_in_progress = False self.__video_crop_args = None self.__video_crop_args2 = None
def __stop_broadcast_if_broadcasting(self, was_skipped=False): if not self.__is_broadcast_in_progress: return if self.__broadcast_proc: self.__logger.info( "Killing broadcast proc (if it's still running)...") was_killed = True try: os.kill(self.__broadcast_proc.pid, signal.SIGTERM) except Exception: # might raise: `ProcessLookupError: [Errno 3] No such process` was_killed = False exit_status = self.__broadcast_proc.wait() if exit_status != 0: if was_killed and exit_status == signal.SIGTERM: pass # We expect a specific non-zero exit code if the broadcast was killed. else: self.__logger.error( f'Got non-zero exit_status for broadcast proc: {exit_status}' ) self.__control_message_helper.send_msg( ControlMessageHelper.TYPE_SKIP_VIDEO, {}) if self.__playlist_item: if self.__should_reenqueue_current_playlist_item(was_skipped): self.__playlist.reenqueue( self.__playlist_item["playlist_video_id"]) else: self.__playlist.end_video( self.__playlist_item["playlist_video_id"]) self.__logger.info("Ended video broadcast.") Logger.set_uuid('') self.__broadcast_proc = None self.__playlist_item = None self.__is_broadcast_in_progress = False