コード例 #1
0
class VideoPlayer:
    """ plays a track using omxplayer
        See pp_imageplayer for common software design description
    """

    _CLOSED = "omx_closed"    #probably will not exist
    _STARTING = "omx_starting"  #track is being prepared
    _PLAYING = "omx_playing"  #track is playing to the screen, may be paused
    _ENDING = "omx_ending"  #track is in the process of ending due to quit or end of track


# ***************************************
# EXTERNAL COMMANDS
# ***************************************

    def __init__(self,
                         show_id,
                         root,
                        canvas,
                        show_params,
                        track_params ,
                         pp_dir,
                        pp_home,
                        pp_profile):

        self.mon=Monitor()
        self.mon.on()
        
        #instantiate arguments
        self.show_id=show_id
        self.root=root
        self.canvas = canvas
        self.show_params=show_params
        self.track_params=track_params
        self.pp_dir=pp_dir
        self.pp_home=pp_home
        self.pp_profile=pp_profile


        # get config from medialist if there.
        if self.track_params['omx-audio']<>"":
            self.omx_audio= self.track_params['omx-audio']
        else:
            self.omx_audio= self.show_params['omx-audio']
        if self.omx_audio<>"": self.omx_audio= "-o "+ self.omx_audio
        
        if self.track_params['omx-volume']<>"":
            self.omx_volume= self.track_params['omx-volume']
        else:
            self.omx_volume= self.show_params['omx-volume']
        if self.omx_volume<>"":
            self.omx_volume= "--vol "+ str(int(self.omx_volume)*100) + ' '

        if self.track_params['omx-window']<>'':
            self.omx_window= self.track_params['omx-window']
        else:
            self.omx_window= self.show_params['omx-window']


        # get background image from profile.
        self.background_file=''
        if self.track_params['background-image']<>"":
            self.background_file= self.track_params['background-image']
        else:
            if self.track_params['display-show-background']=='yes':
                self.background_file= self.show_params['background-image']
            
        # get background colour from profile.
        if self.track_params['background-colour']<>"":
            self.background_colour= self.track_params['background-colour']
        else:
            self.background_colour= self.show_params['background-colour']
        
        self.centre_x = int(self.canvas['width'])/2
        self.centre_y = int(self.canvas['height'])/2
        
        #get animation instructions from profile
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']

        # open the plugin Manager
        self.pim=PluginManager(self.show_id,self.root,self.canvas,self.show_params,self.track_params,self.pp_dir,self.pp_home,self.pp_profile) 

        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()        
        
        # could put instance generation in play, not sure which is better.
        self.omx=OMXDriver(self.canvas)
        self.tick_timer=None
        self.init_play_state_machine()



    def play(self, track,
                     showlist,
                     end_callback,
                     ready_callback,
                     enable_menu=False):
                         
        #instantiate arguments
        self.track=track
        self.showlist=showlist
        self.ready_callback=ready_callback   #callback when ready to play
        self.end_callback=end_callback         # callback when finished
        self.enable_menu = enable_menu
 
        # callback to the calling object to e.g remove egg timer and enable click areas.
        if self.ready_callback<>None:
            self.ready_callback()

        # create an  instance of showmanager so we can control concurrent shows
        self.show_manager=ShowManager(self.show_id,self.showlist,self.show_params,self.root,self.canvas,self.pp_dir,self.pp_profile,self.pp_home)

        #set up video window
        reason,message,comand,has_window,x1,y1,x2,y2= self.parse_window(self.omx_window)
        if reason =='error':
            self.mon.err(self,'omx window error: ' + message + ' in ' + self.omx_window)
            self.end_callback(reason,message)
        else:
            if has_window==True:
                self.omx_window= '--win " '+ str(x1) +  ' ' + str(y1) + ' ' + str(x2) + ' ' + str(y2) + ' " '
            else:
                self.omx_window=''

             # Control other shows at beginning
            reason,message=self.show_manager.show_control(self.track_params['show-control-begin'])
            if reason in ('error','killed'):
                self.end_callback(reason,message)
                self=None
            else:      
                #display content
                reason,message=self.display_content()
                if reason == 'error':
                    self.mon.err(self,message)
                    self.end_callback(reason,message)
                    self=None
                else:
                    # create animation events
                    reason,message=self.ppio.animate(self.animate_begin_text,id(self))
                    if reason=='error':
                        self.mon.err(self,message)
                        self.end_callback(reason,message)
                        self=None
                    else:
                        # start playing the video.
                        if self.play_state == VideoPlayer._CLOSED:
                            self.mon.log(self,">play track received")
                            self.start_play_state_machine(self.track)
                        else:
                            self.mon.err(self,'play track rejected')
                            self.end_callback('error','play track rejected')
                            self=None

    def terminate(self,reason):
        # circumvents state machine and does not wait for omxplayer to close
        if self.omx<>None:
            self.mon.log(self,"sent terminate to omxdriver")
            self.omx.terminate(reason)
            self.end('killed',' end without waiting for omxplayer to finish') # end without waiting
        else:
            self.mon.log(self,"terminate, omxdriver not running")
            self.end('killed','terminate, mplayerdriver not running')


    def input_pressed(self,symbol):
        if symbol[0:4]=='omx-':
            self.control(symbol[4])
            
        elif symbol =='pause':
            self.pause()

        elif symbol=='stop':
            self.stop()
        else:
            pass


    def get_links(self):
        return self.track_params['links']

            
                
# ***************************************
# INTERNAL FUNCTIONS
# ***************************************

    # respond to normal stop
    def stop(self):
        # send signal to stop the track to the state machine
        self.mon.log(self,">stop received")
        self.quit_signal=True


    #toggle pause
    def pause(self):
        if self.play_state in (VideoPlayer._PLAYING,VideoPlayer._ENDING):
            self.omx.pause()
            return True
        else:
            self.mon.log(self,"!<pause rejected")
            return False
        
    # other control when playing
    def control(self,char):
        if self.play_state==VideoPlayer._PLAYING and char not in ('q'):
            self.mon.log(self,"> send control to omx: "+ char)
            self.omx.control(char)
            return True
        else:
            self.mon.log(self,"!<control rejected")
            return False



# ***********************
# sequencing
# **********************

    """self. play_state controls the playing sequence, it has the following values.
         I am not entirely sure the starting and ending states are required.
         - _closed - the omx process is not running, omx process can be initiated
         - _starting - omx process is running but is not yet able to receive controls
         - _playing - playing a track, controls can be sent
         - _ending - omx is doing its termination, controls cannot be sent
    """

    def init_play_state_machine(self):
        self.quit_signal=False
        self.play_state=VideoPlayer._CLOSED
 
    def start_play_state_machine(self,track):
        #initialise all the state machine variables
        #self.iteration = 0                             # for debugging
        self.quit_signal=False     # signal that user has pressed stop
        self.play_state=VideoPlayer._STARTING
        
        #play the selected track
        options=self.omx_audio+ " " + self.omx_volume + ' ' + self.omx_window + ' ' + self.show_params['omx-other-options']+" "
        self.omx.play(track,options)
        self.mon.log (self,'Playing track from show Id: '+ str(self.show_id))
        # and start polling for state changes
        self.tick_timer=self.canvas.after(50, self.play_state_machine)
 

    def play_state_machine(self):      
        if self.play_state == VideoPlayer._CLOSED:
            self.mon.log(self,"      State machine: " + self.play_state)
            return 
                
        elif self.play_state == VideoPlayer._STARTING:
            self.mon.log(self,"      State machine: " + self.play_state)
            
            # if omxplayer is playing the track change to play state
            if self.omx.start_play_signal==True:
                self.mon.log(self,"            <start play signal received from omx")
                self.omx.start_play_signal=False
                self.play_state=VideoPlayer._PLAYING
                self.mon.log(self,"      State machine: omx_playing started")
            self.tick_timer=self.canvas.after(50, self.play_state_machine)

        elif self.play_state == VideoPlayer._PLAYING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            # service any queued stop signals
            if self.quit_signal==True:
                self.mon.log(self,"      Service stop required signal")
                self.stop_omx()
                self.quit_signal=False
                # self.play_state = VideoPlayer._ENDING
                
            # omxplayer reports it is terminating so change to ending state
            if self.omx.end_play_signal:                    
                self.mon.log(self,"            <end play signal received")
                self.mon.log(self,"            <end detected at: " + str(self.omx.video_position))
                if self.omx.end_play_reason<>'nice_day':
                    # deal with omxplayer not sending 'have a nice day'
                    self.mon.warn(self,"            <end detected at: " + str(self.omx.video_position))
                    self.mon.warn(self,"            <pexpect reports: "+self.omx.end_play_reason)
                    self.mon.warn(self,'pexpect.before  is'+self.omx.xbefore)
                self.play_state = VideoPlayer._ENDING
                self.ending_count=0
                
            self.tick_timer=self.canvas.after(200, self.play_state_machine)

        elif self.play_state == VideoPlayer._ENDING:
            self.mon.log(self,"      State machine: " + self.play_state)
            # if spawned process has closed can change to closed state
            self.mon.log (self,"      State machine : is omx process running? -  "  + str(self.omx.is_running()))
            if self.omx.is_running() ==False:
                self.mon.log(self,"            <omx process is dead")
                self.play_state = VideoPlayer._CLOSED
                self.end('normal','quit by user or system')
            else:
                self.ending_count+=1
                if self.ending_count>10:
                    # deal with omxplayer not terminating at the end of a track
                    self.mon.warn(self,"            <omxplayer failed to close at: " + str(self.omx.video_position))
                    self.mon.warn(self,'pexpect.before  is'+self.omx.xbefore)
                    self.omx.kill()
                    self.mon.warn(self,'omxplayer now  terminated ')
                    self.play_state = VideoPlayer._CLOSED
                    self.end('normal','end from omxplayer failed to terminate')
                else:
                    self.tick_timer=self.canvas.after(200, self.play_state_machine)

    def stop_omx(self):
        # send signal to stop the track to the state machine
        self.mon.log(self,"         >stop omx received from state machine")
        if self.play_state==VideoPlayer._PLAYING:
            self.omx.stop()
            return True
        else:
            self.mon.log(self,"!<stop rejected")
            return False




# *****************
# ending the player
# *****************

    def end(self,reason,message):

            # stop the plugin
            if self.track_params['plugin']<>'':
                self.pim.stop_plugin()

            # os.system("xrefresh -display :0")
            # abort the timer
            if self.tick_timer<>None:
                self.canvas.after_cancel(self.tick_timer)
                self.tick_timer=None
            
            if reason in ('error','killed'):
                self.end_callback(reason,message)
                self=None

            else:
                # normal end so do show control and animation

                # Control concurrent shows at end
                reason,message=self.show_manager.show_control(self.track_params['show-control-end'])
                if reason =='error':
                    self.mon.err(self,message)
                    self.end_callback(reason,message)
                    self=None
                else:
                   # clear events list for this track
                    if self.track_params['animate-clear']=='yes':
                        self.ppio.clear_events_list(id(self))
                    
                    # create animation events for ending
                    reason,message=self.ppio.animate(self.animate_begin_text,id(self))
                    if reason=='error':
                        self.mon.err(self,message)
                        self.end_callback(reason,message)
                        self=None
                    else:
                        self.end_callback('normal',"track has terminated or quit")
                        self=None



# *****************
# displaying things
# *****************
    def display_content(self):

        #background colour
        if  self.background_colour<>'':   
           self.canvas.config(bg=self.background_colour)
            
        # delete previous content
        self.canvas.delete('pp-content')

        # background image
        if self.background_file<>'':
            self.background_img_file = self.complete_path(self.background_file)
            if not os.path.exists(self.background_img_file):
                self.mon.err(self,"Video background file not found: "+ self.background_img_file)
                self.end('error',"Video background file not found")
            else:
                pil_background_img=PIL.Image.open(self.background_img_file)
                self.background = PIL.ImageTk.PhotoImage(pil_background_img)
                self.drawn = self.canvas.create_image(int(self.canvas['width'])/2,
                                             int(self.canvas['height'])/2,
                                             image=self.background,
                                            anchor=CENTER,
                                            tag='pp-content')

        # execute the plugin if required
        if self.track_params['plugin']<>'':

            reason,message,self.track = self.pim.do_plugin(self.track,self.track_params['plugin'],)
            if reason <> 'normal':
                return reason,message

                          
        # display show text if enabled
        if self.show_params['show-text']<> '' and self.track_params['display-show-text']=='yes':
            self.canvas.create_text(int(self.show_params['show-text-x']),int(self.show_params['show-text-y']),
                                                    anchor=NW,
                                                  text=self.show_params['show-text'],
                                                  fill=self.show_params['show-text-colour'],
                                                  font=self.show_params['show-text-font'],
                                                  tag='pp-content')


        # display track text if enabled
        if self.track_params['track-text']<> '':
            self.canvas.create_text(int(self.track_params['track-text-x']),int(self.track_params['track-text-y']),
                                                    anchor=NW,
                                                  text=self.track_params['track-text'],
                                                  fill=self.track_params['track-text-colour'],
                                                  font=self.track_params['track-text-font'],
                                                  tag='pp-content')

        # display instructions if enabled
        if self.enable_menu== True:
            self.canvas.create_text(int(self.show_params['hint-x']),
                                                    int(self.show_params['hint-y']),
                                                  text=self.show_params['hint-text'],
                                                  fill=self.show_params['hint-colour'],
                                                font=self.show_params['hint-font'],
                                                anchor=NW,
                                                tag='pp-content')

        self.canvas.tag_raise('pp-click-area')
        self.canvas.update_idletasks( )
        return 'normal',''


# ****************
# utilities
# *****************

    def complete_path(self,track_file):
        #  complete path of the filename of the selected entry
        if track_file[0]=="+":
                track_file=self.pp_home+track_file[1:]
        self.mon.log(self,"Background image is "+ track_file)
        return track_file

# original _
# warp _ or xy2


    def parse_window(self,line):
        
            fields = line.split()
            # check there is a command field
            if len(fields) < 1:
                    return 'error','no type field','',False,0,0,0,0
                
            # deal with original which has 1
            if fields[0]=='original':
                if len(fields) <> 1:
                        return 'error','number of fields for original','',False,0,0,0,0    
                return 'normal','',fields[0],False,0,0,0,0


            #deal with warp which has 1 or 5  arguments
            # check basic syntax
            if  fields[0] <>'warp':
                    return 'error','not a valid type','',False,0,0,0,0
            if len(fields) not in (1,5):
                    return 'error','wrong number of coordinates for warp','',False,0,0,0,0

            # deal with window coordinates    
            if len(fields) == 5:
                #window is specified
                if not (fields[1].isdigit() and fields[2].isdigit() and fields[3].isdigit() and fields[4].isdigit()):
                    return 'error','coordinates are not positive integers','',False,0,0,0,0
                has_window=True
                return 'normal','',fields[0],has_window,int(fields[1]),int(fields[2]),int(fields[3]),int(fields[4])
            else:
                # fullscreen
                has_window=True
                return 'normal','',fields[0],has_window,0,0,self.canvas['width'],self.canvas['height']
コード例 #2
0
class VideoPlayer:
    """ plays a track using omxplayer
        See pp_imageplayer for common software design description
    """

    _CLOSED = "omx_closed"    #probably will not exist
    _STARTING = "omx_starting"  #track is being prepared
    _PLAYING = "omx_playing"  #track is playing to the screen, may be paused
    _ENDING = "omx_ending"  #track is in the process of ending due to quit or end of track


