def __init__(self, stream_url): # Make absolute paths from relative ones if stream_url.startswith('file://.'): stream_url = "file://" + os.path.abspath(stream_url.lstrip('file:/')) self.url = stream_url self._cache_file = CONSTANTS.CACHE_DIR + "streaminfo" self.codec_name = "" self.height = 0 self.width = 0 self.framerate = 0 self.has_audio = False self.force_udp = False self._parse_stream_details() self.valid_url = self._is_url_valid() self.valid_video_windowed = self._is_video_valid(windowed=True) self.valid_video_fullscreen = self._is_video_valid(windowed=False) self.weight = self._calculate_weight() self.quality = self.width * self.height LOG.INFO(self._LOG_NAME, "stream properties '%s', resolution '%ix%i@%i', codec '%s', " "calculated weight '%i', valid url '%i', has audio '%s', " "valid video 'windowed %i fullscreen %i', force UDP '%s'" % ( self.printable_url(), self.width, self.height, self.framerate, self.codec_name, self.weight, self.valid_url, self.has_audio, self.valid_video_windowed, self.valid_video_fullscreen, self.force_udp)) LOG.INFO(self._LOG_NAME, "RUN 'camplayer --rebuild-cache' IF THIS STREAM INFORMATION IS OUT OF DATE!!")
def __init__(self, x1, y1, x2, y2, gridindex, screen_idx, window_idx, display_idx): self.x1 = x1 # Upper left x-position of window self.y1 = y1 # Upper left y-position of window self.x2 = x2 # Lower right x-position of window self.y2 = y2 # Lower right y-position of window self.gridindex = gridindex # Grid indices covered by this window self.omx_player_pid = 0 # OMXplayer PID self._omx_audio_enabled = False # OMXplayer audio stream enabled self._omx_duration = 0 # OMXplayer reported stream duration self._layer = 0 # Player dispmanx layer self.visible = False # Is window in visible area? self._forced_fullscreen = self.native_fullscreen # Is window forced in fullscreen mode? self._fail_rate_hr = 0 # Stream failure rate of last hour self._time_playstatus = 0 # Timestamp of last playstatus check self._time_streamstart = 0 # Timestamp of last stream start self.streams = [] # Assigned stream(s) self.active_stream = None # Currently playing stream self._display_name = "" # Video OSD display name self._player = PLAYER.NONE # Currently active player for this window (OMX or VLC) self.playstate = PLAYSTATE.NONE # Current stream play state for this window self._window_num = window_idx + 1 self._screen_num = screen_idx + 1 self._display_num = display_idx + 1 self._omx_dbus_ident = str( "org.mpris.MediaPlayer2.omxplayer_D%02d_S%02d_W%02d" % (self._display_num, self._screen_num, self._window_num)) LOG.DEBUG( self._LOG_NAME, "init window with position '%i %i %i %i', gridindex '%s', " "omxplayer dbus name '%s'" % (x1, y1, x2, y2, str(gridindex), self._omx_dbus_ident))
def stream_switch_quality_down(self, check_only=False): """Switch to the next lower quality stream, if any""" if self.active_stream and self.playstate != PLAYSTATE.NONE: resolution = 10000 stream = None # Select the the next lower resolution stream for strm in self.streams: video_valid = strm.valid_video_fullscreen \ if self.fullscreen_mode else strm.valid_video_windowed if resolution < strm.quality < self.active_stream.quality and video_valid: resolution = strm.quality stream = strm # The lowest quality stream is already playing if not stream: LOG.INFO(self._LOG_NAME, "lowest quality stream already playing") return False if not check_only: self.stream_stop() time.sleep(0.1) self._stream_start(stream) return stream return False
def set_display_name(self, display_name): """Set player OSD text for this window""" if not display_name or self._display_name: return sub_file = CONSTANTS.CACHE_DIR + display_name + ".srt" try: # Create folder if not exist if not os.path.isdir(os.path.dirname(sub_file)): os.system("mkdir -p %s" % os.path.dirname(sub_file)) # Create subtitle file if not exist if not os.path.isfile(sub_file): with open(sub_file, 'w+') as file: # Important note: we can only show subs for a 99 hour period! file.write('00:00:00,00 --> 99:00:00,00\n') file.write(display_name + '\n') self._display_name = display_name except: # TODO: filter for read-only error only LOG.ERROR(self._LOG_NAME, "writing subtitle file failed, read only?")
def load_icons(cls): """Load pipng icon queue""" if not CONFIG.ENABLE_ICONS or not GLOBALS.PIPNG_SUPPORT: return for display_idx in range(GLOBALS.NUM_DISPLAYS): if len(cls._icons[display_idx]) <= 0: continue pngview_cmd = ["pipng", "-b", "0", # No 2nd background layer under image "-l", str(cls._foreground_layer), # Set layer number "-d", "2" if display_idx == 0 else "7", # Set display number "-i", # Start with all images invisible "-x", str(CONSTANTS.ICON_OFFSET_X), # 60px offset x-axis "-y", str(CONSTANTS.ICON_OFFSET_Y), # 60px offset y-axis ] # Add all images, currently limited to 10 for image in cls._icons[display_idx]: pngview_cmd.append(CONSTANTS.RESOURCE_DIR_ICONS + image) LOG.DEBUG(cls._MODULE, "Loading pipng for display '%i' with command '%s'" % (display_idx, pngview_cmd)) cls._proc_icons[display_idx] = \ subprocess.Popen(pngview_cmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
def pidpool_update(cls): """Update the PID pool of OMXplayer and VLC media player instances""" cls._player_pid_pool_cmdline = [[], []] try: player_pids = subprocess.check_output(['pidof', 'vlc'], universal_newlines=True, timeout=5).split() LOG.DEBUG("PIDpool", "active VLCplayer PIDs '%s'" % player_pids) for player_pid in player_pids: cls._player_pid_pool_cmdline[0].append(int(player_pid)) cls._player_pid_pool_cmdline[1].append( subprocess.check_output( ['cat', str('/proc/%s/cmdline' % player_pid)], universal_newlines=True, timeout=5)) except subprocess.CalledProcessError: pass try: player_pids = subprocess.check_output(['pidof', 'omxplayer.bin'], universal_newlines=True, timeout=5).split() LOG.DEBUG("PIDpool", "active OMXplayer PIDs '%s'" % player_pids) for player_pid in player_pids: cls._player_pid_pool_cmdline[0].append(int(player_pid)) cls._player_pid_pool_cmdline[1].append( subprocess.check_output( ['cat', str('/proc/%s/cmdline' % player_pid)], universal_newlines=True, timeout=5)) except subprocess.CalledProcessError: pass
def stream_stop(self): """Stop the playing stream""" if self.playstate == PLAYSTATE.NONE: return LOG.INFO( self._LOG_NAME, "stopping stream '%s' '%s'" % (self._omx_dbus_ident, self.active_stream.printable_url())) # VLC: # - send Dbus stop command, vlc stays idle in the background # - not every window has it's own vlc instance as vlc can only be used for fullscreen playback, # therefore we have to be sure that 'our instance' isn't already playing another stream. if self._player == PLAYER.VLCPLAYER and \ Window._vlc_active_stream_url[self._display_num - 1] == self.active_stream.url: # Stop playback but do not quit self._send_dbus_command(DBUS_COMMAND.PLAY_STOP) Window._vlc_active_stream_url[self._display_num - 1] = "" # OMXplayer: # - omxplayer doen't support an idle state, stopping playback will close omxplayer, # so in this case we also have to cleanup the pids. elif self._player == PLAYER.OMXPLAYER and self.omx_player_pid: try: os.kill(self.omx_player_pid, signal.SIGTERM) except Exception as error: LOG.ERROR(self._LOG_NAME, "pid kill error: %s" % str(error)) self._pidpool_remove_pid(self.omx_player_pid) self.omx_player_pid = 0 if self.active_stream: Window._total_weight -= self.get_weight(self.active_stream) self.active_stream = None self.playstate = PLAYSTATE.NONE self._omx_duration = 0
def _pidpool_remove_pid(cls, pid): """Remove Player PID from pidpool""" for idx, _pid in enumerate(cls._player_pid_pool_cmdline[0]): if _pid == pid: LOG.DEBUG("PIDpool", "removed Player PID '%i' from pool" % pid) del cls._player_pid_pool_cmdline[0][idx] del cls._player_pid_pool_cmdline[1][idx] return True return False
def load_backgrounds(cls): """Load pipng background queue""" if CONFIG.BACKGROUND_MODE == BACKGROUND.OFF or not GLOBALS.PIPNG_SUPPORT: return if CONFIG.BACKGROUND_MODE == BACKGROUND.HIDE_FRAMEBUFFER: for display_idx in range(GLOBALS.NUM_DISPLAYS): if len(cls._backgrounds[display_idx]) <= 0: continue subprocess.Popen(["pipng", "-b", "000F", "-n", "-d", "2" if display_idx == 0 else "7"], shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE) else: static_background = CONFIG.BACKGROUND_MODE == BACKGROUND.STATIC for display_idx in range(GLOBALS.NUM_DISPLAYS): if len(cls._backgrounds[display_idx]) <= 0: continue pngview_cmd = ["pipng", "-b", "0", # No 2nd background layer under image "-l", str(cls._background_layer), # Set layer number "-d", "2" if display_idx == 0 else "7", # Set display number "-h", # Hide lower layers (less GPU performance impact) ] if not static_background: pngview_cmd.append("-i") # Start with all images invisible # Add all background images, currently limited to 10 for image in cls._backgrounds[display_idx]: pngview_cmd.append(image) # TODO: find best match with static backgrounds and multiple screens if static_background: break LOG.DEBUG(cls._MODULE, "Loading pipng for display '%i' with command '%s'" % (display_idx + 1, pngview_cmd)) cls._proc_background[display_idx] = \ subprocess.Popen(pngview_cmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
def show_icon(cls, filename, display_idx=0): """Show pipng icon from queue""" if not CONFIG.ENABLE_ICONS or not GLOBALS.PIPNG_SUPPORT: return display_idx = 1 if display_idx == 1 else 0 # Show new image/icon for idx, image in enumerate(cls._icons[display_idx]): if filename == image: LOG.DEBUG(cls._MODULE, "setting icon '%s' visible for display '%i" % (filename, display_idx)) cls._proc_icons[display_idx].stdin.write(str(idx).encode('utf-8')) cls._proc_icons[display_idx].stdin.flush() cls.active_icon[display_idx] = filename
def stream_set_invisible(self, _async=False): """Keep the stream open but set it off screen""" if self.playstate == PLAYSTATE.NONE: return if self.visible: LOG.INFO( self._LOG_NAME, "stream set invisible '%s' '%s'" % (self._omx_dbus_ident, self.active_stream.printable_url())) if self._player == PLAYER.OMXPLAYER: # OMXplayer instance is playing inside the visible screen area. # Sending the position command with offset will move this instance out of the visible screen area. if self._omx_audio_enabled: self.visible = False self.stream_refresh() return videopos_arg = str( "%i %i %i %i" % (self.x1 + CONSTANTS.WINDOW_OFFSET, self.y1, self.x2 + CONSTANTS.WINDOW_OFFSET, self.y2)) if _async: setinvisible_thread = threading.Thread( target=self._send_dbus_command, args=( DBUS_COMMAND.OMXPLAYER_VIDEOPOS, videopos_arg, )) setinvisible_thread.start() else: self._send_dbus_command(DBUS_COMMAND.OMXPLAYER_VIDEOPOS, videopos_arg) else: # It's possible that another window hijacked our vlc instance, so do not send 'stop' then. if self.active_stream.url == Window._vlc_active_stream_url[ self._display_num - 1]: self._send_dbus_command(DBUS_COMMAND.PLAY_STOP) Window._vlc_active_stream_url[self._display_num - 1] = "" self.visible = False
def scale_background(cls, src_path, dest_path, dest_width, dest_height): """Scale background image to the requested width and height""" if not GLOBALS.FFMPEG_SUPPORT: return False ffmpeg_cmd = str("ffmpeg -i '%s' -vf scale=%i:%i '%s'" % (src_path, dest_width, dest_height, dest_path)) try: subprocess.check_output(ffmpeg_cmd, shell=True, stderr=subprocess.STDOUT, timeout=5) except (subprocess.CalledProcessError, subprocess.TimeoutExpired): LOG.ERROR(cls._MODULE, "Scaling background image '%s' failed" % src_path) if os.path.isfile(dest_path): return True return False
def stop_all_players(cls, sigkill=False): """Stop all players the fast and hard way""" term_cmd = '-9' if sigkill else '-15' try: subprocess.Popen(['killall', term_cmd, 'omxplayer.bin'], shell=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) subprocess.Popen(['killall', term_cmd, 'vlc'], shell=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) except Exception as error: LOG.ERROR(cls._LOG_NAME, "stop_all_players pid kill error: %s" % str(error))
def show_background(cls, filename, display_idx=0): """Show pipng background from queue""" if CONFIG.BACKGROUND_MODE != BACKGROUND.DYNAMIC or not GLOBALS.PIPNG_SUPPORT: return display_idx = 1 if display_idx == 1 else 0 if cls.active_background[display_idx] == filename: return # Show new image/icon for idx, image in enumerate(cls._backgrounds[display_idx]): if filename == image: LOG.DEBUG(cls._MODULE, "setting background '%s' visible for display '%i" % (filename, display_idx)) cls._proc_background[display_idx].stdin.write(str(idx).encode('utf-8')) cls._proc_background[display_idx].stdin.flush() cls.active_background[display_idx] = filename
def hide_icon(cls, display_idx=0): """Hide active pipng iconn""" if not CONFIG.ENABLE_ICONS or not GLOBALS.PIPNG_SUPPORT: return display_idx = 1 if display_idx == 1 else 0 if not cls.active_icon[display_idx]: return LOG.DEBUG(cls._MODULE, "hiding icon '%s' for display '%i" % (cls.active_icon[display_idx], display_idx)) cls._proc_icons[display_idx].stdin.write("i".encode('utf-8')) cls._proc_icons[display_idx].stdin.flush() cls.active_icon[display_idx] = "" # pipng needs some milliseconds to read stdin # Especially important when hide_icon() will be immediately followed by show_icon() time.sleep(0.025)
def stream_switch_quality_up(self, check_only=False, limit_default=True): """Switch to the next higher quality stream, if any""" if self.active_stream and self.playstate != PLAYSTATE.NONE: resolution = sys.maxsize stream = None # Limit max quality to the default for performance reasons if limit_default: resolution = self.get_default_stream( windowed=not self.fullscreen_mode).quality + 1 # Select the the next higher resolution stream for strm in self.streams: video_valid = strm.valid_video_fullscreen \ if self.fullscreen_mode else strm.valid_video_windowed if resolution > strm.quality > self.active_stream.quality and video_valid: resolution = strm.quality stream = strm # The highest quality stream is already playing if not stream: LOG.INFO(self._LOG_NAME, "highest quality stream already playing") return False if not check_only: self.stream_stop() time.sleep(0.1) self._stream_start(stream) return stream return False
def log(comp, params, add_trailing_row=False, add_heading_row=False): if add_heading_row: LOG(horizontal_row) LOG(template.format(comp, str(params))) if add_trailing_row: LOG(horizontal_row)
def get_stream_playstate(self): """ Get and update the stream's playstate, don't use this time consuming method too often, use 'self.playstate' when you can. """ if self.playstate == PLAYSTATE.NONE: return self.playstate # Allow at least 1 second for the player to startup if self.playstate == PLAYSTATE.INIT1 and self.playtime < 1: return self.playstate old_playstate = self.playstate # Assign the player PID if self.playstate == PLAYSTATE.INIT1: if self._player == PLAYER.VLCPLAYER: pid = self.get_vlc_pid(self._display_num) else: pid = self.get_omxplayer_pid() if pid > 0: if self._player == PLAYER.VLCPLAYER: Window.vlc_player_pid[self._display_num - 1] = pid else: self.omx_player_pid = pid self.playstate = PLAYSTATE.INIT2 LOG.DEBUG( self._LOG_NAME, "assigned PID '%i' for stream '%s' '%s'" % (pid, self._omx_dbus_ident, self.active_stream.printable_url())) elif self.playtime > CONSTANTS.PLAYER_INITIALIZE_MS / 1000: self.playstate = PLAYSTATE.BROKEN # Check if the player is actually playing media # DBus calls are time consuming, so limit them elif time.monotonic() > (self._time_playstatus + 10) or \ (self.playstate == PLAYSTATE.INIT2 and time.monotonic() > (self._time_playstatus + 1)): LOG.DEBUG( self._LOG_NAME, "fetching playstate for stream '%s' '%s'" % (self._omx_dbus_ident, self.active_stream.printable_url())) duration_diff = 0 output = "" # Check playstate and kill the player if it does not respond properly # 04/04/2020: Under some circumstances omxplayer freezes with corrupt streams (bad wifi/network quality etc.), # while it still reports its playstate as 'playing', # therefore we monitor will monitor the reported 'duration' (for livestreams) from now on. if not self.active_stream.url.startswith( 'file://') and self._player == PLAYER.OMXPLAYER: output = self._send_dbus_command( DBUS_COMMAND.PLAY_DURATION, kill_player_on_error=self.playtime > CONFIG.PLAYTIMEOUT_SEC) try: duration = int(output.split("int64")[1].strip()) duration_diff = duration - self._omx_duration self._omx_duration = duration except Exception: self._omx_duration = 0 else: output = self._send_dbus_command( DBUS_COMMAND.PLAY_STATUS, kill_player_on_error=self.playtime > CONFIG.PLAYTIMEOUT_SEC) if (output and "playing" in str(output).lower()) or duration_diff > 0: self.playstate = PLAYSTATE.PLAYING else: # Only set broken after a timeout period, # so keep the init state the first seconds if self.playtime > CONFIG.PLAYTIMEOUT_SEC: if self._player == PLAYER.OMXPLAYER or self.visible: # Don't set broken when VLC is in "stopped" state # Stopped state occurs when not visible self.playstate = PLAYSTATE.BROKEN self._time_playstatus = time.monotonic() if old_playstate != self.playstate: LOG.INFO( self._LOG_NAME, "stream playstate '%s' for stream '%s' '%s'" % (self.playstate.name, self._omx_dbus_ident, self.active_stream.printable_url())) return self.playstate
def _send_dbus_command(self, command, argument="", kill_player_on_error=True, retries=CONSTANTS.DBUS_RETRIES): """Send command to player with DBus""" response = "" command_destination = "" command_prefix = "" if self._player == PLAYER.OMXPLAYER: command_destination = self._omx_dbus_ident # OMXplayer needs some environment variables command_prefix = str( "export DBUS_SESSION_BUS_ADDRESS=`cat /tmp/omxplayerdbus.%s` && " "export DBUS_SESSION_BUS_PID=`cat /tmp/omxplayerdbus.%s.pid` && " % (GLOBALS.USERNAME, GLOBALS.USERNAME)) elif self._player == PLAYER.VLCPLAYER: command_destination = Window._vlc_dbus_ident[self._display_num - 1] # VLC changes its DBus string to 'org.mpris.MediaPlayer2.vlc.instancePID' # when opening a second instance, so we have to append this PID first. if 'instance' in command_destination: command_destination += str( Window.vlc_player_pid[self._display_num - 1]) for i in range(retries + 1): try: if command == DBUS_COMMAND.OMXPLAYER_VIDEOPOS: response = subprocess.check_output( command_prefix + "dbus-send --print-reply=literal --reply-timeout=%i " "--dest=%s /org/mpris/MediaPlayer2 " "org.mpris.MediaPlayer2.Player.%s objpath:/not/used " "string:'%s'" % (CONSTANTS.DBUS_TIMEOUT_MS, command_destination, command, argument), shell=True, stderr=subprocess.STDOUT).decode().strip() elif command == DBUS_COMMAND.PLAY_STOP: response = subprocess.check_output( command_prefix + "dbus-send --print-reply=literal --reply-timeout=%i " "--dest=%s /org/mpris/MediaPlayer2 " "org.mpris.MediaPlayer2.Player.%s" % (CONSTANTS.DBUS_TIMEOUT_MS, command_destination, command), shell=True, stderr=subprocess.STDOUT).decode().strip() elif command == DBUS_COMMAND.PLAY_PLAY: response = subprocess.check_output( command_prefix + "dbus-send --print-reply=literal --reply-timeout=%i " "--dest=%s /org/mpris/MediaPlayer2 " "org.mpris.MediaPlayer2.Player.%s string:'%s'" % (CONSTANTS.DBUS_TIMEOUT_MS, command_destination, command, self.active_stream.url), shell=True, stderr=subprocess.STDOUT).decode().strip() elif command == DBUS_COMMAND.PLAY_VOLUME: response = subprocess.check_output( command_prefix + "dbus-send --print-reply=literal --reply-timeout=%i " "--dest=%s /org/mpris/MediaPlayer2 " "org.freedesktop.DBus.Properties.Set " "string:'org.mpris.MediaPlayer2.Player' string:'%s' variant:double:%f" % (CONSTANTS.DBUS_TIMEOUT_MS, command_destination, command, argument), shell=True, stderr=subprocess.STDOUT).decode().strip() else: response = subprocess.check_output( command_prefix + "dbus-send --print-reply=literal --reply-timeout=%i " "--dest=%s /org/mpris/MediaPlayer2 " "org.freedesktop.DBus.Properties.Get " "string:'org.mpris.MediaPlayer2.Player' string:'%s'" % (CONSTANTS.DBUS_TIMEOUT_MS, command_destination, command), shell=True, stderr=subprocess.STDOUT).decode().strip() LOG.DEBUG( self._LOG_NAME, "DBus response to command '%s:%s %s' is '%s'" % (command_destination, command, argument, response)) except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as ex: if i == retries: if self._player == PLAYER.VLCPLAYER: player_pid = Window.vlc_player_pid[self._display_num - 1] else: player_pid = self.omx_player_pid LOG.ERROR( self._LOG_NAME, "DBus '%s' is not responding correctly after '%i' attemps, " "give up now" % (command_destination, retries + 1)) if kill_player_on_error and player_pid > 0: LOG.ERROR( self._LOG_NAME, "DBus '%s' closing the associated player " "with PID '%i' now" % (command_destination, player_pid)) try: os.kill(player_pid, signal.SIGKILL) except ProcessLookupError: LOG.DEBUG(self._LOG_NAME, "killing PID '%i' failed" % player_pid) self._pidpool_remove_pid(player_pid) if self._player == PLAYER.VLCPLAYER: Window.vlc_player_pid[self._display_num - 1] = 0 else: self.omx_player_pid = 0 else: LOG.WARNING( self._LOG_NAME, "DBus '%s' is not responding correctly, " "retrying within 250ms" % command_destination) time.sleep(0.25) continue break return response
def train_mc(self, dataloader, physics, epochs, lr, ckp_interval, schedule, residual=True, pretrained=None, task='', loss_type='l2', cat=True, report_psnr=False, lr_cos=False): save_path = './ckp/{}_mc_{}'.format(get_timestamp(), 'res' if residual else '', task) os.makedirs(save_path, exist_ok=True) generator = UNet(in_channels=self.in_channels, out_channels=self.out_channels, compact=4, residual=residual, circular_padding=True, cat=cat).to(self.device) if pretrained: checkpoint = torch.load(pretrained) generator.load_state_dict(checkpoint['state_dict']) if loss_type == 'l2': criterion_mc = torch.nn.MSELoss().to(self.device) if loss_type == 'l1': criterion_mc = torch.nn.L1Loss().to(self.device) optimizer = Adam(generator.parameters(), lr=lr['G'], weight_decay=lr['WD']) if report_psnr: log = LOG(save_path, filename='training_loss', field_name=['epoch', 'loss_fc', 'psnr', 'mse']) else: log = LOG(save_path, filename='training_loss', field_name=['epoch', 'loss_fc']) for epoch in range(epochs): adjust_learning_rate(optimizer, epoch, lr['G'], lr_cos, epochs, schedule) loss = closure_mc(generator, dataloader, physics, optimizer, criterion_mc, self.dtype, self.device, report_psnr) log.record(epoch + 1, *loss) if report_psnr: print('{}\tEpoch[{}/{}]\tmc={:.4e}\tpsnr={:.4f}\tmse={:.4e}'. format(get_timestamp(), epoch, epochs, *loss)) else: print('{}\tEpoch[{}/{}]\tmc={:.4e}'.format( get_timestamp(), epoch, epochs, *loss)) if epoch % ckp_interval == 0 or epoch + 1 == epochs: state = { 'epoch': epoch, 'state_dict': generator.state_dict(), 'optimizer': optimizer.state_dict() } torch.save( state, os.path.join(save_path, 'ckp_{}.pth.tar'.format(epoch))) log.close()
def stream_set_visible(self, _async=False, fullscreen=None): """Set an active off screen stream back on screen""" if self._player == PLAYER.VLCPLAYER \ and not self.get_vlc_pid(self._display_num): return if self.playstate == PLAYSTATE.NONE: return if fullscreen is None: fullscreen = self.fullscreen_mode if not self.visible or (fullscreen != self.fullscreen_mode): LOG.INFO( self._LOG_NAME, "stream set visible '%s' '%s'" % (self._omx_dbus_ident, self.active_stream.printable_url())) self.fullscreen_mode = fullscreen if self._player == PLAYER.OMXPLAYER: # OMXplayer instance is playing outside the visible screen area. # Sending the position command will move this instance into the visible screen area. if fullscreen: videopos_arg = str("%i %i %i %i" % (CONSTANTS.VIRT_SCREEN_OFFSET_X, CONSTANTS.VIRT_SCREEN_OFFSET_Y, CONSTANTS.VIRT_SCREEN_OFFSET_X + CONSTANTS.VIRT_SCREEN_WIDTH, CONSTANTS.VIRT_SCREEN_OFFSET_Y + CONSTANTS.VIRT_SCREEN_HEIGHT)) else: videopos_arg = str("%i %i %i %i" % (self.x1, self.y1, self.x2, self.y2)) # Re-open OMXplayer with the audio stream enabled if CONFIG.AUDIO_MODE == AUDIOMODE.FULLSCREEN and fullscreen \ and not self._omx_audio_enabled and self.active_stream.has_audio: self.visible = True self.stream_refresh() return # Re-open OMXplayer with the audio stream disabled if self._omx_audio_enabled and not fullscreen: self.visible = True self.stream_refresh() return if _async: setvisible_thread = threading.Thread( target=self._send_dbus_command, args=( DBUS_COMMAND.OMXPLAYER_VIDEOPOS, videopos_arg, )) setvisible_thread.start() else: self._send_dbus_command(DBUS_COMMAND.OMXPLAYER_VIDEOPOS, videopos_arg) elif fullscreen: # VLC player instance can be playing or in idle state. # Sending the play command will start fullscreen playback of our video/stream. # When VLC is playing other content, we will hijack it. # Start our stream self._send_dbus_command(DBUS_COMMAND.PLAY_PLAY) # Pretend like the player just started again self.playstate = PLAYSTATE.INIT2 self._time_streamstart = time.monotonic() # Mark our steam as the active one for this display Window._vlc_active_stream_url[self._display_num - 1] = self.active_stream.url else: # Windowed with VLC not supported -> stop video self.stream_stop() self.visible = True
def log(msg, trailing_line=False): LOG(msg, trailing_line)
def _parse_stream_details(self): """Read stream details from cache file or parse stream directly""" if not self._is_url_valid(): return parsed_ok = False video_found = False if os.path.isfile(self._cache_file): with open(self._cache_file, 'r') as stream_file: data = json.load(stream_file) if self.printable_url() in data.keys(): stream_props = data.get(self.printable_url()) self.codec_name = stream_props.get('codec_name') self.height = stream_props.get('height') self.width = stream_props.get('width') self.framerate = stream_props.get('framerate') self.has_audio = stream_props.get('audio') parsed_ok = True if not parsed_ok: try: # Invoke ffprobe, 20s timeout required for pi zero streams = subprocess.check_output([ 'ffprobe', '-v', 'error', '-show_entries', 'stream=codec_type,height,width,codec_name,bit_rate,max_bit_rate,avg_frame_rate', self.url], universal_newlines=True, timeout=10, stderr=subprocess.STDOUT).split("[STREAM]") for stream in streams: streamprops = stream.split() if "codec_type=video" in stream and not video_found: video_found = True for streamproperty in streamprops: if "codec_name" in streamproperty: self.codec_name = streamproperty.split("=")[1] if "height" in streamproperty: self.height = int(streamproperty.split("=")[1]) if "width" in streamproperty: self.width = int(streamproperty.split("=")[1]) if "avg_frame_rate" in streamproperty: try: framerate = streamproperty.split("=")[1] # ffprobe returns framerate as fraction, # a zero division exception is therefore possible self.framerate = int( framerate.split("/")[0])/int(framerate.split("/")[1]) except Exception: self.framerate = 0 elif "codec_type=audio" in stream and not self.has_audio: self.has_audio = True if video_found: try: self._write_stream_details() # TODO: filter read-only exception except Exception: LOG.ERROR(self._LOG_NAME, "writing ffprobe results to file failed, read only?") # TODO: logging exceptions can spawn credentials?? except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as ex: LOG.ERROR(self._LOG_NAME, "ffprobe exception: %s" % str(ex))
def train_ei_adv(self, dataloader, physics, transform, epochs, lr, alpha, ckp_interval, schedule, residual=True, pretrained=None, task='', loss_type='l2', cat=True, report_psnr=False, lr_cos=False): save_path = './ckp/{}_ei_adv_{}'.format(get_timestamp(), task) os.makedirs(save_path, exist_ok=True) generator = UNet(in_channels=self.in_channels, out_channels=self.out_channels, compact=4, residual=residual, circular_padding=True, cat=cat) if pretrained: checkpoint = torch.load(pretrained) generator.load_state_dict(checkpoint['state_dict']) discriminator = Discriminator( (self.in_channels, self.img_width, self.img_height)) generator = generator.to(self.device) discriminator = discriminator.to(self.device) if loss_type == 'l2': criterion_mc = torch.nn.MSELoss().to(self.device) criterion_ei = torch.nn.MSELoss().to(self.device) if loss_type == 'l1': criterion_mc = torch.nn.L1Loss().to(self.device) criterion_ei = torch.nn.L1Loss().to(self.device) criterion_gan = torch.nn.MSELoss().to(self.device) optimizer_G = Adam(generator.parameters(), lr=lr['G'], weight_decay=lr['WD']) optimizer_D = Adam(discriminator.parameters(), lr=lr['D'], weight_decay=0) if report_psnr: log = LOG(save_path, filename='training_loss', field_name=[ 'epoch', 'loss_mc', 'loss_ei', 'loss_g', 'loss_G', 'loss_D', 'psnr', 'mse' ]) else: log = LOG(save_path, filename='training_loss', field_name=[ 'epoch', 'loss_mc', 'loss_ei', 'loss_g', 'loss_G', 'loss_D' ]) for epoch in range(epochs): adjust_learning_rate(optimizer_G, epoch, lr['G'], lr_cos, epochs, schedule) adjust_learning_rate(optimizer_D, epoch, lr['D'], lr_cos, epochs, schedule) loss = closure_ei_adv(generator, discriminator, dataloader, physics, transform, optimizer_G, optimizer_D, criterion_mc, criterion_ei, criterion_gan, alpha, self.dtype, self.device, report_psnr) log.record(epoch + 1, *loss) if report_psnr: print( '{}\tEpoch[{}/{}]\tfc={:.4e}\tti={:.4e}\tg={:.4e}\tG={:.4e}\tD={:.4e}\tpsnr={:.4f}\tmse={:.4e}' .format(get_timestamp(), epoch, epochs, *loss)) else: print( '{}\tEpoch[{}/{}]\tfc={:.4e}\tti={:.4e}\tg={:.4e}\tG={:.4e}\tD={:.4e}' .format(get_timestamp(), epoch, epochs, *loss)) if epoch % ckp_interval == 0 or epoch + 1 == epochs: state = { 'epoch': epoch, 'state_dict_G': generator.state_dict(), 'state_dict_D': discriminator.state_dict(), 'optimizer_G': optimizer_G.state_dict(), 'optimizer_D': optimizer_D.state_dict() } torch.save( state, os.path.join(save_path, 'ckp_{}.pth.tar'.format(epoch))) log.close()
def main(): """Application entry point""" global running num_array = [] last_added = time.monotonic() ignore_quit = False if not platform.system() == "Linux": sys.exit("'%s' OS not supported!" % platform.system()) if os.geteuid() == 0: sys.exit("Camplayer is not supposed to be run as root!") GLOBALS.PYTHON_VER = sys.version_info if GLOBALS.PYTHON_VER < CONSTANTS.PYTHON_VER_MIN: sys.exit("Python version '%i.%i' or newer required!" % (CONSTANTS.PYTHON_VER_MIN[0], CONSTANTS.PYTHON_VER_MIN[1])) # Started with arguments? if len(sys.argv) > 1: for idx, arg in enumerate(sys.argv): # 1st argument is application if idx == 0: continue # Help info if arg == "-h" or arg == "--help": print(" -h --help Print this help") print(" -v --version Print version info") print(" -c --config Use a specific config file") print(" --rebuild-cache Rebuild cache on startup") print(" --rebuild-cache-exit Rebuild cache and exit afterwards") print(" -d --demo Demo mode") print(" --ignorequit Don't quit when the 'Q' key is pressed") sys.exit(0) # Run in a specific mode if arg == "--rebuild-cache" or arg == "--rebuild-cache-exit": # Clearing the cache clear_cache() # Rebuild cache only and exit if arg == "--rebuild-cache-exit": # Exit when reaching the main loop running = False # Run with a specific config file if arg == "-c" or arg == "--config" and (idx + 1) < len(sys.argv): CONSTANTS.CONFIG_PATH = sys.argv[idx + 1] # Display version info if arg == "-v" or arg == "--version": print("version " + __version__) sys.exit(0) # Run demo mode if arg == "-d" or arg == "--demo": CONSTANTS.CONFIG_PATH = CONSTANTS.DEMO_CONFIG_PATH # Ignore keyboard 'quit' command if arg == "--ignorequit": ignore_quit = True # Load settings from config file CONFIG.load() # Signal handlers signal.signal(signal.SIGTERM, signal_handler) signal.signal(signal.SIGINT, signal_handler) LOG.INFO(_LOG_NAME, "Starting camplayer version %s" % __version__) LOG.INFO(_LOG_NAME, "Using config file '%s' and cache directory '%s'" % (CONSTANTS.CONFIG_PATH, CONSTANTS.CACHE_DIR)) # Cleanup some stuff in case something went wrong on the previous run utils.kill_service('omxplayer.bin', force=True) utils.kill_service('vlc', force=True) utils.kill_service('pipng', force=True) # OMXplayer is absolutely required! if not utils.os_package_installed("omxplayer.bin"): sys.exit("OMXplayer not installed but required!") # ffprobe is absolutely required! if not utils.os_package_installed("ffprobe"): sys.exit("ffprobe not installed but required!") # Get system info sys_info = utils.get_system_info() gpu_mem = utils.get_gpu_memory() hw_info = utils.get_hardware_info() # Set some globals for later use GLOBALS.PI_SOC = hw_info.get("soc") # Not very reliable, usually reports BCM2835 GLOBALS.PI_MODEL = hw_info.get("model") GLOBALS.PI_SOC_HEVC = hw_info.get('hevc') GLOBALS.NUM_DISPLAYS = 2 if hw_info.get('dual_hdmi') else 1 GLOBALS.VLC_SUPPORT = utils.os_package_installed("vlc") GLOBALS.PIPNG_SUPPORT = utils.os_package_installed("pipng") GLOBALS.FFMPEG_SUPPORT = utils.os_package_installed("ffmpeg") GLOBALS.USERNAME = os.getenv('USER') # Log system info LOG.INFO(_LOG_NAME, "********************** SYSTEM INFO **********************") LOG.INFO(_LOG_NAME, str("Camplayer version = %s" % __version__)) LOG.INFO(_LOG_NAME, str("Operating system = %s" % sys_info)) LOG.INFO(_LOG_NAME, str("Raspberry Pi SoC = %s" % hw_info.get("soc"))) LOG.INFO(_LOG_NAME, str("Raspberry Pi revision = %s" % hw_info.get("revision"))) LOG.INFO(_LOG_NAME, str("Raspberry Pi model name = %s" % hw_info.get("model"))) LOG.INFO(_LOG_NAME, str("GPU memory allocation = %i MB" % gpu_mem)) LOG.INFO(_LOG_NAME, str("Python version = %s MB" % sys.version.splitlines()[0])) LOG.INFO(_LOG_NAME, str("VLC installed = %s" % GLOBALS.VLC_SUPPORT)) LOG.INFO(_LOG_NAME, str("pipng installed = %s" % GLOBALS.PIPNG_SUPPORT)) LOG.INFO(_LOG_NAME, str("ffmpeg installed = %s" % GLOBALS.FFMPEG_SUPPORT)) LOG.INFO(_LOG_NAME, "*********************************************************") # Register for keyboard 'press' events, requires root # TODO: check privileges? keyboard = InputMonitor(event_type=['press']) # Log overwrites for debugging purpose for setting in CONFIG.advanced_overwritten: LOG.INFO(_LOG_NAME, "advanced setting overwritten for '%s' is '%s'" % (setting[0], setting[1])) # Does this system fulfill the minimal requirements if CONFIG.HARDWARE_CHECK: if not hw_info.get("supported"): sys.exit("Unsupported hardware with revision %s ..." % hw_info.get("revision")) if gpu_mem < CONSTANTS.MIN_GPU_MEM: sys.exit("GPU memory of '%i' MB insufficient ..." % gpu_mem) # Auto detect screen resolution # For the raspberry pi 4: # both HDMI displays are supposed to have the same configuration if CONFIG.SCREEN_HEIGHT == 0 or CONFIG.SCREEN_WIDTH == 0: display_conf = utils.get_display_mode() CONFIG.SCREEN_HEIGHT = display_conf.get('res_height') CONFIG.SCREEN_WIDTH = display_conf.get('res_width') LOG.INFO(_LOG_NAME, "Detected screen resolution for HDMI0 is '%ix%i@%iHz'" % ( CONFIG.SCREEN_WIDTH, CONFIG.SCREEN_HEIGHT, display_conf.get('framerate'))) if CONFIG.SCREEN_HEIGHT <= 0: CONFIG.SCREEN_HEIGHT = 1080 if CONFIG.SCREEN_WIDTH <= 0: CONFIG.SCREEN_WIDTH = 1920 # Are we sure the 2nd HDMI is on for dual HDMI versions? if GLOBALS.NUM_DISPLAYS == 2: # Check for resolution instead of display name as the latter one is empty with force HDMI hotplug if not utils.get_display_mode(display=7).get('res_height'): GLOBALS.NUM_DISPLAYS = 1 # Calculate the virtual screen size now that we now the physical screen size CONSTANTS.VIRT_SCREEN_WIDTH = int(CONFIG.SCREEN_WIDTH * (100 - CONFIG.SCREEN_DOWNSCALE) / 100) CONSTANTS.VIRT_SCREEN_HEIGHT = int(CONFIG.SCREEN_HEIGHT * (100 - CONFIG.SCREEN_DOWNSCALE) / 100) CONSTANTS.VIRT_SCREEN_OFFSET_X = int((CONFIG.SCREEN_WIDTH - CONSTANTS.VIRT_SCREEN_WIDTH) / 2) CONSTANTS.VIRT_SCREEN_OFFSET_Y = int((CONFIG.SCREEN_HEIGHT - CONSTANTS.VIRT_SCREEN_HEIGHT) / 2) LOG.INFO(_LOG_NAME, "Using a virtual screen resolution of '%ix%i'" % (CONSTANTS.VIRT_SCREEN_WIDTH, CONSTANTS.VIRT_SCREEN_HEIGHT)) # Workaround: srt subtitles have a maximum display time of 99 hours if CONFIG.VIDEO_OSD and (not CONFIG.REFRESHTIME_MINUTES or CONFIG.REFRESHTIME_MINUTES >= 99 * 60): CONFIG.REFRESHTIME_MINUTES = 99 * 60 LOG.WARNING(_LOG_NAME, "Subtitle based OSD enabled, forcing 'refreshtime' to '%i'" % CONFIG.REFRESHTIME_MINUTES) # Show 'loading' on master display BackGroundManager.show_icon_instant(BackGround.LOADING, display_idx=0) # Initialize screens and windows screenmanager = ScreenManager() if screenmanager.valid_screens < 1: sys.exit("No valid screen configuration found, check your config file!") # Hide 'loading' message on master display BackGroundManager.hide_icon_instant(display_idx=0) # Working loop while running: # Trigger screenmanager working loop screenmanager.do_work() for event in keyboard.get_events(): last_added = time.monotonic() if event.code in KEYCODE.KEY_NUM.keys(): LOG.DEBUG(_LOG_NAME, "Numeric key event: %i" % KEYCODE.KEY_NUM.get(event.code)) num_array.append(KEYCODE.KEY_NUM.get(event.code)) # Two digit for numbers from 0 -> 99 if len(num_array) > 2: num_array.pop(0) else: # Non numeric key, clear numeric num_array num_array.clear() if event.code == KEYCODE.KEY_RIGHT: screenmanager.on_action(Action.SWITCH_NEXT) elif event.code == KEYCODE.KEY_LEFT: screenmanager.on_action(Action.SWITCH_PREV) elif event.code == KEYCODE.KEY_UP: screenmanager.on_action(Action.SWITCH_QUALITY_UP) elif event.code == KEYCODE.KEY_DOWN: screenmanager.on_action(Action.SWITCH_QUALITY_DOWN) elif event.code == KEYCODE.KEY_ENTER or event.code == KEYCODE.KEY_KPENTER: screenmanager.on_action(Action.SWITCH_SINGLE, 0) elif event.code == KEYCODE.KEY_ESC or event.code == KEYCODE.KEY_EXIT: screenmanager.on_action(Action.SWITCH_GRID) elif event.code == KEYCODE.KEY_SPACE: screenmanager.on_action(Action.SWITCH_PAUSE_UNPAUSE) elif event.code == KEYCODE.KEY_D: screenmanager.on_action(Action.SWITCH_DISPLAY_CONTROL) elif event.code == KEYCODE.KEY_Q and not ignore_quit: running = False break # Timeout between key presses expired? if time.monotonic() > (last_added + (CONSTANTS.KEY_TIMEOUT_MS / 1000)): num_array.clear() # 1 second delay to accept multiple digit numbers elif time.monotonic() > (last_added + (CONSTANTS.KEY_MULTIDIGIT_MS / 1000)) and len(num_array) > 0: LOG.INFO(_LOG_NAME, "Process numeric key input '%s'" % str(num_array)) number = 0 number += num_array[-2] * 10 if len(num_array) > 1 else 0 number += num_array[-1] if number == 0: num_array.clear() screenmanager.on_action(Action.SWITCH_GRID) else: num_array.clear() screenmanager.on_action(Action.SWITCH_SINGLE, number - 1) time.sleep(0.1) # Cleanup stuff before exit keyboard.destroy() BackGroundManager.destroy() utils.kill_service('omxplayer.bin', force=True) utils.kill_service('vlc', force=True) utils.kill_service('pipng', force=True) LOG.INFO(_LOG_NAME, "Exiting raspberry pi camplayer, have a nice day!") sys.exit(0)
def log(msg, add_trailing=True): LOG(msg, add_trailing)
def _stream_start(self, stream=None): """Start the specified stream if any, else the default will be played""" if self.playstate != PLAYSTATE.NONE: return if len(self.streams) <= 0: return if not stream: stream = self.get_default_stream() if not stream: return win_width = CONSTANTS.VIRT_SCREEN_WIDTH if self.fullscreen_mode else self.window_width win_height = CONSTANTS.VIRT_SCREEN_HEIGHT if self.fullscreen_mode else self.window_height sub_file = "" if self._display_name and CONFIG.VIDEO_OSD: sub_file = CONSTANTS.CACHE_DIR + self._display_name + ".srt" LOG.INFO( self._LOG_NAME, "starting stream '%s' '%s' with resolution '%ix%i' and weight '%i' in a window '%ix%i'" % (self._omx_dbus_ident, stream.printable_url(), stream.width, stream.height, self.get_weight(stream), win_width, win_height)) # OMXplayer can play in fullscreen and windowed mode # One instance per window if stream.valid_video_windowed: self._player = PLAYER.OMXPLAYER # Layer should be unique to avoid visual glitches/collisions omx_layer_arg = (self._screen_num * CONSTANTS.MAX_WINDOWS) + self._window_num if self.fullscreen_mode and self.visible: # Window position also required for fullscreen playback, # otherwise lower layers will be disabled when moving the window position later on omx_pos_arg = str("%i %i %i %i" % (CONSTANTS.VIRT_SCREEN_OFFSET_X, CONSTANTS.VIRT_SCREEN_OFFSET_Y, CONSTANTS.VIRT_SCREEN_OFFSET_X + CONSTANTS.VIRT_SCREEN_WIDTH, CONSTANTS.VIRT_SCREEN_OFFSET_Y + CONSTANTS.VIRT_SCREEN_HEIGHT)) else: omx_pos_arg = str( "%i %i %i %i" % (self.x1 + (0 if self.visible else CONSTANTS.WINDOW_OFFSET), self.y1, self.x2 + (0 if self.visible else CONSTANTS.WINDOW_OFFSET), self.y2)) player_cmd = [ 'omxplayer', '--no-keys', # No keyboard input '--no-osd', # No OSD '--aspect-mode', 'stretch', # Stretch video if aspect doesn't match '--dbus_name', self. _omx_dbus_ident, # Dbus name for controlling position etc. '--threshold', str(CONFIG.BUFFERTIME_MS / 1000), # Threshold of buffer in seconds '--layer', str(omx_layer_arg), # Dispmanx layer '--alpha', '255', # No transparency '--nodeinterlace', # Assume progressive streams '--nohdmiclocksync', # Clock sync makes no sense with multiple clock sources '--avdict', 'rtsp_transport:tcp', # Force RTSP over TCP '--display', '7' if self._display_num == 2 else '2', # 2 is HDMI0 (default), 7 is HDMI1 (pi4) '--timeout', str(CONFIG.PLAYTIMEOUT_SEC ), # Give up playback after this period of trying '--win', omx_pos_arg # Window position ] if stream.url.startswith('file://'): player_cmd.append( '--loop') # Loop for local files (demo/test mode) else: player_cmd.append( '--live') # Avoid sync issues with long playing streams if CONFIG.AUDIO_MODE == AUDIOMODE.FULLSCREEN and \ self.visible and self.fullscreen_mode and stream.has_audio: # OMXplayer can only open 8 instances instead of 16 when audio is enabled, # this can also lead to total system lockups... # Work around this by disabling the audio stream when in windowed mode, # in fullscreen mode, we can safely enable audio again. # set_visible() and set_invisible() methods are also adopted for this. # Volume % to millibels conversion volume = int(2000 * math.log10(max(CONFIG.AUDIO_VOLUME, 0.001) / 100)) player_cmd.extend(['--vol', str(volume)]) # Set audio volume self._omx_audio_enabled = True else: player_cmd.extend(['--aidx', '-1']) # Disable audio stream self._omx_audio_enabled = False # Show our channel name with a custom subtitle file? # OMXplayer OSD not supported on pi4 hardware if sub_file and not "4B" in GLOBALS.PI_MODEL: if os.path.isfile(sub_file): player_cmd.extend(['--subtitles', sub_file ]) # Add channel name as subtitle player_cmd.extend([ '--no-ghost-box', '--align', 'center', '--lines', '1' ]) # Set subtitle properties # VLC media player can play only in fullscreen mode # One fullscreen instance per display elif self.fullscreen_mode and stream.valid_video_fullscreen: self._player = PLAYER.VLCPLAYER player_cmd = [ 'cvlc', '--fullscreen', # VLC does not support windowed mode without X11 '--network-caching=' + str(CONFIG.BUFFERTIME_MS ), # Threshold of buffer in miliseconds '--rtsp-tcp', # Force RTSP over TCP '--no-keyboard-events', # No keyboard events '--mmal-display=hdmi-' + str(self._display_num), # Select the correct display '--mmal-layer=0', # OMXplayer uses layers starting from 0, don't interference '--input-timeshift-granularity=0', # Disable timeshift feature '--vout=mmal_vout', # Force MMAL mode '--gain=1', # Audio gain '--no-video-title-show' # Disable filename popup on start ] # Keep in mind that VLC instances can be reused for # other windows with possibly other audio settings! # So don't disable the audio output to quickly! if CONFIG.AUDIO_MODE == AUDIOMODE.FULLSCREEN: # VLC does not have a command line volume argument?? pass else: player_cmd.append('--no-audio') # Disable audio stream if stream.url.startswith('file://'): player_cmd.append( '--repeat') # Loop for local files (demo/test mode) # Show our channel name with a custom subtitle file? if sub_file and os.path.isfile(sub_file): player_cmd.extend(['--sub-file', sub_file]) # Add channel name as subtitle # TODO: we need te reopen VLC every time for the correct sub? if ((sub_file and os.path.isfile(sub_file)) or Window._vlc_subs_enabled[self._display_num - 1]) and \ self.get_vlc_pid(self._display_num): LOG.WARNING( self._LOG_NAME, "closing already active VLC instance for display '%i' " "as subtitles (video OSD) are enabled" % self._display_num) player_pid = self.get_vlc_pid(self._display_num) utils.terminate_process(player_pid, force=True) self._pidpool_remove_pid(player_pid) Window.vlc_player_pid[self._display_num - 1] = 0 else: LOG.ERROR( self._LOG_NAME, "stream '%s' with codec '%s' is not valid for playback" % (stream.printable_url(), stream.codec_name)) return # Check hardware video decoder impact if Window._total_weight + self.get_weight( stream) > CONSTANTS.HW_DEC_MAX_WEIGTH and CONFIG.HARDWARE_CHECK: LOG.ERROR( self._LOG_NAME, "current hardware decoder weight is '%i', max decoder weight is '%i'" % (Window._total_weight, CONSTANTS.HW_DEC_MAX_WEIGTH)) return else: Window._total_weight += self.get_weight(stream) # Set URL before stripping self.active_stream = stream url = stream.url if self._player == PLAYER.VLCPLAYER and self.get_vlc_pid( self._display_num): LOG.DEBUG( self._LOG_NAME, "reusing already active VLC instance for display '%i'" % self._display_num) if self.visible: # VLC player instance can be playing or in idle state. # Sending the play command will start fullscreen playback of our video/stream. # When VLC is playing other content, we will hijack it. # Enable/disable audio if CONFIG.AUDIO_MODE == AUDIOMODE.FULLSCREEN: volume = CONFIG.AUDIO_VOLUME / 100 self._send_dbus_command(DBUS_COMMAND.PLAY_VOLUME, volume) # Start our stream self._send_dbus_command(DBUS_COMMAND.PLAY_PLAY) # Mark our steam as the active one for this display Window._vlc_active_stream_url[self._display_num - 1] = self.active_stream.url else: # Play command will be sent by 'stream_set_visible' later on. pass # Pretend like the player just started again self.playstate = PLAYSTATE.INIT2 self._time_streamstart = time.monotonic() return else: LOG.DEBUG(self._LOG_NAME, "starting player with arguments '%s'" % player_cmd) # Add URL now, as we don't want sensitive credentials in the logfile... if self._player == PLAYER.OMXPLAYER: player_cmd.append(url) elif self._player == PLAYER.VLCPLAYER and self.visible: player_cmd.append(url) Window._vlc_active_stream_url[self._display_num - 1] = url if self._player == PLAYER.VLCPLAYER: # VLC changes its DBus string to 'org.mpris.MediaPlayer2.vlc.instancePID' # when opening a second instance, so we have to adjust it later on when we know the PID # Max number of VLC instances = number of displays = 2 if self._pidpool_get_pid("--mmal-display=hdmi-"): Window._vlc_dbus_ident[ self._display_num - 1] = "org.mpris.MediaPlayer2.vlc.instance" else: Window._vlc_dbus_ident[self._display_num - 1] = "org.mpris.MediaPlayer2.vlc" # Save the subtitle state for later use Window._vlc_subs_enabled[self._display_num - 1] = (sub_file and os.path.isfile(sub_file)) subprocess.Popen(player_cmd, shell=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) if self._player == PLAYER.VLCPLAYER: # VLC does not have a command line argument for volume control?? # As workaround, wait for VLC startup and send the desired volume with DBus time.sleep(0.5) self._send_dbus_command(DBUS_COMMAND.PLAY_VOLUME, CONFIG.AUDIO_VOLUME / 100, retries=5) self._time_streamstart = time.monotonic() self.playstate = PLAYSTATE.INIT1 self._omx_duration = 0
def log(msg, new_section=False): LOG(msg, new_section)