grim/pidgin3-gstreamer-poc
Create a user interface for testing and wire up the user's video with a tee to a gtksink
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore Tue Apr 12 03:56:01 2022 -0500
@@ -0,0 +1,4 @@
--- /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 @@
+gi.require_version('Gst', '1.0') +from gi.repository import Gst --- /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 @@
+gi.require_version('Gst', '1.0') +from gi.repository import Gst +from .consts import UI_VIDEO_SINK_PAD, UI_VIDEO_SOURCE_PAD + 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 + 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.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) + # now that everything is wired up, start the pipeline + bus = self.pipeline.get_bus() + bus.connect("message", self.message_handler) + self.pipeline.set_state(Gst.State.PLAYING) + self.pipeline.set_state(Gst.State.NULL) + # This is used mainly to expose the ui's sink so that the ui can get + def get_bin(self, name): + return self.pipeline.get_by_name(name) + def message_handler(self, bus, message): + if t == Gst.MessageType.EOS: + self.pipeline.set_state(Gst.State.NULL) + 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}') + # 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 @@
+ def __init__(self, ui): + def new_call(self, protocol): + 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 @@
+gi.require_version('Gst', '1.0') +from gi.repository import Gst +from .consts import PROTOCOL_VIDEO_SINK_PAD + def get_source_element(self): + 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) + user_video_sink_pad = user_video.get_static_pad('sink') + ghost_pad = Gst.GhostPad.new(PROTOCOL_VIDEO_SINK_PAD, user_video_sink_pad) + user_video.set_state(Gst.State.PLAYING) --- /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 @@
+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 .window import POCWindow + self.window = POCWindow(self) + def get_source_element(self): + bin = Gst.Bin.new("ui-source") + video = Gst.ElementFactory.make('videotestsrc', 'video-source') + video_source_pad = video.get_static_pad('src') + ghost_pad = Gst.GhostPad.new(UI_VIDEO_SOURCE_PAD, video_source_pad) + def get_sink_element(self): + bin = Gst.Bin.new("ui-sink") + video_sink = Gst.ElementFactory.make('gtksink', 'video-sink') + video_sink_pad = video_sink.get_static_pad('sink') + ghost_pad = Gst.GhostPad.new(UI_VIDEO_SINK_PAD, video_sink_pad) --- /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 @@
+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__() + @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') + self.video_sink = sink.get_property('widget') + if self.video_sink is not None: + self.frame.add(self.video_sink) + if self.video_sink is not None: + self.frame.remove(self.video_sink) + @Gtk.Template.Callback() + def poc_user1_toggled(self, button): + @Gtk.Template.Callback() + def poc_user1_toggled(self, button): + @Gtk.Template.Callback() + def poc_user2_toggled(self, button): + @Gtk.Template.Callback() + def poc_user3_toggled(self, button): + @Gtk.Template.Callback() + def poc_delete_event(self, *args): --- /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 --> + <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"/> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <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 type="label_item"> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="homogeneous">True</property> + <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"/> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + <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"/> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + <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"/> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + <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"/> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property>