# ***************************************
# EXTERNAL COMMANDS
# ***************************************

    def __init__(self,
                         show_id,
                         root,
                        canvas,
                        show_params,
                        track_params ,
                         pp_dir,
                        pp_home,
                        pp_profile):

        self.mon=Monitor()
        self.mon.on()
        
        #instantiate arguments
        self.show_id=show_id
        self.root=root
        self.canvas = canvas
        self.show_params=show_params
        self.track_params=track_params
        self.pp_dir=pp_dir
        self.pp_home=pp_home
        self.pp_profile=pp_profile


        # get config from medialist if there.
        if self.track_params['omx-audio']<>"":
            self.omx_audio= self.track_params['omx-audio']
        else:
            self.omx_audio= self.show_params['omx-audio']
        if self.omx_audio<>"": self.omx_audio= "-o "+ self.omx_audio
        
        if self.track_params['omx-volume']<>"":
            self.omx_volume= self.track_params['omx-volume']
        else:
            self.omx_volume= self.show_params['omx-volume']
        if self.omx_volume<>"":
            self.omx_volume= "--vol "+ str(int(self.omx_volume)*100) + ' '

        if self.track_params['omx-window']<>'':
            self.omx_window= self.track_params['omx-window']
        else:
            self.omx_window= self.show_params['omx-window']


        # get background image from profile.
        self.background_file=''
        if self.track_params['background-image']<>"":
            self.background_file= self.track_params['background-image']
        else:
            if self.track_params['display-show-background']=='yes':
                self.background_file= self.show_params['background-image']
            
        # get background colour from profile.
        if self.track_params['background-colour']<>"":
            self.background_colour= self.track_params['background-colour']
        else:
            self.background_colour= self.show_params['background-colour']
        
        self.centre_x = int(self.canvas['width'])/2
        self.centre_y = int(self.canvas['height'])/2
        
        #get animation instructions from profile
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']

        # open the plugin Manager
        self.pim=PluginManager(self.show_id,self.root,self.canvas,self.show_params,self.track_params,self.pp_dir,self.pp_home,self.pp_profile) 

        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()        
        
        # could put instance generation in play, not sure which is better.
        self.omx=OMXDriver(self.canvas)
        self.tick_timer=None
        self.init_play_state_machine()



    def play(self, track,
                     showlist,
                     end_callback,
                     ready_callback,
                     enable_menu=False):
                         
        #instantiate arguments
        self.track=track
        self.showlist=showlist
        self.ready_callback=ready_callback   #callback when ready to play
        self.end_callback=end_callback         # callback when finished
        self.enable_menu = enable_menu
 
        # callback to the calling object to e.g remove egg timer and enable click areas.
        if self.ready_callback<>None:
            self.ready_callback()

        # create an  instance of showmanager so we can control concurrent shows
        self.show_manager=ShowManager(self.show_id,self.showlist,self.show_params,self.root,self.canvas,self.pp_dir,self.pp_profile,self.pp_home)

        #set up video window
        reason,message,comand,has_window,x1,y1,x2,y2= self.parse_window(self.omx_window)
        if reason =='error':
            self.mon.err(self,'omx window error: ' + message + ' in ' + self.omx_window)
            self.end_callback(reason,message)
        else:
            if has_window==True:
                self.omx_window= '--win " '+ str(x1) +  ' ' + str(y1) + ' ' + str(x2) + ' ' + str(y2) + ' " '
            else:
                self.omx_window=''

             # Control other shows at beginning
            reason,message=self.show_manager.show_control(self.track_params['show-control-begin'])
            if reason in ('error','killed'):
                self.end_callback(reason,message)
                self=None
            else:      
                #display content
                reason,message=self.display_content()
                if reason == 'error':
                    self.mon.err(self,message)
                    self.end_callback(reason,message)
                    self=None
                else:
                    # create animation events
                    reason,message=self.ppio.animate(self.animate_begin_text,id(self))
                    if reason=='error':
                        self.mon.err(self,message)
                        self.end_callback(reason,message)
                        self=None
                    else:
                        # start playing the video.
                        if self.play_state == VideoPlayer._CLOSED:
                            self.mon.log(self,">play track received")
                            self.start_play_state_machine(self.track)
                        else:
                            self.mon.err(self,'play track rejected')
                            self.end_callback('error','play track rejected')
                            self=None

    def terminate(self,reason):
        # circumvents state machine and does not wait for omxplayer to close
        if self.omx<>None:
            self.mon.log(self,"sent terminate to omxdriver")
            self.omx.terminate(reason)
            self.end('killed',' end without waiting for omxplayer to finish') # end without waiting
        else:
            self.mon.log(self,"terminate, omxdriver not running")
            self.end('killed','terminate, mplayerdriver not running')


    def input_pressed(self,symbol):
        if symbol[0:4]=='omx-':
            self.control(symbol[4])
            
        elif symbol =='pause':
            self.pause()

        elif symbol=='stop':
            self.stop()
        else:
            pass


    def get_links(self):
        return self.track_params['links']

            
                
# ***************************************
# INTERNAL FUNCTIONS
# ***************************************

    # respond to normal stop
    def stop(self):
        # send signal to stop the track to the state machine
        self.mon.log(self,">stop received")
        self.quit_signal=True


    #toggle pause
    def pause(self):
        if self.play_state in (VideoPlayer._PLAYING,VideoPlayer._ENDING):
            self.omx.pause()
            return True
        #NIK
        # for pausing video after first frame
        elif self.play_state == VideoPlayer._STARTING:
            self.omx.delayed_pause = True
            return True
        #NIK
        else:
            self.mon.log(self,"!<pause rejected")
            return False
        
    # other control when playing
    def control(self,char):
        if self.play_state==VideoPlayer._PLAYING and char not in ('q'):
            self.mon.log(self,"> send control to omx: "+ char)
            self.omx.control(char)
            return True
        else:
            self.mon.log(self,"!<control rejected")
            return False



# ***********************
# sequencing
# **********************

    """self. play_state controls the playing sequence, it has the following values.
         I am not entirely sure the starting and ending states are required.
         - _closed - the omx process is not running, omx process can be initiated
         - _starting - omx process is running but is not yet able to receive controls
         - _playing - playing a track, controls can be sent
         - _ending - omx is doing its termination, controls cannot be sent
    """

    def init_play_state_machine(self):
        self.quit_signal=False
        self.play_state=VideoPlayer._CLOSED
 
    def start_play_state_machine(self,track):
        #initialise all the state machine variables
        #self.iteration = 0                             # for debugging
        self.quit_signal=False     # signal that user has pressed stop
        self.play_state=VideoPlayer._STARTING
        
        #play the selected track
        options=self.omx_audio+ " " + self.omx_volume + ' ' + self.omx_window + ' ' + self.show_params['omx-other-options']+" "
        # NIK ADDITION
        # adding subtitles file for video
        if 'omx-subtitles' in self.track_params and self.track_params['omx-subtitles'] <> '':
            subtitles_full_path = self.complete_path(self.track_params['omx-subtitles'])
            if os.path.exists (subtitles_full_path):
                options += '--font-size 40 --subtitles "' + subtitles_full_path + '" '

        if 'omx-subtitles-numlines' in self.track_params and self.track_params['omx-subtitles-numlines'] <> '':
            options += '--lines ' + self.track_params['omx-subtitles-numlines'] + ' '
        # END NIK ADDITION
        self.omx.play(track,options)
        self.mon.log (self,'Playing track from show Id: '+ str(self.show_id))
        # and start polling for state changes
        self.tick_timer=self.canvas.after(50, self.play_state_machine)
 

    def play_state_machine(self):      
        if self.play_state == VideoPlayer._CLOSED:
            self.mon.log(self,"      State machine: " + self.play_state)
            return 
                
        elif self.play_state == VideoPlayer._STARTING:
            self.mon.log(self,"      State machine: " + self.play_state)
            
            # if omxplayer is playing the track change to play state
            if self.omx.start_play_signal==True:
                self.mon.log(self,"            <start play signal received from omx")
                self.omx.start_play_signal=False
                self.play_state=VideoPlayer._PLAYING
                self.mon.log(self,"      State machine: omx_playing started")

            self.tick_timer=self.canvas.after(50, self.play_state_machine)

        elif self.play_state == VideoPlayer._PLAYING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            # service any queued stop signals
            if self.quit_signal==True:
                self.mon.log(self,"      Service stop required signal")
                self.stop_omx()
                self.quit_signal=False
                # self.play_state = VideoPlayer._ENDING
                
            # omxplayer reports it is terminating so change to ending state
            if self.omx.end_play_signal:                    
                self.mon.log(self,"            <end play signal received")
                self.mon.log(self,"            <end detected at: " + str(self.omx.video_position))
                if self.omx.end_play_reason<>'nice_day':
                    # deal with omxplayer not sending 'have a nice day'
                    self.mon.warn(self,"            <end detected at: " + str(self.omx.video_position))
                    self.mon.warn(self,"            <pexpect reports: "+self.omx.end_play_reason)
                    self.mon.warn(self,'pexpect.before  is'+self.omx.xbefore)
                self.play_state = VideoPlayer._ENDING
                self.ending_count=0
                
            self.tick_timer=self.canvas.after(200, self.play_state_machine)

        elif self.play_state == VideoPlayer._ENDING:
            self.mon.log(self,"      State machine: " + self.play_state)
            # if spawned process has closed can change to closed state
            self.mon.log (self,"      State machine : is omx process running? -  "  + str(self.omx.is_running()))
            if self.omx.is_running() ==False:
                self.mon.log(self,"            <omx process is dead")
                self.play_state = VideoPlayer._CLOSED
                self.end('normal','quit by user or system')
            else:
                self.ending_count+=1
                if self.ending_count>10:
                    # deal with omxplayer not terminating at the end of a track
                    self.mon.warn(self,"            <omxplayer failed to close at: " + str(self.omx.video_position))
                    self.mon.warn(self,'pexpect.before  is'+self.omx.xbefore)
                    self.omx.kill()
                    self.mon.warn(self,'omxplayer now  terminated ')
                    self.play_state = VideoPlayer._CLOSED
                    self.end('normal','end from omxplayer failed to terminate')
                else:
                    self.tick_timer=self.canvas.after(200, self.play_state_machine)

    def stop_omx(self):
        # send signal to stop the track to the state machine
        self.mon.log(self,"         >stop omx received from state machine")
        if self.play_state==VideoPlayer._PLAYING:
            self.omx.stop()
            return True
        else:
            self.mon.log(self,"!<stop rejected")
            return False




# *****************
# ending the player
# *****************

    def end(self,reason,message):

            # stop the plugin
            if self.track_params['plugin']<>'':
                self.pim.stop_plugin()

            # os.system("xrefresh -display :0")
            # abort the timer
            if self.tick_timer<>None:
                self.canvas.after_cancel(self.tick_timer)
                self.tick_timer=None
            
            if reason in ('error','killed'):
                self.end_callback(reason,message)
                self=None

            else:
                # normal end so do show control and animation

                # Control concurrent shows at end
                reason,message=self.show_manager.show_control(self.track_params['show-control-end'])
                if reason =='error':
                    self.mon.err(self,message)
                    self.end_callback(reason,message)
                    self=None
                else:
                   # clear events list for this track
                    if self.track_params['animate-clear']=='yes':
                        self.ppio.clear_events_list(id(self))
                    
                    # create animation events for ending
                    reason,message=self.ppio.animate(self.animate_end_text,id(self))
                    if reason=='error':
                        self.mon.err(self,message)
                        self.end_callback(reason,message)
                        self=None
                    else:
                        self.end_callback('normal',"track has terminated or quit")
                        self=None



# *****************
# displaying things
# *****************
    def display_content(self):

        #background colour
        if  self.background_colour<>'':   
           self.canvas.config(bg=self.background_colour)
            
        # delete previous content
        self.canvas.delete('pp-content')

        # background image
        if self.background_file<>'':
            self.background_img_file = self.complete_path(self.background_file)
            if not os.path.exists(self.background_img_file):
                self.mon.err(self,"Video background file not found: "+ self.background_img_file)
                self.end('error',"Video background file not found")
            else:
                pil_background_img=PIL.Image.open(self.background_img_file)
                self.background = PIL.ImageTk.PhotoImage(pil_background_img)
                self.drawn = self.canvas.create_image(int(self.canvas['width'])/2,
                                             int(self.canvas['height'])/2,
                                             image=self.background,
                                            anchor=CENTER,
                                            tag='pp-content')

        # execute the plugin if required
        if self.track_params['plugin']<>'':

            reason,message,self.track = self.pim.do_plugin(self.track,self.track_params['plugin'],)
            if reason <> 'normal':
                return reason,message

                          
        # display show text if enabled
        if self.show_params['show-text']<> '' and self.track_params['display-show-text']=='yes':
            self.canvas.create_text(int(self.show_params['show-text-x']),int(self.show_params['show-text-y']),
                                                    anchor=NW,
                                                  text=self.show_params['show-text'],
                                                  fill=self.show_params['show-text-colour'],
                                                  font=self.show_params['show-text-font'],
                                                  tag='pp-content')


        # display track text if enabled
        if self.track_params['track-text']<> '':
            self.canvas.create_text(int(self.track_params['track-text-x']),int(self.track_params['track-text-y']),
                                                    anchor=NW,
                                                  text=self.track_params['track-text'],
                                                  fill=self.track_params['track-text-colour'],
                                                  font=self.track_params['track-text-font'],
                                                  tag='pp-content')

        # display instructions if enabled
        if self.enable_menu== True:
            self.canvas.create_text(int(self.show_params['hint-x']),
                                                    int(self.show_params['hint-y']),
                                                  text=self.show_params['hint-text'],
                                                  fill=self.show_params['hint-colour'],
                                                font=self.show_params['hint-font'],
                                                anchor=NW,
                                                tag='pp-content')

        self.canvas.tag_raise('pp-click-area')
        self.canvas.update_idletasks( )
        return 'normal',''


# ****************
# utilities
# *****************

    def complete_path(self,track_file):
        #  complete path of the filename of the selected entry
        if track_file[0]=="+":
                track_file=self.pp_home+track_file[1:]
        self.mon.log(self,"Background image is "+ track_file)
        return track_file

# original _
# warp _ or xy2


    def parse_window(self,line):
        
            fields = line.split()
            # check there is a command field
            if len(fields) < 1:
                    return 'error','no type field','',False,0,0,0,0
                
            # deal with original which has 1
            if fields[0]=='original':
                if len(fields) <> 1:
                        return 'error','number of fields for original','',False,0,0,0,0    
                return 'normal','',fields[0],False,0,0,0,0


            #deal with warp which has 1 or 5  arguments
            # check basic syntax
            if  fields[0] <>'warp':
                    return 'error','not a valid type','',False,0,0,0,0
            if len(fields) not in (1,5):
                    return 'error','wrong number of coordinates for warp','',False,0,0,0,0

            # deal with window coordinates    
            if len(fields) == 5:
                #window is specified
                if not (fields[1].isdigit() and fields[2].isdigit() and fields[3].isdigit() and fields[4].isdigit()):
                    return 'error','coordinates are not positive integers','',False,0,0,0,0
                has_window=True
                return 'normal','',fields[0],has_window,int(fields[1]),int(fields[2]),int(fields[3]),int(fields[4])
            else:
                # fullscreen
                has_window=True
                return 'normal','',fields[0],has_window,0,0,self.canvas['width'],self.canvas['height']
コード例 #3
0
class BrowserPlayer:


    #state constants
    _CLOSED = "player_closed"    #probably will not exist
    _STARTING = "player_starting"  #uzbl beinf loaded and fifo created
    _WAITING = "wait for timeout" # waiting for browser to appear on the screen
    _PLAYING = "player_playing"  #track is playing to the screen
    _ENDING = "player_ending"  #track is in the process of ending due to quit or duration exceeded




# ***************************************
# EXTERNAL COMMANDS
# ***************************************

    def __init__(self,
                        show_id,
                         root,
                        canvas,
                        show_params,
                        track_params,
                     pp_dir,
                        pp_home,
                        pp_profile):

        self.mon=Monitor()
        self.mon.on()

        
        #instantiate arguments
        self.show_id=show_id
        self.root=root,
        self.canvas = canvas
        self.show_params=show_params  
        self.track_params=track_params
        self.pp_dir=pp_dir
        self.pp_home=pp_home
        self.pp_profile=pp_profile

        # get duration limit (secs ) from profile
        if self.track_params['duration']<>"":
            self.duration= int(self.track_params['duration'])
        else:
            self.duration= int(self.show_params['duration'])
        self.duration_limit=20*self.duration


        # get background image from profile.
        self.background_file=''
        if self.track_params['background-image']<>"":
            self.background_file= self.track_params['background-image']
        else:
            if self.track_params['display-show-background']=='yes':
                self.background_file= self.show_params['background-image']
            
        # get background colour from profile.
        if self.track_params['background-colour']<>"":
            self.background_colour= self.track_params['background-colour']
        else:
            self.background_colour= self.show_params['background-colour']

        #get animation instructions from profile
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']

        # open the plugin Manager
        self.pim=PluginManager(self.show_id,self.root,self.canvas,self.show_params,self.track_params,self.pp_dir,self.pp_home,self.pp_profile) 

        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()

        # could put instance generation in play, not sure which is better.
        self.bplayer=uzblDriver(self.canvas)
        self.command_timer=None
        self.tick_timer=None
        self.init_play_state_machine()



    def play(self, track,
                     showlist,
                     end_callback,
                     ready_callback,
                     enable_menu=False):

        #instantiate arguments
        self.track=track
        self.showlist=showlist
        self.end_callback=end_callback         # callback when finished
        self.ready_callback=ready_callback   #callback when ready to play
        self.enable_menu=enable_menu

        # callback to the calling object to e.g remove egg timer.
        if self.ready_callback<>None:
            self.ready_callback()

        # create an  instance of showmanager so we can control concurrent shows
        self.show_manager=ShowManager(self.show_id,self.showlist,self.show_params,self.root,self.canvas,self.pp_dir,self.pp_profile,self.pp_home)

        #web window                  
        if self.track_params['web-window']<>'':
            self.web_window= self.track_params['web-window']
        else:
            self.web_window= self.show_params['web-window']

        reason,message,command,has_window,x1,y1,x2,y2=self.parse_window(self.web_window)
        if reason =='error':
            self.mon.err(self,'web window error: '+'  ' + message + ' in ' + self.web_window)
            self.end_callback(reason,message)
            self=None
        else:
            #deal with web_window
            if has_window==False:
                self.geometry = ' --geometry=maximized '
            else:
                width=x2-x1
                height=y2-y1
                self.geometry = "--geometry=%dx%d%+d%+d "  % (width,height,x1,y1)

            # get browser commands
            reason,message=self.parse_commands(self.track_params['browser-commands'])
            if reason != 'normal':
                self.mon.err(self,message)
                self.end_callback(reason,message)
                self=None
            else:
            
                # Control other shows at beginning
                reason,message=self.show_manager.show_control(self.track_params['show-control-begin'])
                if reason == 'error':
                    self.mon.err(self,message)
                    self.end_callback(reason,message)
                    self=None
                else:
                    #display content
                    reason,message=self.display_content()
                    if reason == 'error':
                        self.mon.err(self,message)
                        self.end_callback(reason,message)
                        self=None
                    else:
                        # create animation events
                        reason,message=self.ppio.animate(self.animate_begin_text,id(self))
                        if reason=='error':
                            self.mon.err(self,message)
                            self.end_callback(reason,message)
                            self=None
                        else:     
                            # start playing the track.
                            self.start_play_state_machine()

    def terminate(self,reason):
        """
        terminate the  player in special circumstances
        normal user termination if by key_pressed 'stop'
        reason will be killed or error
        """
        # circumvents state machine to terminate lower level and then itself.
        if self.bplayer<>None:
            self.mon.log(self,"sent terminate to uzbldriver")
            self.bplayer.terminate(reason)
            self.end('killed',' end without waiting for uzbl to finish') # end without waiting
        else:
            self.mon.log(self,"terminate, uzbldriver not running")
            self.end('killed','terminate, uzbldriver not running')

    def get_links(self):
        return self.track_params['links']

    def input_pressed(self,symbol):
        # print symbol, symbol[0:5]
        if symbol[0:5]=='uzbl-':
            self.control(symbol[5:])
            
        elif symbol == 'pause':
            self.pause()

        elif symbol=='stop':
            self.stop()


        
