Handle non-existant call support

This commit is contained in:
fiaxh 2021-04-17 14:50:31 +02:00
parent 3880628de4
commit 5d85b6cdb0
6 changed files with 65 additions and 11 deletions

View file

@ -85,6 +85,8 @@ public abstract interface ConversationAdditionPopulator : ConversationItemPopula
} }
public abstract interface VideoCallPlugin : Object { public abstract interface VideoCallPlugin : Object {
public abstract bool supports(string media);
// Video widget // Video widget
public abstract VideoCallWidget? create_widget(WidgetType type); public abstract VideoCallWidget? create_widget(WidgetType type);

View file

@ -83,7 +83,7 @@ namespace Dino {
we_should_send_video[call] = video; we_should_send_video[call] = video;
we_should_send_audio[call] = true; we_should_send_audio[call] = true;
if (yield has_jmi_resources(conversation)) { if (has_jmi_resources(conversation)) {
XmppStream? stream = stream_interactor.get_stream(conversation.account); XmppStream? stream = stream_interactor.get_stream(conversation.account);
jmi_call[conversation.account] = call; jmi_call[conversation.account] = call;
jmi_video[conversation.account] = video; jmi_video[conversation.account] = video;
@ -245,8 +245,28 @@ namespace Dino {
// If video_feed == null && !mute we're trying to mute a non-existant feed. It will be muted as soon as it is created. // If video_feed == null && !mute we're trying to mute a non-existant feed. It will be muted as soon as it is created.
} }
public async bool can_do_calls(Conversation conversation) { public async bool can_do_audio_calls_async(Conversation conversation) {
return (yield get_call_resources(conversation)).size > 0 || yield has_jmi_resources(conversation); if (!can_do_audio_calls()) return false;
return (yield get_call_resources(conversation)).size > 0 || has_jmi_resources(conversation);
}
private bool can_do_audio_calls() {
Plugins.VideoCallPlugin? plugin = Application.get_default().plugin_registry.video_call_plugin;
if (plugin == null) return false;
return plugin.supports("audio");
}
public async bool can_do_video_calls_async(Conversation conversation) {
if (!can_do_video_calls()) return false;
return (yield get_call_resources(conversation)).size > 0 || has_jmi_resources(conversation);
}
private bool can_do_video_calls() {
Plugins.VideoCallPlugin? plugin = Application.get_default().plugin_registry.video_call_plugin;
if (plugin == null) return false;
return plugin.supports("video");
} }
private async Gee.List<Jid> get_call_resources(Conversation conversation) { private async Gee.List<Jid> get_call_resources(Conversation conversation) {
@ -266,7 +286,7 @@ namespace Dino {
return ret; return ret;
} }
private async bool has_jmi_resources(Conversation conversation) { private bool has_jmi_resources(Conversation conversation) {
int64 jmi_resources = db.entity.select() int64 jmi_resources = db.entity.select()
.with(db.entity.jid_id, "=", db.get_jid_id(conversation.counterpart)) .with(db.entity.jid_id, "=", db.get_jid_id(conversation.counterpart))
.join_with(db.entity_feature, db.entity.caps_hash, db.entity_feature.entity) .join_with(db.entity_feature, db.entity.caps_hash, db.entity_feature.entity)
@ -289,6 +309,11 @@ namespace Dino {
} }
private void on_incoming_call(Account account, Xep.Jingle.Session session) { private void on_incoming_call(Account account, Xep.Jingle.Session session) {
if (!can_do_audio_calls()) {
warning("Incoming call but no call support detected. Ignoring.");
return;
}
bool counterpart_wants_video = false; bool counterpart_wants_video = false;
foreach (Xep.Jingle.Content content in session.contents) { foreach (Xep.Jingle.Content content in session.contents) {
Xep.JingleRtp.Parameters? rtp_content_parameter = content.content_params as Xep.JingleRtp.Parameters; Xep.JingleRtp.Parameters? rtp_content_parameter = content.content_params as Xep.JingleRtp.Parameters;
@ -550,6 +575,11 @@ namespace Dino {
Xep.JingleMessageInitiation.Module mi_module = stream_interactor.module_manager.get_module(account, Xep.JingleMessageInitiation.Module.IDENTITY); Xep.JingleMessageInitiation.Module mi_module = stream_interactor.module_manager.get_module(account, Xep.JingleMessageInitiation.Module.IDENTITY);
mi_module.session_proposed.connect((from, to, sid, descriptions) => { mi_module.session_proposed.connect((from, to, sid, descriptions) => {
if (!can_do_audio_calls()) {
warning("Incoming call but no call support detected. Ignoring.");
return;
}
bool audio_requested = descriptions.any_match((description) => description.ns_uri == Xep.JingleRtp.NS_URI && description.get_attribute("media") == "audio"); bool audio_requested = descriptions.any_match((description) => description.ns_uri == Xep.JingleRtp.NS_URI && description.get_attribute("media") == "audio");
bool video_requested = descriptions.any_match((description) => description.ns_uri == Xep.JingleRtp.NS_URI && description.get_attribute("media") == "video"); bool video_requested = descriptions.any_match((description) => description.ns_uri == Xep.JingleRtp.NS_URI && description.get_attribute("media") == "video");
if (!audio_requested && !video_requested) return; if (!audio_requested && !video_requested) return;

View file

@ -34,6 +34,9 @@ namespace Dino.Ui {
private StreamInteractor stream_interactor; private StreamInteractor stream_interactor;
private Conversation conversation; private Conversation conversation;
private ModelButton audio_button = new ModelButton() { text="Audio call", visible=true };
private ModelButton video_button = new ModelButton() { text="Video call", visible=true };
public CallButton(StreamInteractor stream_interactor) { public CallButton(StreamInteractor stream_interactor) {
this.stream_interactor = stream_interactor; this.stream_interactor = stream_interactor;
@ -42,7 +45,6 @@ namespace Dino.Ui {
Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu(); Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu();
Box box = new Box(Orientation.VERTICAL, 0) { margin=10, visible=true }; Box box = new Box(Orientation.VERTICAL, 0) { margin=10, visible=true };
ModelButton audio_button = new ModelButton() { text="Audio call", visible=true };
audio_button.clicked.connect(() => { audio_button.clicked.connect(() => {
stream_interactor.get_module(Calls.IDENTITY).initiate_call.begin(conversation, false, (_, res) => { stream_interactor.get_module(Calls.IDENTITY).initiate_call.begin(conversation, false, (_, res) => {
Call call = stream_interactor.get_module(Calls.IDENTITY).initiate_call.end(res); Call call = stream_interactor.get_module(Calls.IDENTITY).initiate_call.end(res);
@ -50,7 +52,7 @@ namespace Dino.Ui {
}); });
}); });
box.add(audio_button); box.add(audio_button);
ModelButton video_button = new ModelButton() { text="Video call", visible=true };
video_button.clicked.connect(() => { video_button.clicked.connect(() => {
stream_interactor.get_module(Calls.IDENTITY).initiate_call.begin(conversation, true, (_, res) => { stream_interactor.get_module(Calls.IDENTITY).initiate_call.begin(conversation, true, (_, res) => {
Call call = stream_interactor.get_module(Calls.IDENTITY).initiate_call.end(res); Call call = stream_interactor.get_module(Calls.IDENTITY).initiate_call.end(res);
@ -116,9 +118,12 @@ namespace Dino.Ui {
private async void update_visibility() { private async void update_visibility() {
if (conversation.type_ == Conversation.Type.CHAT) { if (conversation.type_ == Conversation.Type.CHAT) {
Conversation conv_bak = conversation; Conversation conv_bak = conversation;
bool can_do_calls = yield stream_interactor.get_module(Calls.IDENTITY).can_do_calls(conversation); bool audio_works = yield stream_interactor.get_module(Calls.IDENTITY).can_do_audio_calls_async(conversation);
bool video_works = yield stream_interactor.get_module(Calls.IDENTITY).can_do_audio_calls_async(conversation);
if (conv_bak != conversation) return; if (conv_bak != conversation) return;
visible = can_do_calls;
visible = audio_works;
video_button.visible = video_works;
} else { } else {
visible = false; visible = false;
} }

View file

@ -225,6 +225,7 @@ public class Dino.Plugins.Rtp.CodecUtil {
} }
public string? get_encode_element_name(string media, string? codec) { public string? get_encode_element_name(string media, string? codec) {
if (!is_element_supported(get_pay_element_name(media, codec))) return null;
foreach (string candidate in get_encode_candidates(media, codec)) { foreach (string candidate in get_encode_candidates(media, codec)) {
if (is_element_supported(candidate)) return candidate; if (is_element_supported(candidate)) return candidate;
} }

View file

@ -64,13 +64,13 @@ public class Dino.Plugins.Rtp.Module : JingleRtp.Module {
} }
private async bool is_payload_supported(string media, JingleRtp.PayloadType payload_type) { private async bool is_payload_supported(string media, JingleRtp.PayloadType payload_type) {
string codec = CodecUtil.get_codec_from_payload(media, payload_type); string? codec = CodecUtil.get_codec_from_payload(media, payload_type);
if (codec == null) return false; if (codec == null) return false;
if (unsupported_codecs.contains(codec)) return false; if (unsupported_codecs.contains(codec)) return false;
if (supported_codecs.contains(codec)) return true; if (supported_codecs.contains(codec)) return true;
string encode_element = codec_util.get_encode_element_name(media, codec); string? encode_element = codec_util.get_encode_element_name(media, codec);
string decode_element = codec_util.get_decode_element_name(media, codec); string? decode_element = codec_util.get_decode_element_name(media, codec);
if (encode_element == null || decode_element == null) { if (encode_element == null || decode_element == null) {
debug("No suitable encoder or decoder found for %s", codec); debug("No suitable encoder or decoder found for %s", codec);
unsupported_codecs.add(codec); unsupported_codecs.add(codec);

View file

@ -278,6 +278,22 @@ public class Dino.Plugins.Rtp.Plugin : RootInterface, VideoCallPlugin, Object {
Gst.deinit(); Gst.deinit();
} }
public bool supports(string media) {
if (rtpbin == null) return false;
if (media == "audio") {
if (get_devices("audio", false).is_empty) return false;
if (get_devices("audio", true).is_empty) return false;
}
if (media == "video") {
if (Gst.ElementFactory.make("gtksink", null) == null) return false;
if (get_devices("video", false).is_empty) return false;
}
return true;
}
public VideoCallWidget? create_widget(WidgetType type) { public VideoCallWidget? create_widget(WidgetType type) {
if (type == WidgetType.GTK) { if (type == WidgetType.GTK) {
return new VideoWidget(this); return new VideoWidget(this);