From dba577f39c489b9db29960f2bf5cc78ac4f29d7b Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sun, 4 Oct 2026 14:42:16 +0200 Subject: Move OMEMO, OpenPGP and RTP plugins into main/libdino --- plugins/rtp/src/plugin.vala | 504 -------------------------------------------- 1 file changed, 504 deletions(-) delete mode 100644 plugins/rtp/src/plugin.vala (limited to 'plugins/rtp/src/plugin.vala') diff --git a/plugins/rtp/src/plugin.vala b/plugins/rtp/src/plugin.vala deleted file mode 100644 index d09b7edf..00000000 --- a/plugins/rtp/src/plugin.vala +++ /dev/null @@ -1,504 +0,0 @@ -using Gee; -using Xmpp; -using Xmpp.Xep; - -public class Dino.Plugins.Rtp.Plugin : RootInterface, VideoCallPlugin, Object { - public Dino.Application app { get; private set; } - public CodecUtil codec_util { get; private set; } - public Gst.DeviceMonitor? device_monitor { get; private set; } - public Gst.Pipeline? pipe { get; private set; } - public Gst.Bin? rtpbin { get; private set; } - public Gst.Element? echoprobe { get; private set; } - - private Gee.List streams = new ArrayList(); - private Gee.List devices = new ArrayList(); - private int64 last_devices_refresh = 0; - // private Gee.List participants = new ArrayList(); - - public void registered(Dino.Application app) { - this.app = app; - this.codec_util = new CodecUtil(); - app.add_option_group(Gst.init_get_option_group()); - app.stream_interactor.module_manager.initialize_account_modules.connect((account, list) => { - list.add(new Module(this)); - }); - app.plugin_registry.video_call_plugin = this; - } - - private int pause_count = 0; - public void pause() { -// if (pause_count == 0) { -// debug("Pausing pipe for modifications"); -// pipe.set_state(Gst.State.PAUSED); -// } - pause_count++; - } - public void unpause() { - pause_count--; - if (pause_count == 0) { - debug("Continue pipe after modifications"); - pipe.set_state(Gst.State.PLAYING); - } - if (pause_count < 0) warning("Pause count below zero!"); - } - - private void handle_existing_devices(Gst.DeviceMonitor device_monitor) { - var new_devices = new ArrayList(); - foreach (Gst.Device device in device_monitor.get_devices()) { - if (device.properties == null) continue; - if (device.properties.has_name("pipewire-proplist") && device.has_classes("Audio")) continue; - if (device.properties.get_string("device.class") == "monitor") continue; - var pre_device = devices.first_match((it) => it.matches(device)); - if (pre_device != null) { - if (!new_devices.contains(pre_device)) new_devices.add(pre_device); - continue; - } - var new_device = new Device(this, device); - devices.add(new_device); - new_devices.add(new_device); - } - devices.retain_all(new_devices); - } - - private void refresh_devices() { - if (device_monitor != null) return; // No manual refresh needed, we're monitoring actively. - if (last_devices_refresh > 0 && last_devices_refresh + 5000000 > get_monotonic_time()) return; // Already current - var device_monitor = new Gst.DeviceMonitor(); - device_monitor.show_all = true; - device_monitor.start(); - handle_existing_devices(device_monitor); - device_monitor.stop(); - last_devices_refresh = get_monotonic_time(); - } - - private void start_device_monitor() { - if (device_monitor != null) return; - device_monitor = new Gst.DeviceMonitor(); - device_monitor.show_all = true; - device_monitor.get_bus().add_watch(Priority.DEFAULT, on_device_monitor_message); - device_monitor.start(); - handle_existing_devices(device_monitor); - } - - private void stop_device_monitor() { - if (device_monitor == null) return; - device_monitor.stop(); - device_monitor = null; - } - - private void init_call_pipe() { - if (pipe != null) return; - debug("Creating call pipe."); - start_device_monitor(); - pipe = new Gst.Pipeline(null); - - // RTP - rtpbin = Gst.ElementFactory.make("rtpbin", null) as Gst.Bin; - if (rtpbin == null) { - warning("RTP not supported"); - pipe = null; - return; - } - rtpbin.pad_added.connect(on_rtp_pad_added); - rtpbin.@set("latency", 100); - rtpbin.@set("do-lost", true); -// rtpbin.@set("do-sync-event", true); - rtpbin.@set("drop-on-latency", true); - rtpbin.connect("signal::request-pt-map", request_pt_map, this); - pipe.add(rtpbin); - -#if WITH_VOICE_PROCESSOR - // Audio echo probe - echoprobe = new EchoProbe(); - if (echoprobe != null) pipe.add(echoprobe); -#endif - - // Pipeline - pipe.auto_flush_bus = true; - pipe.bus.add_watch(GLib.Priority.DEFAULT, (_, message) => { - on_pipe_bus_message(message); - return true; - }); - pipe.set_state(Gst.State.PLAYING); - } - - public void destroy_call_pipe_if_unused() { - if (streams.is_empty && !VideoWidget.has_instances()) { - destroy_call_pipe(); - } - } - - private void destroy_call_pipe() { - if (pipe == null) return; - pipe.set_state(Gst.State.NULL); - rtpbin = null; -#if WITH_VOICE_PROCESSOR - echoprobe = null; -#endif - pipe = null; - stop_device_monitor(); - debug("Call pipe destroyed"); - } - - private static Gst.Caps? request_pt_map(Gst.Element rtpbin, uint session, uint pt, Plugin plugin) { - debug("request-pt-map"); - return null; - } - - private void on_rtp_pad_added(Gst.Pad pad) { - debug("pad added: %s", pad.name); - if (pad.name.has_prefix("recv_rtp_src_")) { - string[] split = pad.name.split("_"); - uint8 rtpid = (uint8)int.parse(split[3]); - foreach (Stream stream in streams) { - if (stream.rtpid == rtpid) { - stream.on_ssrc_pad_added((uint32) split[4].to_uint64(), pad); - } - } - } - if (pad.name.has_prefix("send_rtp_src_")) { - string[] split = pad.name.split("_"); - uint8 rtpid = (uint8)int.parse(split[3]); - debug("pad %s for stream %hhu", pad.name, rtpid); - foreach (Stream stream in streams) { - if (stream.rtpid == rtpid) { - stream.on_send_rtp_src_added(pad); - } - } - } - } - - private void on_pipe_bus_message(Gst.Message message) { - switch (message.type) { - case Gst.MessageType.ERROR: - Error error; - string str; - message.parse_error(out error, out str); - warning("Error in pipeline: %s", error.message); - debug(str); - break; - case Gst.MessageType.WARNING: - Error error; - string str; - message.parse_warning(out error, out str); - warning("Warning in pipeline: %s", error.message); - debug(str); - break; - case Gst.MessageType.CLOCK_LOST: - debug("Clock lost. Restarting"); - pipe.set_state(Gst.State.READY); - pipe.set_state(Gst.State.PLAYING); - break; - case Gst.MessageType.STATE_CHANGED: - // Ignore - break; - case Gst.MessageType.STREAM_STATUS: - Gst.StreamStatusType status; - Gst.Element owner; - message.parse_stream_status(out status, out owner); - if (owner != null) { - debug("%s stream changed status to %s", owner.name, status.to_string()); - } - break; - case Gst.MessageType.ELEMENT: - unowned Gst.Structure struc = message.get_structure(); - if (struc != null && message.src is Gst.Element) { - debug("Message from %s in pipeline: %s", ((Gst.Element)message.src).name, struc.to_string()); - } - break; - case Gst.MessageType.NEW_CLOCK: - debug("New clock."); - break; - case Gst.MessageType.TAG: - // Ignore - break; - case Gst.MessageType.QOS: - // Ignore - break; - case Gst.MessageType.LATENCY: - if (message.src != null && message.src.name != null && message.src is Gst.Element) { - Gst.Query latency_query = new Gst.Query.latency(); - if (((Gst.Element)message.src).query(latency_query)) { - bool live; - Gst.ClockTime min_latency, max_latency; - latency_query.parse_latency(out live, out min_latency, out max_latency); - debug("Latency message from %s: live=%s, min_latency=%s, max_latency=%s", message.src.name, live.to_string(), min_latency.to_string(), max_latency.to_string()); - } - } - break; - default: - debug("Pipe bus message: %s", message.type.to_string()); - break; - } - } - - private bool on_device_monitor_message(Gst.Bus bus, Gst.Message message) { - Gst.Device? old_gst_device = null; - Gst.Device? gst_device = null; - Device? device = null; - switch (message.type) { - case Gst.MessageType.DEVICE_ADDED: - message.parse_device_added(out gst_device); - if (devices.any_match((it) => it.matches(gst_device))) return Source.CONTINUE; - device = new Device(this, gst_device); - devices.add(device); - break; - case Gst.MessageType.DEVICE_CHANGED: - message.parse_device_changed(out gst_device, out old_gst_device); - device = devices.first_match((it) => it.matches(old_gst_device)); - if (device != null) device.update(gst_device); - break; - case Gst.MessageType.DEVICE_REMOVED: - message.parse_device_removed(out gst_device); - device = devices.first_match((it) => it.matches(gst_device)); - if (device != null) devices.remove(device); - break; - default: - break; - } - if (device != null) { - devices_changed(device.media, device.is_sink); - } - return Source.CONTINUE; - } - - public uint8 next_free_id() { - uint8 rtpid = 0; - while (streams.size < 100 && streams.any_match((stream) => stream.rtpid == rtpid)) { - rtpid++; - } - return rtpid; - } - - // public Participant get_participant(Jid full_jid, bool self) { -// foreach (Participant participant in participants) { -// if (participant.full_jid.equals(full_jid)) { -// return participant; -// } -// } -// Participant participant; -// if (self) { -// participant = new SelfParticipant(pipe, full_jid); -// } else { -// participant = new Participant(pipe, full_jid); -// } -// participants.add(participant); -// return participant; -// } - - public Stream open_stream(Xmpp.Xep.Jingle.Content content) { - init_call_pipe(); - var content_params = content.content_params as Xmpp.Xep.JingleRtp.Parameters; - if (content_params == null) return null; - Stream stream; - if (content_params.media == "video") { - stream = new VideoStream(this, content); - } else { - stream = new Stream(this, content); - } - streams.add(stream); - return stream; - } - - public void close_stream(Stream stream) { - streams.remove(stream); - stream.destroy(); - destroy_call_pipe_if_unused(); - } - - public void shutdown() { - stop_device_monitor(); - destroy_call_pipe(); - Gst.deinit(); - } - - public bool supported() { - return codec_util.is_element_supported("rtpbin"); - } - - public VideoCallWidget? create_widget(WidgetType type) { - init_call_pipe(); - if (type == WidgetType.GTK4) { - return new VideoWidget(this); - } - return null; - } - - public Gee.List get_devices(string media, bool incoming) { - refresh_devices(); - Gee.List devices; - if (media == "video" && !incoming) { - devices = get_video_sources(); - } else if (media == "audio") { - devices = get_audio_devices(incoming); - } else { - devices = new ArrayList(); - devices.add_all_iterator(this.devices.filter(it => it.media == media && (incoming && it.is_sink || !incoming && it.is_source) && !it.is_monitor)); - } - devices.sort((media_left, media_right) => { - return strcmp(media_left.id, media_right.id); - }); - - return devices; - } - - public Gee.List get_audio_devices(bool incoming) { - ArrayList pulse_devices = new ArrayList(); - ArrayList other_devices = new ArrayList(); - - foreach (Device device in devices) { - if (device.media != "audio") continue; - if (incoming && !device.is_sink || !incoming && !device.is_source) continue; - - // Skip monitors - if (device.is_monitor) continue; - - if (device.protocol == DeviceProtocol.PULSEAUDIO) { - pulse_devices.add(device); - } else { - other_devices.add(device); - } - } - - // If we have any pulseaudio devices, present only those. Don't want duplicated devices from pipewire and pulseaudio. - return pulse_devices.size > 0 ? pulse_devices : other_devices; - } - - public Gee.List get_video_sources() { - ArrayList pipewire_devices = new ArrayList(); - ArrayList other_devices = new ArrayList(); - - foreach (Device device in devices) { - if (device.media != "video") continue; - if (device.is_sink) continue; - - // Skip monitors - if (device.is_monitor) continue; - - bool is_color = false; - for (int i = 0; i < device.device.caps.get_size(); i++) { - unowned Gst.Structure structure = device.device.caps.get_structure(i); - if (!structure.has_field("format")) continue; - // "format" might be an array and get_string() will then return null. We just assume arrays to be fine. - string? format = structure.get_string("format"); - if (format == null || !format.has_prefix("GRAY")) { - is_color = true; - } - } - - // Don't allow grey-scale devices - if (!is_color) continue; - - if (device.protocol == DeviceProtocol.PIPEWIRE) { - pipewire_devices.add(device); - } else { - other_devices.add(device); - } - } - - // If we have any pipewire devices, present only those. Don't want duplicated devices from pipewire and video for linux. - return pipewire_devices.size > 0 ? pipewire_devices : other_devices; - } - - private int get_max_fps(Device device) { - int fps = 0; - for (int i = 0; i < device.device.caps.get_size(); i++) { - unowned Gst.Structure structure = device.device.caps.get_structure(i); - - if (structure.has_field("framerate")) { - Value framerate = structure.get_value("framerate"); - if (framerate.type() == typeof(Gst.Fraction)) { - int num = Gst.Value.get_fraction_numerator(framerate); - int den = Gst.Value.get_fraction_denominator(framerate); - fps = int.max(fps, num / den); - } else if (framerate.type() == typeof(Gst.ValueList)) { - for(uint j = 0; j < Gst.ValueList.get_size(framerate); j++) { - Value fraction = Gst.ValueList.get_value(framerate, j); - int num = Gst.Value.get_fraction_numerator(fraction); - int den = Gst.Value.get_fraction_denominator(fraction); - fps = int.max(fps, num / den); - } - } else { - debug("Unknown type for framerate %s on device %s", framerate.type_name(), device.display_name); - } - } - } - - debug("Max framerate for device %s: %d", device.display_name, fps); - return fps; - } - - public MediaDevice? get_preferred_device(string media, bool incoming) { - Gee.List devices = new ArrayList(); - foreach (MediaDevice media_device in get_devices(media, incoming)) { - if (media_device is Device) devices.add((Device)media_device); - } - if (devices.is_empty) { - warning("No preferred device for %s %s. Media will not be processed.", incoming ? "incoming" : "outgoing", media); - return null; - } - - // Take default if present - foreach (Device device in devices) { - if (device.is_default) { - debug("Using %s for %s %s as it's default", device.display_name, incoming ? "incoming" : "outgoing", media); - return device; - } - } - - if (media == "video") { - // Pick best FPS - int max_fps = -1; - Device? max_fps_device = null; - foreach (Device device in devices) { - int fps = get_max_fps(device); - if (fps > max_fps || max_fps_device == null) { - max_fps = fps; - max_fps_device = device; - } - } - debug("Using %s for %s %s as it has max FPS (%d)", max_fps_device.display_name, incoming ? "incoming" : "outgoing", media, max_fps); - return max_fps_device; - } else { - // Pick any - Device? device = devices.first(); - debug("Using %s for %s %s as it's first pick", device.display_name, incoming ? "incoming" : "outgoing", media); - return device; - } - } - - public MediaDevice? get_device(Xmpp.Xep.JingleRtp.Stream? stream, bool incoming) { - Stream? plugin_stream = stream as Stream?; - if (plugin_stream == null) return null; - MediaDevice? device = incoming ? plugin_stream.output_device : plugin_stream.input_device; - return device ?? get_preferred_device(stream.media, incoming); - } - - public void dump_dot() { - if (pipe == null) return; - string name = @"pipe-$(pipe.clock.get_time())-$(pipe.current_state)"; - Gst.Debug.bin_to_dot_file(pipe, Gst.DebugGraphDetails.ALL, name); - print(@"Stored pipe details as $name\n"); - } - - public void set_pause(Xmpp.Xep.JingleRtp.Stream? stream, bool pause) { - Stream? plugin_stream = stream as Stream?; - if (plugin_stream == null) return; - if (pause) { - plugin_stream.pause(); - } else { - plugin_stream.unpause(); - } - } - - public void set_device(Xmpp.Xep.JingleRtp.Stream? stream, MediaDevice? device) { - Device? real_device = device as Device?; - Stream? plugin_stream = stream as Stream?; - if (real_device == null || plugin_stream == null) return; - if (real_device.is_source) { - plugin_stream.input_device = real_device; - } else if (real_device.is_sink) { - plugin_stream.output_device = real_device; - } - } -} -- cgit v1.3