# ***************************************
# INTERNAL FUNCTIONS
# ***************************************

    #browser do not do pause
    def pause(self):
        self.mon.log(self,"!<pause rejected")
        return False
        
    # other control when playing, not currently used
    def control(self,char):
        if self.play_state==BrowserPlayer._PLAYING and char not in ('exit'):
            self.mon.log(self,"> send control to uzbl:"+ char)
            self.bplayer.control(char)
            return True
        else:
            self.mon.log(self,"!<control rejected")
            return False

    # respond to normal stop
    def stop(self):
        # send signal to stop the track to the state machine
        self.mon.log(self,">stop received")
        self.quit_signal=True

         
      
# ***************************************
#  sequencing
# ***************************************

    """self. play_state controls the playing sequence, it has the following values.
         I am not entirely sure the starting and ending states are required.
         - _closed - the mplayer process is not running, mplayer process can be initiated
         - _starting - mplayer process is running but is not yet able to receive controls
         - _playing - playing a track, controls can be sent
         - _ending - mplayer is doing its termination, controls cannot be sent
    """

    def init_play_state_machine(self):
        self.quit_signal=False
        self.play_state=BrowserPlayer._CLOSED
 
    def start_play_state_machine(self):
        #initialise all the state machine variables
        self.duration_count = 0
        self.quit_signal=False     # signal that user has pressed stop

        #play the track
        self.bplayer.play(self.track,self.geometry)
        self.mon.log (self,'Playing track from show Id: '+ str(self.show_id))
        self.play_state=BrowserPlayer._STARTING
        
        # and start polling for state changes and count duration
        self.tick_timer=self.canvas.after(50, self.play_state_machine)


    def play_state_machine(self):

        if self.play_state == BrowserPlayer._CLOSED:
            self.mon.log(self,"      State machine: " + self.play_state)
            return 
                
        elif self.play_state == BrowserPlayer._STARTING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            
            # if uzbl fifo is available can send comands to uzbl but change to wait state to wait for it to appear on screen
            if self.bplayer.start_play_signal==True:
                self.mon.log(self,"            <fifo available signal received from uzbl")
                self.bplayer.start_play_signal=False
                self.play_state=BrowserPlayer._WAITING
                # get rid of status bar
                self.bplayer.control('set show_status = 0')
                # and get ready to wait for browser to appear
                self.wait_count= 50   # 10 seconds at 200mS steps 
                self.mon.log(self,"      State machine: uzbl process alive")
                
            self.tick_timer=self.canvas.after(200, self.play_state_machine)


        elif self.play_state == BrowserPlayer._WAITING:
            if self.wait_count==0:
                # set state to playing
                self.play_state = BrowserPlayer._PLAYING
                # and start executing the browser commands
                self.play_commands()
                self.mon.log(self,"      State machine: uzbl_playing started")
                
            self.wait_count -=1
            self.tick_timer=self.canvas.after(200, self.play_state_machine)

        elif self.play_state == BrowserPlayer._PLAYING:
            self.duration_count+=1
            # self.mon.log(self,"      State machine: " + self.play_state)
            
            # service any queued stop signals and test duration count
            if self.quit_signal==True or (self.duration_limit>0 and self.duration_count>self.duration_limit):
                self.mon.log(self,"      Service stop required signal or timeout")
                # self.quit_signal=False
                self.stop_bplayer()
                self.play_state = BrowserPlayer._ENDING

            # uzbl reports it is terminating so change to ending state
            if self.bplayer.end_play_signal:                    
                self.mon.log(self,"            <end play signal received")
                self.play_state = BrowserPlayer._ENDING
            self.tick_timer=self.canvas.after(50, self.play_state_machine)

        elif self.play_state == BrowserPlayer._ENDING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            # if spawned process has closed can change to closed state
            # self.mon.log (self,"      State machine : is luakit process running? -  "  + str(self.bplayer.is_running()))
            if self.bplayer.is_running() ==False:
                self.mon.log(self,"            <uzbl process is dead")
                if self.quit_signal==True:
                    self.quit_signal=False
                self.play_state = BrowserPlayer._CLOSED
                self.end('normal','quit required or timeout')
            else:
                self.tick_timer=self.canvas.after(50, self.play_state_machine)
                


    def stop_bplayer(self):
        # send signal to stop the track to the state machine
        self.mon.log(self,"         >send stop to uzbl driver")
        if self.play_state==BrowserPlayer._PLAYING:
            self.bplayer.stop()
            return True
        else:
            self.mon.log(self,"!<stop rejected")
            return False

# *****************
# ending the player
# *****************

    def end(self,reason,message):
            # stop the plugin
            if self.pim<>None:
                self.pim.stop_plugin()

            # abort the timers
            if self.tick_timer<>None:
                self.canvas.after_cancel(self.tick_timer)
                self.tick_timer=None
            if self.command_timer<>None:
                self.canvas.after_cancel(self.command_timer)
                self.tick_timer=None
            # clean up and fifos and sockets left by uzbl
            os.system('rm -f  /tmp/uzbl_*')
            if reason in ('error','killed'):
                self.end_callback(reason,message)
                self=None

            else:
                # normal end so do show control and animation

                # Control concurrent shows at end
                reason,message=self.show_manager.show_control(self.track_params['show-control-end'])
                if reason =='error':
                    self.mon.err(self,message)
                    self.end_callback(reason,message)
                    self=None
                else:
                   # clear events list for this track
                    if self.track_params['animate-clear']=='yes':
                        self.ppio.clear_events_list(id(self))
                    
                    # create animation events for ending
                    reason,message=self.ppio.animate(self.animate_end_text,id(self))
                    if reason=='error':
                        self.mon.err(self,message)
                        self.end_callback(reason,message)
                        self=None
                    else:
                        self.end_callback('normal',"track has terminated or quit")
                        self=None



# *****************
# displaying things
# *****************
            
    def display_content(self):
       

        self.canvas.delete('pp-content')


        #background colour
        if  self.background_colour<>'':
            self.canvas.config(bg=self.background_colour)
            
        if self.background_file<>'':
            self.background_img_file = self.complete_path(self.background_file)
            if not os.path.exists(self.background_img_file):
                self.mon.err(self,"Audio background file not found: "+ self.background_img_file)
                self.end('error',"Audio background file not found")
            else:
                pil_background_img=PIL.Image.open(self.background_img_file)
                self.background = PIL.ImageTk.PhotoImage(pil_background_img)
                self.drawn = self.canvas.create_image(int(self.canvas['width'])/2,
                                              int(self.canvas['height'])/2,
                                              image=self.background,
                                              anchor=CENTER,
                                                tag='pp-content')

        # execute the plugin if required
        if self.track_params['plugin']<>'':

            reason,message,self.track = self.pim.do_plugin(self.track,self.track_params['plugin'],)
            if reason <> 'normal':
                return reason,message

                
        # display hint text if enabled
       
        if self.enable_menu== True:
            self.canvas.create_text(int(self.show_params['hint-x']),
                                                    int(self.show_params['hint-y']),
                                                  text=self.show_params['hint-text'],
                                                  fill=self.show_params['hint-colour'],
                                                font=self.show_params['hint-font'],
                                                    anchor=NW,
                                                tag='pp-content')

            
        # display show text if enabled
        if self.show_params['show-text']<> ''and self.track_params['display-show-text']=='yes':
            self.canvas.create_text(int(self.show_params['show-text-x']),int(self.show_params['show-text-y']),
                                                    anchor=NW,
                                                  text=self.show_params['show-text'],
                                                  fill=self.show_params['show-text-colour'],
                                                  font=self.show_params['show-text-font'],
                                                tag='pp-content')
            
        # display track text if enabled
        if self.track_params['track-text']<> '':
            self.canvas.create_text(int(self.track_params['track-text-x']),int(self.track_params['track-text-y']),
                                                    anchor=NW,
                                                  text=self.track_params['track-text'],
                                                  fill=self.track_params['track-text-colour'],
                                                  font=self.track_params['track-text-font'],
                                                tag='pp-content')

        self.mon.log(self,"Displayed background and text ")

        self.canvas.tag_raise('pp-click-area')
        
        self.canvas.update_idletasks( )

        return 'normal',''


     
# *******************   
# browser commands
# ***********************

    def parse_commands(self,command_text):
        self.command_list=[]
        lines = command_text.split('\n')
        for line in lines:
            if line.strip()=="":
                continue
            reason,entry=self.parse_command(line)
            if reason != 'normal':
                return 'error',entry
            self.command_list.append(copy.deepcopy(entry))
        # print self.command_list
        return 'normal',''

    def parse_command(self,line):
        fields = line.split()
        if fields[0]=='uzbl':
            # print fields[0], line[4:]
            return  'normal',[fields[0],line[4:]]
        
        if len(fields) not in (1,2):
            return 'error',"incorrect number of fields in command: " + line
        command=fields[0]
        arg=''
        if command not in ('load','refresh','wait','exit','loop'):
            return 'error','unknown command: '+ command
            
        if command in ('refresh','exit','loop') and len(fields)<>1:
            return 'error','incorrect number of fields for '+ command + 'in: ' + line
            
        if command == 'load':
            if len(fields)<>2:
                return 'error','incorrect number of fields for '+ command + 'in: ' + line
            else:
                arg = fields[1]


        if command == 'wait':
            if len(fields)<>2:
                return 'error','incorrect number of fields for '+ command + 'in: ' + line
            else:
                arg = fields[1]
                if not arg.isdigit():return 'error','Argument for Wait is not 0 or positive number in: ' + line

        return 'normal',[command,arg]


        
    def play_commands(self):
        if len(self.command_list)==0:
            return
        self.loop=0
        self.command_index=0
        self.canvas.after(100,self.execute_command)

        
    def execute_command(self):
        entry=self.command_list[self.command_index]
        command=entry[0]
        arg=entry[1]
        if self.command_index==len(self.command_list)-1:
            self.command_index=self.loop
        else:
            self.command_index+=1
            
        # execute command
        if command == 'load':
            #self.canvas.focus_force()
            #self.root.lower()
            file=self.complete_path(arg)
            self.bplayer.control('uri '+ file)
            self.command_timer=self.canvas.after(10,self.execute_command)
        elif command == 'refresh':
            self.bplayer.control('reload_ign_cache')
            self.command_timer=self.canvas.after(10,self.execute_command)
        elif command == 'wait':
            self.command_timer=self.canvas.after(1000*int(arg),self.execute_command)        
        elif  command=='exit':
            self.quit_signal=True
        elif command=='loop':
            self.loop=self.command_index
            self.command_timer=self.canvas.after(10,self.execute_command)
        elif command=='uzbl':
            self.bplayer.control(arg)
            self.command_timer=self.canvas.after(10,self.execute_command)

        
        
        
# *****************
# utilities
# *****************

    def complete_path(self,track_file):
        #  complete path of the filename of the selected entry
        if track_file[0]=="+":
                track_file=self.pp_home+track_file[1:]
        self.mon.log(self,"Background image is "+ track_file)
        return track_file     
            





    def parse_window(self,line):
            # parses warp _ or xy2
            
            fields = line.split()
            # check there is a command field
            if len(fields) < 1:
                    return 'error','no type field','',False,0,0,0,0
                

            #deal with warp which has 1 or 5  arguments
            # check basic syntax
            if  fields[0] <>'warp':
                    return 'error','not a valid type','',False,0,0,0,0
            if len(fields) not in (1,5):
                    return 'error','wrong number of coordinates for warp','',False,0,0,0,0

            # deal with window coordinates    
            if len(fields) == 5:
                #window is specified
                if not (fields[1].isdigit() and fields[2].isdigit() and fields[3].isdigit() and fields[4].isdigit()):
                    return 'error','coordinates are not positive integers','',False,0,0,0,0
                has_window=True
                return 'normal','',fields[0],has_window,int(fields[1]),int(fields[2]),int(fields[3]),int(fields[4])
            else:
                # fullscreen
                has_window=False
                return 'normal','',fields[0],has_window,0,0,0,0
コード例 #4
0
class AudioPlayer:
    """       
            plays an audio track using mplayer against a coloured backgroud and image
            track can be paused and interrupted
            See pp_imageplayer for common software design description
    """

    #state constants
    _CLOSED = "mplayer_closed"  #probably will not exist
    _STARTING = "mplayer_starting"  #track is being prepared
    _PLAYING = "mplayer_playing"  #track is playing to the screen, may be paused
    _ENDING = "mplayer_ending"  #track is in the process of ending due to quit or end of track
    _WAITING = "wait for timeout"  # track has finished but timeout still running

    # audio mixer matrix settings
    _LEFT = "channels=2:1:0:0:1:1"
    _RIGHT = "channels=2:1:0:1:1:0"
    _STEREO = "channels=2"

    # ***************************************
    # EXTERNAL COMMANDS
    # ***************************************

    def __init__(self, show_id, root, canvas, show_params, track_params,
                 pp_dir, pp_home, pp_profile):

        self.mon = Monitor()
        self.mon.off()

        #instantiate arguments
        self.show_id = show_id
        self.root = root
        self.canvas = canvas
        self.show_params = show_params
        self.track_params = track_params
        self.pp_dir = pp_dir
        self.pp_home = pp_home
        self.pp_profile = pp_profile

        # get duration limit (secs ) from profile
        if self.track_params['duration'] <> '':
            self.duration = int(self.track_params['duration'])
            self.duration_limit = 20 * self.duration
        else:
            self.duration_limit = -1

        # get background image from profile.
        self.background_file = ''
        if self.track_params['background-image'] <> "":
            self.background_file = self.track_params['background-image']
        else:
            if self.track_params['display-show-background'] == 'yes':
                self.background_file = self.show_params['background-image']

        # get background colour from profile.
        if self.track_params['background-colour'] <> "":
            self.background_colour = self.track_params['background-colour']
        else:
            self.background_colour = self.show_params['background-colour']

        # get audio device from profile.
        if self.track_params['mplayer-audio'] <> "":
            self.mplayer_audio = self.track_params['mplayer-audio']
        else:
            self.mplayer_audio = self.show_params['mplayer-audio']

        # get audio volume from profile.
        if self.track_params['mplayer-volume'] <> "":
            self.mplayer_volume = self.track_params['mplayer-volume'].strip()
        else:
            self.mplayer_volume = self.show_params['mplayer-volume'].strip()
        self.volume_option = 'volume=' + self.mplayer_volume

        #get speaker from profile
        if self.track_params['audio-speaker'] <> "":
            self.audio_speaker = self.track_params['audio-speaker']
        else:
            self.audio_speaker = self.show_params['audio-speaker']

        if self.audio_speaker == 'left':
            self.speaker_option = AudioPlayer._LEFT
        elif self.audio_speaker == 'right':
            self.speaker_option = AudioPlayer._RIGHT
        else:
            self.speaker_option = AudioPlayer._STEREO

        #get animation instructions from profile
        self.animate_begin_text = self.track_params['animate-begin']
        self.animate_end_text = self.track_params['animate-end']

        # open the plugin Manager
        self.pim = PluginManager(self.show_id, self.root, self.canvas,
                                 self.show_params, self.track_params,
                                 self.pp_dir, self.pp_home, self.pp_profile)

        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()

        # could put instance generation in play, not sure which is better.
        self.mplayer = mplayerDriver(self.canvas)
        self.tick_timer = None
        self.init_play_state_machine()

    def play(self,
             track,
             showlist,
             end_callback,
             ready_callback,
             enable_menu=False):

        #instantiate arguments
        self.track = track
        self.showlist = showlist
        self.end_callback = end_callback  # callback when finished
        self.ready_callback = ready_callback  #callback when ready to play
        self.enable_menu = enable_menu

        # select the sound device
        if self.mplayer_audio <> "":
            if self.mplayer_audio == 'hdmi':
                os.system("amixer -q -c 0 cset numid=3 2")
            else:
                os.system("amixer -q -c 0 cset numid=3 1")

        # callback to the calling object to e.g remove egg timer.
        if self.ready_callback <> None:
            self.ready_callback()

        # create an  instance of showmanager so we can control concurrent shows
        self.show_manager = ShowManager(self.show_id, self.showlist,
                                        self.show_params, self.root,
                                        self.canvas, self.pp_dir,
                                        self.pp_profile, self.pp_home)

        # Control other shows at beginning
        reason, message = self.show_manager.show_control(
            self.track_params['show-control-begin'])
        if reason == 'error':
            self.mon.err(self, message)
            self.end_callback(reason, message)
            self = None
        else:
            # display image and text
            reason, message = self.display_content()
            if reason == 'error':
                self.mon.err(self, message)
                self.end_callback(reason, message)
                self = None
            else:
                # create animation events
                reason, message = self.ppio.animate(self.animate_begin_text,
                                                    id(self))
                if reason == 'error':
                    self.mon.err(self, message)
                    self.end_callback(reason, message)
                    self = None
                else:
                    # start playing the track.
                    if self.duration_limit <> 0:
                        self.start_play_state_machine()
                    else:
                        self.tick_timer = self.canvas.after(10, self.end_zero)

    def end_zero(self):
        self.end('normal', 'zero duration')

    def terminate(self, reason):
        """
        terminate the  player in special circumstances
        normal user termination if by key_pressed 'stop'
        reason will be killed or error
        """
        # circumvents state machine to terminate lower level and then itself.
        if self.mplayer <> None:
            self.mon.log(self, "sent terminate to mplayerdriver")
            self.mplayer.terminate(reason)
            self.end('killed', ' end without waiting for mplayer to finish'
                     )  # end without waiting
        else:
            self.mon.log(self, "terminate, mplayerdriver not running")
            self.end('killed', 'terminate, mplayerdriver not running')

    def get_links(self):
        return self.track_params['links']

    def input_pressed(self, symbol):
        if symbol[0:6] == 'mplay-':
            self.control(symbol[6])

        elif symbol == 'pause':
            self.pause()

        elif symbol == 'stop':
            self.stop()

# ***************************************
# INTERNAL FUNCTIONS
# ***************************************

#toggle pause

    def pause(self):
        if self.play_state in (AudioPlayer._PLAYING,
                               AudioPlayer._ENDING) and self.track <> '':
            self.mplayer.pause()
            return True
        else:
            self.mon.log(self, "!<pause rejected")
            return False

    # other control when playing, not currently used
    def control(self, char):
        if self.play_state == AudioPlayer._PLAYING and self.track <> '' and char not in (
                'q'):
            self.mon.log(self, "> send control to mplayer: " + char)
            self.mplayer.control(char)
            return True
        else:
            self.mon.log(self, "!<control rejected")
            return False

    # respond to normal stop
    def stop(self):
        # send signal to stop the track to the state machine
        self.mon.log(self, ">stop received")
        self.quit_signal = True

# ***************************************
#  sequencing
# ***************************************

    """self. play_state controls the playing sequence, it has the following values.
         I am not entirely sure the starting and ending states are required.
         - _closed - the mplayer process is not running, mplayer process can be initiated
         - _starting - mplayer process is running but is not yet able to receive controls
         - _playing - playing a track, controls can be sent
         - _ending - mplayer is doing its termination, controls cannot be sent
    """
    def init_play_state_machine(self):
        self.quit_signal = False
        self.play_state = AudioPlayer._CLOSED

    def start_play_state_machine(self):
        #initialise all the state machine variables
        self.duration_count = 0
        self.quit_signal = False  # signal that user has pressed stop

        #play the track
        options = self.show_params[
            'mplayer-other-options'] + '-af ' + self.speaker_option + ',' + self.volume_option + ' '
        if self.track <> '':
            self.mplayer.play(self.track, options)
            self.mon.log(self,
                         'Playing track from show Id: ' + str(self.show_id))
            self.play_state = AudioPlayer._STARTING
        else:
            self.play_state = AudioPlayer._PLAYING
        # and start polling for state changes and count duration
        self.tick_timer = self.canvas.after(50, self.play_state_machine)

    def play_state_machine(self):
        self.duration_count += 1

        if self.play_state == AudioPlayer._CLOSED:
            self.mon.log(self, "      State machine: " + self.play_state)
            return

        elif self.play_state == AudioPlayer._STARTING:
            self.mon.log(self, "      State machine: " + self.play_state)

            # if mplayer is playing the track change to play state
            if self.mplayer.start_play_signal == True:
                self.mon.log(
                    self,
                    "            <start play signal received from mplayer")
                self.mplayer.start_play_signal = False
                self.play_state = AudioPlayer._PLAYING
                self.mon.log(self,
                             "      State machine: mplayer_playing started")
            self.tick_timer = self.canvas.after(50, self.play_state_machine)

        elif self.play_state == AudioPlayer._PLAYING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            # service any queued stop signals
            if self.quit_signal == True or (
                    self.duration_limit > 0
                    and self.duration_count > self.duration_limit):
                self.mon.log(self,
                             "      Service stop required signal or timeout")
                # self.quit_signal=False
                if self.track <> '':
                    self.stop_mplayer()
                    self.play_state = AudioPlayer._ENDING
                else:
                    self.play_state = AudioPlayer._CLOSED
                    self.end('normal', 'stop required signal or timeout')

            # mplayer reports it is terminating so change to ending state
            if self.track <> '' and self.mplayer.end_play_signal:
                self.mon.log(self, "            <end play signal received")
                self.mon.log(
                    self, "            <end detected at: " +
                    str(self.mplayer.audio_position))
                self.play_state = AudioPlayer._ENDING
            self.tick_timer = self.canvas.after(50, self.play_state_machine)

        elif self.play_state == AudioPlayer._ENDING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            # if spawned process has closed can change to closed state
            # self.mon.log (self,"      State machine : is mplayer process running? -  "  + str(self.mplayer.is_running()))
            if self.mplayer.is_running() == False:
                self.mon.log(self, "            <mplayer process is dead")
                if self.quit_signal == True:
                    self.quit_signal = False
                    self.play_state = AudioPlayer._CLOSED
                    self.end('normal', 'quit required or timeout')
                elif self.duration_limit > 0 and self.duration_count < self.duration_limit:
                    self.play_state = AudioPlayer._WAITING
                    self.tick_timer = self.canvas.after(
                        50, self.play_state_machine)
                else:
                    self.play_state = AudioPlayer._CLOSED
                    self.end('normal', 'mplayer dead')
            else:
                self.tick_timer = self.canvas.after(50,
                                                    self.play_state_machine)

        elif self.play_state == AudioPlayer._WAITING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            if self.quit_signal == True or (
                    self.duration_limit > 0
                    and self.duration_count > self.duration_limit):
                self.mon.log(
                    self,
                    "      Service stop required signal or timeout from wait")
                self.quit_signal = False
                self.play_state = AudioPlayer._CLOSED
                self.end('normal', 'mplayer dead')
            else:
                self.tick_timer = self.canvas.after(50,
                                                    self.play_state_machine)

    def stop_mplayer(self):
        # send signal to stop the track to the state machine
        self.mon.log(self,
                     "         >stop mplayer received from state machine")
        if self.play_state == AudioPlayer._PLAYING:
            self.mplayer.stop()
            return True
        else:
            self.mon.log(self, "!<stop rejected")
            return False

# *****************
# ending the player
# *****************

    def end(self, reason, message):
        # stop the plugin
        if self.track_params['plugin'] <> '':
            self.pim.stop_plugin()

        # abort the timer
        if self.tick_timer <> None:
            self.canvas.after_cancel(self.tick_timer)
            self.tick_timer = None

        if reason in ('error', 'killed'):
            self.end_callback(reason, message)
            self = None

        else:
            # normal end so do show control and animation

            # Control concurrent shows at end
            reason, message = self.show_manager.show_control(
                self.track_params['show-control-end'])
            if reason == 'error':
                self.mon.err(self, message)
                self.end_callback(reason, message)
                self = None
            else:
                # clear events list for this track
                if self.track_params['animate-clear'] == 'yes':
                    self.ppio.clear_events_list(id(self))

                # create animation events for ending
                reason, message = self.ppio.animate(self.animate_end_text,
                                                    id(self))
                if reason == 'error':
                    self.mon.err(self, message)
                    self.end_callback(reason, message)
                    self = None
                else:
                    self.end_callback('normal', "track has terminated or quit")
                    self = None

# *****************
# displaying things
# *****************

    def display_content(self):

        #if self.background_file<>'' or self.show_params['show-text']<> '' or #self.track_params['track-text']<> '' or self.enable_menu== True or #self.track_params['clear-screen']=='yes':

        if self.track_params['clear-screen'] == 'yes':
            self.canvas.delete('pp-content')
            # self.canvas.update()

        #background colour
        if self.background_colour <> '':
            self.canvas.config(bg=self.background_colour)

        if self.background_file <> '':
            self.background_img_file = self.complete_path(self.background_file)
            if not os.path.exists(self.background_img_file):
                self.mon.err(
                    self, "Audio background file not found: " +
                    self.background_img_file)
                self.end('error', "Audio background file not found")
            else:
                pil_background_img = PIL.Image.open(self.background_img_file)
                self.background = PIL.ImageTk.PhotoImage(pil_background_img)
                self.drawn = self.canvas.create_image(
                    int(self.canvas['width']) / 2,
                    int(self.canvas['height']) / 2,
                    image=self.background,
                    anchor=CENTER,
                    tag='pp-content')

        # execute the plugin if required
        if self.track_params['plugin'] <> '':

            reason, message, self.track = self.pim.do_plugin(
                self.track,
                self.track_params['plugin'],
            )
            if reason <> 'normal':
                return reason, message

        # display hint text if enabled

        if self.enable_menu == True:
            self.canvas.create_text(int(self.show_params['hint-x']),
                                    int(self.show_params['hint-y']),
                                    text=self.show_params['hint-text'],
                                    fill=self.show_params['hint-colour'],
                                    font=self.show_params['hint-font'],
                                    anchor=NW,
                                    tag='pp-content')

        # display show text if enabled
        if self.show_params['show-text'] <> '' and self.track_params[
                'display-show-text'] == 'yes':
            self.canvas.create_text(int(self.show_params['show-text-x']),
                                    int(self.show_params['show-text-y']),
                                    anchor=NW,
                                    text=self.show_params['show-text'],
                                    fill=self.show_params['show-text-colour'],
                                    font=self.show_params['show-text-font'],
                                    tag='pp-content')

        # display track text if enabled
        if self.track_params['track-text'] <> '':
            self.canvas.create_text(
                int(self.track_params['track-text-x']),
                int(self.track_params['track-text-y']),
                anchor=NW,
                text=self.track_params['track-text'],
                fill=self.track_params['track-text-colour'],
                font=self.track_params['track-text-font'],
                tag='pp-content')

        self.mon.log(self, "Displayed background and text ")

        self.canvas.tag_raise('pp-click-area')

        self.canvas.update_idletasks()

        return 'normal', ''

# *****************
# utilities
# *****************

    def complete_path(self, track_file):
        #  complete path of the filename of the selected entry
        if track_file[0] == "+":
            track_file = self.pp_home + track_file[1:]
        self.mon.log(self, "Background image is " + track_file)
        return track_file
