~ubuntu-branches/ubuntu/trusty/miro/trusty

« back to all changes in this revision

Viewing changes to platform/gtk-x11/plat/renderers/xinerenderer.py

  • Committer: Daniel Hahler
  • Date: 2010-04-13 18:51:35 UTC
  • mfrom: (1.2.10 upstream)
  • Revision ID: ubuntu-launchpad@thequod.de-20100413185135-xi24v1diqg8w406x
Merging shared upstream rev into target branch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Miro - an RSS based video player application
2
 
# Copyright (C) 2005-2009 Participatory Culture Foundation
3
 
#
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.
8
 
#
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.
13
 
#
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
17
 
#
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
20
 
# library.
21
 
#
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.
28
 
 
29
 
import os.path
30
 
import logging
31
 
 
32
 
import gtk
33
 
import gobject
34
 
 
35
 
from miro import app
36
 
from miro import config
37
 
from miro import xine
38
 
from miro.plat import options
39
 
from miro.plat import resources
40
 
from miro.plat.frontends.widgets import threads
41
 
 
42
 
class Sniffer:
43
 
    def __init__(self):
44
 
        self.xine = xine.Xine()
45
 
        self.xine.setup_sniffer()
46
 
 
47
 
    def get_item_type(self, filename):
48
 
        return self.xine.get_type(filename)
49
 
 
50
 
class Renderer:
51
 
    def __init__(self):
52
 
        logging.info("Xine version:      %s", xine.getXineVersion())
53
 
        self.xine = xine.Xine()
54
 
        self._playing = False
55
 
        self._volume = 0
56
 
        self.xine.set_eos_callback(self.on_eos)
57
 
 
58
 
    def on_eos(self):
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)
62
 
 
63
 
    def select_file(self, filename, success_callback, error_callback):
64
 
        logging.error("Not implemented.")
65
 
 
66
 
    def get_progress(self):
67
 
        try:
68
 
            pos, length = self.xine.get_position_and_length()
69
 
        except (SystemExit, KeyboardInterrupt):
70
 
            raise
71
 
        except:
72
 
            logging.warn("get_current_time: caught exception: %s" % e)
73
 
            return None
74
 
 
75
 
    def get_current_time(self):
76
 
        try:
77
 
            pos, length = self.xine.get_position_and_length()
78
 
            return pos / 1000.0
79
 
        except Exception, e:
80
 
            logging.warn("get_current_time: caught exception: %s" % e)
81
 
            return None
82
 
 
83
 
    def set_current_time(self, seconds):
84
 
        self.seek(seconds)
85
 
 
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.
93
 
 
94
 
        if self._playing:
95
 
            self.xine.seek(int(seconds * 1000))
96
 
 
97
 
        else:
98
 
            self._playing = True
99
 
            self.xine.set_volume(0)
100
 
            self.xine.seek(int(seconds * 1000))
101
 
            self.pause()
102
 
            self.set_volume(self._volume)
103
 
 
104
 
    def get_duration(self):
105
 
        try:
106
 
            pos, length = self.xine.get_position_and_length()
107
 
            return length / 1000.0
108
 
        except (SystemExit, KeyboardInterrupt):
109
 
            raise
110
 
        except:
111
 
            logging.exception("get_duration: caught exception")
112
 
 
113
 
    def set_volume(self, level):
114
 
        self._volume = level
115
 
        self.xine.set_volume(int(level * 100))
116
 
 
117
 
    def play(self):
118
 
        self.xine.play()
119
 
        self._playing = True
120
 
 
121
 
    def pause(self):
122
 
        if self._playing:
123
 
            self.xine.pause()
124
 
            self._playing = False
125
 
 
126
 
    stop = pause
127
 
    reset = pause
128
 
 
129
 
    def get_rate(self):
130
 
        logging.warn("get_rate not implemented for xine")
131
 
 
132
 
    def set_rate(self, rate):
133
 
        logging.warn("set_rate not implemented for xine")
134
 
 
135
 
class VideoRenderer(Renderer):
136
 
    def __init__(self):
137
 
        Renderer.__init__(self)
