grim/pidgin3-gstreamer-poc

Parents 3fe81f4ddd0c
Children 9c7e07ebed82
Create a user interface for testing and wire up the user's video with a tee to a gtksink
  • +4 -0
    .hgignore
  • +12 -0
    poc/__main__.py
  • +71 -0
    poc/call.py
  • +4 -0
    poc/consts.py
  • +15 -0
    poc/core.py
  • +26 -0
    poc/protocol.py
  • +54 -0
    poc/ui.py
  • +68 -0
    poc/window.py
  • +105 -0
    window.ui
  • --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/.hgignore Tue Apr 12 03:56:01 2022 -0500
    @@ -0,0 +1,4 @@
    +syntax: glob
    +__pycache__
    +*.ui~
    +
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/poc/__main__.py Tue Apr 12 03:56:01 2022 -0500
    @@ -0,0 +1,12 @@
    +import gi
    +
    +gi.require_version('Gst', '1.0')
    +from gi.repository import Gst
    +
    +from .ui import UI
    +
    +Gst.init(None)
    +
    +ui = UI()
    +ui.startup()
    +
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/poc/call.py Tue Apr 12 03:56:01 2022 -0500
    @@ -0,0 +1,71 @@
    +import gi
    +
    +gi.require_version('Gst', '1.0')
    +from gi.repository import Gst
    +
    +from .consts import UI_VIDEO_SINK_PAD, UI_VIDEO_SOURCE_PAD
    +
    +
    +call_num = 0
    +
    +
    +class Call(object):
    + def __init__(self, ui_source_element, ui_sink_element, protocol_source_element, protocol_sink_element):
    + self.ui_source_element = ui_source_element
    + self.ui_sink_element = ui_sink_element
    + self.protocol_source_element = protocol_source_element
    + self.protocol_sink_element = protocol_sink_element
    +
    + global call_num
    +
    + self.pipeline = Gst.Pipeline.new(f'call-{call_num}')
    + call_num = call_num + 1
    +
    + # add the ui and protocol elements
    + self.pipeline.add(self.ui_source_element)
    + self.pipeline.add(self.ui_sink_element)
    + self.pipeline.add(self.protocol_sink_element)
    +
    + # create or tee to support our video monitor
    + tee = Gst.ElementFactory.make('tee')
    + self.pipeline.add(tee)
    + self.ui_source_element.link_pads(UI_VIDEO_SOURCE_PAD, tee, 'sink')
    +
    + # output the video to the user
    + tee.link_pads('src_0', self.ui_sink_element, UI_VIDEO_SINK_PAD)
    +
    +
    + def start(self):
    + # now that everything is wired up, start the pipeline
    + bus = self.pipeline.get_bus()
    + bus.add_signal_watch()
    + bus.connect("message", self.message_handler)
    + self.pipeline.set_state(Gst.State.PLAYING)
    +
    +
    + def stop(self):
    + self.pipeline.set_state(Gst.State.NULL)
    +
    +
    + # This is used mainly to expose the ui's sink so that the ui can get
    + # properties from it.
    + def get_bin(self, name):
    + return self.pipeline.get_by_name(name)
    +
    +
    + def message_handler(self, bus, message):
    + t = message.type
    + if t == Gst.MessageType.EOS:
    + self.pipeline.set_state(Gst.State.NULL)
    + print('Got EOS')
    + elif t == Gst.MessageType.STATE_CHANGED:
    + Gst.debug_bin_to_dot_file_with_ts(self.pipeline, Gst.DebugGraphDetails.ALL, 'foo')
    + elif t == Gst.MessageType.ERROR:
    + err, debug = message.parse_error()
    + print(f'Error: {err}, {debug}')
    + self.pipeline.set_state(Gst.State.NULL)
    + elif t == Gst.MessageType.WARNING:
    + err, debug = message.parse_warning()
    + print(f'Warning: {err}, {debug}')
    + # else:
    + # print(f'unknown message type {t}')
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/poc/consts.py Tue Apr 12 03:56:01 2022 -0500
    @@ -0,0 +1,4 @@
    +UI_VIDEO_SOURCE_PAD = 'ui-video-source-pad'
    +UI_VIDEO_SINK_PAD = 'ui-video-sink-pad'
    +UI_AUDIO_SOURCE_PAD = 'ui-audio-source-pad'
    +PROTOCOL_VIDEO_SINK_PAD = 'protocol-video-sink-pad'
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/poc/core.py Tue Apr 12 03:56:01 2022 -0500
    @@ -0,0 +1,15 @@
    +from .call import Call
    +
    +
    +class Core(object):
    + def __init__(self, ui):
    + self.ui = ui
    +
    +
    + def new_call(self, protocol):
    + return Call(
    + self.ui.get_source_element(),
    + self.ui.get_sink_element(),
    + protocol.get_source_element(),
    + protocol.get_sink_element(),
    + )
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/poc/protocol.py Tue Apr 12 03:56:01 2022 -0500
    @@ -0,0 +1,26 @@
    +import gi
    +
    +gi.require_version('Gst', '1.0')
    +from gi.repository import Gst
    +
    +from .consts import PROTOCOL_VIDEO_SINK_PAD
    +
    +
    +class Protocol(object):
    + def get_source_element(self):
    + pass
    +
    + def get_sink_element(self):
    + bin = Gst.Bin.new('protocol')
    +
    + user_video = Gst.ElementFactory.make('fakesink', 'user-video-sink')
    + user_video.set_property("sync", True)
    + bin.add(user_video)
    +
    + user_video_sink_pad = user_video.get_static_pad('sink')
    + ghost_pad = Gst.GhostPad.new(PROTOCOL_VIDEO_SINK_PAD, user_video_sink_pad)
    + bin.add_pad(ghost_pad)
    +
    + user_video.set_state(Gst.State.PLAYING)
    +
    + return bin
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/poc/ui.py Tue Apr 12 03:56:01 2022 -0500
    @@ -0,0 +1,54 @@
    +import gi
    +
    +gi.require_version('Gtk', '3.0')
    +from gi.repository import Gtk
    +
    +gi.require_version('Gst', '1.0')
    +from gi.repository import Gst
    +
    +from .consts import UI_VIDEO_SINK_PAD, UI_VIDEO_SOURCE_PAD
    +from .core import Core
    +from .window import POCWindow
    +
    +
    +class UI(object):
    + def __init__(self):
    + self.core = Core(self)
    + self.window = POCWindow(self)
    +
    +
    + def get_source_element(self):
    + bin = Gst.Bin.new("ui-source")
    +
    + video = Gst.ElementFactory.make('videotestsrc', 'video-source')
    + bin.add(video)
    +
    + video_source_pad = video.get_static_pad('src')
    + ghost_pad = Gst.GhostPad.new(UI_VIDEO_SOURCE_PAD, video_source_pad)
    + bin.add_pad(ghost_pad)
    +
    + return bin
    +
    +
    + def get_sink_element(self):
    + bin = Gst.Bin.new("ui-sink")
    +
    + video_sink = Gst.ElementFactory.make('gtksink', 'video-sink')
    + bin.add(video_sink)
    +
    + video_sink_pad = video_sink.get_static_pad('sink')
    + ghost_pad = Gst.GhostPad.new(UI_VIDEO_SINK_PAD, video_sink_pad)
    + bin.add_pad(ghost_pad)
    +
    + return bin
    +
    +
    + def startup(self):
    + self.window.show()
    +
    + Gtk.main()
    +
    +
    + def shutdown(self):
    + Gtk.main_quit()
    +
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/poc/window.py Tue Apr 12 03:56:01 2022 -0500
    @@ -0,0 +1,68 @@
    +import gi
    +
    +gi.require_version('Gtk', '3.0')
    +from gi.repository import Gtk
    +
    +from .protocol import Protocol
    +
    +
    +@Gtk.Template(filename='window.ui')
    +class POCWindow(Gtk.Window):
    + __gtype_name__ = 'POCWindow'
    +
    + frame = Gtk.Template.Child()
    +
    + def __init__(self, ui):
    + super(Gtk.Window, self).__init__()
    +
    + self.ui = ui
    + self.call = None
    +
    + self.video_sink = None
    +
    +
    + @Gtk.Template.Callback()
    + def poc_call_toggled(self, button):
    + if button.get_active():
    + self.call = self.ui.core.new_call(Protocol())
    +
    +
    + sink = self.call.get_bin('video-sink')
    + if sink is not None:
    + self.video_sink = sink.get_property('widget')
    + if self.video_sink is not None:
    + self.frame.add(self.video_sink)
    + self.video_sink.show()
    +
    + self.call.start()
    + else:
    + if self.video_sink is not None:
    + self.frame.remove(self.video_sink)
    + self.video_sink = None
    +
    + self.call.stop()
    +
    +
    + @Gtk.Template.Callback()
    + def poc_user1_toggled(self, button):
    + print('user1 toggled')
    +
    +
    + @Gtk.Template.Callback()
    + def poc_user1_toggled(self, button):
    + print('user1 toggled')
    +
    +
    + @Gtk.Template.Callback()
    + def poc_user2_toggled(self, button):
    + print('user2 toggled')
    +
    +
    + @Gtk.Template.Callback()
    + def poc_user3_toggled(self, button):
    + print('user3 toggled')
    +
    +
    + @Gtk.Template.Callback()
    + def poc_delete_event(self, *args):
    + self.ui.shutdown()
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/window.ui Tue Apr 12 03:56:01 2022 -0500
    @@ -0,0 +1,105 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<!-- Generated with glade 3.38.2 -->
    +<interface>
    + <requires lib="gtk+" version="3.24"/>
    + <template class="POCWindow" parent="GtkWindow">
    + <property name="can-focus">False</property>
    + <signal name="delete-event" handler="poc_delete_event" swapped="no"/>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can-focus">False</property>
    + <property name="orientation">vertical</property>
    + <child>
    + <object class="GtkFrame" id="frame">
    + <property name="width-request">640</property>
    + <property name="height-request">480</property>
    + <property name="visible">True</property>
    + <property name="can-focus">False</property>
    + <property name="label-xalign">0</property>
    + <property name="shadow-type">none</property>
    + <child>
    + <placeholder/>
    + </child>
    + <child type="label_item">
    + <placeholder/>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can-focus">False</property>
    + <property name="homogeneous">True</property>
    + <child>
    + <object class="GtkToggleButton">
    + <property name="label" translatable="yes">Call Active</property>
    + <property name="visible">True</property>
    + <property name="can-focus">True</property>
    + <property name="receives-default">True</property>
    + <signal name="toggled" handler="poc_call_toggled" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkToggleButton">
    + <property name="label" translatable="yes">User 1</property>
    + <property name="visible">True</property>
    + <property name="can-focus">True</property>
    + <property name="receives-default">True</property>
    + <signal name="toggled" handler="poc_user1_toggled" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkToggleButton">
    + <property name="label" translatable="yes">User 2</property>
    + <property name="visible">True</property>
    + <property name="can-focus">True</property>
    + <property name="receives-default">True</property>
    + <signal name="toggled" handler="poc_user2_toggled" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkToggleButton">
    + <property name="label" translatable="yes">User 3</property>
    + <property name="visible">True</property>
    + <property name="can-focus">True</property>
    + <property name="receives-default">True</property>
    + <signal name="toggled" handler="poc_user3_toggled" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">3</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </template>
    +</interface>