コード例 #5
0
class AudioPlayer:
    """       
            plays an audio track using mplayer against a coloured backgroud and image
            track can be paused and interrupted
            See pp_imageplayer for common software design description
    """

    # state constants
    _CLOSED = "mplayer_closed"  # probably will not exist
    _STARTING = "mplayer_starting"  # track is being prepared
    _PLAYING = "mplayer_playing"  # track is playing to the screen, may be paused
    _ENDING = "mplayer_ending"  # track is in the process of ending due to quit or end of track
    _WAITING = "wait for timeout"  # track has finished but timeout still running

    # audio mixer matrix settings
    _LEFT = "channels=2:1:0:0:1:1"
    _RIGHT = "channels=2:1:0:1:1:0"
    _STEREO = "channels=2"

    # ***************************************
    # EXTERNAL COMMANDS
    # ***************************************

    def __init__(self, show_id, root, canvas, show_params, track_params, pp_dir, pp_home, pp_profile):

        self.mon = Monitor()
        self.mon.off()

        # instantiate arguments
        self.show_id = show_id
        self.root = root
        self.canvas = canvas
        self.show_params = show_params
        self.track_params = track_params
        self.pp_dir = pp_dir
        self.pp_home = pp_home
        self.pp_profile = pp_profile

        # get duration limit (secs ) from profile
        if self.track_params["duration"] <> "":
            self.duration = int(self.track_params["duration"])
            self.duration_limit = 20 * self.duration
        else:
            self.duration_limit = -1

        # get background image from profile.
        self.background_file = ""
        if self.track_params["background-image"] <> "":
            self.background_file = self.track_params["background-image"]
        else:
            if self.track_params["display-show-background"] == "yes":
                self.background_file = self.show_params["background-image"]

        # get background colour from profile.
        if self.track_params["background-colour"] <> "":
            self.background_colour = self.track_params["background-colour"]
        else:
            self.background_colour = self.show_params["background-colour"]

        # get audio device from profile.
        if self.track_params["mplayer-audio"] <> "":
            self.mplayer_audio = self.track_params["mplayer-audio"]
        else:
            self.mplayer_audio = self.show_params["mplayer-audio"]

        # get audio volume from profile.
        if self.track_params["mplayer-volume"] <> "":
            self.mplayer_volume = self.track_params["mplayer-volume"].strip()
        else:
            self.mplayer_volume = self.show_params["mplayer-volume"].strip()
        self.volume_option = "volume=" + self.mplayer_volume

        # get speaker from profile
        if self.track_params["audio-speaker"] <> "":
            self.audio_speaker = self.track_params["audio-speaker"]
        else:
            self.audio_speaker = self.show_params["audio-speaker"]

        if self.audio_speaker == "left":
            self.speaker_option = AudioPlayer._LEFT
        elif self.audio_speaker == "right":
            self.speaker_option = AudioPlayer._RIGHT
        else:
            self.speaker_option = AudioPlayer._STEREO

        # get animation instructions from profile
        self.animate_begin_text = self.track_params["animate-begin"]
        self.animate_end_text = self.track_params["animate-end"]

        # open the plugin Manager
        self.pim = PluginManager(
            self.show_id,
            self.root,
            self.canvas,
            self.show_params,
            self.track_params,
            self.pp_dir,
            self.pp_home,
            self.pp_profile,
        )

        # create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()

        # could put instance generation in play, not sure which is better.
        self.mplayer = mplayerDriver(self.canvas)
        self.tick_timer = None
        self.init_play_state_machine()

    def play(self, track, showlist, end_callback, ready_callback, enable_menu=False):

        # instantiate arguments
        self.track = track
        self.showlist = showlist
        self.end_callback = end_callback  # callback when finished
        self.ready_callback = ready_callback  # callback when ready to play
        self.enable_menu = enable_menu

        # select the sound device
        if self.mplayer_audio <> "":
            if self.mplayer_audio == "hdmi":
                os.system("amixer -q -c 0 cset numid=3 2")
            else:
                os.system("amixer -q -c 0 cset numid=3 1")

        # callback to the calling object to e.g remove egg timer.
        if self.ready_callback <> None:
            self.ready_callback()

        # create an  instance of showmanager so we can control concurrent shows
        self.show_manager = ShowManager(
            self.show_id,
            self.showlist,
            self.show_params,
            self.root,
            self.canvas,
            self.pp_dir,
            self.pp_profile,
            self.pp_home,
        )

        # Control other shows at beginning
        reason, message = self.show_manager.show_control(self.track_params["show-control-begin"])
        if reason == "error":
            self.mon.err(self, message)
            self.end_callback(reason, message)
            self = None
        else:
            # display image and text
            reason, message = self.display_content()
            if reason == "error":
                self.mon.err(self, message)
                self.end_callback(reason, message)
                self = None
            else:
                # create animation events
                reason, message = self.ppio.animate(self.animate_begin_text, id(self))
                if reason == "error":
                    self.mon.err(self, message)
                    self.end_callback(reason, message)
                    self = None
                else:
                    # start playing the track.
                    if self.duration_limit <> 0:
                        self.start_play_state_machine()
                    else:
                        self.tick_timer = self.canvas.after(10, self.end_zero)

    def end_zero(self):
        self.end("normal", "zero duration")

    def terminate(self, reason):
        """
        terminate the  player in special circumstances
        normal user termination if by key_pressed 'stop'
        reason will be killed or error
        """
        # circumvents state machine to terminate lower level and then itself.
        if self.mplayer <> None:
            self.mon.log(self, "sent terminate to mplayerdriver")
            self.mplayer.terminate(reason)
            self.end("killed", " end without waiting for mplayer to finish")  # end without waiting
        else:
            self.mon.log(self, "terminate, mplayerdriver not running")
            self.end("killed", "terminate, mplayerdriver not running")

    def get_links(self):
        return self.track_params["links"]

    def input_pressed(self, symbol):
        if symbol[0:6] == "mplay-":
            self.control(symbol[6])

        elif symbol == "pause":
            self.pause()

        elif symbol == "stop":
            self.stop()

    # ***************************************
    # INTERNAL FUNCTIONS
    # ***************************************

    # toggle pause
    def pause(self):
        if self.play_state in (AudioPlayer._PLAYING, AudioPlayer._ENDING) and self.track <> "":
            self.mplayer.pause()
            return True
        else:
            self.mon.log(self, "!<pause rejected")
            return False

    # other control when playing, not currently used
    def control(self, char):
        if self.play_state == AudioPlayer._PLAYING and self.track <> "" and char not in ("q"):
            self.mon.log(self, "> send control to mplayer: " + char)
            self.mplayer.control(char)
            return True
        else:
            self.mon.log(self, "!<control rejected")
            return False

    # respond to normal stop
    def stop(self):
        # send signal to stop the track to the state machine
        self.mon.log(self, ">stop received")
        self.quit_signal = True

    # ***************************************
    #  sequencing
    # ***************************************

    """self. play_state controls the playing sequence, it has the following values.
         I am not entirely sure the starting and ending states are required.
         - _closed - the mplayer process is not running, mplayer process can be initiated
         - _starting - mplayer process is running but is not yet able to receive controls
         - _playing - playing a track, controls can be sent
         - _ending - mplayer is doing its termination, controls cannot be sent
    """

    def init_play_state_machine(self):
        self.quit_signal = False
        self.play_state = AudioPlayer._CLOSED

    def start_play_state_machine(self):
        # initialise all the state machine variables
        self.duration_count = 0
        self.quit_signal = False  # signal that user has pressed stop

        # play the track
        options = (
            self.show_params["mplayer-other-options"] + "-af " + self.speaker_option + "," + self.volume_option + " "
        )
        if self.track <> "":
            self.mplayer.play(self.track, options)
            self.mon.log(self, "Playing track from show Id: " + str(self.show_id))
            self.play_state = AudioPlayer._STARTING
        else:
            self.play_state = AudioPlayer._PLAYING
        # and start polling for state changes and count duration
        self.tick_timer = self.canvas.after(50, self.play_state_machine)

    def play_state_machine(self):
        self.duration_count += 1

        if self.play_state == AudioPlayer._CLOSED:
            self.mon.log(self, "      State machine: " + self.play_state)
            return

        elif self.play_state == AudioPlayer._STARTING:
            self.mon.log(self, "      State machine: " + self.play_state)

            # if mplayer is playing the track change to play state
            if self.mplayer.start_play_signal == True:
                self.mon.log(self, "            <start play signal received from mplayer")
                self.mplayer.start_play_signal = False
                self.play_state = AudioPlayer._PLAYING
                self.mon.log(self, "      State machine: mplayer_playing started")
            self.tick_timer = self.canvas.after(50, self.play_state_machine)

        elif self.play_state == AudioPlayer._PLAYING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            # service any queued stop signals
            if self.quit_signal == True or (self.duration_limit > 0 and self.duration_count > self.duration_limit):
                self.mon.log(self, "      Service stop required signal or timeout")
                # self.quit_signal=False
                if self.track <> "":
                    self.stop_mplayer()
                    self.play_state = AudioPlayer._ENDING
                else:
                    self.play_state = AudioPlayer._CLOSED
                    self.end("normal", "stop required signal or timeout")

            # mplayer reports it is terminating so change to ending state
            if self.track <> "" and self.mplayer.end_play_signal:
                self.mon.log(self, "            <end play signal received")
                self.mon.log(self, "            <end detected at: " + str(self.mplayer.audio_position))
                self.play_state = AudioPlayer._ENDING
            self.tick_timer = self.canvas.after(50, self.play_state_machine)

        elif self.play_state == AudioPlayer._ENDING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            # if spawned process has closed can change to closed state
            # self.mon.log (self,"      State machine : is mplayer process running? -  "  + str(self.mplayer.is_running()))
            if self.mplayer.is_running() == False:
                self.mon.log(self, "            <mplayer process is dead")
                if self.quit_signal == True:
                    self.quit_signal = False
                    self.play_state = AudioPlayer._CLOSED
                    self.end("normal", "quit required or timeout")
                elif self.duration_limit > 0 and self.duration_count < self.duration_limit:
                    self.play_state = AudioPlayer._WAITING
                    self.tick_timer = self.canvas.after(50, self.play_state_machine)
                else:
                    self.play_state = AudioPlayer._CLOSED
                    self.end("normal", "mplayer dead")
            else:
                self.tick_timer = self.canvas.after(50, self.play_state_machine)

        elif self.play_state == AudioPlayer._WAITING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            if self.quit_signal == True or (self.duration_limit > 0 and self.duration_count > self.duration_limit):
                self.mon.log(self, "      Service stop required signal or timeout from wait")
                self.quit_signal = False
                self.play_state = AudioPlayer._CLOSED
                self.end("normal", "mplayer dead")
            else:
                self.tick_timer = self.canvas.after(50, self.play_state_machine)

    def stop_mplayer(self):
        # send signal to stop the track to the state machine
        self.mon.log(self, "         >stop mplayer received from state machine")
        if self.play_state == AudioPlayer._PLAYING:
            self.mplayer.stop()
            return True
        else:
            self.mon.log(self, "!<stop rejected")
            return False

    # *****************
    # ending the player
    # *****************

    def end(self, reason, message):
        # stop the plugin
        if self.track_params["plugin"] <> "":
            self.pim.stop_plugin()

        # abort the timer
        if self.tick_timer <> None:
            self.canvas.after_cancel(self.tick_timer)
            self.tick_timer = None

        if reason in ("error", "killed"):
            self.end_callback(reason, message)
            self = None

        else:
            # normal end so do show control and animation

            # Control concurrent shows at end
            reason, message = self.show_manager.show_control(self.track_params["show-control-end"])
            if reason == "error":
                self.mon.err(self, message)
                self.end_callback(reason, message)
                self = None
            else:
                # clear events list for this track
                if self.track_params["animate-clear"] == "yes":
                    self.ppio.clear_events_list(id(self))

                # create animation events for ending
                reason, message = self.ppio.animate(self.animate_end_text, id(self))
                if reason == "error":
                    self.mon.err(self, message)
                    self.end_callback(reason, message)
                    self = None
                else:
                    self.end_callback("normal", "track has terminated or quit")
                    self = None

    # *****************
    # displaying things
    # *****************

    def display_content(self):

        # if self.background_file<>'' or self.show_params['show-text']<> '' or #self.track_params['track-text']<> '' or self.enable_menu== True or #self.track_params['clear-screen']=='yes':

        if self.track_params["clear-screen"] == "yes":
            self.canvas.delete("pp-content")
            # self.canvas.update()

        # background colour
        if self.background_colour <> "":
            self.canvas.config(bg=self.background_colour)

        if self.background_file <> "":
            self.background_img_file = self.complete_path(self.background_file)
            if not os.path.exists(self.background_img_file):
                self.mon.err(self, "Audio background file not found: " + self.background_img_file)
                self.end("error", "Audio background file not found")
            else:
                pil_background_img = PIL.Image.open(self.background_img_file)
                self.background = PIL.ImageTk.PhotoImage(pil_background_img)
                self.drawn = self.canvas.create_image(
                    int(self.canvas["width"]) / 2,
                    int(self.canvas["height"]) / 2,
                    image=self.background,
                    anchor=CENTER,
                    tag="pp-content",
                )

        # execute the plugin if required
        if self.track_params["plugin"] <> "":

            reason, message, self.track = self.pim.do_plugin(self.track, self.track_params["plugin"])
            if reason <> "normal":
                return reason, message

        # display hint text if enabled

        if self.enable_menu == True:
            self.canvas.create_text(
                int(self.show_params["hint-x"]),
                int(self.show_params["hint-y"]),
                text=self.show_params["hint-text"],
                fill=self.show_params["hint-colour"],
                font=self.show_params["hint-font"],
                anchor=NW,
                tag="pp-content",
            )

        # display show text if enabled
        if self.show_params["show-text"] <> "" and self.track_params["display-show-text"] == "yes":
            self.canvas.create_text(
                int(self.show_params["show-text-x"]),
                int(self.show_params["show-text-y"]),
                anchor=NW,
                text=self.show_params["show-text"],
                fill=self.show_params["show-text-colour"],
                font=self.show_params["show-text-font"],
                tag="pp-content",
            )

        # display track text if enabled
        if self.track_params["track-text"] <> "":
            self.canvas.create_text(
                int(self.track_params["track-text-x"]),
                int(self.track_params["track-text-y"]),
                anchor=NW,
                text=self.track_params["track-text"],
                fill=self.track_params["track-text-colour"],
                font=self.track_params["track-text-font"],
                tag="pp-content",
            )

        self.mon.log(self, "Displayed background and text ")

        self.canvas.tag_raise("pp-click-area")

        self.canvas.update_idletasks()

        return "normal", ""

    # *****************
    # utilities
    # *****************

    def complete_path(self, track_file):
        #  complete path of the filename of the selected entry
        if track_file[0] == "+":
            track_file = self.pp_home + track_file[1:]
        self.mon.log(self, "Background image is " + track_file)
        return track_file
コード例 #6
0
class AudioPlayer:

    _CLOSED = "mplayer_closed"    #probably will not exist
    _STARTING = "mplayer_starting"  #track is being prepared
    _PLAYING = "mplayer_playing"  #track is playing to the screen, may be paused
    _ENDING = "mplayer_ending"  #track is in the process of ending due to quit or end of track
    _WAITING = "wait for timeout" # track has finished but timeout still running

    #_LEFT = "-af channels=2:1:0:0:1:1,resample=48000:1 "
   # _RIGHT = "-af channels=2:1:0:1:1:0,resample=48000:1 "
    #_STEREO = "-af channels=2,resample=48000:1 "

    _LEFT = "channels=2:1:0:0:1:1"
    _RIGHT = "channels=2:1:0:1:1:0"
    _STEREO = "channels=2"

# ***************************************
# EXTERNAL COMMANDS
# ***************************************

    def __init__(self,
                        show_id,
                        canvas,
                         pp_home,
                        show_params,
                        track_params ):
        """       
            canvas - the canvas onto which the background image is to be drawn
            show_params - configuration of show playing the track
            track_params - config dictionary for this track overrides show_params
        """

        self.mon=Monitor()
        self.mon.on()

        
        #instantiate arguments
        self.show_id=show_id
        self.show_params=show_params       #configuration dictionary for the videoplayer
        self.canvas = canvas  #canvas onto which video should be played but isn't! Use as widget for alarm
        self.pp_home=pp_home
        self.track_params=track_params

        # get duration (secs ) from profile
        self.duration= int(self.track_params['duration'])
        self.duration_limit=20*self.duration


        # get background image from profile.
        self.background_file  = self.track_params['background-image']


        # get audio sink from profile.
        if  self.track_params['mplayer-audio']<>"":
            self.mplayer_audio= self.track_params['mplayer-audio']
        else:
            self.mplayer_audio= self.show_params['mplayer-audio']
            
        # get audio volume from profile.
        if  self.track_params['mplayer-volume']<>"":
            self.mplayer_volume= self.track_params['mplayer-volume'].strip()
        else:
            self.mplayer_volume= self.show_params['mplayer-volume'].strip()
        self.volume_option= 'volume=' + self.mplayer_volume


        #get speaker from profile
        if  self.track_params['audio-speaker']<>"":
            self.audio_speaker= self.track_params['audio-speaker']
        else:
            self.audio_speaker= self.show_params['audio-speaker']

        if self.audio_speaker=='left':
            self.speaker_option=AudioPlayer._LEFT
        elif self.audio_speaker=='right':
            self.speaker_option=AudioPlayer._RIGHT
        else:
            self.speaker_option=AudioPlayer._STEREO

        #get animation instructions from profile
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']


        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()

        # could put instance generation in play, not sure which is better.
        self.mplayer=mplayerDriver(self.canvas)
        self._tick_timer=None
        self.error=False
        self.terminate_me=False
        self._init_play_state_machine()



    def play(self, track,
                     end_callback,
                     ready_callback,
                     enable_menu=False, 
                     starting_callback=None,
                     playing_callback=None,
                     ending_callback=None):

        """
        play - plays the specified track, the first call after __init__
        track - full  path of track to play
        end_callback - callback when track ends (reason,message)
             reason = killed - return from a terminate with reason = killed
                           error - return because player or lower level has generated and runtime error
                           normal - anything else
            message = ant tesxt, used for debugging 
        ready_callback - callback when the track is ready to play, use to stop eggtimer etc.
        enable_menu - True if the track is to have a child show
        starting/playing/ending callback - called repeatedly in each state for show to display status, time etc.

        """
                         
        #instantiate arguments
        self.track=track
        self.ready_callback=ready_callback   #callback when ready to play
        self.enable_menu=enable_menu
        self.end_callback=end_callback         # callback when finished
        self.starting_callback=starting_callback  #callback during starting state
        self.playing_callback=playing_callback    #callback during playing state
        self.ending_callback=ending_callback      # callback during ending state
        # enable_menu is not used by AudioPlayer


        # select the sound sink
        if self.mplayer_audio<>"":
            if self.mplayer_audio=='hdmi':
                os.system("amixer -q -c 0 cset numid=3 2")
            else:
                os.system("amixer -q -c 0 cset numid=3 1")

        # callback to the calling object to e.g remove egg timer.
        if self.ready_callback<>None:
            self.ready_callback()

 
        # display image and text
        self.display_image()

        # create animation events
        error_text=self.ppio.animate(self.animate_begin_text,id(self))
        if error_text<>'':
            self.mon.err(self,error_text)
            self.error=True
            self._end('error',error_text)
 
        # and start playing the track.
        if self.play_state == AudioPlayer._CLOSED:
            self.mon.log(self,">play track received")
            self._start_play_state_machine()
            return True
        else:
            self.mon.log(self,"!< play track rejected")
            return False


    def key_pressed(self,key_name):
        """
        respond to user or system key  presses
        """
        if key_name=='':
            return
        elif key_name in ('p',' '):
            self._pause()
            return
        elif key_name=='escape':
            self._stop()
            return



    def button_pressed(self,button,edge):
        """
        respond to user button  presses
        """
        if button =='pause':
            self._pause()
            return
        elif button=='stop':
            self._stop()
            return


    def terminate(self,reason):
        """
        terminate the  player in special circumstances
        normal user termination if by key_pressed 'escape'
        reason will be killed or error
        """
        # circumvents state machine to terminate lower level and then itself.
        self.terminate_me=True
        if self.mplayer<>None:
            self.mon.log(self,"sent terminate to mplayerdriver")
            self.mplayer.terminate(reason)
            self._end('killed',' end without waiting') # end without waiting
        else:
            self.mon.log(self,"terminate, mplayerdriver not running")
            self._end('killed','terminate, mplayerdriver not running')
            
                

        
# ***************************************
# INTERNAL FUNCTIONS
# ***************************************

    #toggle pause
    def _pause(self):
        if self.play_state in (AudioPlayer._PLAYING,AudioPlayer._ENDING) and self.track<>'':
            self.mplayer.pause()
            return True
        else:
            self.mon.log(self,"!<pause rejected")
            return False
        
    # other control when playing, not currently used
    def _control(self,char):
        if self.play_state==AudioPlayer._PLAYING and self.track<>'':
            self.mon.log(self,"> send control to mplayer: "+ char)
            self.mplayer.control(char)
            return True
        else:
            self.mon.log(self,"!<control rejected")
            return False

    # respond to normal stop
    def _stop(self):
        # send signal to stop the track to the state machine
        self.mon.log(self,">stop received")
        self._stop_required_signal=True

    #respond to internal error by setting flags to cause state machine to stop player
    #use this rather than end if the driver and its spawned process might still be running
    def _error(self):
        self.error=True
        self._stop_required_signal=True


    # tidy up and end AudioPlayer.
    def _end(self,reason,message):
            # self.canvas.delete(ALL)
            # abort the timer
            if self._tick_timer<>None:
                self.canvas.after_cancel(self._tick_timer)
                self._tick_timer=None
            
            if self.error==True or reason=='error':
                self.end_callback("error",message)
                self=None
                
            elif self.terminate_me==True:
                self.end_callback("killed",message)
                self=None
            else:
               # clear events list for this track
                if self.track_params['animate-clear']=='yes':
                    self.ppio.clear_events_list(id(self))
                # create animation events for ending
                error_text=self.ppio.animate(self.animate_end_text,id(self))
                if error_text=='':
                    self.end_callback('normal',"track has terminated or quit")
                    self=None
                else:
                    self.mon.err(self,error_text)
                    self.end_callback("error",error_text)
                    self=None
                
      
