~ubuntu-branches/ubuntu/precise/gst0.10-python/precise

« back to all changes in this revision

Viewing changes to examples/play.py

  • Committer: Bazaar Package Importer
  • Author(s): Loic Minier
  • Date: 2006-06-25 19:37:45 UTC
  • Revision ID: james.westby@ubuntu.com-20060625193745-9yeg0wq56r24n57x
Tags: upstream-0.10.4
ImportĀ upstreamĀ versionĀ 0.10.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
# -*- Mode: Python -*-
 
3
# vi:si:et:sw=4:sts=4:ts=4
 
4
 
 
5
import pygtk
 
6
pygtk.require('2.0')
 
7
 
 
8
import sys
 
9
 
 
10
import gobject
 
11
 
 
12
import pygst
 
13
pygst.require('0.10')
 
14
import gst
 
15
import gst.interfaces
 
16
import gtk
 
17
 
 
18
class GstPlayer:
 
19
    def __init__(self, videowidget):
 
20
        self.playing = False
 
21
        self.player = gst.element_factory_make("playbin", "player")
 
22
        self.videowidget = videowidget
 
23
        self.on_eos = False
 
24
 
 
25
        bus = self.player.get_bus()
 
26
        bus.enable_sync_message_emission()
 
27
        bus.add_signal_watch()
 
28
        bus.connect('sync-message::element', self.on_sync_message)
 
29
        bus.connect('message', self.on_message)
 
30
 
 
31
    def on_sync_message(self, bus, message):
 
32
        if message.structure is None:
 
33
            return
 
34
        if message.structure.get_name() == 'prepare-xwindow-id':
 
35
            self.videowidget.set_sink(message.src)
 
36
            message.src.set_property('force-aspect-ratio', True)
 
37
            
 
38
    def on_message(self, bus, message):
 
39
        t = message.type
 
40
        if t == gst.MESSAGE_ERROR:
 
41
            err, debug = message.parse_error()
 
42
            print "Error: %s" % err, debug
 
43
            if self.on_eos:
 
44
                self.on_eos()
 
45
            self.playing = False
 
46
        elif t == gst.MESSAGE_EOS:
 
47
            if self.on_eos:
 
48
                self.on_eos()
 
49
            self.playing = False
 
50
 
 
51
    def set_location(self, location):
 
52
        self.player.set_property('uri', location)
 
53
 
 
54
    def query_position(self):
 
55
        "Returns a (position, duration) tuple"
 
56
        try:
 
57
            position, format = self.player.query_position(gst.FORMAT_TIME)
 
58
        except:
 
59
            position = gst.CLOCK_TIME_NONE
 
60
 
 
61
        try:
 
62
            duration, format = self.player.query_duration(gst.FORMAT_TIME)
 
63
        except:
 
64
            duration = gst.CLOCK_TIME_NONE
 
65
 
 
66
        return (position, duration)
 
67
 
 
68
    def seek(self, location):
 
69
        """
 
70
        @param location: time to seek to, in nanoseconds
 
71
        """
 
72
        gst.debug("seeking to %r" % location)
 
73
        event = gst.event_new_seek(1.0, gst.FORMAT_TIME,
 
74
            gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE,
 
75
            gst.SEEK_TYPE_SET, location,
 
76
            gst.SEEK_TYPE_NONE, 0)
 
77
 
 
78
        res = self.player.send_event(event)
 
79
        if res:
 
80
            gst.info("setting new stream time to 0")
 
81
            self.player.set_new_stream_time(0L)
 
82
        else:
 
83
            gst.error("seek to %r failed" % location)
 
84
 
 
85
    def pause(self):
 
86
        gst.info("pausing player")
 
87
        self.player.set_state(gst.STATE_PAUSED)
 
88
        self.playing = False
 
89
 
 
90
    def play(self):
 
91
        gst.info("playing player")
 
92
        self.player.set_state(gst.STATE_PLAYING)
 
93
        self.playing = True
 
94
        
 
95
    def stop(self):
 
