def get_timing_controller(pad, field): cs = GstController.InterpolationControlSource() cs.set_property('mode', GstController.InterpolationMode.LINEAR) cb = GstController.DirectControlBinding.new_absolute(pad, field, cs) pad.add_control_binding(cb) return cs
def showKeyframes(self, element, propname, isDefault=False): binding = element.get_control_binding(propname.name) if not binding: source = GstController.InterpolationControlSource() source.props.mode = GstController.InterpolationMode.LINEAR if not (element.set_control_source(source, propname.name, "direct")): print("There was something like a problem captain") return binding = element.get_control_binding(propname.name) self.binding = binding self.prop = propname self.keyframedElement = element self.source = self.binding.props.control_source self.source.connect("value-added", self._valueAddedCb) self.source.connect("value-removed", self._valueRemovedCb) self.source.connect("value-changed", self._valueChanged) if isDefault: self.default_prop = propname self.default_element = element self.keyframesVisible = True self.updateKeyframes()
def __set_control_bindings(self): adding_kfs = not self.__source_uses_keyframes() if adding_kfs: self.app.action_log.begin( "Transformation properties keyframes activate", toplevel=True) for prop in ["posx", "posy", "width", "height"]: binding = self.source.get_control_binding(prop) if not binding: control_source = GstController.InterpolationControlSource() control_source.props.mode = GstController.InterpolationMode.LINEAR self.__own_bindings_change = True self.source.set_control_source(control_source, prop, "direct-absolute") self.__own_bindings_change = False self.__set_default_keyframes_values(control_source, prop) binding = self.source.get_control_binding(prop) self.__control_bindings[prop] = binding if adding_kfs: self.app.action_log.commit( "Transformation properties keyframes activate")
def test_control_source_value_changed(self): uri = common.get_sample_uri("tears_of_steel.webm") asset = GES.UriClipAsset.request_sync(uri) clip = asset.extract() self.layer.add_clip(clip) source = clip.get_children(False)[1] self.assertTrue(isinstance(source, GES.VideoUriSource)) control_source = GstController.InterpolationControlSource() control_source.props.mode = GstController.InterpolationMode.LINEAR self.assert_control_source_values(control_source, [], []) source.set_control_source(control_source, "alpha", "direct") self.assert_control_source_values(control_source, [1, 1], [0, 2003000000]) self.assertTrue(control_source.set(Gst.SECOND * 0.5, 0.2)) self.assert_control_source_values(control_source, [1, 0.2, 1], [0, Gst.SECOND * 0.5, 2003000000]) with self.action_log.started("keyframe changed"): self.assertTrue(control_source.set(Gst.SECOND * 0.5, 0.9)) self.assert_control_source_values(control_source, [1, 0.9, 1], [0, Gst.SECOND * 0.5, 2003000000]) self.action_log.undo() self.assert_control_source_values(control_source, [1, 0.2, 1], [0, Gst.SECOND * 0.5, 2003000000]) self.action_log.redo() self.assert_control_source_values(control_source, [1, 0.9, 1], [0, Gst.SECOND * 0.5, 2003000000])
def _get_control_source(self, elem, prop='alpha'): ctrl = elem.get_control_binding(prop) if ctrl: return ctrl.get_property('control_source') cs = GstController.InterpolationControlSource() cs.set_property('mode', GstController.InterpolationMode.LINEAR) cb = GstController.DirectControlBinding.new(elem, prop, cs) elem.add_control_binding(cb) return cs
def __createControlBinding(self, element): if self.__controlledProperty: element.connect("control-binding-added", self.__controlBindingAddedCb) binding = \ element.get_control_binding(self.__controlledProperty.name) if binding: self.__createKeyframeCurve(binding) return source = GstController.InterpolationControlSource() source.props.mode = GstController.InterpolationMode.LINEAR element.set_control_source(source, self.__controlledProperty.name, "direct")
def __createControlBinding(self, element): """Creates the required ControlBinding and keyframes.""" if self.__controlledProperty: element.connect("control-binding-added", self.__controlBindingAddedCb) binding = \ element.get_control_binding(self.__controlledProperty.name) if binding: self.__ensure_keyframes(binding) return source = GstController.InterpolationControlSource() source.props.mode = GstController.InterpolationMode.LINEAR element.set_control_source(source, self.__controlledProperty.name, "direct")
def testControlSourceValueAdded(self): uri = common.get_sample_uri("tears_of_steel.webm") asset = GES.UriClipAsset.request_sync(uri) clip = asset.extract() self.layer.add_clip(clip) source = clip.get_children(False)[1] self.assertTrue(isinstance(source, GES.VideoUriSource)) control_source = GstController.InterpolationControlSource() control_source.props.mode = GstController.InterpolationMode.LINEAR source.set_control_source(control_source, "alpha", "direct") with self.action_log.started("keyframe added"): self.assertTrue(control_source.set(Gst.SECOND * 0.5, 0.2)) self.assertEqual(1, len(control_source.get_all())) self.action_log.undo() self.assertEqual(0, len(control_source.get_all())) self.action_log.redo() keyframes = control_source.get_all() self.assertEqual(1, len(keyframes)) self.assertEqual(Gst.SECOND * 0.5, keyframes[0].timestamp) self.assertEqual(0.2, keyframes[0].value)
def __init__(self): print("Init GStreamer...") # This is used to keep track of time between callbacks. self.player_timer = Timer() # Store the track object that is currently playing self.loaded_track = None # This is used to keep note of what state of playing we should be in self.play_state = 0 # 0 is stopped, 1 is playing, 2 is paused # Initiate GSteamer Gst.init([]) self.mainloop = GLib.MainLoop() # Populate list of output devices with defaults outputs = {} devices = [ "PulseAudio", "ALSA", "JACK", ] if tauon.snap_mode: # Snap permissions don't support these by default devices.remove("JACK") devices.remove("ALSA") # Get list of available audio device self.dm = Gst.DeviceMonitor() self.dm.start() for device in self.dm.get_devices(): if device.get_device_class() == "Audio/Sink": element = device.create_element(None) type_name = element.get_factory().get_name() if hasattr(element.props, "device"): device_name = element.props.device display_name = device.get_display_name() # This is used by the UI to present list of options to the user in audio settings outputs[display_name] = (type_name, device_name) devices.append(display_name) # dm.stop() # Causes a segfault sometimes pctl.gst_outputs = outputs pctl.gst_devices = devices # Create main "playbin" pipeline for playback self.playbin = Gst.ElementFactory.make("playbin", "player") # Create custom output bin from user preferences if not prefs.gst_use_custom_output: prefs.gst_output = prefs.gen_gst_out() self._output = Gst.parse_bin_from_description( prefs.gst_output, ghost_unlinked_pads=True) # Create a bin for the audio pipeline self._sink = Gst.ElementFactory.make("bin", "sink") self._sink.add(self._output) # Spectrum ------------------------- # Cant seem to figure out how to process these magnitudes in a way that looks good # self.spectrum = Gst.ElementFactory.make("spectrum", "spectrum") # self.spectrum.set_property('bands', 20) # self.spectrum.set_property('interval', 10000000) # self.spectrum.set_property('threshold', -100) # self.spectrum.set_property('post-messages', True) # self.spectrum.set_property('message-magnitude', True) # # self.playbin.set_property('audio-filter', self.spectrum) # # ------------------------------------ # # Level Meter ------------------------- self.level = Gst.ElementFactory.make("level", "level") self.level.set_property('interval', 20000000) self.playbin.set_property('audio-filter', self.level) # # ------------------------------------ self._eq = Gst.ElementFactory.make("equalizer-nbands", "eq") self._vol = Gst.ElementFactory.make("volume", "volume") self._sink.add(self._eq) self._sink.add(self._vol) self._eq.link(self._vol) self._vol.link(self._output) self._eq.set_property("num-bands", 10 + 2) # Set the equalizer based on user preferences # Using workaround for "inverted slider" bug. # Thanks to Lollypop and Clementine for discovering this. # Ref https://github.com/Taiko2k/TauonMusicBox/issues/414 for i in range(10 + 2): band = self._eq.get_child_by_index(i) band.set_property("freq", 0) band.set_property("bandwidth", 0) band.set_property("gain", 0) self.set_eq() # if prefs.use_eq: # self._eq.set_property("band" + str(i), level * -1) # else: # self._eq.set_property("band" + str(i), 0.0) # Set up sink pad for the intermediate bin via the # first element (volume) ghost = Gst.GhostPad.new("sink", self._eq.get_static_pad("sink")) self._sink.add_pad(ghost) # Connect the playback bin to to the intermediate bin sink pad self.playbin.set_property("audio-sink", self._sink) # The pipeline should look something like this - # (player) -> [(eq) -> (volume) -> (output)] # Create controller for pause/resume volume fading self.c_source = GstController.InterpolationControlSource() self.c_source.set_property('mode', GstController.InterpolationMode.LINEAR) self.c_binding = GstController.DirectControlBinding.new( self._vol, "volume", self.c_source) self._vol.add_control_binding(self.c_binding) # Set callback for the main callback loop GLib.timeout_add(50, self.main_callback) self.playbin.connect("about-to-finish", self.about_to_finish) # Not used # Setup bus and select what types of messages we want to listen for bus = self.playbin.get_bus() bus.add_signal_watch() bus.connect('message::element', self.on_message) bus.connect('message::buffering', self.on_message) bus.connect('message::error', self.on_message) bus.connect('message::tag', self.on_message) bus.connect('message::warning', self.on_message) # bus.connect('message::eos', self.on_message) # Variables used with network downloading self.temp_id = "a" self.url = None self.dl_ready = True self.using_cache = False self.temp_path = "" # Full path + filename # self.level_train = [] self.seek_timer = Timer() self.seek_timer.force_set(10) self.buffering = False # Other self.end_timer = Timer() # Start GLib mainloop self.mainloop.run()
def __add_widgets(self, values, with_reset_button): """Prepares a Gtk.Grid containing the property widgets of an element. Each property is on a separate row. A row is typically a label followed by the widget and a reset button. If there are no properties, returns a "No properties" label. """ self.properties.clear() self.__bindings_by_keyframe_button = {} self.__widgets_by_keyframe_button = {} is_effect = isinstance(self.element, GES.Effect) if is_effect: props = [ prop for prop in self.element.list_children_properties() if prop.name not in self.ignore ] else: props = [ prop for prop in GObject.list_properties(self.element) if prop.name not in self.ignore ] if not props: widget = Gtk.Label(label=_("No properties.")) self.pack_start(widget, expand=False, fill=False, padding=0) widget.show() return grid = Gtk.Grid() grid.props.row_spacing = SPACING grid.props.column_spacing = SPACING grid.props.border_width = SPACING for y, prop in enumerate(props): # We do not know how to work with GObjects, so blacklist # them to avoid noise in the UI if (not prop.flags & GObject.PARAM_WRITABLE or not prop.flags & GObject.PARAM_READABLE or GObject.type_is_a(prop.value_type, GObject.Object)): continue if is_effect: result, prop_value = self.element.get_child_property(prop.name) if not result: self.debug("Could not get value for property: %s", prop.name) else: if prop.name not in values.keys(): # Use the default value. prop_value = self.element.get_property(prop.name) else: prop_value = values[prop.name] widget = self._makePropertyWidget(prop, prop_value) if isinstance(widget, ToggleWidget): widget.set_label(prop.nick) grid.attach(widget, 0, y, 2, 1) else: text = _("%(preference_label)s:") % { "preference_label": prop.nick } label = Gtk.Label(label=text) label.set_alignment(0.0, 0.5) grid.attach(label, 0, y, 1, 1) grid.attach(widget, 1, y, 1, 1) if hasattr(prop, 'blurb'): widget.set_tooltip_text(prop.blurb) self.properties[prop] = widget if not self.__controllable or isinstance(widget, DefaultWidget): continue keyframe_button = None if not isinstance(widget, (ToggleWidget, ChoiceWidget)): res, element, pspec = self.element.lookup_child(prop.name) assert res binding = GstController.DirectControlBinding.new( element, prop.name, GstController.InterpolationControlSource()) if binding.pspec: # The prop can be controlled (keyframed). keyframe_button = self.__create_keyframe_toggle_button( prop, widget) grid.attach(keyframe_button, 2, y, 1, 1) # The "reset to default" button associated with this property if with_reset_button: button = self.__create_reset_to_default_button( prop, widget, keyframe_button) grid.attach(button, 3, y, 1, 1) self.element.connect('deep-notify', self._propertyChangedCb) self.pack_start(grid, expand=False, fill=False, padding=0) self.show_all()
def __init__(self, filename="default.gst", config=dict()): self.eventhandlers = dict() self.eventkeys = dict() self.restart = False self.controller = dict() self.setter = dict() self.pipestring = "" self.pipeline = None self.bus = None self.previewOut = None self.liveOut = None self.recorder = None self.config = config conffile = None log.info("pipefile: %s" % (filename)) extension = ".gst" if filename.endswith(extension): conffile = filename[:-len(extension)] + ".ctl" # (self.pipestring, ctrls) = # _pipeParseCtrl(_pipeRead(filename, config)) self.pipestring = _pipeRead(filename, config) ctrls = _ctrlRead(conffile) log.info("pipeline: %s" % (self.pipestring)) log.info("ctrls: %s" % (ctrls)) self.setEventHandlers({Gst.MessageType.EOS: self._EOS}) self.setEventHandlers( {Gst.MessageType.ELEMENT: self._handleElementEvent}) self.setEventKeys(None) self.pipeline = Gst.parse_launch(self.pipestring) self.bus = self.pipeline.get_bus() self.bus.add_watch(GLib.PRIORITY_DEFAULT, self._async_handler, None) # # enabling the following triggers an assertion (and exists) # Gst.Bus.add_signal_watch(self.bus) self.previewOut = self.pipeline.get_by_name("preview") self.liveOut = self.pipeline.get_by_name("preview") self.recorder = self.pipeline.get_by_name("recorder") log.info("OUT: %s\t%s", self.previewOut, self.liveOut) # get all controllables control_dict = {} try: pipelements = iter(self.pipeline.iterate_elements()) except TypeError: log.exception("'python3-gst-1.0' required?") return for lmn in pipelements: for p in lmn.props: if False and p.flags & Gst.PARAM_CONTROLLABLE: if lmn.props.name not in control_dict: control_dict[lmn.props.name] = [] control_dict[lmn.props.name] += [p.name] if ctrls: for ctl, elemprop in ctrls.items(): # ctl = 'FOO' # elemprop = { # 'textoverlay_3': ['xpos'], # 'textoverlay_1': ['ypos', 'xpos'] # } for elem, props in elemprop.items(): # elem = 'textoverlay_3' # prop = ['xpos'] ctlprops = [] setprops = [] for p in props: if ((elem in control_dict and p in control_dict[elem])): ctlprops += [p] setprops += [p] lmn = self.pipeline.get_by_name(elem) log.debug("element '%s' %s" % (elem, lmn)) # create a controller if ctlprops: log.info("ctlprops: %s@%s : %s" % (elem, ctlprops, ctl)) # tmpctl = Gst.Controller(lmn, *ctlprops) for cp in ctlprops: v = lmn.get_property(cp) if not isinstance(v, float): continue tmpctl = GstController.InterpolationControlSource() tmpctl.set_property( 'mode', GstController.InterpolationMode.LINEAR) cb = GstController.DirectControlBinding.new( lmn, cp, tmpctl) lmn.add_control_binding(cb) log.debug("%s: %s" % (ctl, v)) tmpctl.set(0, v) if ctl not in self.controller: self.controller[ctl] = [] self.controller[ctl] += [(tmpctl, ctlprops)] # create setters # (this is just a list so we remember) if setprops: if ctl not in self.setter: self.setter[ctl] = {} if lmn not in self.setter[ctl]: self.setter[ctl][lmn] = [] self.setter[ctl][lmn] += setprops log.warn("controller: %s" % (self.controller, )) log.warn("setter: %s" % (self.setter, ))