~ubuntu-branches/ubuntu/quantal/pitivi/quantal

« back to all changes in this revision

Viewing changes to pitivi/ui/viewer.py

  • Committer: Bazaar Package Importer
  • Author(s): Sebastian Dröge
  • Date: 2008-12-12 10:22:29 UTC
  • mfrom: (1.1.6 upstream)
  • mto: (3.2.2 jaunty) (1.2.2 upstream)
  • mto: This revision was merged to the branch mainline in revision 6.
  • Revision ID: james.westby@ubuntu.com-20081212102229-7c3etvaoy9ys0x28
Tags: upstream-0.11.3
Import upstream version 0.11.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21
21
# Boston, MA 02111-1307, USA.
22
22
 
23
 
import os.path
24
23
import gobject
25
 
import time
26
24
import gtk
27
25
import gst
28
 
import gst.interfaces
29
 
from glade import GladeWindow
30
26
 
31
 
import plumber
 
27
import pitivi.plumber as plumber
32
28
import pitivi.instance as instance
33
29
from pitivi.bin import SmartTimelineBin
34
 
import pitivi.dnd as dnd
35
30
from pitivi.signalgroup import SignalGroup
36
31
 
37
 
from gettext import gettext as _
38
 
 
39
 
def time_to_string(value):
40
 
    if value == -1:
41
 
        return "--:--:--.---"
42
 
    ms = value / gst.MSECOND
43
 
    sec = ms / 1000
44
 
    ms = ms % 1000
45
 
    mins = sec / 60
46
 
    sec = sec % 60
47
 
    hours = mins / 60
48
 
    return "%02d:%02d:%02d.%03d" % (hours, mins, sec, ms)
 
32
from pitivi.utils import time_to_string
 
33
import dnd
49
34
 
50
35
class PitiviViewer(gtk.VBox):
51
36
    """ Pitivi's viewer widget with controls """
54
39
        gst.log("New PitiviViewer")
55
40
        gtk.VBox.__init__(self)
56
41
        self.current_time = long(0)
57
 
        self.requested_time = long(0)
 
42
        self.requested_time = gst.CLOCK_TIME_NONE
58
43
        self.current_frame = -1
59
44
        self.valuechangedid = 0
60
45
        self.currentlySeeking = False
75
60
        # signal for timeline duration changes : (composition, sigid)
76
61
        self._timelineDurationChangedSigId = (None, None)
77
62
 
78
 
        self._addTimelineToPlayground()
 
63
 
79
64
 
80
65
    def _connectToProject(self, project):
