Module GPlayer
[hide private]
[frames] | no frames]

Source Code for Module GPlayer

  1  #    This file is part of Subtle 
  2  # 
  3  #    This program is free software: you can redistribute it and/or modify 
  4  #    it under the terms of the GNU General Public License as published by 
  5  #    the Free Software Foundation, either version 3 of the License, or 
  6  #    (at your option) any later version. 
  7  # 
  8  #    This program is distributed in the hope that it will be useful, 
  9  #    but WITHOUT ANY WARRANTY; without even the implied warranty of 
 10  #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 11  #    GNU General Public License for more details. 
 12  # 
 13  #    You should have received a copy of the GNU General Public License 
 14  #    along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 15   
 16  import pygtk 
 17  pygtk.require('2.0') 
 18   
 19  import gobject 
 20  gobject.threads_init() 
 21   
 22  import pygst 
 23  pygst.require('0.10') 
 24  import gst 
 25  import gst.interfaces 
 26  import gtk 
 27   
 28  ## \file GPlayer.py 
 29  # Documentation for GPlayer module of Subtle project. 
 30  # \todo Add better seeking. 
 31   
 32   
 33  ## GstPlayer class. 
 34  # Class for playing media in GStreamer. 
35 -class GstPlayer:
36 ## Construstor 37 # \param videowidget - VideoWidget class.
38 - def __init__(self, videowidget):
39 self.playing = False 40 bin = gst.Bin('my-bin') 41 self.textoverlay = gst.element_factory_make('textoverlay') 42 bin.add(self.textoverlay) 43 pad = self.textoverlay.get_pad("video_sink") 44 ghostpad = gst.GhostPad("sink", pad) 45 bin.add_pad(ghostpad) 46 color = gst.element_factory_make('ffmpegcolorspace') 47 bin.add(color) 48 scale = gst.element_factory_make('videoscale') 49 bin.add(scale) 50 self.videosink = gst.element_factory_make('autovideosink') 51 bin.add(self.videosink) 52 gst.element_link_many(self.textoverlay, color, scale, self.videosink) 53 self.player = gst.element_factory_make("playbin", "player") 54 self.player.set_property("video-sink", bin) 55 self.videowidget = videowidget 56 57 bus = self.player.get_bus() 58 bus.enable_sync_message_emission() 59 bus.add_signal_watch() 60 bus.connect('sync-message::element', self.on_sync_message) 61 bus.connect('message', self.on_message) 62 self.cur_frame = 0 63 self.rate = 1.0
64
65 - def on_sync_message(self, bus, message):
66 if message.structure is None: 67 return 68 if message.structure.get_name() == 'prepare-xwindow-id': 69 self.videowidget.set_sink(message.src) 70 message.src.set_property('force-aspect-ratio', True)
71
72 - def on_message(self, bus, message):
73 t = message.type 74 if t == gst.MESSAGE_ERROR: 75 err, debug = message.parse_error() 76 print "Error: %s" % err, debug 77 self.playing = False 78 elif t == gst.MESSAGE_EOS: 79 self.playing = False
80
81 - def fast_forward(self):
82 """ 83 Here we will fast forward the stream for as many times 84 as this is called 85 """ 86 if self.rate < 8.0: 87 self.rate = self.rate*2.0 88 event = gst.event_new_seek(self.rate, gst.FORMAT_TIME, 89 gst.SEEK_FLAG_FLUSH, 90 gst.SEEK_TYPE_SET, self.query_position()[0], 91 gst.SEEK_TYPE_NONE, 0) 92 93 res = self.player.send_event(event) 94 if res: 95 gst.info("fast forwarding at rate: %f" % self.rate) 96 self.player.set_new_stream_time(0L) 97 else: 98 gst.error("change rate to %f failed" % self.rate) 99 return
100
101 - def slow_motion(self):
102 """ 103 Here we will slow motion the stream for as many times 104 as this is called 105 """ 106 self.rate = self.rate/2.0 107 event = gst.event_new_seek(self.rate, gst.FORMAT_TIME, 108 gst.SEEK_FLAG_FLUSH, 109 gst.SEEK_TYPE_SET, self.query_position()[0], 110 gst.SEEK_TYPE_NONE, 0) 111 112 res = self.player.send_event(event) 113 if res: 114 gst.info("slowing playback to rate: %f" % self.rate) 115 self.player.set_new_stream_time(0L) 116 else: 117 gst.error("change rate to %f failed" % self.rate) 118 119 return
120
121 - def get_rate(self):
122 """ 123 Get the playing rate at the moment 124 """ 125 return self.rate
126 127 ## Set location. 128 # Set location of the source. 129 # \param location - URI of the source.
130 - def set_location(self, location):
131 self.player.set_state(gst.STATE_NULL) 132 self.player.set_property('uri', location)
133 134 ## Set Subtitle Text 135 # Set subtitle text to be overlayed. 136 # \param text - Text (may have pango tags) 137 # \param font - Pango FontDescrition for the text
138 - def set_subtitle_text(self, text, font=None):
139 if font: 140 self.textoverlay.set_property('subtitle-font-desc', font) 141 self.textoverlay.set_property('text', text) 142 return
143 144 ## Get location. 145 # Get location of the source.
146 - def get_location(self):
147 return self.player.get_property('uri')
148
149 - def query_position(self):
150 "Returns a (position, duration) tuple" 151 try: 152 position, format = self.player.query_position(gst.FORMAT_TIME) 153 except: 154 position = gst.CLOCK_TIME_NONE 155 156 try: 157 duration, format = self.player.query_duration(gst.FORMAT_TIME) 158 except: 159 duration = gst.CLOCK_TIME_NONE 160 161 return (position, duration)
162
163 - def query_frame(self, position):
164 "Query the frame position" 165 if position != gst.CLOCK_TIME_NONE: 166 pad = self.videosink.get_pad('sink') 167 caps = pad.get_negotiated_caps() 168 if caps is not None: 169 framerate = caps[0]['framerate'] 170 position = float(position)/float(1000000000) 171 self.cur_frame = (float(position)*float( 172 framerate.num))/float(framerate.denom) 173 return self.cur_frame
174 175 ## Seek. 176 # Seek media. 177 # \param location - location to the seek.
178 - def seek(self, location):
179 gst.debug("seeking to %r" % location) 180 event = gst.event_new_seek(self.rate, gst.FORMAT_TIME, 181 gst.SEEK_FLAG_FLUSH, 182 gst.SEEK_TYPE_SET, location, 183 gst.SEEK_TYPE_NONE, 0) 184 185 res = self.player.send_event(event) 186 if res: 187 gst.info("setting new stream time to 0") 188 self.player.set_new_stream_time(0L) 189 else: 190 gst.error("seek to %r failed" % location)
191 192 ## Pause. 193 # Media pause.
194 - def pause(self):
195 gst.info("pausing player") 196 self.player.set_state(gst.STATE_PAUSED) 197 self.playing = False
198 199 ## Play. 200 # Media play.
201 - def play(self):
202 """ 203 Change the stream state to playing or simply 204 change its playing rate to normal rate 205 """ 206 if self.rate != 1.0: 207 self.rate = 1.0 208 event = gst.event_new_seek(self.rate, gst.FORMAT_TIME, 209 gst.SEEK_FLAG_FLUSH, 210 gst.SEEK_TYPE_SET, self.query_position()[0], 211 gst.SEEK_TYPE_NONE, 0) 212 213 res = self.player.send_event(event) 214 if res: 215 gst.info("slowing playback to rate: %f" % self.rate) 216 self.player.set_new_stream_time(0L) 217 else: 218 gst.error("change rate to %f failed" % self.rate) 219 else: 220 gst.info("playing player") 221 self.player.set_state(gst.STATE_PLAYING) 222 self.playing = True 223 return
224 225 ## Stop 226 # Media stop.
227 - def stop(self):
228 self.player.set_state(gst.STATE_READY) 229 self.playing = False 230 gst.info("stopped player")
231 232 ## Get state. 233 # Get current state of the media. 234 # \param timeout - time out of the operation. 235 # \raturn state of the media.
236 - def get_state(self, timeout=1):
237 return self.player.get_state(timeout=timeout)
238 239 ## Is playing 240 # \return TRUE if media is playing.
241 - def is_playing(self):
242 return self.playing
243 244 ## \var playing 245 # Bool variable, TRUE - if media is playing. 246 247 ## \var player 248 # GStreamer playerbin element. 249 250 ## \var videowidget 251 # GTK+ widget for video render. 252 253 #============================================================================== 254 ## VideoWidget class. 255 # VideoWidget class for render video stream on GTK+ widget.
256 -class VideoWidget:
257 ## Constructor. 258 # \param TArea - GTK+ drowing area widget.
259 - def __init__(self, TArea):
260 self.Area=TArea 261 self.imagesink = None 262 self.Area.unset_flags(gtk.DOUBLE_BUFFERED)
263 264 ## \var Area 265 # GTK+ drowing area widget. 266 267 ## \var imagesink 268 # Sink element for 269
270 - def do_expose_event(self, event):
271 if self.imagesink: 272 self.imagesink.expose() 273 return False 274 else: 275 return True
276
277 - def set_sink(self, sink):
278 assert self.Area.window.xid 279 self.imagesink = sink 280 self.imagesink.set_xwindow_id(self.Area.window.xid)
281