138
 
        self.driver = config.get(options.XINE_DRIVER)
139
 
        logging.info("Xine video driver: %s", self.driver)
140
 
 
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)
145
 
        self.widget = widget
146
 
 
147
 
        # flush gdk output to ensure that the window we're passing to xine has
148
 
        # been created
149
 
        gtk.gdk.flush()
150
 
        displayName = gtk.gdk.display_get_default().get_name()
151
 
        self.xine.attach(displayName,
152
 
                         widget.persistent_window.xid,
153
 
                         self.driver,
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")
158
 
 
159
 
    def on_destroy(self, widget):
160
 
        self.xine.detach()
161
 
 
162
 
    def on_configure_event(self, widget, event):
163
 
        self.xine.set_area(event.x, event.y, event.width, event.height)
164
 
 
165
 
    def on_expose_event(self, widget, event):
166
 
        # if we wanted to draw an image for audio-only items, this is where
167
 
        # we'd do it.
168
 
        widget.window.draw_rectangle(self.gc,
169
 
                                     True,
170
 
                                     0, 0,
171
 
                                     widget.allocation.width,
172
 
                                     widget.allocation.height)
173
 
        self.xine.got_expose_event(event.area.x, event.area.y, event.area.width,
174
 
                event.area.height)
175
 
 
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.
182
 
 
183
 
        def fullscreen_expose_workaround():
184
 
            try:
185
 
                _, _, width, height, _ = self.widget.window.get_geometry()
186
 
                self.xine.got_expose_event(0, 0, width, height)
187
 
            except (SystemExit, KeyboardInterrupt):
188
 
                raise
189
 
            except:
190
 
                return True
191
 
            return False
192
 
 
193
 
        gobject.timeout_add(500, fullscreen_expose_workaround)
194
 
        gobject.timeout_add(1000, fullscreen_expose_workaround)
195
 
 
196
 
    def exit_fullscreen(self):
197
 
        """Handle when the video window exits fullscreen mode."""
198
 
        # nothing to do here
199
 
        pass
200
 
 
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():
206
 
                try:
207
 
                    _, _, width, height, _ = self.widget.window.get_geometry()
208
 
                    self.xine.got_expose_event(0, 0, width, height)
209
 
                except (SystemExit, KeyboardInterrupt):
210
 
                    raise
211
 
                except:
212
 
                    return True
213
 
                return False
214
 
 
215
 
            gobject.timeout_add(500, expose_workaround)
216
 
            self.seek(0)
217
 
        else:
218
 
            gobject.idle_add(error_callback)
219
 
 
220
 
class AudioRenderer(Renderer):
221
 
    def __init__(self):
222
 
        Renderer.__init__(self)
223
 
        self._attached = False
224
 
 
225
 
    def attach(self):
226
 
        if self._attached:
227
 
            self.detach()
228
 
        self.xine.attach("", 0, "none", 0, 0)
229
 
        self._attached = True
230
 
 
231
 
    def detach(self):
232
 
        self.xine.detach()
233
 
        self._attached = False
234
 
 
235
 
    def select_file(self, filename, success_callback, error_callback):
236
 
        if not self._attached:
237
 
            self.attach()
238
 
 
239
 
        self._filename = filename
240
 
        if self.xine.select_file(filename):
241
 
            gobject.idle_add(success_callback)
242
 
            self.seek(0)
243
 
        else:
244
 
            gobject.idle_add(error_callback)
245
 
 
246
 
    def on_eos(self):
247
 
        Renderer.on_eos(self)
248
 
 
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)
253
 
    else:
254
 
        logging.error("xine_extractor cannot be found.")
255
 
        raise NotImplementedError()
256
 
 
257
 
_SNIFFER = Sniffer()
258
 
 
259
 
def get_item_type(item_info, success_callback, error_callback):
260
 
    item_type = _SNIFFER.get_item_type(item_info.video_path)
261
 
    if item_type == -1:
262
 
        error_callback()
263
 
    elif item_type == 0:
264
 
        success_callback("video")
265
 
    elif item_type == 1:
266
 
        success_callback("audio")
267
 
    else:
268
 
        success_callback("unplayable")