Skip to content

Instantly share code, notes, and snippets.

@lexbailey
Created February 20, 2021 12:13
Show Gist options
  • Select an option

  • Save lexbailey/594e77728e55b88ff2b81cbf0cbde651 to your computer and use it in GitHub Desktop.

Select an option

Save lexbailey/594e77728e55b88ff2b81cbf0cbde651 to your computer and use it in GitHub Desktop.
BROKEN CODE DO NOT USE: Trying to get an AppSrc to feed data into a GES timeline, not working yest
#!/usr/bin/env python3
"""
Attempted demo of AppSrc with GES timeline
Does not work
Creates a simple GTK Window, inits GES to preview to that window, adds a test video clip (muted) and
adds the custom audio synth element, but the audio synth element sound does not play
Currently reports some assertion errors.
"""
import sys
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gst', '1.0')
gi.require_version('GstVideo', '1.0')
gi.require_version('GES', '1.0')
gi.require_version('GstApp', '1.0')
from gi.repository import Gtk, GObject, Gst
from gi.repository import GstVideo
from gi.repository import GES, Gio
from gi.repository import GstApp
Gtk.init(sys.argv)
Gst.init_check(sys.argv)
GES.init()
class AudioSynth(GstApp.AppSrc):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.sample_rate = 8000
self.wave = self.sinwave_sequence(300)
self.rawcaps = Gst.Caps.from_string(
'audio/x-raw,format=U8,rate={},channels=1,layout=interleaved'.format(self.sample_rate)
)
def sinwave_sequence(self, freq):
""" Placeholder: just an interesting and easily recognised noise, TODO replace with actual synth code """
n = 0
freqs = [freq, freq + 100, freq + 200, freq + 300]
freqs += list(reversed(freqs[1:3]))
f = 0
while True:
n += 1
samples_per_cycle = self.sample_rate/freq
yield int(
127 * (math.sin(
math.pi*2*((n%samples_per_cycle)/samples_per_cycle)
) + 1.0)
)
if n % int(0.15 * self.sample_rate) == 0:
f += 1; f = f % len(freqs)
freq = freqs[f]
def do_enough_data(self):
print("Got enough samples")
def do_need_data(self, need):
print("Pushing {} more samples".format(need))
# TODO seeking won't work with this code
buf = Gst.Buffer.new_wrapped(bytes(islice(self.wave, 4096)))
self.push_buffer(buf)
def link_with_raw_caps(self, dest):
self.link_filtered(dest, self.rawcaps)
class SynthAudioSourceElement(GES.AudioSource):
def __init__(self):
print("Created source")
super().__init__()
def do_create_source(self):
print("Creating audio synth element source")
self.pipeline = Gst.ElementFactory.make("pipeline", None)
source = AudioSynth()
convert = Gst.ElementFactory.make("audioconvert")
pipeline.add(source)
pipeline.add(convert)
source.link_with_raw_caps(convert)
return self.pipeline
class SynthAudioClip(GES.SourceClip):
def __init__(self):
super().__init__()
self.set_supported_formats(GES.TrackType.AUDIO)
def do_create_track_element(self, type_):
print("Creating audio synth element")
assert type_ == GES.TrackType.AUDIO
self.element = SynthAudioSourceElement()
print(self.element)
return self.element
class EditorWindow(Gtk.ApplicationWindow):
def __init__(self):
super().__init__(title='test')
self.connect('destroy', self.quit)
self.connect('realize', self._on_realize)
self.set_default_size(1200, 800)
self.project = None
def _on_realize(self, widget):
self.sink = Gst.ElementFactory.make("gtksink", None)
if self.sink is None:
print("Error, unable to create video sink", file=sys.stderr)
sys.exit(1)
self.add(self.sink.props.widget)
self.show_all()
def play(self):
self.pipeline.set_state(Gst.State.PLAYING)
def _asset_added(self, ges_proj, asset):
dur = 10 * Gst.SECOND
print("Adding asset to layer", asset)
if asset.get_id() == 'GESTestClip':
# If this is the test clip, mute it (just so we can hear the audio synth easier)
clip = self.layer.add_asset(asset, 0, 0, dur, GES.TrackType.UNKNOWN)
clip.set_mute(True)
else:
# Otherwise, just add asset audio track
clip = self.layer2.add_asset(asset, 0, 0, dur, GES.TrackType.AUDIO)
def test_clip(self):
clip = GES.TestClip()
self.layer.add_clip(clip)
clip2 = SynthAudioClip()
self.layer2.add_clip(clip2)
def _error_loading_asset(self, ges_proj, error, asset_id, _type):
print("Error adding asset to timeline.", error, asset_id, _type)
def _bus_message(self, bus, message):
if message.type == Gst.MessageType.EOS:
print("end of playback")
elif message.type == Gst.MessageType.ERROR:
error = message.parse_error()
print("error %s" % error[1])
elif message.type == Gst.MessageType.WARNING:
warning = message.parse_warning()
print("warning: %s" == warning[1])
elif message.type & Gst.MessageType.STATE_CHANGED:
pass#print(message)
else:
pass#print(message.type)
def init_playback(self):
self.timeline = GES.Timeline.new_audio_video()
self.ges_proj = self.timeline.get_asset()
self.ges_proj.connect("asset-added", self._asset_added)
self.ges_proj.connect("error-loading-asset", self._error_loading_asset)
self.layer = self.timeline.append_layer()
self.layer2 = self.timeline.append_layer()
self.pipeline = GES.Pipeline()
self.pipeline.set_timeline(self.timeline)
self.pipeline.preview_set_video_sink(self.sink)
bus = self.pipeline.get_bus()
bus.add_signal_watch()
bus.connect("message", self._bus_message)
def quit(self, window):
Gtk.main_quit()
ew = EditorWindow()
ew.show_all()
ew.init_playback()
ew.test_clip()
ew.play()
Gtk.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment