1
# Miro - an RSS based video player application
2
# Copyright (C) 2005-2009 Participatory Culture Foundation
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
# In addition, as a special exception, the copyright holders give
19
# permission to link the code of portions of this program with the OpenSSL
22
# You must obey the GNU General Public License in all respects for all of
23
# the code used other than OpenSSL. If you modify file(s) with this
24
# exception, you may extend this exception to your version of the file(s),
25
# but you are not obligated to do so. If you do not wish to do so, delete
26
# this exception statement from your version. If you delete this exception
27
# statement from all source files in the program, then also delete it here.
36
from miro import config
38
from miro.plat import options
39
from miro.plat import resources
40
from miro.plat.frontends.widgets import threads
44
self.xine = xine.Xine()
45
self.xine.setup_sniffer()
47
def get_item_type(self, filename):
48
return self.xine.get_type(filename)
52
logging.info("Xine version: %s", xine.getXineVersion())
53
self.xine = xine.Xine()
56
self.xine.set_eos_callback(self.on_eos)
59
# on_eos gets called by one of the xine threads, so we want to switch
60
# to the ui thread to do things.
61
threads.call_on_ui_thread(app.playback_manager.on_movie_finished)
63
def select_file(self, filename, success_callback, error_callback):
64
logging.error("Not implemented.")
66
def get_progress(self):
68
pos, length = self.xine.get_position_and_length()
69
except (SystemExit, KeyboardInterrupt):
72
logging.warn("get_current_time: caught exception: %s" % e)
75
def get_current_time(self):
77
pos, length = self.xine.get_position_and_length()
80
logging.warn("get_current_time: caught exception: %s" % e)
83
def set_current_time(self, seconds):
86
def seek(self, seconds):
87
# this is really funky. what's going on here is that xine-lib doesn't
88
# provide a way to seek while paused. if you seek, then it induces
89
# playing, but that's not what we want.
90
# so we do this sneaky thing where if we're paused, we shut the volume
91
# off, seek, pause, and turn the volume back on. that allows us to
92
# seek, remain paused, and doesn't cause a hiccup in sound.
95
self.xine.seek(int(seconds * 1000))
99
self.xine.set_volume(0)
100
self.xine.seek(int(seconds * 1000))
102
self.set_volume(self._volume)
104
def get_duration(self):
106
pos, length = self.xine.get_position_and_length()
107
return length / 1000.0
108
except (SystemExit, KeyboardInterrupt):
111
logging.exception("get_duration: caught exception")
113
def set_volume(self, level):
115
self.xine.set_volume(int(level * 100))
124
self._playing = False
130
logging.warn("get_rate not implemented for xine")
132
def set_rate(self, rate):
133
logging.warn("set_rate not implemented for xine")
135
class VideoRenderer(Renderer):
137
Renderer.__init__(self)
138
self.driver = config.get(options.XINE_DRIVER)
139
logging.info("Xine video driver: %s", self.driver)
141
def set_widget(self, widget):
142
widget.connect("destroy", self.on_destroy)
143
widget.connect("configure-event", self.on_configure_event)
144
widget.connect("expose-event", self.on_expose_event)
147
# flush gdk output to ensure that the window we're passing to xine has
150
displayName = gtk.gdk.display_get_default().get_name()
151
self.xine.attach(displayName,
152
widget.persistent_window.xid,
154
int(options.shouldSyncX),
155
int(config.get(options.USE_XINE_XV_HACK)))
156
self.gc = widget.persistent_window.new_gc()
157
self.gc.foreground = gtk.gdk.color_parse("black")
159
def on_destroy(self, widget):
162
def on_configure_event(self, widget, event):
163
self.xine.set_area(event.x, event.y, event.width, event.height)
165
def on_expose_event(self, widget, event):
166
# if we wanted to draw an image for audio-only items, this is where
168
widget.window.draw_rectangle(self.gc,
171
widget.allocation.width,
172
widget.allocation.height)
173
self.xine.got_expose_event(event.area.x, event.area.y, event.area.width,
176
def go_fullscreen(self):
177
"""Handle when the video window goes fullscreen."""
178
# Sometimes xine doesn't seem to handle the expose events properly and
179
# only thinks part of the window is exposed. To work around this we
180
# send it a couple of fake expose events for the entire window, after
181
# a short time delay.
183
def fullscreen_expose_workaround():
185
_, _, width, height, _ = self.widget.window.get_geometry()
186
self.xine.got_expose_event(0, 0, width, height)
187
except (SystemExit, KeyboardInterrupt):
193
gobject.timeout_add(500, fullscreen_expose_workaround)
194
gobject.timeout_add(1000, fullscreen_expose_workaround)
196
def exit_fullscreen(self):
197
"""Handle when the video window exits fullscreen mode."""
201
def select_file(self, filename, success_callback, error_callback):
202
self._filename = filename
203
if self.xine.select_file(filename):
204
gobject.idle_add(success_callback)
205
def expose_workaround():
207
_, _, width, height, _ = self.widget.window.get_geometry()
208
self.xine.got_expose_event(0, 0, width, height)
209
except (SystemExit, KeyboardInterrupt):
215
gobject.timeout_add(500, expose_workaround)
218
gobject.idle_add(error_callback)
220
class AudioRenderer(Renderer):
222
Renderer.__init__(self)
223
self._attached = False
228
self.xine.attach("", 0, "none", 0, 0)
229
self._attached = True
233
self._attached = False
235
def select_file(self, filename, success_callback, error_callback):
236
if not self._attached:
239
self._filename = filename
240
if self.xine.select_file(filename):
241
gobject.idle_add(success_callback)
244
gobject.idle_add(error_callback)
247
Renderer.on_eos(self)
249
def movie_data_program_info(movie_path, thumbnail_path):
250
if os.path.exists(resources.path('../../../lib/miro/xine_extractor')):
251
path = resources.path('../../../lib/miro/xine_extractor')
252
return ((path, movie_path, thumbnail_path), None)
254
logging.error("xine_extractor cannot be found.")
255
raise NotImplementedError()
259
def get_item_type(item_info, success_callback, error_callback):
260
item_type = _SNIFFER.get_item_type(item_info.video_path)
264
success_callback("video")
266
success_callback("audio")
268
success_callback("unplayable")