# ***************************************
# # PLAYING STATE MACHINE
# ***************************************

    """self. play_state controls the playing sequence, it has the following values.
         I am not entirely sure the starting and ending states are required.
         - _closed - the mplayer process is not running, mplayer process can be initiated
         - _starting - mplayer process is running but is not yet able to receive controls
         - _playing - playing a track, controls can be sent
         - _ending - mplayer is doing its termination, controls cannot be sent
    """

    def _init_play_state_machine(self):
        self._stop_required_signal=False
        self.play_state=AudioPlayer._CLOSED
 
    def _start_play_state_machine(self):
        #initialise all the state machine variables
        self.duration_count = 0
        self._stop_required_signal=False     # signal that user has pressed stop

        #play the track

        options = self.show_params['mplayer-other-options'] + '-af '+ self.speaker_option+','+self.volume_option + ' '
        if self.track<>'':
            self.mplayer.play(self.track,options)
            self.mon.log (self,'Playing track from show Id: '+ str(self.show_id))
            self.play_state=AudioPlayer._STARTING
        else:
            self.play_state=AudioPlayer._PLAYING
        # and start polling for state changes and count duration
        self._tick_timer=self.canvas.after(50, self._play_state_machine)
 

    def _play_state_machine(self):
        self.duration_count+=1
           
        if self.play_state == AudioPlayer._CLOSED:
            self.mon.log(self,"      State machine: " + self.play_state)
            return 
                
        elif self.play_state == AudioPlayer._STARTING:
            self.mon.log(self,"      State machine: " + self.play_state)
            
            # if mplayer is playing the track change to play state
            if self.mplayer.start_play_signal==True:
                self.mon.log(self,"            <start play signal received from mplayer")
                self.mplayer.start_play_signal=False
                self.play_state=AudioPlayer._PLAYING
                self.mon.log(self,"      State machine: mplayer_playing started")
            self._do_starting()
            self._tick_timer=self.canvas.after(50, self._play_state_machine)

        elif self.play_state == AudioPlayer._PLAYING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            # service any queued stop signals
            if self._stop_required_signal==True or (self.duration_limit<>0 and self.duration_count>self.duration_limit):
                self.mon.log(self,"      Service stop required signa or timeout")
                # self._stop_required_signal=False
                if self.track<>'':
                    self._stop_mplayer()
                    self.play_state = AudioPlayer._ENDING
                else:
                    self.play_state = AudioPlayer._CLOSED
                    self._end('normal','stop required signa or timeout')

            # mplayer reports it is terminating so change to ending state
            if self.track<>'' and self.mplayer.end_play_signal:                    
                self.mon.log(self,"            <end play signal received")
                self.mon.log(self,"            <end detected at: " + str(self.mplayer.audio_position))
                self.play_state = AudioPlayer._ENDING
            self._do_playing()
            self._tick_timer=self.canvas.after(50, self._play_state_machine)

        elif self.play_state == AudioPlayer._ENDING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            self._do_ending()
            # if spawned process has closed can change to closed state
            # self.mon.log (self,"      State machine : is mplayer process running? -  "  + str(self.mplayer.is_running()))
            if self.mplayer.is_running() ==False:
                self.mon.log(self,"            <mplayer process is dead")
                if self._stop_required_signal==True:
                    self._stop_required_signal=False
                    self.play_state = AudioPlayer._CLOSED
                    self._end('normal','mplayer dead')
                elif self.duration_limit<>0 and self.duration_count<self.duration_limit:
                    self.play_state= AudioPlayer._WAITING
                    self._tick_timer=self.canvas.after(50, self._play_state_machine)
                else:
                    self.play_state = AudioPlayer._CLOSED
                    self._end('normal','mplayer dead')
            else:
                self._tick_timer=self.canvas.after(50, self._play_state_machine)
                
        elif self.play_state == AudioPlayer._WAITING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            if self._stop_required_signal==True or (self.duration_limit<>0 and self.duration_count>self.duration_limit):
                self.mon.log(self,"      Service stop required signal or timeout from wait")
                self._stop_required_signal=False
                self.play_state = AudioPlayer._CLOSED
                self._end('normal','mplayer dead')
            else:
                self._tick_timer=self.canvas.after(50, self._play_state_machine)
                    



    # allow calling object do things in each state by calling the appropriate callback
 
    def _do_playing(self):
        if self.track<>'':
            self.audio_position=self.mplayer.audio_position
        if self.playing_callback<>None:
                self.playing_callback() 

    def _do_starting(self):
        self.audio_position=0.0
        if self.starting_callback<>None:
                self.starting_callback() 

    def _do_ending(self):
        if self.ending_callback<>None:
                self.ending_callback() 

    def _stop_mplayer(self):
        # send signal to stop the track to the state machine
        self.mon.log(self,"         >stop mplayer received from state machine")
        if self.play_state==AudioPlayer._PLAYING:
            self.mplayer.stop()
            return True
        else:
            self.mon.log(self,"!<stop rejected")
            return False

# *****************
# image and text
# *****************
            
    def display_image(self):

        if self.background_file<>'' or self.show_params['show-text']<> '' or self.track_params['track-text']<> '' or self.enable_menu== True or self.track_params['clear-screen']=='yes':
            self.canvas.config(bg='black')
            self.canvas.delete(ALL)
        if self.background_file<>'':
            self.background_img_file = self.complete_path(self.background_file)
            if not os.path.exists(self.background_img_file):
                self.mon.err(self,"Audio background file not found: "+ self.background_img_file)
                self._end('error',"Audio background file not found")
            else:
                pil_background_img=PIL.Image.open(self.background_img_file)
                self.background = PIL.ImageTk.PhotoImage(pil_background_img)
                self.drawn = self.canvas.create_image(int(self.canvas['width'])/2,
                                              int(self.canvas['height'])/2,
                                              image=self.background,
                                              anchor=CENTER)
                
        # display hint text if enabled
       
        if self.enable_menu== True:
            self.canvas.create_text(int(self.canvas['width'])/2, int(self.canvas['height']) - int(self.show_params['hint-y']),
                                                  text=self.show_params['hint-text'],
                                                  fill=self.show_params['hint-colour'],
                                                font=self.show_params['hint-font'])

            
        # display show text if enabled
        if self.show_params['show-text']<> '':
            self.canvas.create_text(int(self.show_params['show-text-x']),int(self.show_params['show-text-y']),
                                                    anchor=NW,
                                                  text=self.show_params['show-text'],
                                                  fill=self.show_params['show-text-colour'],
                                                  font=self.show_params['show-text-font'])
            
        # display track text if enabled
        if self.track_params['track-text']<> '':
            self.canvas.create_text(int(self.track_params['track-text-x']),int(self.track_params['track-text-y']),
                                                    anchor=NW,
                                                  text=self.track_params['track-text'],
                                                  fill=self.track_params['track-text-colour'],
                                                  font=self.track_params['track-text-font'])

        self.mon.log(self,"Displayed background and text ")
        
        self.canvas.update_idletasks( )

    def complete_path(self,track_file):
        #  complete path of the filename of the selected entry
        if track_file[0]=="+":
                track_file=self.pp_home+track_file[1:]
        self.mon.log(self,"Background image is "+ track_file)
        return track_file     
コード例 #7
0
class VideoPlayer:

    _CLOSED = "omx_closed"    #probably will not exist
    _STARTING = "omx_starting"  #track is being prepared
    _PLAYING = "omx_playing"  #track is playing to the screen, may be paused
    _ENDING = "omx_ending"  #track is in the process of ending due to quit or end of track


# ***************************************
# EXTERNAL COMMANDS
# ***************************************

    def __init__(self,
                         show_id,
                        canvas,
                        pp_home,
                        show_params,
                        track_params ):
        """       
            canvas - the canvas onto which the video is to be drawn (not!!)
            cd - configuration dictionary
            track_params - config dictionary for this track overides cd
        """
        self.mon=Monitor()
        self.mon.on()
        
        #instantiate arguments
        self.show_id=show_id
        self.show_params=show_params       #configuration dictionary for the videoplayer
        self.pp_home=pp_home
        self.canvas = canvas  #canvas onto which video should be played but isn't! Use as widget for alarm
        self.track_params=track_params
        
        # get config from medialist if there.
        if self.track_params['omx-audio']<>"":
            self.omx_audio= self.track_params['omx-audio']
        else:
            self.omx_audio= self.show_params['omx-audio']
        if self.omx_audio<>"": self.omx_audio= "-o "+ self.omx_audio
        
        if self.track_params['omx-volume']<>"":
            self.omx_volume= self.track_params['omx-volume']
        else:
            self.omx_volume= self.show_params['omx-volume']
        if self.omx_volume<>"": self.omx_volume= "--vol "+ str(int(self.omx_volume)*100) + ' '
        self.omx_volume=' '
        #get animation instructions from profile
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']

        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()        
        
        # could put instance generation in play, not sure which is better.
        self.omx=OMXDriver(self.canvas)
        self._tick_timer=None
        self.error=False
        self.terminate_required=False
        self._init_play_state_machine()



    def play(self, track,
                     end_callback,
                     ready_callback,
                     enable_menu=False, 
                     starting_callback=None,
                     playing_callback=None,
                     ending_callback=None):
                         
        #instantiate arguments
        self.ready_callback=ready_callback   #callback when ready to play
        self.end_callback=end_callback         # callback when finished
        self.starting_callback=starting_callback  #callback during starting state
        self.playing_callback=playing_callback    #callback during playing state
        self.ending_callback=ending_callback      # callback during ending state
        # enable_menu is not used by videoplayer
 

        # create animation events
        error_text=self.ppio.animate(self.animate_begin_text,id(self))
        if error_text<>'':
            self.mon.err(self,error_text)
            self.error=True
            self._end()
            
        # and start playing the video.
        if self.play_state == VideoPlayer._CLOSED:
            self.mon.log(self,">play track received")
            self._start_play_state_machine(track)
            return True
        else:
            self.mon.log(self,"!< play track rejected")
            return False


    def key_pressed(self,key_name):
        if key_name=='':
            return
        elif key_name in ('p',' '):
            self._pause()
            return
        elif key_name=='escape':
            self._stop()
            return



    def button_pressed(self,button,edge):
        if button =='pause':
            self._pause()
            return
        elif button=='stop':
            self._stop()
            return


    def terminate(self,reason):
        # circumvents state machine
        self.terminate_required=True
        if self.omx<>None:
            self.mon.log(self,"sent terminate to omxdriver")
            self.omx.terminate(reason)
        else:
            self.mon.log(self,"terminate, omxdriver not running")
            self._end()
            
                

        
# ***************************************
# INTERNAL FUNCTIONS
# ***************************************

    # respond to normal stop
    def _stop(self):
        # send signal to stop the track to the state machine
        self.mon.log(self,">stop received")
        self._stop_required_signal=True

    #respond to internal error
    def _error(self):
        self.error=True
        self._stop_required_signal=True

    #toggle pause
    def _pause(self):
        if self.play_state in (VideoPlayer._PLAYING,VideoPlayer._ENDING):
            self.omx.pause()
            return True
        else:
            self.mon.log(self,"!<pause rejected")
            return False
        
    # other control when playing, used?
    def _control(self,char):
        if self.play_state==VideoPlayer._PLAYING:
            self.mon.log(self,"> send control ot omx: "+ char)
            self.omx.control(char)
            return True
        else:
            self.mon.log(self,"!<control rejected")
            return False

    # called to end omxdriver
    def _end(self):
            os.system("xrefresh -display :0")
            if self._tick_timer<>None:
                self.canvas.after_cancel(self._tick_timer)
                self._tick_timer=None
            if self.error==True:
                self.end_callback("error",'error')
                self=None 
            elif self.terminate_required==True:
                self.end_callback("killed",'killed')
                self=None
            else:
               # clear events list for this track
                if self.track_params['animate-clear']=='yes':
                    self.ppio.clear_events_list(id(self))
                # create animation events for ending
                error_text=self.ppio.animate(self.animate_end_text,id(self))
                if error_text=='':
                    self.end_callback('normal',"track has terminated or quit")
                    self=None
                else:
                    self.mon.err(self,error_text)
                    self.end_callback("error",'error')
                    self=None

# ***************************************
# # PLAYING STATE MACHINE
# ***************************************

    """self. play_state controls the playing sequence, it has the following values.
         I am not entirely sure the starting and ending states are required.
         - _closed - the omx process is not running, omx process can be initiated
         - _starting - omx process is running but is not yet able to receive controls
         - _playing - playing a track, controls can be sent
         - _ending - omx is doing its termination, controls cannot be sent
    """

    def _init_play_state_machine(self):
        self._stop_required_signal=False
        self.play_state=VideoPlayer._CLOSED
 
    def _start_play_state_machine(self,track):
        #initialise all the state machine variables
        #self.iteration = 0                             # for debugging
        self._stop_required_signal=False     # signal that user has pressed stop
        self.play_state=VideoPlayer._STARTING
        
        #play the selected track
        options=self.omx_audio+ " " + self.omx_volume + ' ' + self.show_params['omx-other-options']+" "
        self.omx.play(track,options)
        self.mon.log (self,'Playing track from show Id: '+ str(self.show_id))
        # and start polling for state changes
        self._tick_timer=self.canvas.after(50, self._play_state_machine)
 

    def _play_state_machine(self):      
        if self.play_state == VideoPlayer._CLOSED:
            self.mon.log(self,"      State machine: " + self.play_state)
            return 
                
        elif self.play_state == VideoPlayer._STARTING:
            self.mon.log(self,"      State machine: " + self.play_state)
            
            # if omxplayer is playing the track change to play state
            if self.omx.start_play_signal==True:
                self.mon.log(self,"            <start play signal received from omx")
                self.omx.start_play_signal=False
                # callback to the calling object to e.g remove egg timer.
                if self.ready_callback<>None:
                    self.ready_callback()
                self.canvas.config(bg='black')
                self.canvas.delete(ALL)
                self.display_image()
                self.play_state=VideoPlayer._PLAYING
                self.mon.log(self,"      State machine: omx_playing started")
            self._do_starting()
            self._tick_timer=self.canvas.after(50, self._play_state_machine)

        elif self.play_state == VideoPlayer._PLAYING:
            # self.mon.log(self,"      State machine: " + self.play_state)
            # service any queued stop signals
            if self._stop_required_signal==True:
                self.mon.log(self,"      Service stop required signal")
                self._stop_omx()
                self._stop_required_signal=False
                self.play_state = VideoPlayer._ENDING
                
            # omxplayer reports it is terminating so change to ending state
            if self.omx.end_play_signal:                    
                self.mon.log(self,"            <end play signal received")
                self.mon.log(self,"            <end detected at: " + str(self.omx.video_position))
                self.play_state = VideoPlayer._ENDING
                
            self._do_playing()
            self._tick_timer=self.canvas.after(200, self._play_state_machine)

        elif self.play_state == VideoPlayer._ENDING:
            self.mon.log(self,"      State machine: " + self.play_state)
            self._do_ending()
            # if spawned process has closed can change to closed state
            # self.mon.log (self,"      State machine : is omx process running? -  "  + str(self.omx.is_running()))
            if self.omx.is_running() ==False:
                self.mon.log(self,"            <omx process is dead")
                self.play_state = VideoPlayer._CLOSED
                self._end()
            else:
                self._tick_timer=self.canvas.after(200, self._play_state_machine)


    # allow calling object do things in each state by calling the appropriate callback
 
    def _do_playing(self):
        self.video_position=self.omx.video_position
        self.audio_position=self.omx.audio_position
        if self.playing_callback<>None:
                self.playing_callback() 

    def _do_starting(self):
        self.video_position=0.0
        self.audio_position=0.0
        if self.starting_callback<>None:
                self.starting_callback() 

    def _do_ending(self):
        if self.ending_callback<>None:
                self.ending_callback() 

    def _stop_omx(self):
        # send signal to stop the track to the state machine
        self.mon.log(self,"         >stop omx received from state machine")
        if self.play_state==VideoPlayer._PLAYING:
            self.omx.stop()
            return True
        else:
            self.mon.log(self,"!<stop rejected")
            return False
# *****************
# image and text
# *****************
            
    def display_image(self):

        # display track text if enabled
        if self.track_params['track-text']<> '':
            self.canvas.create_text(int(self.track_params['track-text-x']),int(self.track_params['track-text-y']),
                                                    anchor=NW,
                                                  text=self.track_params['track-text'],
                                                  fill=self.track_params['track-text-colour'],
                                                  font=self.track_params['track-text-font'])

        self.mon.log(self,"Displayed  text ")
        
        self.canvas.update_idletasks( )
コード例 #8
0
class MessagePlayer:
    """ Displays lines of text in the centre of a coloured screen with background image
        See pp_imageplayer for common software design description
    """
    

# *******************
# external commands
# *******************

    def __init__(self,show_id,root,canvas,show_params,track_params,pp_dir,pp_home,pp_profile):

        self.mon=Monitor()
        self.mon.on()

        self.root=root
        self.canvas=canvas
        self.show_id=show_id
        self.track_params=track_params
        self.show_params=show_params
        self.pp_dir=pp_dir
        self.pp_home=pp_home
        self.pp_profile=pp_profile

       # get config from medialist if there.
       
        if 'duration' in self.track_params and self.track_params['duration']<>"":
            self.duration= int(self.track_params['duration'])
        else:
            self.duration= int(self.show_params['duration'])

        # get background image from profile.
        self.background_file=''
        if self.track_params['background-image']<>"":
            self.background_file= self.track_params['background-image']
        else:
            if self.track_params['display-show-background']=='yes':
                self.background_file= self.show_params['background-image']
            
        # get background colour from profile.
        if self.track_params['background-colour']<>"":
            self.background_colour= self.track_params['background-colour']
        else:
            self.background_colour= self.show_params['background-colour']
            
        self.centre_x = int(self.canvas['width'])/2
        self.centre_y = int(self.canvas['height'])/2
        
        # keep tick as an integer sub-multiple of 1 second         
        self.tick = 100 # tick time for image display (milliseconds)
        self.dwell = 1000*self.duration

        #get animation instructions from profile
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']

        # open the plugin Manager
        self.pim=PluginManager(self.show_id,self.root,self.canvas,self.show_params,self.track_params,self.pp_dir,self.pp_home,self.pp_profile) 

        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO() 


    def play(self,
                    text,
                    showlist,
                    end_callback,
                    ready_callback,
                    enable_menu=False):
                        
        # instantiate arguments
        self.text=text
        self.showlist=showlist
        self.end_callback=end_callback
        self.ready_callback=ready_callback
        self.enable_menu=enable_menu
        
        #init state and signals
        self.quit_signal=False
        self.tick_timer=None
        self.drawn=None


        # create an  instance of showmanager so we can control concurrent shows
        self.show_manager=ShowManager(self.show_id,self.showlist,self.show_params,self.root,self.canvas,self.pp_dir,self.pp_profile,self.pp_home)

     # Control other shows at beginning
        reason,message=self.show_manager.show_control(self.track_params['show-control-begin'])
        if reason == 'error':
            self.end_callback(reason,message)
            self=None
        else:
            #display content
            reason,message=self.display_content()
            if reason == 'error':
                self.mon.err(self,message)
                self.end_callback(reason,message)
                self=None
            else:
                # create animation events
                reason,message=self.ppio.animate(self.animate_begin_text,id(self))
                if reason=='error':
                    self.mon.err(self,message)
                    self.end_callback(reason,message)
                    self=None
                else:
                    # start text display
                    self.start_dwell()

    def terminate(self,reason):
        # no lower level things to terminate so just go to end
        self.end(reason,'kill or error')

    def get_links(self):
        return self.track_params['links']

    def input_pressed(self,symbol):
        self.mon.log(self,"input received: "+symbol)
        if symbol=='stop':
            self.stop()



