Commit fbcf6e94 authored by Edward Hervey's avatar Edward Hervey

* pitivi/bin.py:

Moved getRealVideoSink(), record() and stopRecording() methods up from
SmartTimelineBin to SmartBin.
* pitivi/playground.py:
Documentation cleanup.
Make relevant functions return False for failures.
Added _handleError() and _handleWarning() methods for analyzing the
error/warning information and creating user-friendly messages.
* pitivi/utils.py:
New function bin_contains() in order to figure out if an element is in
a bin recursively.
* pitivi/ui/mainwindow.py:
Only display one ErrorDialogBox at a time
* pitivi/ui/viewer.py:
Improve aspect ratio handling


git-svn-id: svn+ssh://svn.gnome.org/svn/pitivi/trunk@751 d3729300-e425-0410-8a4c-d956edccc248
parent 9f33f1ca
2006-04-20 Edward Hervey <edward@fluendo.com>
* pitivi/bin.py:
Moved getRealVideoSink(), record() and stopRecording() methods up from
SmartTimelineBin to SmartBin.
* pitivi/playground.py:
Documentation cleanup.
Make relevant functions return False for failures.
Added _handleError() and _handleWarning() methods for analyzing the
error/warning information and creating user-friendly messages.
* pitivi/utils.py:
New function bin_contains() in order to figure out if an element is in
a bin recursively.
* pitivi/ui/mainwindow.py:
Only display one ErrorDialogBox at a time
* pitivi/ui/viewer.py:
Improve aspect ratio handling
2006-04-10 Edward Hervey <edward@fluendo.com>
* pitivi/playground.py:
......
......@@ -59,6 +59,8 @@ class SmartBin(gst.Pipeline):
self._connectSource()
self.asinkthread = None
self.vsinkthread = None
self.encthread = None
self.tmpasink = None
def _addSource(self):
""" add the source to self """
......@@ -69,7 +71,10 @@ class SmartBin(gst.Pipeline):
raise NotImplementedError
def setAudioSinkThread(self, asinkthread):
""" set the audio sink thread """
"""
Set the audio sink thread.
Returns False if there was a problem.
"""
self.debug("asinkthread : %s" % asinkthread)
res, state, pending = self.get_state(0)
if state == gst.STATE_PLAYING:
......@@ -85,7 +90,10 @@ class SmartBin(gst.Pipeline):
return True
def setVideoSinkThread(self, vsinkthread):
""" set the video sink thread """
"""
Set the video sink thread.
Returns False if there was a problem.
"""
self.debug("vsinkthread : %s" % vsinkthread)
res , state , pending = self.get_state(0)
if state == gst.STATE_PLAYING:
......@@ -106,7 +114,10 @@ class SmartBin(gst.Pipeline):
return True
def removeAudioSinkThread(self):
""" remove the audio sink thread """
"""
Remove the audio sink thread.
Returns False if there was a problem.
"""
self.debug("asinkthread : %s" % self.asinkthread)
result, state, pending = self.get_state(0)
if state in [gst.STATE_PAUSED, gst.STATE_PLAYING]:
......@@ -121,7 +132,10 @@ class SmartBin(gst.Pipeline):
return True
def removeVideoSinkThread(self):
""" remove the videos sink thread """
"""
Remove the videos sink thread.
Returns False if there was a problem.
"""
self.debug("vsinkthread : %s" % self.vsinkthread)
result, state, pending = self.get_state(0)
if state in [gst.STATE_PAUSED, gst.STATE_PLAYING]:
......@@ -135,104 +149,15 @@ class SmartBin(gst.Pipeline):
self.vsinkthread = None
return True
class SmartFileBin(SmartBin):
"""
SmartBin for file sources from FileSourceFactory
"""
def __init__(self, factory):
gst.log("new SmartFileBin for factory:%s, audio:%s, video:%s" % (factory, factory.is_audio, factory.is_video))
self.factory = factory
self.has_video = factory.is_video
self.has_audio = factory.is_audio
self.length = factory.length
if self.factory.video_info:
struct = self.factory.video_info[0]
self.height = struct["height"]
self.width = struct["width"]
self.source = self.factory.makeBin()
SmartBin.__init__(self, "smartfilebin-" + factory.name,
displayname=factory.displayname)
def _addSource(self):
self.add(self.source)
def _connectSource(self):
self.source.connect("pad-added", self._binNewDecodedPadCb)
self.source.connect("pad-removed", self._binRemovedDecodedPadCb)
def _binNewDecodedPadCb(self, unused_bin, pad):
# connect to good tee
self.debug("SmartFileBin's source has a new pad: %s %s" % (pad , pad.get_caps().to_string()))
if pad.get_caps().to_string().startswith("audio"):
pad.link(self.atee.get_pad("sink"))
elif pad.get_caps().to_string().startswith("video"):
pad.link(self.vtee.get_pad("sink"))
def _binRemovedDecodedPadCb(self, unused_bin, pad):
if pad.get_caps().to_string().startswith("audio"):
pad.unlink(self.atee.get_pad("sink"))
elif pad.get_caps().to_string().startswith("video"):
pad.unlink(self.vtee.get_pad("sink"))
def do_destroy(self):
self.info("destroyed")
self.factory.binIsDestroyed(self.source)
class SmartTimelineBin(SmartBin):
"""
SmartBin for GnlTimeline
"""
def __init__(self, project):
gst.log("new SmartTimelineBin for project %s" % project)
self.project = project
# TODO : change this to use the project settings
self.has_video = True
self.has_audio = True
self.width = project.settings.videowidth
self.height = project.settings.videoheight
self.log("source is %s" % project.timeline.timeline)
self.source = project.timeline.timeline
self.project.settings.connect("settings-changed", self._settingsChangedCb)
project.timeline.videocomp.connect("start-duration-changed", self._startDurationChangedCb)
self.length = project.timeline.videocomp.duration
self.encthread = None
self.tmpasink = None
SmartBin.__init__(self, "project-" + project.name,
displayname = "Project: " + project.name)
def _addSource(self):
self.add(self.source)
def _connectSource(self):
self.source.connect("pad-added", self._newPadCb)
self.source.connect("pad-removed", self._removedPadCb)
def _settingsChangedCb(self, settings):
self.width = settings.videowidth
self.height = settings.videoheight
def _newPadCb(self, unused_source, pad):
if pad.get_name() == "asrc":
pad.link(self.atee.get_pad("sink"))
elif pad.get_name() == "vsrc":
pad.link(self.vtee.get_pad("sink"))
def _removedPadCb(self, unused_source, pad):
self.debug("pad %r went away" % pad)
if pad.get_name() == "asrc":
pad.unlink(self.atee.get_pad("sink"))
elif pad.get_name() == "vsrc":
pad.unlink(self.vtee.get_pad("sink"))
def getRealVideoSink(self):
""" returns the real video sink element or None """
if not self.vsinkthread:
return None
return self.vsinkthread.videosink.realsink
def record(self, uri, settings=None):
"""
render the timeline to the given uri.
Render the SmartBin to the given uri.
Returns : True if the encoding process could be started properly, False otherwise."""
self.debug("setting to READY")
if self.set_state(gst.STATE_READY) == gst.STATE_CHANGE_FAILURE:
......@@ -247,6 +172,9 @@ class SmartTimelineBin(SmartBin):
self.debug("creating and adding encoding thread")
self.encthread = self._makeEncThread(uri, settings)
if not self.encthread:
gst.warning("Couldn't create encoding thread")
return False
self.add(self.encthread)
self.debug("encoding thread added")
......@@ -280,27 +208,22 @@ class SmartTimelineBin(SmartBin):
apad = self.encthread.get_pad("vsink")
apad.get_peer().unlink(apad)
apad = self.encthread.get_pad("asink")
apad.get_peer().unlink(apad)
#self.vtee.unlink(self.encthread)
#self.atee.unlink(self.encthread)
apad.get_peer().unlink(apad)
self.remove(self.encthread)
del self.encthread
self.encthread= None
self.encthread = None
self.setAudioSinkThread(self.tmpasink)
self.tmpasink = None
self.getRealVideoSink().set_property("sync", True)
def getRealVideoSink(self):
""" returns the real video sink element or None """
if not self.vsinkthread:
return None
return self.vsinkthread.videosink.realsink
def _makeEncThread(self, uri, settings=None):
# TODO : verify if encoders take video/x-raw-yuv and audio/x-raw-int
if not settings:
settings = self.project.settings
if isinstance(self, SmartTimelineBin):
settings = self.project.settings
else:
return None
ainq = gst.element_factory_make("queue", "ainq")
aoutq = gst.element_factory_make("queue", "aoutq")
vinq = gst.element_factory_make("queue", "vinq")
......@@ -346,6 +269,99 @@ class SmartTimelineBin(SmartBin):
return thread
class SmartFileBin(SmartBin):
"""
SmartBin for file sources from FileSourceFactory
"""
def __init__(self, factory):
gst.log("new SmartFileBin for factory:%s, audio:%s, video:%s" % (factory, factory.is_audio, factory.is_video))
self.factory = factory
self.has_video = factory.is_video
self.has_audio = factory.is_audio
self.length = factory.length
if self.factory.video_info:
struct = self.factory.video_info[0]
self.height = struct["height"]
self.width = struct["width"]
self.source = self.factory.makeBin()
SmartBin.__init__(self, "smartfilebin-" + factory.name,
displayname=factory.displayname)
def _addSource(self):
self.add(self.source)
def _connectSource(self):
self.source.connect("pad-added", self._binNewDecodedPadCb)
self.source.connect("pad-removed", self._binRemovedDecodedPadCb)
def _binNewDecodedPadCb(self, unused_bin, pad):
# connect to good tee
self.debug("SmartFileBin's source has a new pad: %s %s" % (pad , pad.get_caps().to_string()))
if pad.get_caps().to_string().startswith("audio"):
pad.link(self.atee.get_pad("sink"))
elif pad.get_caps().to_string().startswith("video"):
pad.link(self.vtee.get_pad("sink"))
def _binRemovedDecodedPadCb(self, unused_bin, pad):
if pad.get_caps().to_string().startswith("audio"):
pad.unlink(self.atee.get_pad("sink"))
elif pad.get_caps().to_string().startswith("video"):
pad.unlink(self.vtee.get_pad("sink"))
def do_destroy(self):
self.info("destroyed")
self.factory.binIsDestroyed(self.source)
class SmartTimelineBin(SmartBin):
"""
SmartBin for GnlTimeline
"""
def __init__(self, project):
gst.log("new SmartTimelineBin for project %s" % project)
self.project = project
# TODO : change this to use the project settings
self.has_video = True
self.has_audio = True
self.width = project.settings.videowidth
self.height = project.settings.videoheight
self.log("source is %s" % project.timeline.timeline)
self.source = project.timeline.timeline
self.project.settings.connect("settings-changed", self._settingsChangedCb)
project.timeline.videocomp.connect("start-duration-changed", self._startDurationChangedCb)
self.length = project.timeline.videocomp.duration
SmartBin.__init__(self, "project-" + project.name,
displayname = "Project: " + project.name)
def _addSource(self):
self.add(self.source)
def _connectSource(self):
self.source.connect("pad-added", self._newPadCb)
self.source.connect("pad-removed", self._removedPadCb)
def _settingsChangedCb(self, settings):
self.width = settings.videowidth
self.height = settings.videoheight
def _newPadCb(self, unused_source, pad):
if pad.get_name() == "asrc":
pad.link(self.atee.get_pad("sink"))
elif pad.get_name() == "vsrc":
pad.link(self.vtee.get_pad("sink"))
def _removedPadCb(self, unused_source, pad):
self.debug("pad %r went away" % pad)
if pad.get_name() == "asrc":
pad.unlink(self.atee.get_pad("sink"))
elif pad.get_name() == "vsrc":
pad.unlink(self.vtee.get_pad("sink"))
def _startDurationChangedCb(self, unused_videocomp, start, duration):
self.info("smart timeline bin: start duration changed %d %d" %( start, duration ))
self.length = duration
......
......@@ -21,25 +21,30 @@
# Boston, MA 02111-1307, USA.
"""
Where all gstreamer pipelines play
Where all gstreamer pipelines play.
"""
import gobject
import gst
from bin import SmartBin, SmartDefaultBin, SmartFileBin
from utils import bin_contains
class PlayGround(gobject.GObject):
"""
Holds all the applications pipelines in a GstThread.
They all share the same (audio,video) sink threads.
Holds all the applications pipelines.
Multimedia sinks can be shared amongst the various pipelines, to offer
seamless pipeline switching.
Only one pipeline uses those sinks at any given time, but other pipelines
can be in a PLAYED state (because they can be encoding).
Only SmartBin can be added to the PlayGround
Only SmartBin can be added to the PlayGround.
Signals:
current-changed : There's a new bin playing
current-state : The state of the current bin has changed
bin-added : The given bin was added to the playground
bin-removed : The given bin was removed from the playground
error : An error was seen (two strings : reason, details)
"""
__gsignals__ = {
......@@ -84,47 +89,58 @@ class PlayGround(gobject.GObject):
self.cur_state_signal = None
self.cur_eos_signal = None
self.switchToDefault()
self.state = gst.STATE_READY
self.current.set_state(self.state)
#self.playthread.set_state(self.state)
if self.switchToDefault():
if self.current.set_state(self.state) == gst.STATE_CHANGE_FAILURE:
gst.warning("Couldn't set default bin to READY")
def addPipeline(self, pipeline):
""" add a pipeline to the playground """
"""
Adds the given pipeline to the playground.
Returns True if the pipeline was added to the playground.
"""
gst.debug("pipeline : %s" % pipeline)
if not isinstance(pipeline, SmartBin):
return
return False
self.pipelines.append(pipeline)
bus = pipeline.get_bus()
bus.add_signal_watch()
bus.connect("message", self._busMessageCb, pipeline)
self.emit("bin-added", pipeline)
return True
def removePipeline(self, pipeline):
""" remove a pipeline from the playground """
"""
Removes the given pipeline from the playground.
Return True if everything went well.
"""
gst.debug("pipeline : %s" % pipeline)
if not pipeline in self.pipelines:
return
return True
bus = pipeline.get_bus()
bus.remove_signal_watch()
pipeline.set_state(gst.STATE_READY)
if pipeline.set_state(gst.STATE_READY) == gst.STATE_CHANGE_FAILURE:
return False
if self.current == pipeline:
self.switchToDefault()
if not self.switchToDefault():
return False
self.pipelines.remove(pipeline)
self.emit("bin-removed", pipeline)
return True
def switchToPipeline(self, pipeline):
"""
switch to the given pipeline for play output
Switch to the given pipeline for play output.
Returns True if the switch was possible.
"""
pipeline.debug("BEGINNING")
if self.current == pipeline:
return
return True
if not pipeline in self.pipelines and not pipeline == self.default:
return
return True
if self.current:
self.current.info("setting to READY")
self.current.set_state(gst.STATE_READY)
......@@ -139,23 +155,25 @@ class PlayGround(gobject.GObject):
if self.current == self.tempsmartbin:
self.tempsmartbin = None
self.current = None
if pipeline.has_video and self.vsinkthread:
if not pipeline.setVideoSinkThread(self.vsinkthread):
return False
if pipeline.has_audio and self.asinkthread:
if not pipeline.setAudioSinkThread(self.asinkthread):
return False
if not pipeline == self.default:
pipeline.log("Setting the new pipeline to PAUSED so it prerolls")
if pipeline.set_state(gst.STATE_PAUSED) == gst.STATE_CHANGE_FAILURE:
return False
self.current = pipeline
if self.current.has_video and self.vsinkthread:
#self.vsinkthread.set_state(gst.STATE_READY)
self.current.setVideoSinkThread(self.vsinkthread)
if self.current.has_audio and self.asinkthread:
#self.asinkthread.set_state(gst.STATE_READY)
self.current.setAudioSinkThread(self.asinkthread)
self.current.log("Setting the new pipeline to PAUSED so it prerolls")
self.current.set_state(gst.STATE_PAUSED)
self.emit("current-changed", self.current)
pipeline.debug("END")
def switchToDefault(self):
""" switch to the default pipeline """
gst.debug("switching to default")
self.switchToPipeline(self.default)
return self.switchToPipeline(self.default)
def setVideoSinkThread(self, vsinkthread):
""" sets the video sink thread """
......@@ -178,7 +196,10 @@ class PlayGround(gobject.GObject):
self.current.setAudioSinkThread(self.asinkthread)
def _playTemporaryBin(self, tempbin):
""" temporarely play a smartbin """
"""
Temporarely play a smartbin.
Return False if there was a problem.
"""
gst.debug("BEGINNING tempbin : %s" % tempbin)
self.pause()
self.addPipeline(tempbin)
......@@ -186,26 +207,34 @@ class PlayGround(gobject.GObject):
if self.tempsmartbin:
self.removePipeline(self.tempsmartbin)
self.tempsmartbin = tempbin
self.play()
if self.play() == gst.STATE_CHANGE_FAILURE:
return False
gst.debug("END tempbin : %s" % tempbin)
return True
def playTemporaryFilesourcefactory(self, factory):
""" temporarely play a FileSourceFactory """
"""
Temporarely play a FileSourceFactory.
Returns False if there was a problem.
"""
gst.debug("factory : %s" % factory)
if isinstance(self.current, SmartFileBin) and self.current.factory == factory:
gst.info("Already playing factory : %s" % factory)
return
return True
tempbin = SmartFileBin(factory)
self._playTemporaryBin(tempbin)
return self._playTemporaryBin(tempbin)
def seekInCurrent(self, value, format=gst.FORMAT_TIME):
""" seek to the given position in the current playing bin """
"""
Seek to the given position in the current playing bin.
Returns True if the seek was possible.
"""
if format == gst.FORMAT_TIME:
gst.debug("value : %s" % gst.TIME_ARGS (value))
else:
gst.debug("value : %d , format:%d" % (value, format))
if not self.current:
return
return False
target = self.current
# actual seeking
......@@ -214,10 +243,9 @@ class PlayGround(gobject.GObject):
gst.SEEK_TYPE_NONE, -1)
if not res:
gst.warning ("Seeking in current failed !");
else:
gst.debug("Seeking to %s succeeded" % gst.TIME_ARGS (value))
# bring back current to previous state
return False
gst.debug("Seeking to %s succeeded" % gst.TIME_ARGS (value))
return True
def shutdown(self):
""" shutdown the playground and all pipelines """
......@@ -240,38 +268,82 @@ class PlayGround(gobject.GObject):
if message.src == self.current:
if pending == gst.STATE_VOID_PENDING:
self.emit("current-state", newstate)
elif message.type in [ gst.MESSAGE_ERROR, gst.MESSAGE_WARNING ]:
self.current.warning("%s" % message.structure.to_string())
if message.type == gst.MESSAGE_ERROR:
error, detail = message.parse_error()
self.emit("error", str(error), str(detail))
elif message.type == gst.MESSAGE_ERROR:
error, detail = message.parse_error()
self._handleError(error, detail, message.src)
elif message.type == gst.MESSAGE_WARNING:
error, detail = message.parse_warning()
self._handleError(error, detail, message.src)
#
# Error handling
#
def _handleError(self, gerror, detail, source):
"""
Uses the information from the Gerror, detail and source to
create meaningful error messages for the User Interface.
"""
gst.warning("gerror:%s , detail:%s , source:%s" % (gerror, detail, source))
gst.warning("GError : code:%s, domain:%s (%s), message:%s" % (gerror.code, gerror.domain,
type(gerror.domain), gerror.message))
if bin_contains(self.vsinkthread, source):
if gerror.domain == gst.RESOURCE_ERROR and gerror.code == gst.RESOURCE_ERROR_BUSY:
self.emit("error", "Video output is busy",
"Please check that your video output device isn't already used by another application")
else:
self.emit("error", "Video output problem",
"There is a problem with your video output device")
elif bin_contains(self.asinkthread, source):
if gerror.domain == gst.RESOURCE_ERROR and gerror.code == gst.RESOURCE_ERROR_BUSY:
self.emit("error", "Audio output device is busy",
"Please check that your audio output device isn't already used by another application.")
else:
self.emit("error", "Audio output problem"<
"There is a problem with your audio output device")
else:
self.emit("error", detail, gerror.message)
def _handleWarning(self, error, detail, source):
"""
Uses the information from the Gerror, detail and source to
create meaningful warning messages for the User Interface.
"""
gst.warning("gerror:%s , detail:%s , source:%s" % (gerror, detail, source))
gst.warning("GError : code:%s, domain:%s, message:%s" % (gerror.code, gerror.domain, gerror.message))
#
# playing proxy functions
#
def play(self):
""" play the current pipeline """
"""
Set the current Pipeline to Play.
Returns the state transition result (gst.StateChangeResult).
"""
gst.debug("play")
if not self.current or not self.asinkthread or not self.vsinkthread:
gst.warning("returning ???")
return
return gst.STATE_CHANGE_FAILURE
self.state = gst.STATE_PLAYING
ret = self.current.set_state(self.state)
gst.debug("change state returned %s" % ret)
return ret
## gst.debug("set_state() done, getting state now")
## value = self.current.get_state(None)
## gst.debug("got_state : %s" % str(value))
def pause(self):
""" pause the current pipeline """
"""
Set the current Pipeline to Pause.
Returns the state change transition result (gst.StateChangeResult).
"""
gst.debug("pause")
if not self.current or self.current == self.default:
return
return gst.STATE_CHANGE_SUCCESS
self.state = gst.STATE_PAUSED
self.current.set_state(self.state)
return self.current.set_state(self.state)
def fast_forward(self):
""" fast forward the current pipeline """
......
......@@ -51,6 +51,7 @@ class PitiviMainWindow(gtk.Window):
self._createUi()
self.isFullScreen = False
self.errorDialogBox = None
instance.PiTiVi.connect("new-project", self._newProjectCb)
instance.PiTiVi.connect("closing-project", self._closingProjectCb)
......@@ -153,17 +154,20 @@ class PitiviMainWindow(gtk.Window):
def _errorMessageResponseCb(self, dialogbox, unused_response):
dialogbox.hide()
dialogbox.destroy()
self.errorDialogBox = None
def _playGroundErrorCb(self, unused_playground, error, detail):
dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL,
gtk.MESSAGE_ERROR,
gtk.BUTTONS_OK,
None)
dialog.set_markup("<b>%s</b>" % error)
dialog.connect("response", self._errorMessageResponseCb)
if self.errorDialogBox:
return
self.errorDialogBox = gtk.MessageDialog(None, gtk.DIALOG_MODAL,
gtk.MESSAGE_ERROR,
gtk.BUTTONS_OK,
None)
self.errorDialogBox.set_markup("<b>%s</b>" % error)
self.errorDialogBox.connect("response", self._errorMessageResponseCb)
if detail:
dialog.format_secondary_text(detail)
dialog.show()
self.errorDialogBox.format_secondary_text(detail)
self.errorDialogBox.show()
## UI Callbacks
......
......@@ -175,14 +175,18 @@ class PitiviViewer(gtk.VBox):
try:
width = caps[0]["width"]
height = caps[0]["height"]
except:
gst.warning("Something went wrong when getting the video sink aspect ratio")
else:
try:
par = caps[0]["pixel-aspect-ratio"]
self.aframe.set_property("ratio", float(width * par.num) / float(par.denom * height))
except:
# set aspect ratio
self.aframe.set_property("ratio", float(width) / float(height))
except:
gst.warning("Something went wrong when getting the video sink aspect ratio")
gst.warning("setting aspectratio to %f" % (float(width) / float(height)))
else:
self.aframe.set_property("ratio", float(width * par.num) / float(par.denom * height))
gst.warning("setting aspectratio to %f" % (float(width * par.num) / float(par.denom * height)))
def _createSinkThreads(self):
""" Creates the sink threads for the playground """
......@@ -193,14 +197,12 @@ class PitiviViewer(gtk.VBox):
vqueue = gst.element_factory_make('queue')
cspace = gst.element_factory_make('ffmpegcolorspace')
vsinkthread.add(self.videosink, vqueue, cspace)
cspace.link(vqueue)
vqueue.link(self.videosink)
cspace.link(vqueue)
vsinkthread.videosink = self.videosink
vsinkthread.add_pad(gst.GhostPad("sink", cspace.get_pad('sink')))
if self.videosink.realsink:
self.videosink.info("Setting callback on 'notify::caps'")
self.videosink.realsink.get_pad("sink").connect("notify::caps", self._videosinkCapsNotifyCb)
vsinkthread.get_pad("sink").connect("notify::caps", self._videosinkCapsNotifyCb)
self.drawingarea.videosink = self.videosink
self.videosink.set_xwindow_id(self.drawingarea.window.xid)
......@@ -424,12 +426,6 @@ class PitiviViewer(gtk.VBox):
## Playground callbacks
def _currentPlaygroundChangedCb(self, playground, smartbin):
if smartbin.width and smartbin.height:
if isinstance(smartbin, SmartFileBin) and smartbin.factory.video_info_stream:
ratio = float(smartbin.factory.video_info_stream.dar.denom) / float(smartbin.factory.video_info_stream.dar.num)
self.aframe.set_property("ratio", ratio)
else:
self.aframe.set_property("ratio", 4.0/3.0)
if not smartbin == playground.default:
if isinstance(smartbin, SmartTimelineBin):
gst.info("switching to Timeline, setting duration to %s" % (gst.TIME_ARGS(smartbin.project.timeline.videocomp.duration)))
......
......@@ -22,7 +22,22 @@
# set of utility functions