96
        self.player.set_state(gst.STATE_NULL)
 
97
        gst.info("stopped player")
 
98
 
 
99
    def get_state(self, timeout=1):
 
100
        return self.player.get_state(timeout=timeout)
 
101
 
 
102
    def is_playing(self):
 
103
        return self.playing
 
104
    
 
105
class VideoWidget(gtk.DrawingArea):
 
106
    def __init__(self):
 
107
        gtk.DrawingArea.__init__(self)
 
108
        self.imagesink = None
 
109
        self.unset_flags(gtk.DOUBLE_BUFFERED)
 
110
 
 
111
    def do_expose_event(self, event):
 
112
        if self.imagesink:
 
113
            self.imagesink.expose()
 
114
            return False
 
115
        else:
 
116
            return True
 
117
 
 
118
    def set_sink(self, sink):
 
119
        assert self.window.xid
 
120
        self.imagesink = sink
 
121
        self.imagesink.set_xwindow_id(self.window.xid)
 
122
 
 
123
class PlayerWindow(gtk.Window):
 
124
    UPDATE_INTERVAL = 500
 
125
    def __init__(self):
 
126
        gtk.Window.__init__(self)
 
127
        self.set_default_size(410, 325)
 
128
 
 
129
        self.create_ui()
 
130
 
 
131
        self.player = GstPlayer(self.videowidget)
 
132
 
 
133
        def on_eos():
 
134
            self.player.seek(0L)
 
135
            self.play_toggled()
 
136
        self.player.on_eos = lambda *x: on_eos()
 
137
        
 
138
        self.update_id = -1
 
139
        self.changed_id = -1
 
140
        self.seek_timeout_id = -1
 
141
 
 
142
        self.p_position = gst.CLOCK_TIME_NONE
 
143
        self.p_duration = gst.CLOCK_TIME_NONE
 
144
 
 
145
        def on_delete_event():
 
146
            self.player.stop()
 
147
            gtk.main_quit()
 
148
        self.connect('delete-event', lambda *x: on_delete_event())
 
149
 
 
150
    def load_file(self, location):
 
151
        self.player.set_location(location)
 
152
                                  
 
153
    def create_ui(self):
 
154
        vbox = gtk.VBox()
 
155
        self.add(vbox)
 
156
 
 
157
        self.videowidget = VideoWidget()
 
158
        vbox.pack_start(self.videowidget)
 
159
        
 
160
        hbox = gtk.HBox()
 
161
        vbox.pack_start(hbox, fill=False, expand=False)
 
162
        
 
163
        self.pause_image = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PAUSE,
 
164
                                                    gtk.ICON_SIZE_BUTTON)
 
165
        self.pause_image.show()
 
166
        self.play_image = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PLAY,
 
167
                                                   gtk.ICON_SIZE_BUTTON)
 
168
        self.play_image.show()
 
169
        self.button = button = gtk.Button()
 
170
        button.add(self.play_image)
 
171
        button.set_property('can-default', True)
 
172
        button.set_focus_on_click(False)
 
173
        button.show()
 
174
        hbox.pack_start(button, False)
 
175
        button.set_property('has-default', True)
 
176
        button.connect('clicked', lambda *args: self.play_toggled())
 
177
        
 
178
        self.adjustment = gtk.Adjustment(0.0, 0.00, 100.0, 0.1, 1.0, 1.0)
 
179
        hscale = gtk.HScale(self.adjustment)
 
180
        hscale.set_digits(2)
 
181
        hscale.set_update_policy(gtk.UPDATE_CONTINUOUS)
 
182
        hscale.connect('button-press-event', self.scale_button_press_cb)
 
183
        hscale.connect('button-release-event', self.scale_button_release_cb)
 
184
        hscale.connect('format-value', self.scale_format_value_cb)
 
185
        hbox.pack_start(hscale)
 
186
        self.hscale = hscale
 
187
 
 
188
        self.videowidget.connect_after('realize',
 
189
                                       lambda *x: self.play_toggled())
 