# *******************
# internal functions
# *******************

    def stop(self):
        self.quit_signal=True

        

            
# *******************
# sequencing
# *******************

    def start_dwell(self):
        self.dwell_counter=0
        if self.ready_callback<>None:
            self.ready_callback()
   
        self.tick_timer=self.canvas.after(self.tick, self.do_dwell)

        
    def do_dwell(self):
        if self.quit_signal == True:
            self.mon.log(self,"quit received")
            self.end('normal','user quit')
        else:
            if self.dwell<>0:
                self.dwell_counter=self.dwell_counter+1
                if self.dwell_counter==self.dwell/self.tick:
                    self.end('normal','finished')
                else:
                    self.tick_timer=self.canvas.after(self.tick, self.do_dwell)
            else:
                    self.tick_timer=self.canvas.after(self.tick, self.do_dwell)


# *****************
# ending the player
# *****************

    def end(self,reason,message):
        # stop the plugin
        if self.track_params['plugin']<>'':
            self.pim.stop_plugin()

        # abort the timer
        if self.tick_timer<>None:
            self.canvas.after_cancel(self.tick_timer)
            self.tick_timer=None
        
        if reason in ('error','killed'):
            self.end_callback(reason,message)
            self=None

        else:
            # normal end so do show control 
            # Control concurrent shows at end
            reason,message=self.show_manager.show_control(self.track_params['show-control-end'])
            if reason =='error':
                self.mon.err(self,message)
                self.end_callback(reason,message)
                self=None
            else:
                # clear events list for this track
                if self.track_params['animate-clear']=='yes':
                    self.ppio.clear_events_list(id(self))
                
                # create animation events for ending
                reason,message=self.ppio.animate(self.animate_end_text,id(self))
                if reason=='error':
                    self.mon.err(self,message)
                    self.end_callback(reason,message)
                    self=None
                else:
                    self.end_callback('normal',"track has terminated or quit")
                    self=None



# *****************
# displaying things
# *****************

    def display_content(self):

        if  self.background_colour<>'':   
            self.canvas.config(bg=self.background_colour)
        
        self.canvas.delete('pp-content')
      
        if self.background_file<>'':
            self.background_img_file = self.complete_path(self.background_file)
            if not os.path.exists(self.background_img_file):
                self.mon.err(self,"Message background file not found: "+ self.background_img_file)
                self.end('error',"Message background file not found")
            else:
                pil_background_img=PIL.Image.open(self.background_img_file)
                self.background = PIL.ImageTk.PhotoImage(pil_background_img)
                self.drawn = self.canvas.create_image(int(self.canvas['width'])/2,
                                              int(self.canvas['height'])/2,
                                              image=self.background,
                                              anchor=CENTER,
                                              tag='pp-content')

         # display show text if enabled
        if self.show_params['show-text']<> ''and self.track_params['display-show-text']=='yes':
            self.canvas.create_text(int(self.show_params['show-text-x']),int(self.show_params['show-text-y']),
                                                    anchor=NW,
                                                  text=self.show_params['show-text'],
                                                  fill=self.show_params['show-text-colour'],
                                                  font=self.show_params['show-text-font'],
                                                tag='pp-content')

        # display track text if enabled
        if self.track_params['track-text']<> '':
            self.canvas.create_text(int(self.track_params['track-text-x']),int(self.track_params['track-text-y']),
                                                    anchor=NW,
                                                  text=self.track_params['track-text'],
                                                  fill=self.track_params['track-text-colour'],
                                                  font=self.track_params['track-text-font'],
                                                tag='pp-content')

        # execute the plugin if required
        if self.track_params['plugin']<>'':
            reason,message,self.text = self.pim.do_plugin(self.text,self.track_params['plugin'])
            if reason <> 'normal':
                return reason,message

 
        # display message text
        if self.track_params['message-x']<>'':
             self.canvas.create_text(int(self.track_params['message-x']), int(self.track_params['message-y']),
                                                    text=self.text.rstrip('\n'),
                                                    fill=self.track_params['message-colour'],
                                                    font=self.track_params['message-font'],
                                                    justify=self.track_params['message-justify'],
                                                    anchor = 'nw',
                                                    tag='pp-content')
        else:
            self.canvas.create_text(int(self.canvas['width'])/2, int(self.canvas['height'])/2,
                                                    text=self.text.rstrip('\n'),
                                                    fill=self.track_params['message-colour'],
                                                    font=self.track_params['message-font'],
                                                    justify=self.track_params['message-justify'],
                                                    tag='pp-content')     


        # display instructions (hint)
        if self.enable_menu==True:
            self.canvas.create_text(int(self.show_params['hint-x']),
                                            int(self.show_params['hint-y']),
                                            text=self.show_params['hint-text'],
                                            fill=self.show_params['hint-colour'],
                                            font=self.show_params['hint-font'],
                                            anchor=NW,
                                           tag='pp-content')
            
        self.canvas.tag_raise('pp-click-area')
        self.canvas.update_idletasks( )
        return 'normal',''

# *****************
# utilities
# *****************


    def complete_path(self,track_file):
        #  complete path of the filename of the selected entry
        if track_file[0]=="+":
                track_file=self.pp_home+track_file[1:]
        self.mon.log(self,"Background image is "+ track_file)
        return track_file     
コード例 #9
0
class ImagePlayer:
    """ Displays an image on a canvas for a period of time. Image display can be paused and interrupted
        __init_ just makes sure that all the things the player needs are available
        play starts playing the track and returns immeadiately
        play must end up with a call to tkinter's after, the after callback will interrogae the playing state at intervals and
        eventually return through end_callback
        input-pressed receives user input while the track is playing. it might pass the input on to the driver
        Input-pressed must not wait, it must set a signal and return immeadiately.
        The signal is interrogated by the after callback.
    """

    # slide state constants
    NO_SLIDE = 0
    SLIDE_DWELL= 1

# *******************
# external commands
# *******************

    def __init__(self,show_id,root,canvas,show_params,track_params,pp_dir,pp_home,pp_profile):
        """
                show_id - show instance that player is run from (for monitoring only)
                canvas - the canvas onto which the image is to be drawn
                show_params -  dictionary of show parameters
                track_params - disctionary of track paramters
                pp_home - data home directory
                pp_profile - profile name
        """

        self.mon=Monitor()
        self.mon.off()

        self.show_id=show_id
        self.root=root
        self.canvas=canvas
        self.show_params=show_params
        self.track_params=track_params
        self.pp_dir=pp_dir
        self.pp_home=pp_home
        self.pp_profile=pp_profile


        # open resources
        self.rr=ResourceReader()

        # get parameters 
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']
        
        if self.track_params['duration']<>"":
            self.duration= int(self.track_params['duration'])
        else:
            self.duration= int(self.show_params['duration'])
        
        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()

        # get background image from profile.
        self.background_file=''
        if self.track_params['background-image']<>'':
            self.background_file= self.track_params['background-image']
        else:
            if self.track_params['display-show-background']=='yes':
                self.background_file= self.show_params['background-image']
            
        # get background colour from profile.
        if self.track_params['background-colour']<>"":
            self.background_colour= self.track_params['background-colour']
        else:
            self.background_colour= self.show_params['background-colour']


        # get  image window from profile
        if self.track_params['image-window'].strip()<>"":
            self.image_window= self.track_params['image-window'].strip()
        else:
            self.image_window= self.show_params['image-window'].strip()

        # open the plugin Manager
        self.pim=PluginManager(self.show_id,self.root,self.canvas,self.show_params,self.track_params,self.pp_dir,self.pp_home,self.pp_profile) 

    def play(self,
                    track,
                    showlist,
                    end_callback,
                    ready_callback,
                    enable_menu=False):

        """
                track - filename of track to be played
                showlist - from which track was taken
                end_callback - callback when player terminates
                ready_callback - callback just before anytthing is displayed
                enable_menu  - there will be a child track so display the hint text
        """

        # instantiate arguments
        self.track=track
        self.showlist=showlist
        self.enable_menu=enable_menu
        self.ready_callback=ready_callback
        self.end_callback=end_callback

        #init state and signals  
        self.canvas_centre_x = int(self.canvas['width'])/2
        self.canvas_centre_y = int(self.canvas['height'])/2
        self.tick = 100 # tick time for image display (milliseconds)
        self.dwell = 10*self.duration
        self.dwell_counter=0
        self.state=ImagePlayer.NO_SLIDE
        self.quit_signal=False
        self.drawn=None
        self.paused=False
        self.pause_text=None
        self.tick_timer=None
        

        #parse the image_window
        error,self.command,self.has_coords,self.image_x1,self.image_y1,self.image_x2,self.image_y2,self.filter=self.parse_window(self.image_window)
        if error =='error':
            self.mon.err(self,'image window error: '+self.image_window)
            self.end('error','image window error')
        else:


            # create an  instance of showmanager so we can control concurrent shows
            self.show_manager=ShowManager(self.show_id,self.showlist,self.show_params,self.root,self.canvas,self.pp_dir,self.pp_profile,self.pp_home)

             # Control other shows at beginning
            reason,message=self.show_manager.show_control(self.track_params['show-control-begin'])
            if reason == 'error':
                self.end_callback(reason,message)
                self=None
            else:
                #display content
                reason,message=self.display_content()
                if reason == 'error':
                    self.mon.err(self,message)
                    self.end_callback(reason,message)
                    self=None
                else:
                    # create animation events
                    reason,message=self.ppio.animate(self.animate_begin_text,id(self))
                    if reason=='error':
                        self.mon.err(self,message)
                        self.end_callback(reason,message)
                        self=None
                    else:                              
                        # start dwelling
                        self.mon.log(self,'playing track from show Id: '+str(self.show_id))
                        self.start_dwell()

        
    def input_pressed(self,symbol):
        if symbol =='pause':
            self.pause()
        elif symbol=='stop':
            self.stop()
            return

    def terminate(self,reason):
        # no lower level things to terminate so just go to end
        self.end(reason,'kill or error')

    def get_links(self):
        return self.track_params['links']
      
# *******************
# internal functions
# *******************

    def pause(self):
        if not self.paused:
            self.paused = True
        else:
            self.paused=False

        #print "self.paused is "+str(self.paused)

    def stop(self):
        self.quit_signal=True
        


        
# ******************************************
# Sequencing
# ********************************************

    def start_dwell(self):

        if self.ready_callback<>None:
            self.ready_callback()
        self.state=ImagePlayer.SLIDE_DWELL
        self.tick_timer=self.canvas.after(self.tick, self.do_dwell)

        
    def do_dwell(self):
        if self.quit_signal == True:
            self.mon.log(self,"quit received")
            self.end('normal','user quit')
        else:
            if self.paused == False:
                self.dwell_counter=self.dwell_counter+1

            #print "self.paused is "+str(self.paused)
            #if self.pause_text<>None:
                #print "self.pause_text exists"

            # one time flipping of pause text
            #NIK
            if self.paused==True and self.pause_text==None:
                self.pause_text=self.canvas.create_text(0,900, anchor=NW,
                                                      text=self.resource('imageplayer','m01'),
                                                      fill="white",
                                                      font="arial 25 bold")
                self.canvas.update_idletasks( )
            #NIK

            if self.paused==False and self.pause_text<>None:
                self.canvas.delete(self.pause_text)
                self.pause_text=None
                self.canvas.update_idletasks( )

            if self.dwell<>0 and self.dwell_counter==self.dwell:
                self.end('normal','user quit or duration exceeded')
            else:
                self.tick_timer=self.canvas.after(self.tick, self.do_dwell)



# *****************
# ending the player
# *****************

    def end(self,reason,message):
            self.state=self.NO_SLIDE

            # stop the plugin
            if self.track_params['plugin']<>'':
                self.pim.stop_plugin()
                             
            # abort the timer
            if self.tick_timer<>None:
                self.canvas.after_cancel(self.tick_timer)
                self.tick_timer=None
            
            if reason in ('error','killed'):
                self.end_callback(reason,message)
                self=None

            else:
                # normal end so do show control and animation

                # Control concurrent shows at end
                reason,message=self.show_manager.show_control(self.track_params['show-control-end'])
                if reason =='error':
                    self.mon.err(self,message)
                    self.end_callback(reason,message)
                    self=None
                else:
                   # clear events list for this track
                    if self.track_params['animate-clear']=='yes':
                        self.ppio.clear_events_list(id(self))

                    # create animation events for ending
                    reason,message=self.ppio.animate(self.animate_end_text,id(self))
                    if reason=='error':
                        self.mon.err(self,message)
                        self.end_callback(reason,message)
                        self=None
                    else:
                        self.end_callback('normal',"track has terminated or quit")
                        #NIK
                        if self.pause_text<>None:
                            self.canvas.delete(self.pause_text)
                            self.pause_text=None
                            self.canvas.update_idletasks( )
                        #NIK
                        self=None



# **********************************
# displaying things
# **********************************

    def display_content(self):

        #background colour
        if  self.background_colour<>'':   
           self.canvas.config(bg=self.background_colour)
           
        self.canvas.delete('pp-content')


        # background image
        if self.background_file<> '':
            self.background_img_file = self.complete_path(self.background_file)
            if not os.path.exists(self.background_img_file):
                self.mon.err(self,"Background file not found: "+ self.background_img_file)
                self.end('error',"Background file not found")
            else:
                pil_background_img=PIL.Image.open(self.background_img_file)
                self.background = PIL.ImageTk.PhotoImage(pil_background_img)
                self.drawn = self.canvas.create_image(int(self.canvas['width'])/2,
                                             int(self.canvas['height'])/2,
                                             image=self.background,
                                            anchor=CENTER,
                                            tag='pp-content')

        # execute the plugin if required
        if self.track_params['plugin']<>'':

            reason,message,self.track = self.pim.do_plugin(self.track,self.track_params['plugin'],)
            if reason <> 'normal':
                return reason,message

        #get the track to be displayed
        if os.path.exists(self.track)==True:
            self.pil_image=PIL.Image.open(self.track)
        else:
            self.pil_image=None
            
        # display track image                                    
        if self.pil_image<>None:
            self.image_width,self.image_height=self.pil_image.size

            if self.command=='original':
                # display image at its original size
                if self.has_coords==False:
                    # load and display the unmodified image in centre
                    self.tk_img=PIL.ImageTk.PhotoImage(self.pil_image)
                    self.drawn = self.canvas.create_image(self.canvas_centre_x, self.canvas_centre_y,
                                                  image=self.tk_img, anchor=CENTER,
                                                  tag='pp-content')
                else:
                    # load and display the unmodified image at x1,y1
                    self.tk_img=PIL.ImageTk.PhotoImage(self.pil_image)
                    self.drawn = self.canvas.create_image(self.image_x1, self.image_y1,
                                                      image=self.tk_img, anchor=NW,
                                                      tag='pp-content')


            elif self.command in ('fit','shrink'):
                    # shrink fit the window or screen preserving aspect
                    if self.has_coords==True:
                        window_width=self.image_x2 - self.image_x1
                        window_height=self.image_y2 - self.image_y1
                        window_centre_x=(self.image_x2+self.image_x1)/2
                        window_centre_y= (self.image_y2+self.image_y1)/2
                    else:
                        window_width=int(self.canvas['width'])
                        window_height=int(self.canvas['height'])
                        window_centre_x=self.canvas_centre_x
                        window_centre_y=self.canvas_centre_y
                    
                    if (self.image_width > window_width or self.image_height > window_height and self.command=='fit') or (self.command=='shrink') :
                        # original image is larger or , shrink it to fit the screen preserving aspect
                        self.pil_image.thumbnail((window_width,window_height),eval(self.filter))                 
                        self.tk_img=PIL.ImageTk.PhotoImage(self.pil_image)
                        self.drawn = self.canvas.create_image(window_centre_x, window_centre_y,
                                                      image=self.tk_img, anchor=CENTER,
                                                      tag='pp-content')
                    else:
                        # fitting and original image is smaller, expand it to fit the screen preserving aspect
                        prop_x = float(window_width) / self.image_width
                        prop_y = float(window_height) / self.image_height
                        if prop_x > prop_y:
                            prop=prop_y
                        else:
                            prop=prop_x
                            
                        increased_width=int(self.image_width * prop)
                        increased_height=int(self.image_height * prop)
                        # print 'result',prop, increased_width,increased_height
                        self.pil_image=self.pil_image.resize((increased_width, increased_height),eval(self.filter))
                        self.tk_img=PIL.ImageTk.PhotoImage(self.pil_image)
                        self.drawn = self.canvas.create_image(window_centre_x, window_centre_y,
                                                      image=self.tk_img, anchor=CENTER,
                                                      tag='pp-content')                                                 
                                                  
            elif self.command in ('warp'):
                    # resize to window or screen without preserving aspect
                    if self.has_coords==True:
                        window_width=self.image_x2 - self.image_x1
                        window_height=self.image_y2 - self.image_y1
                        window_centre_x=(self.image_x2+self.image_x1)/2
                        window_centre_y= (self.image_y2+self.image_y1)/2
                    else:
                        window_width=int(self.canvas['width'])
                        window_height=int(self.canvas['height'])
                        window_centre_x=self.canvas_centre_x
                        window_centre_y=self.canvas_centre_y
                    
                    self.pil_image=self.pil_image.resize((window_width, window_height),eval(self.filter))
                    self.tk_img=PIL.ImageTk.PhotoImage(self.pil_image)
                    self.drawn = self.canvas.create_image(window_centre_x, window_centre_y,
                                                  image=self.tk_img, anchor=CENTER,
                                                  tag='pp-content')                                                        
                                              
        # display hint if enabled
       
        if self.enable_menu== True:
            self.canvas.create_text(int(self.show_params['hint-x']),
                                                    int(self.show_params['hint-y']),
                                                  text=self.show_params['hint-text'],
                                                  fill=self.show_params['hint-colour'],
                                                font=self.show_params['hint-font'],
                                                anchor=NW,
                                                tag='pp-content')

        # display show text if enabled
        if self.show_params['show-text']<> ''and self.track_params['display-show-text']=='yes':
            self.canvas.create_text(int(self.show_params['show-text-x']),int(self.show_params['show-text-y']),
                                                    anchor=NW,
                                                  text=self.show_params['show-text'],
                                                  fill=self.show_params['show-text-colour'],
                                                  font=self.show_params['show-text-font'],
                                                    tag='pp-content')
            
        # display track text if enabled
        if self.track_params['track-text']<> '':
            self.canvas.create_text(int(self.track_params['track-text-x']),int(self.track_params['track-text-y']),
                                                    anchor=NW,
                                                  text=self.track_params['track-text'],
                                                  fill=self.track_params['track-text-colour'],
                                                  font=self.track_params['track-text-font'],
                                                tag='pp-content')
            
        self.canvas.tag_raise('pp-click-area')            
        self.canvas.update_idletasks( )
        return 'normal',''

