Package elisa :: Package base_components :: Module player_engine
[hide private]
[frames] | no frames]

Source Code for Module elisa.base_components.player_engine

  1  # -*- coding: utf-8 -*- 
  2  # Elisa - Home multimedia server 
  3  # Copyright (C) 2006-2008 Fluendo Embedded S.L. (www.fluendo.com). 
  4  # All rights reserved. 
  5  # 
  6  # This file is available under one of two license agreements. 
  7  # 
  8  # This file is licensed under the GPL version 3. 
  9  # See "LICENSE.GPL" in the root of this distribution including a special 
 10  # exception to use Elisa with Fluendo's plugins. 
 11  # 
 12  # The GPL part of Elisa is also available under a commercial licensing 
 13  # agreement from Fluendo. 
 14  # See "LICENSE.Elisa" in the root directory of this distribution package 
 15  # for details on that license. 
 16   
 17   
 18  __maintainer__ = 'Benjamin Kampmann <benjamin@fluendo.com>' 
 19   
 20  from elisa.core.component import Component 
 21   
 22  from elisa.core import common 
 23   
 24  from elisa.core.player import PlayerPlaying, PlayerStopping, \ 
 25                                PlayerPausing, PlayerLoading, \ 
 26                                STATES 
 27   
 28  from twisted.internet import reactor 
 29   
 30  import gst 
 31   