81
66
        """Connect signal handlers to a project.
88
73
                                     None, self._tmpIsReadyCb)
89
74
        self.project_signals.connect(project, "settings-changed",
90
75
                                     None, self._settingsChangedCb)
 
76
        # we should add the timeline to the playground here, so
 
77
        # that the new timeline bin will be added to the
 
78
        # playground when the project loads
 
79
        self._addTimelineToPlayground()
91
80
 
92
81
    def _createUi(self):
93
82
        """ Creates the Viewer GUI """
95
84
        self.set_spacing(5)
96
85
 
97
86
        # drawing area
98
 
        self.aframe = gtk.AspectFrame(xalign=0.5, yalign=0.5, ratio=4.0/3.0, obey_child=False)
 
87
        self.aframe = gtk.AspectFrame(xalign=0.5, yalign=0.5, ratio=4.0/3.0,
 
88
                                      obey_child=False)
99
89
        self.pack_start(self.aframe, expand=True)
100
90
        self.drawingarea = ViewerWidget()
101
 
        self.drawingarea.connect_after("realize", self._drawingAreaRealizeCb)
 
91
        self.drawingarea.connect_after("expose-event", self._drawingAreaExposeCb)
102
92
        self.aframe.add(self.drawingarea)
103
93
 
104
94
        # Slider
145
135
 
146
136
        # current time
147
137
        self.timelabel = gtk.Label()
148
 
        self.timelabel.set_markup("<tt>00m00s000 / --m--s---</tt>")
 
138
        self.timelabel.set_markup("<tt>00:00:00.000 / --:--:--.---</tt>")
149
139
        self.timelabel.set_alignment(1.0, 0.5)
150
140
        self.timelabel.set_padding(5, 5)
151
141
        bbox.pack_start(self.timelabel, expand=False, padding=10)
152
142
 
 
143
        # self.detach_button = gtk.Button()
 
144
        # image = gtk.Image()
 
145
        # image.set_from_stock(gtk.STOCK_LEAVE_FULLSCREEN,
 
146
        #     gtk.ICON_SIZE_SMALL_TOOLBAR)
 
147
        # self.detach_button.set_image(image)
 
148
        # bbox.pack_end(self.detach_button, expand=False, fill=False)
153
149
 
154
150
        # drag and drop
155
151
        self.drag_dest_set(gtk.DEST_DEFAULT_DROP | gtk.DEST_DEFAULT_MOTION,
157
153
                           gtk.gdk.ACTION_COPY)
158
154
        self.connect("drag_data_received", self._dndDataReceivedCb)
159
155
 
160
 
    def _asyncFrameRatioChange(self, ratio):
161
 
        gst.debug("ratio:%f" % ratio)
162
 
        self.aframe.set_property("ratio", ratio)
163
156
 
164
 
    def _videosinkCapsNotifyCb(self, sinkpad, unused_property):
165
 
        caps = sinkpad.get_negotiated_caps()
166
 
        if not caps:
167
 
            return
168
 
        gst.log("caps:%s" % caps.to_string())
 
157
    def setDisplayAspectRatio(self, ratio):
 
158
        """ Sets the DAR of the Viewer to the given ratio """
 
159
        gst.debug("Setting ratio of %f [%r]" % (float(ratio), ratio))
169
160
        try:
170
 
            width = caps[0]["width"]
171
 
            height = caps[0]["height"]
 
161
            self.aframe.set_property("ratio", float(ratio))
172
162
        except:
173
 
            gst.warning("Something went wrong when getting the video sink aspect ratio")
174
 
        else:
175
 
            try:
176
 
                par = caps[0]["pixel-aspect-ratio"]
177
 
            except:
178
 
                # set aspect ratio
179
 
                gobject.idle_add(self._asyncFrameRatioChange, float(width) / float(height))
180
 
            else:
181
 
                gobject.idle_add(self._asyncFrameRatioChange, float(width * par.num) / float(par.denom * height))
 
163
            gst.warning("could not set ratio !")
182
164
 
183
165
    def _createSinkThreads(self):
184
166
        """ Creates the sink threads for the playground """
196
178
        vscale.link(vqueue)
197
179
        vsinkthread.videosink = self.videosink
198
180
        vsinkthread.add_pad(gst.GhostPad("sink", cspace.get_pad('sink')))
199
 
 
200
 
        self.videosink.get_pad("sink").connect("notify::caps", self._videosinkCapsNotifyCb)
201
 
 
202
181
        self.drawingarea.videosink = self.videosink
203
182
 
204
183
        # audio elements
216
195
        # setting sinkthreads on playground
217
196
        instance.PiTiVi.playground.setVideoSinkThread(vsinkthread)
218
197
        instance.PiTiVi.playground.setAudioSinkThread(asinkthread)
219
 
        instance.PiTiVi.playground.connect("current-changed", self._currentPlaygroundChangedCb)
 
198
        instance.PiTiVi.playground.connect("current-changed",
 
199
                                           self._currentPlaygroundChangedCb)
220
200
 
221
201
    def _settingsChangedCb(self, unused_project):
222
202
        gst.info("current project settings changed")
224
204
        # FIXME : do we really need to modify the ratio ??
225
205
        pass
226
206
 
227
 
    def _drawingAreaRealizeCb(self, drawingarea):
 
207
    def _drawingAreaExposeCb(self, drawingarea, event):
 
208
        drawingarea.disconnect_by_func(self._drawingAreaExposeCb)
228
209
        drawingarea.modify_bg(gtk.STATE_NORMAL, drawingarea.style.black)
229
210
        self._createSinkThreads()
230
211
        if not instance.PiTiVi.playground.play() == gst.STATE_CHANGE_FAILURE:
231
212
            self.currentState = gst.STATE_PLAYING
232
213
 
 
214
        return False
 
215
 
233
216
    ## gtk.HScale callbacks for self.slider
234
217
 
235
218
    def _sliderButtonPressCb(self, slider, unused_event):
280
263
            self._doSeek(seekvalue, gst.FORMAT_DEFAULT)
281
264
 
282
265
    def _seekTimeoutCb(self):
 
266
        gst.debug("requested_time %s" % gst.TIME_ARGS(self.requested_time))
283
267
        self.currentlySeeking = False
284
 
        if not self.current_time == self.requested_time:
 
268
        if (self.requested_time != gst.CLOCK_TIME_NONE) and (self.current_time != self.requested_time):
285
269
            self._doSeek(self.requested_time)
 
270
        return False
286
271
 
287
272
    def _doSeek(self, value, format=gst.FORMAT_TIME):
 
273
        gst.debug("%s , currentlySeeking:%r" % (gst.TIME_ARGS(value),
 
274
                                                self.currentlySeeking))
288
275
        if not self.currentlySeeking:
289
276
            self.currentlySeeking = True
290
 
            gobject.timeout_add(80, self._seekTimeoutCb)
291
 
            instance.PiTiVi.playground.seekInCurrent(value, format=format)
292
 
            self._newTime(value)
293
 
        if format == gst.FORMAT_TIME:
294
 
            self.requested_time = value
 
277
            if instance.PiTiVi.playground.seekInCurrent(value, format=format):
 
278
                gst.debug("seek succeeded, request_time = NONE")
 
279
                self.requested_time = gst.CLOCK_TIME_NONE
 
280
                gobject.timeout_add(80, self._seekTimeoutCb)
 
281
                self._newTime(value)
 
282
            else:
 
283
                self.currentlySeeking = False
 
284
        else:
 
285
            if format == gst.FORMAT_TIME:
 
286
                self.requested_time = value
295
287
 
296
288
    def _newTime(self, value, frame=-1):
297
289
        gst.info("value:%s, frame:%d" % (gst.TIME_ARGS(value), frame))
298
290
        self.current_time = value
299
291
        self.current_frame = frame
300
 
        self.timelabel.set_markup("<tt>%s / %s</tt>" % (time_to_string(value), time_to_string(instance.PiTiVi.playground.current.length)))
 
292
        self.timelabel.set_markup("<tt>%s / %s</tt>" % (time_to_string(value),
 
293
                                                        time_to_string(instance.PiTiVi.playground.current.length)))
301
294
        if not self.moving_slider:
302
295
            self.posadjust.set_value(float(value))
303
296
        return False
309
302
        gst.debug("duration : %s" % gst.TIME_ARGS(duration))
310
303
        gst.debug("playground.current.length : %s" % gst.TIME_ARGS(instance.PiTiVi.playground.current.length))
311
304
        self.posadjust.upper = float(duration)
312
 
        self.timelabel.set_markup("<tt>%s / %s</tt>" % (time_to_string(self.current_time), time_to_string(instance.PiTiVi.playground.current.length)))
 
305
        self.timelabel.set_markup("<tt>%s / %s</tt>" % (time_to_string(self.current_time),
 
306
                                                        time_to_string(instance.PiTiVi.playground.current.length)))
313
307
 
314
308
 
315
309
    def _backToDefaultCb(self):
353
347
        self._connectToProject(project)
354
348
 
355
349
    def _addTimelineToPlayground(self):
356
 
        instance.PiTiVi.playground.addPipeline(instance.PiTiVi.current.getBin())
357
 
 
 
350
        # remove old timeline before proceeding
 
351
        pg = instance.PiTiVi.playground
 
352
        timeline = pg.getTimeline()
 
353
        if timeline:
 
354
            pg.switchToDefault()
 
355
            pg.removePipeline(timeline)
 
356
        # add current timeline
 
357
        pg.addPipeline(instance.PiTiVi.current.getBin())
358
358
 
359
359
    ## Control gtk.Button callbacks
360
360
 
378
378
    def _forwardCb(self, unused_button):
379
379
        pass
380
380
 
381
 
 
382
381
    ## Playground callbacks
383
382
 
384
383
    def _playgroundPositionCb(self, unused_playground, unused_smartbin, pos):
386
385
 
387
386
    def _currentPlaygroundChangedCb(self, playground, smartbin):
388
387
        gst.log("smartbin:%s" % smartbin)
389
 
        if smartbin == playground.default:
 
388
 
 
389
        if not smartbin.seekable:
 
390
            # live sources or defaults, no duration/seeking available
390
391
            self.slider.set_sensitive(False)
391
392
            self.playpause_button.set_sensitive(False)
392
393
            self.next_button.set_sensitive(False)
397
398
                self._timelineDurationChangedSigId = (None, None)
398
399
        else:
399
400
            if isinstance(smartbin, SmartTimelineBin):
400
 
                gst.info("switching to Timeline, setting duration to %s" % (gst.TIME_ARGS(smartbin.project.timeline.videocomp.duration)))
 
401
                gst.info("switching to Timeline, setting duration to %s" %
 
402
                         (gst.TIME_ARGS(smartbin.project.timeline.videocomp.duration)))
401
403
                self.posadjust.upper = float(smartbin.project.timeline.videocomp.duration)
402
404
                # FIXME : we need to disconnect from this signal !
403
405
                sigid = smartbin.project.timeline.videocomp.connect("start-duration-changed",
405
407
                self._timelineDurationChangedSigId = (smartbin.project.timeline.videocomp,
406
408
                                                      sigid)
407
409
            else:
408
 
                self.posadjust.upper = float(smartbin.factory.length)
 
410
                self.posadjust.upper = float(smartbin.factory.duration)
409
411
                if not self._timelineDurationChangedSigId == (None, None):
410
412
                    obj, sigid = self._timelineDurationChangedSigId
411
413
                    obj.disconnect(sigid)
416
418
            self.next_button.set_sensitive(True)
417
419
            self.back_button.set_sensitive(True)
418
420
 
 
421
        if isinstance(smartbin, SmartTimelineBin):
 
422
            seti = smartbin.project.getSettings()
 
423
            dar = float(seti.videowidth * seti.videopar.num) / float(seti.videoheight * seti.videopar.denom)
 
424
        elif hasattr(smartbin, 'factory'):
 
425
            dar = smartbin.factory.video_info_stream.dar
 
426
        else:
 
427
            dar = smartbin.width / smartbin.height
 
428
        self.setDisplayAspectRatio(dar)
 
429
 
419
430
    def _currentStateCb(self, unused_playground, state):
420
431
        gst.info("current state changed : %s" % state)
421
432
        if state == int(gst.STATE_PLAYING):
430
441
            dav = self.drawingarea.videosink
431
442
            gst.log('%s' % dav)
432
443
            if dav and dav.realsink and dav.realsink == message.src:
433
 
                self.drawingarea.can_set_xid = True
434
 
                if self.drawingarea.isexposed:
435
 
                    self.drawingarea.set_xwindow_id()
 
444
                self.drawingarea.set_xwindow_id()
436
445
 
437
446
 
438
447
class ViewerWidget(gtk.DrawingArea):
446
455
        gtk.DrawingArea.__init__(self)
447
456
        self.videosink = None
448
457
        self.have_set_xid = False
449
 
        self.can_set_xid = False
450
458
        self.unset_flags(gtk.DOUBLE_BUFFERED)
451
459
        self.unset_flags(gtk.SENSITIVE)
452
 
        self.isexposed = False
453
 
 
454
 
    def do_expose_event(self, unused_event):
455
 
        """ 'expose-event' override """
456
 
        gst.log("expose have_set_xid:%d can_set_xid:%d" % (self.have_set_xid, self.can_set_xid))
457
 
        self.isexposed = True
458
 
        if self.videosink:
459
 
            if not self.have_set_xid and self.can_set_xid:
460
 
                self.set_xwindow_id()
461
 
            elif self.have_set_xid:
462
 
                self.videosink.expose()
463
 
        return False
464
460
 
465
461
    def set_xwindow_id(self):
466
462
        """ set the widget's XID on the configured videosink. """
512
508
            self.set_image(gtk.image_new_from_stock(gtk.STOCK_MEDIA_PAUSE, gtk.ICON_SIZE_BUTTON))
513
509
            self.playing = True
514
510
 
515
 
 
516