# **********************************
# utilties
# **********************************


    def parse_window(self,line):
        
            fields = line.split()
            # check there is a command field
            if len(fields) < 1:
                    return 'error','',False,0,0,0,0,''
                
            # deal with original whch has 0 or 2 arguments
            filter=''
            if fields[0]=='original':
                if len(fields) not in (1,3):
                        return 'error','',False,0,0,0,0,''       
                # deal with window coordinates    
                if len(fields) == 3:
                    #window is specified
                    if not (fields[1].isdigit() and fields[2].isdigit()):
                        return 'error','',False,0,0,0,0,''
                    has_window=True
                    return 'normal',fields[0],has_window,int(fields[1]),int(fields[2]),0,0,filter
                else:
                    # no window
                    has_window=False 
                    return 'normal',fields[0],has_window,0,0,0,0,filter



            #deal with remainder which has 1, 2, 5 or  6arguments
            # check basic syntax
            if  fields[0] not in ('shrink','fit','warp'):
                    return 'error','',False,0,0,0,0,'' 
            if len(fields) not in (1,2,5,6):
                    return 'error','',False,0,0,0,0,''
            if len(fields)==6 and fields[5] not in ('NEAREST','BILINEAR','BICUBIC','ANTIALIAS'):
                    return 'error','',False,0,0,0,0,''
            if len(fields)==2 and fields[1] not in ('NEAREST','BILINEAR','BICUBIC','ANTIALIAS'):
                    return 'error','',False,0,0,0,0,''
            
            # deal with window coordinates    
            if len(fields) in (5,6):
                #window is specified
                if not (fields[1].isdigit() and fields[2].isdigit() and fields[3].isdigit() and fields[4].isdigit()):
                    return 'error','',False,0,0,0,0,''
                has_window=True
                if len(fields)==6:
                    filter=fields[5]
                else:
                    filter='PIL.Image.NEAREST'
                    return 'normal',fields[0],has_window,int(fields[1]),int(fields[2]),int(fields[3]),int(fields[4]),filter
            else:
                # no window
                has_window=False
                if len(fields)==2:
                    filter=fields[1]
                else:
                    filter='PIL.Image.NEAREST'
                return 'normal',fields[0],has_window,0,0,0,0,filter
                


                    
    def complete_path(self,track_file):
        #  complete path of the filename of the selected entry
        if track_file[0]=="+":
                track_file=self.pp_home+track_file[1:]
        self.mon.log(self,"Background image is "+ track_file)
        return track_file     
 
  

# get a text string from resources.cfg
    def resource(self,section,item):
        value=self.rr.get(section,item)
        if value==False:
            self.mon.err(self, "resource: "+section +': '+ item + " not found" )
            self.error()
        else:
            return value
コード例 #10
0
class ImagePlayer:
    """ Displays an image on a canvas for a period of time. Image display can be interrupted
          Implements animation of transitions but Pi is too slow without GPU aceleration."""

    # slide state constants
    NO_SLIDE = 0
    SLIDE_IN = 1
    SLIDE_DWELL= 2
    SLIDE_OUT= 3

# *******************
# external commands
# *******************

    def __init__(self,show_id,canvas,pp_home,show_params,track_params):
        """
                canvas - the canvas onto which the image is to be drawn
                cd -  dictionary of show parameters
                track_params - disctionary of track paramters
        """

        self.mon=Monitor()
        self.mon.on()

        self.show_id=show_id
        self.canvas=canvas
        self.pp_home=pp_home
        self.show_params=show_params
        self.track_params=track_params

        # open resources
        self.rr=ResourceReader()

            
        # get config from medialist if there.
        
        self.animate_begin_text=self.track_params['animate-begin']
        self.animate_end_text=self.track_params['animate-end']
        
        if 'duration' in self.track_params and self.track_params['duration']<>"":
            self.duration= int(self.track_params['duration'])
        else:
            self.duration= int(self.show_params['duration'])
            
        if 'transition' in self.track_params and self.track_params['transition']<>"":
            self.transition= self.track_params['transition']
        else:
            self.transition= self.show_params['transition']
  
        # keep dwell and porch as an integer multiple of tick          
        self.porch = 1000 #length of pre and post porches for an image (milliseconds)
        self.tick = 100 # tick time for image display (milliseconds)
        self.dwell = (1000*self.duration)- (2*self.porch)
        if self.dwell<0: self.dwell=0

        self.centre_x = int(self.canvas['width'])/2
        self.centre_y = int(self.canvas['height'])/2

        #create an instance of PPIO so we can create gpio events
        self.ppio = PPIO()

    def play(self,
                    track,
                    end_callback,
                    ready_callback,
                    enable_menu=False,
                    starting_callback=None,
                    playing_callback=None,
                    ending_callback=None):
                        
        # instantiate arguments
        self.track=track
        self.enable_menu=enable_menu
        self.ready_callback=ready_callback
        self.end_callback=end_callback

        #init state and signals
        self.state=ImagePlayer.NO_SLIDE
        self.quit_signal=False
        self.kill_required_signal=False
        self.error=False
        self._tick_timer=None
        self.drawn=None
        self.paused=False
        self.pause_text=None

        if os.path.exists(self.track)==True:
            self.pil_image=PIL.Image.open(self.track)
            # adjust brightness and rotate (experimental)
            # pil_image_enhancer=PIL.ImageEnhance.Brightness(pil_image)
            # pil_image=pil_image_enhancer.enhance(0.1)
            # pil_image=pil_image.rotate(45)
            # tk_image = PIL.ImageTk.PhotoImage(pil_image)
        else:
            self.pil_image=None

        # and start image rendering
        self.mon.log(self,'playing track from show Id: '+str(self.show_id))
        self._start_front_porch()

        
    def key_pressed(self,key_name):
        if key_name=='':
            return
        elif key_name in ('p',' '):
            self.pause()
        elif key_name=='escape':
            self._stop()
            return

    def button_pressed(self,button,edge):
        if button =='pause':
            self.pause()
        elif button=='stop':
            self._stop()
            return

    def terminate(self,reason):
        if reason=='error':
            self.error=True
            self.quit_signal=True
        else:
            self.kill_required_signal=True
            self.quit_signal=True

    def pause(self):
        if not self.paused:
            self.paused = True
        else:
            self.paused=False

    
        
# *******************
# internal functions
# *******************

    def _stop(self):
        self.quit_signal=True
        
    def _error(self):
        self.error=True
        self.quit_signal=True
  
     #called when back porch has completed or quit signal is received
    def _end(self,reason,message):
        if self._tick_timer<>None:
            self.canvas.after_cancel(self._tick_timer)
            self._tick_timer=None
        self.quit_signal=False
        # self.canvas.delete(ALL)
        self.canvas.update_idletasks( )
        self.state=self.NO_SLIDE
        if self.error==True or reason=='error':
            self.end_callback("error",message)
            self=None          
        elif self.kill_required_signal==True:
            self.end_callback("killed",message)
            self=None           
        else:
           # clear events list for this track
            if self.track_params['animate-clear']=='yes':
                self.ppio.clear_events_list(id(self))
            # create animation events for ending
            error_text=self.ppio.animate(self.animate_end_text,id(self))
            if error_text=='':
                self.end_callback('normal',"track has terminated or quit")
                self=None
            else:
                self.mon.err(self,error_text)
                self.end_callback("error",'error')
                self=None



    def resource(self,section,item):
        value=self.rr.get(section,item)
        if value==False:
            self.mon.err(self, "resource: "+section +': '+ item + " not found" )
            self._error()
        else:
            return value



    def _start_front_porch(self):
        self.state=ImagePlayer.SLIDE_IN
        self.porch_counter=0
        if self.ready_callback<>None: self.ready_callback()

        if self.pil_image<>None or self.enable_menu== True or self.show_params['show-text']<> '' or self.track_params['track-text']<> '':
                self.canvas.delete(ALL)

        if self.transition=="cut":
            #just display the slide full brightness. No need for porch but used for symmetry
            if self.pil_image<>None:
                self.tk_img=PIL.ImageTk.PhotoImage(self.pil_image)
                self.drawn = self.canvas.create_image(self.centre_x, self.centre_y,
                                                      image=self.tk_img, anchor=CENTER)

  
        elif self.transition=="fade":
            #experimental start black and increase brightness (controlled by porch_counter).
            self._display_image()

        elif self.transition == "slide":
            #experimental, start in middle and move to right (controlled by porch_counter)
            if self.pil_image<>None:
                self.tk_img=PIL.ImageTk.PhotoImage(self.pil_image)
                self.drawn = self.canvas.create_image(self.centre_x, self.centre_y,
                                                  image=self.tk_img, anchor=CENTER)
            
        elif self.transition=="crop":
            #experimental, start in middle and crop from right (controlled by porch_counter)
            if self.pil_image<>None:
                self.tk_img=PIL.ImageTk.PhotoImage(self.pil_image)
                self.drawn = self.canvas.create_image(self.centre_x, self.centre_y,
                                                  image=self.tk_img, anchor=CENTER)
        self.canvas.update_idletasks()

        # create animation events
        error_text=self.ppio.animate(self.animate_begin_text,id(self))
        if error_text<>'':
            self.mon.err(self,error_text)
            self.error=True
            self._end()
                                                  
        self._tick_timer=self.canvas.after(self.tick, self._do_front_porch)
        
            
    def _do_front_porch(self):
        if self.quit_signal == True:
            self._end('normal','user quit')
        else:
            self.porch_counter=self.porch_counter+1
            # print "doing slide front porch " +str(self.porch_counter)
            self.canvas.config(bg='black')
            self._display_image()
            if self.porch_counter==self.porch/self.tick:
                self._start_dwell()
            else:
                self._tick_timer=self.canvas.after(self.tick,self._do_front_porch)


    def _start_dwell(self):
        self.state=ImagePlayer.SLIDE_DWELL
        self.dwell_counter=0
        self._tick_timer=self.canvas.after(self.tick, self._do_dwell)

        
    def _do_dwell(self):
        if self.quit_signal == True:
            self.mon.log(self,"quit received")
            self._end('normal','user quit')
        else:
            if self.paused == False:
                self.dwell_counter=self.dwell_counter+1

            # one time flipping of pause text
            if self.paused==True and self.pause_text==None:
                self.pause_text=self.canvas.create_text(100,100, anchor=NW,
                                                      text=self.resource('imageplayer','m01'),
                                                      fill="white",
                                                      font="arial 25 bold")
                self.canvas.update_idletasks( )
                
            if self.paused==False and self.pause_text<>None:
                    self.canvas.delete(self.pause_text)
                    self.pause_text=None
                    self.canvas.update_idletasks( )

            if self.dwell_counter==self.dwell/self.tick:
                self._start_back_porch()
            else:
                self._tick_timer=self.canvas.after(self.tick, self._do_dwell)

    def _start_back_porch(self):
        self.state=ImagePlayer.SLIDE_OUT
        self.porch_counter=self.porch/self.tick
        
        if self.transition=="cut":
             # just keep displaying the slide full brightness.
            # No need for porch but used for symmetry
             pass
            
        elif self.transition=="fade":
            #experimental start full and decrease brightness (controlled by porch_counter).
            self._display_image()

        elif self.transition== "slide":
            #experimental, start in middle and move to right (controlled by porch_counter)
            if self.pil_image<>None:
                self.tk_img=PIL.ImageTk.PhotoImage(self.pil_image)
                self.drawn = self.canvas.create_image(self.centre_x, self.centre_y,
                                                  image=self.tk_img, anchor=CENTER)
            
        elif self.transition =="crop":
            #experimental, start in middle and crop from right (controlled by porch_counter)
            if self.pil_image<>None:
                self.tk_img=PIL.ImageTk.PhotoImage(self.pil_image)
                self.drawn = self.canvas.create_image(self.centre_x, self.centre_y,
                                                  image=self.tk_img, anchor=CENTER)

        self._tick_timer=self.canvas.after(self.tick, self._do_back_porch)

            
    def _do_back_porch(self):
        if self.quit_signal == True:
            self._end('normal','user quit')
        else:
            self.porch_counter=self.porch_counter-1
            self._display_image()
            if self.porch_counter==0:
                self._end('normal','finished')
            else:
                self._tick_timer=self.canvas.after(self.tick,self._do_back_porch)

    



    def _display_image(self):
        if self.transition=="cut":
            pass
        
        # all the methods below have incorrect code !!!
        elif self.transition=="fade":
            if self.pil_image<>None:
                self.enh=PIL.ImageEnhance.Brightness(self.pil_image)
                prop=float(self.porch_counter)/float(20)  #????????
                self.pil_img=self.enh.enhance(prop)
                self.tk_img=PIL.ImageTk.PhotoImage(self.pil_img)
                self.drawn = self.canvas.create_image(self.centre_x, self.centre_y,
                                                      image=self.tk_img, anchor=CENTER)

        elif self.transition=="slide":
            if self.pil_image<>None:
                self.canvas.move(self.drawn,5,0)
            
        elif self.transition=="crop":
            if self.pil_image<>None:
                self.crop= 10*self.porch_counter
                self.pil_img=self.pil_image.crop((0,0,1000-self.crop,1080))
                self.tk_img=PIL.ImageTk.PhotoImage(self.pil_img)           
                self.drawn = self.canvas.create_image(self.centre_x, self.centre_y,
                                                      image=self.tk_img, anchor=CENTER)


        # display instructions if enabled
       
        if self.enable_menu== True:
            self.canvas.create_text(self.centre_x, int(self.canvas['height']) - int(self.show_params['hint-y']),
                                                  text=self.show_params['hint-text'],
                                                  fill=self.show_params['hint-colour'],
                                                font=self.show_params['hint-font'])

        # display show text if enabled
        if self.show_params['show-text']<> '':
            self.canvas.create_text(int(self.show_params['show-text-x']),int(self.show_params['show-text-y']),
                                                    anchor=NW,
                                                  text=self.show_params['show-text'],
                                                  fill=self.show_params['show-text-colour'],
                                                  font=self.show_params['show-text-font'])
            
        # display track text if enabled
        if self.track_params['track-text']<> '':
            self.canvas.create_text(int(self.track_params['track-text-x']),int(self.track_params['track-text-y']),
                                                    anchor=NW,
                                                  text=self.track_params['track-text'],
                                                  fill=self.track_params['track-text-colour'],
                                                  font=self.track_params['track-text-font'])
            
        self.canvas.update_idletasks( )