32 -class PlayerEngine(Component):
33 """ 34 A PlayerEngine provides various media playback related functionalities. It 35 declares the uri schemes it supports and will be automatically instantiated by 36 the L{elisa.core.player_engine_registry.PlayerEngineRegistry} if needed. 37 38 Messages listed at L{elisa.core.player} have to be sent by the engine at 39 appropriate times. 40 41 @cvar uri_schemes: the uri-schemes this engine supports associated 42 with their ranking value between 0 (highest rank) 43 and 255 (lowest rank) 44 @type uri_schemes: dict 45 46 @ivar _pipeline: the internally used pipeline 47 @type _pipeline: L{gst.Pipeline} 48 49 @ivar video_sink: the videosink of this player engine 50 @type video_sink: L{gst.BaseSink} 51 52 @ivar audio_sink: the audiosink of this player engine 53 @type audio_sink: L{gst.BaseSink} 54 55 @ivar visualisation: the visualisation element for the player engine 56 @type visualisation: L{gst.Element} 57 58 @ivar volume: a value between 0 and 10 59 @type volume: float 60 61 @ivar position: the position we are currently playing in 62 nanoseconds 63 @type position: float 64 65 @ivar duration: (read-only) the total length of the loaded uri in 66 nanoseconds 67 @type duration: float 68 69 @ivar speed: The speed of the current playback: 70 - Normal playback is 1.0 71 - a positive value means forward 72 - a negative one backward 73 - a value bigger (+/-) 1.0 means faster 74 - a value smaller (+/-) 1.0 means slower 75 - the value 0.0 (equivalent to pause) is not 76 allowed 77 @type speed: float 78 79 @ivar state: (read-only) The current state. 80 @type state: L{elisa.core.player.STATES} 81 82 @ivar uri: (write-only) change the engine to be able to play 83 the set uri. 84 @type uri: L{elisa.core.media_uri.Mediauri} 85 86 87 @ivar message_sender: who is the sender of messages (per default it is 88 self) 89 @type message_sender: instance 90 """ 91 92 uri_schemes = {} 93
94 - def __init__(self):
95 Component.__init__(self) 96 self._pipeline = None 97 self._state = STATES.STOPPED 98 self._speed = 1.0 99 self.message_sender = self 100 self._current_uri = None
101 102 103 # Main Player Functionality 104
105 - def play(self, trigger_message=True):
106 """ 107 Play the media. If trigger_message is set to True, this triggers first 108 the message L{elisa.core.player.PlayerLoading} message and if the 109 playback is really starting, it triggers 110 L{elisa.core.player.PlayerPlaying}. Otherwise it does not trigger any 111 messages. 112 113 @param trigger_message: should the player trigger messages here 114 @type trigger_message: bool 115 """ 116 if not self._pipeline: 117 return 118 119 if self.state == STATES.STOPPED: 120 self._state = STATES.LOADING 121 122 if trigger_message: 123 self.info("Loading %r", str(self._current_uri)) 124 self._send_msg(PlayerLoading()) 125 126 self._pipeline_set_state(gst.STATE_PLAYING) 127 128 elif self.state == STATES.PAUSED: 129 self._state = STATES.PLAYING 130 131 if trigger_message: 132 self._send_msg(PlayerPlaying()) 133 134 self._pipeline_set_state(gst.STATE_PLAYING)
135 136 137
138 - def pause(self, trigger_message=True):
139 """ 140 Pause the playback. If trigger_message is set to True, this triggers 141 the L{elisa.core.player.PlayerPausing} message. 142 143 @param trigger_message: should the player trigger a message here 144 @type trigger_message: bool 145 """ 146 if self._pipeline: 147 if self.state == STATES.PLAYING: 148 self.info("Pausing Player") 149 self._state = STATES.PAUSED 150 if trigger_message: 151 self._send_msg(PlayerPausing()) 152 self._pipeline_set_state(gst.STATE_PAUSED)
153 154
155 - def stop(self, trigger_message=True):
156 """ 157 Stop the playback. 158 If trigger_message is set, this method triggers the 159 L{elisa.core.player.PlayerStopping} message. 160 161 @param trigger_message: should the player trigger a message here 162 @type trigger_message: bool 163 """ 164 if self._pipeline: 165 self.info("Stopping Player") 166 if self.state != STATES.STOPPED: 167 self._state = STATES.STOPPED 168 self._pipeline_set_state(gst.STATE_READY) 169 if trigger_message: 170 self._send_msg(PlayerStopping())
171 172 # Volume 173
174 - def volume__set(self, volume):
175 pass
176
177 - def volume__get(self):
178 pass
179 180 # For URI Support 181
182 - def uri__set(self, uri):
183 pass
184 185 # States and Attributes of the player 186
187 - def position__get(self):
188 if self.state not in (STATES.PLAYING, STATES.PAUSED): 189 return -1 190 191 try: 192 position, format = self._pipeline.query_position(gst.FORMAT_TIME) 193 return position 194 except: 195 return -1
196
197 - def position__set(self, position):
198 if position >= 0 and position <= self.duration: 199 self.debug("Position set to %s" % position) 200 self._seek_to_location(position)
201
202 - def state__get(self):
203 return self._state
204
205 - def duration__get(self):
206 if self.state not in (STATES.PLAYING, STATES.PAUSED): 207 return -1 208 try: 209 duration, format = self._pipeline.query_duration(gst.FORMAT_TIME) 210 return duration 211 except: 212 return -1
213
214 - def speed__get(self):
215 return self._speed
216
217 - def speed_set(self, speed):
218 self._speed = speed 219 self._seek_to_location(-1)
220 221 # Video Sink 222
223 - def video_sink__get(self):
224 pass
225
226 - def video_sink__set(self, sink):
227 pass
228 229 # Audio Sink 230
231 - def audio_sink__get(self):
232 pass
233
234 - def audio_sink__set(self, sink):
235 pass
236 237 # Visualisation Plugin 238
239 - def visualisation__get(self):
240 pass
241
242 - def visualisation__set(self, gst_element):
243 pass
244 245 # Internal Methods for easier usage 246
247 - def _send_msg(self, message):
248 """ 249 Makes it easier to send a message for the developers... 250 @param message: the message you want to send. Normally one of 251 L{elisa.core.player.PlayerLoading}, 252 L{elisa.core.player.PlayerPlaying}, 253 L{elisa.core.player.PlayerPausing} or 254 L{elisa.core.player.PlayerStopping} 255 @type message: L{elisa.core.bus.bus_message.Message} 256 """ 257 self.debug("Sending message %r" % message) 258 259 common.application.bus.send_message(message, 260 sender = self.message_sender)
261
262 - def _seek_to_location(self, location):
263 """ 264 seek to the given location with self.speed 265 """ 266 267 if location == gst.CLOCK_TIME_NONE or not self._pipeline: 268 return 269 270 human_loc = gst.TIME_ARGS(location) 271 self.info("Seeking to location : %s" % human_loc) 272 273 if location < 0: 274 speed = self.speed 275 if speed > 0: 276 start = self.position 277 end = self.duration 278 else: 279 end = self.position 280 start = 0 281 else: 282 speed = 1.0 283 if location < self.position: 284 start = location 285 end = self.duration 286 else: 287 start = location 288 end = self.duration 289 290 event = self._pipeline.seek(speed, gst.FORMAT_TIME, 291 gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_KEY_UNIT, 292 gst.SEEK_TYPE_SET, start, 293 gst.SEEK_TYPE_SET, end)
294
295 - def _pipeline_set_state(self, state):
296 """ 297 Set the state of the pipeline asynchronisly 298 """ 299 # FIXME: Because of a bug in the gnomevfs gstreamer element 300 # set_state might block 301 # This workaround should be removed as soon as possible. 302 if not self._pipeline: 303 return 304 reactor.callInThread(self._pipeline.set_state, state)
305