190
 
 
191
    def play_toggled(self):
 
192
        self.button.remove(self.button.child)
 
193
        if self.player.is_playing():
 
194
            self.player.pause()
 
195
            self.button.add(self.play_image)
 
196
        else:
 
197
            self.player.play()
 
198
            if self.update_id == -1:
 
199
                self.update_id = gobject.timeout_add(self.UPDATE_INTERVAL,
 
200
                                                     self.update_scale_cb)
 
201
            self.button.add(self.pause_image)
 
202
 
 
203
    def scale_format_value_cb(self, scale, value):
 
204
        if self.p_duration == -1:
 
205
            real = 0
 
206
        else:
 
207
            real = value * self.p_duration / 100
 
208
        
 
209
        seconds = real / gst.SECOND
 
210
 
 
211
        return "%02d:%02d" % (seconds / 60, seconds % 60)
 
212
 
 
213
    def scale_button_press_cb(self, widget, event):
 
214
        # see seek.c:start_seek
 
215
        gst.debug('starting seek')
 
216
        
 
217
        self.button.set_sensitive(False)
 
218
        self.was_playing = self.player.is_playing()
 
219
        if self.was_playing:
 
220
            self.player.pause()
 
221
 
 
222
        # don't timeout-update position during seek
 
223
        if self.update_id != -1:
 
224
            gobject.source_remove(self.update_id)
 
225
            self.update_id = -1
 
226
 
 
227
        # make sure we get changed notifies
 
228
        if self.changed_id == -1:
 
229
            self.changed_id = self.hscale.connect('value-changed',
 
230
                self.scale_value_changed_cb)
 
231
            
 
232
    def scale_value_changed_cb(self, scale):
 
233
        # see seek.c:seek_cb
 
234
        real = long(scale.get_value() * self.p_duration / 100) # in ns
 
235
        gst.debug('value changed, perform seek to %r' % real)
 
236
        self.player.seek(real)
 
237
        # allow for a preroll
 
238
        self.player.get_state(timeout=50*gst.MSECOND) # 50 ms
 
239
 
 
240
    def scale_button_release_cb(self, widget, event):
 
241
        # see seek.cstop_seek
 
242
        widget.disconnect(self.changed_id)
 
243
        self.changed_id = -1
 
244
 
 
245
        self.button.set_sensitive(True)
 
246
        if self.seek_timeout_id != -1:
 
247
            gobject.source_remove(self.seek_timeout_id)
 
248
            self.seek_timeout_id = -1
 
249
        else:
 
250
            gst.debug('released slider, setting back to playing')
 
251
            if self.was_playing:
 
252
                self.player.play()
 
253
 
 
254
        if self.update_id != -1:
 
255
            self.error('Had a previous update timeout id')
 
256
        else:
 
257
            self.update_id = gobject.timeout_add(self.UPDATE_INTERVAL,
 
258
                self.update_scale_cb)
 
259
 
 
260
    def update_scale_cb(self):
 
261
        self.p_position, self.p_duration = self.player.query_position()
 
262
        if self.p_position != gst.CLOCK_TIME_NONE:
 
263
            value = self.p_position * 100.0 / self.p_duration
 
264
            self.adjustment.set_value(value)
 
265
 
 
266
        return True
 
267
 
 
268
def main(args):
 
269
    def usage():
 
270
        sys.stderr.write("usage: %s URI-OF-MEDIA-FILE\n" % args[0])
 
271
        sys.exit(1)
 
272
 
 
273
    w = PlayerWindow()
 
274
 
 
275
    if len(args) != 2:
 
276
        usage()
 
277
 
 
278
    if not gst.uri_is_valid(args[1]):
 
279
        sys.stderr.write("Error: Invalid URI: %s\n" % args[1])
 
280
        sys.exit(1)
 
281
 
 
282
    w.load_file(args[1])
 
283
    w.show_all()
 
284
 
 
285
    gtk.main()
 
286
 
 
287
if __name__ == '__main__':
 
288
    sys.exit(main(sys.argv))