from logging import getLogger
from signal import SIGINT, SIGHUP, SIGTERM
from circuits import Event
from .components.timer import Timer
from .events import log_song, set_status, no_song_to_play
from .players.omx import OMXPlayer, clean_old_video_players
from .players.fbi import ImageViewer
from .players.events import play
from .players.utils import clear_framebuffer
from .settings import SCHEDULE_NEXT_INTERVAL, CHECK_PAUSED_INTERVAL, CHECK_STATUS_INTERVAL
from .audio import MediaPlayer, check_start, start_playing, not_allowed, schedule, play_next, check_status
[docs]class resume_playback(Event):
""" resume paused """
[docs]class stop_playing(Event):
""" stop playing """
[docs]class VideoPlayer(MediaPlayer):
""" Video Player handles switching between 2 omx video players and 2 fbi image viewers according to media type in video stream """
channel = 'video'
def init(self, config):
super().init(config)
self.logger = getLogger(__name__)
self.player_channels = ('video_a', 'video_b', 'image_a', 'image_b')
self.video_a = OMXPlayer(channel='video_a').register(self)
self.video_b = OMXPlayer(channel='video_b').register(self)
self.image_a = ImageViewer(channel='image_a').register(self)
self.image_b = ImageViewer(channel='image_b').register(self)
self.loop = False
self.closing = False
self.next_player = None
self.next_media = None
self.logged_media = None
self.has_initial_data = True # initial data for video does not apply, no need to wait
def _init_info(self):
self.overlap = max(self.info.cfo, self.info.cfd)
self.logger.info('Overlap: %ss', self.overlap)
def started(self, component):
self.logger.info('started: %r', self)
def enable_player(self):
self.enabled = True
self.logger.info('Enabled video player')
self.fire(check_start(), self.channel)
clean_old_video_players()
def disable_player(self):
self.enabled = False
self.logger.info('Disabled video player')
def not_allowed(self):
self.logger.warning('Not allowed to play.')
self._stop_all_players()
clear_framebuffer()
self.fire(set_status('not_allowed'))
def start_playing(self):
if not self._is_running():
if self.stream.n_video > 0:
self.next_media = self.stream.next_video()
self.next_player = self._get_next_player_name(self.next_media)
self._schedule_event(1, play_next, self.next_player, msg='start playback')
else:
self.logger.info('No video files in program')
self.fire(no_song_to_play())
self.fire(set_status('no_song'))
else:
self.logger.warning('Video player already running')
def stop_playing(self):
if self._is_running():
self._stop_all_players()
self._stop_timers()
else:
self.logger.warning('Video player not running')
def _get_next_start(self, duration, position):
delta = duration - position - self.overlap
delta = max(delta, 0.1)
return delta
def _get_next_player_name(self, media, loop=False):
next_name = ''
ct = ''
if media:
ct = media.content_type
if ct in ImageViewer.content_types:
prefix = 'image_'
else:
prefix = 'video_'
channels = [x for x in self.player_channels
if x.startswith(prefix)]
# last character will alternate if not loop
lastchar = self.next_player[-1] if self.next_player else 'b'
if loop:
next_name = [x for x in channels if x.endswith(lastchar)][0]
else:
next_name = [x for x in channels if not x.endswith(lastchar)][0]
else:
next_name = self.player_channels[0]
self.logger.info('Next player name for %s: %s', ct, next_name)
return next_name
def _log_media(self, player_name, media):
if media:
if media != self.logged_media:
self.fire(log_song(media))
self.logged_media = media
else:
self.logger.info("Not sending songs log for the same media: %s", media)
else:
self.logger.warning('Missing media to log for %s', player_name)
def _stop_all_players(self):
for name in self.player_channels:
player = getattr(self, name, None)
if player:
player.stop()
[docs] def play_next(self, player_name):
""" play next media """
if self.closing:
return
self.logger.info('Playing media')
if not self.enabled:
self.fire(not_allowed())
return
# get next media
media = self.next_media
if media:
# start playing
first_time = not self.loop
self.loop = self.stream.n_video_playable == 1
self.fire(play(media, loop=self.loop), player_name)
# send media log only if not loop or first time
if not self.loop or first_time:
self._log_media(player_name, media)
else:
self.logger.info("Not sending songs log for loop.")
self.fire(set_status('playing'))
else:
if self.loop:
self._stop_all_players()
clear_framebuffer()
self.fire(no_song_to_play())
self.fire(set_status('no_song'))
self._schedule_event(SCHEDULE_NEXT_INTERVAL, schedule, player_name, msg='schedule')
[docs] def schedule(self, player_name):
""" schedule start of next media and checking status in between """
if self.closing:
return
self.logger.info('Scheduling %s', player_name)
self.next_media = self.stream.next_video()
current_player = getattr(self, player_name)
self.next_player = self._get_next_player_name(self.next_media, self.loop)
a = current_player.get_status()
self.logger.info(a)
if a.status == 'playing':
delta = self._get_next_start(a.duration, a.position)
self._schedule_event(delta, play_next, self.next_player, msg='start playback')
# calculate intervals for checking status until next media
if int(delta) > CHECK_STATUS_INTERVAL:
for i in range(int(delta) // CHECK_STATUS_INTERVAL):
sd = (i+1) * CHECK_STATUS_INTERVAL
self._schedule_event(sd, check_status, player_name)
# clear fb if playing video
if player_name.startswith('video'):
clear_framebuffer()
elif a.status == 'paused':
self._schedule_event(CHECK_PAUSED_INTERVAL, schedule, player_name)
else:
if 'error' in a.status:
current_player.stop()
next_player = getattr(self, self.next_player)
b = next_player.get_status()
self.logger.info(b)
if b.status in ('playing', 'paused'):
self._schedule_event(CHECK_PAUSED_INTERVAL, schedule, self.next_player)
else:
next_player.stop()
self.logger.warning('Restarting')
self._schedule_event(CHECK_PAUSED_INTERVAL, play_next, self.next_player)
self.loop = False
def reload_programs(self):
self.logger.info('reload programs')
if self.stream:
self.stream.reload()
self.stop_playing()
clear_framebuffer()
if self.loop:
self.loop = False
delta = 0.3
self.logger.info('Starting playing after programs reload in %ss', delta)
Timer(delta, start_playing(), self.channel).register(self)
else:
self.check_start()
def signal(self, signo, stack):
if signo in (SIGINT, SIGTERM):
self.closing = True
self._stop_all_players()
return True