aboutsummaryrefslogtreecommitdiff
path: root/libdino/src
diff options
context:
space:
mode:
Diffstat (limited to 'libdino/src')
-rw-r--r--libdino/src/entity/call.vala2
-rw-r--r--libdino/src/service/calls.vala110
2 files changed, 81 insertions, 31 deletions
diff --git a/libdino/src/entity/call.vala b/libdino/src/entity/call.vala
index b836e2cd..7891dae7 100644
--- a/libdino/src/entity/call.vala
+++ b/libdino/src/entity/call.vala
@@ -77,6 +77,8 @@ namespace Dino.Entities {
.value(db.call.state, State.ENDED); // No point in persisting states that can't survive a restart
if (end_time != null) {
builder.value(db.call.end_time, (long) end_time.to_unix());
+ } else {
+ builder.value(db.call.end_time, (long) local_time.to_unix());
}
id = (int) builder.perform();
diff --git a/libdino/src/service/calls.vala b/libdino/src/service/calls.vala
index 54c353b0..93636c03 100644
--- a/libdino/src/service/calls.vala
+++ b/libdino/src/service/calls.vala
@@ -21,15 +21,16 @@ namespace Dino {
public string id { get { return IDENTITY.id; } }
private StreamInteractor stream_interactor;
+ private Database db;
private Xep.JingleRtp.SessionInfoType session_info_type;
private HashMap<Account, HashMap<Call, string>> sid_by_call = new HashMap<Account, HashMap<Call, string>>(Account.hash_func, Account.equals_func);
private HashMap<Account, HashMap<string, Call>> call_by_sid = new HashMap<Account, HashMap<string, Call>>(Account.hash_func, Account.equals_func);
public HashMap<Call, Xep.Jingle.Session> sessions = new HashMap<Call, Xep.Jingle.Session>(Call.hash_func, Call.equals_func);
- public Call? mi_accepted_call = null;
- public string? mi_accepted_sid = null;
- public bool mi_accepted_video = false;
+ public HashMap<Account, Call> jmi_call = new HashMap<Account, Call>(Account.hash_func, Account.equals_func);
+ public HashMap<Account, string> jmi_sid = new HashMap<Account, string>(Account.hash_func, Account.equals_func);
+ public HashMap<Account, bool> jmi_video = new HashMap<Account, bool>(Account.hash_func, Account.equals_func);
private HashMap<Call, bool> counterpart_sends_video = new HashMap<Call, bool>(Call.hash_func, Call.equals_func);
private HashMap<Call, bool> we_should_send_video = new HashMap<Call, bool>(Call.hash_func, Call.equals_func);
@@ -46,6 +47,7 @@ namespace Dino {
private Calls(StreamInteractor stream_interactor, Database db) {
this.stream_interactor = stream_interactor;
+ this.db = db;
stream_interactor.account_added.connect(on_account_added);
}
@@ -75,22 +77,32 @@ namespace Dino {
stream_interactor.get_module(CallStore.IDENTITY).add_call(call, conversation);
- XmppStream? stream = stream_interactor.get_stream(conversation.account);
- if (stream == null) return null;
+ we_should_send_video[call] = video;
+ we_should_send_audio[call] = true;
- Gee.List<Jid> call_resources = yield get_call_resources(conversation);
- if (call_resources.size > 0) {
- Jid full_jid = call_resources[0];
- Xep.Jingle.Session session = yield stream.get_module(Xep.JingleRtp.Module.IDENTITY).start_call(stream, full_jid, video);
- sessions[call] = session;
- call_by_sid[call.account][session.sid] = call;
- sid_by_call[call.account][call] = session.sid;
+ if (yield has_jmi_resources(conversation)) {
+ XmppStream? stream = stream_interactor.get_stream(conversation.account);
+ jmi_call[conversation.account] = call;
+ jmi_video[conversation.account] = video;
+ jmi_sid[conversation.account] = Xmpp.random_uuid();
- connect_session_signals(call, session);
- }
+ call_by_sid[call.account][jmi_sid[conversation.account]] = call;
- we_should_send_video[call] = video;
- we_should_send_audio[call] = true;
+ var descriptions = new ArrayList<StanzaNode>();
+ descriptions.add(new StanzaNode.build("description", "urn:xmpp:jingle:apps:rtp:1").add_self_xmlns().put_attribute("media", "audio"));
+ if (video) {
+ descriptions.add(new StanzaNode.build("description", "urn:xmpp:jingle:apps:rtp:1").add_self_xmlns().put_attribute("media", "video"));
+ }
+
+ stream.get_module(Xmpp.Xep.JingleMessageInitiation.Module.IDENTITY).send_session_propose_to_peer(stream, conversation.counterpart, jmi_sid[call.account], descriptions);
+ } else {
+ Gee.List<Jid> call_resources = yield get_call_resources(conversation);
+ if (call_resources.size == 0) {
+ warning("No call resources");
+ return null;
+ }
+ yield call_resource(conversation.account, call_resources[0], call, video);
+ }
conversation.last_active = call.time;
call_outgoing(call, conversation);
@@ -98,6 +110,17 @@ namespace Dino {
return call;
}
+ private async void call_resource(Account account, Jid full_jid, Call call, bool video, string? sid = null) {
+ XmppStream? stream = stream_interactor.get_stream(account);
+ if (stream == null) return;
+
+ Xep.Jingle.Session session = yield stream.get_module(Xep.JingleRtp.Module.IDENTITY).start_call(stream, full_jid, video, sid);
+ sessions[call] = session;
+ sid_by_call[call.account][call] = session.sid;
+
+ connect_session_signals(call, session);
+ }
+
public void end_call(Conversation conversation, Call call) {
XmppStream? stream = stream_interactor.get_stream(call.account);
if (stream == null) return;
@@ -130,15 +153,17 @@ namespace Dino {
}
} else {
// Only a JMI so far
- XmppStream stream = stream_interactor.get_stream(call.account);
+ Account account = call.account;
+ string sid = sid_by_call[call.account][call];
+ XmppStream stream = stream_interactor.get_stream(account);
if (stream == null) return;
- mi_accepted_call = call;
- mi_accepted_sid = sid_by_call[call.account][call];
- mi_accepted_video = we_should_send_video[call];
+ jmi_call[account] = call;
+ jmi_sid[account] = sid;
+ jmi_video[account] = we_should_send_video[call];
- stream.get_module(Xep.JingleMessageInitiation.Module.IDENTITY).send_session_accept_to_self(stream, mi_accepted_sid);
- stream.get_module(Xep.JingleMessageInitiation.Module.IDENTITY).send_session_proceed_to_peer(stream, call.counterpart, mi_accepted_sid);
+ stream.get_module(Xep.JingleMessageInitiation.Module.IDENTITY).send_session_accept_to_self(stream, sid);
+ stream.get_module(Xep.JingleMessageInitiation.Module.IDENTITY).send_session_proceed_to_peer(stream, call.counterpart, sid);
}
}
@@ -211,7 +236,11 @@ 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.
}
- public async Gee.List<Jid> get_call_resources(Conversation conversation) {
+ public async bool can_do_calls(Conversation conversation) {
+ return (yield get_call_resources(conversation)).size > 0 || yield has_jmi_resources(conversation);
+ }
+
+ private async Gee.List<Jid> get_call_resources(Conversation conversation) {
ArrayList<Jid> ret = new ArrayList<Jid>();
XmppStream? stream = stream_interactor.get_stream(conversation.account);
@@ -228,6 +257,15 @@ namespace Dino {
return ret;
}
+ private async bool has_jmi_resources(Conversation conversation) {
+ int64 jmi_resources = db.entity.select()
+ .with(db.entity.jid_id, "=", db.get_jid_id(conversation.counterpart))
+ .join_with(db.entity_feature, db.entity.caps_hash, db.entity_feature.entity)
+ .with(db.entity_feature.feature, "=", Xep.JingleMessageInitiation.NS_URI)
+ .count();
+ return jmi_resources > 0;
+ }
+
public bool should_we_send_video(Call call) {
return we_should_send_video[call];
}
@@ -252,13 +290,14 @@ namespace Dino {
}
// Session might have already been accepted via Jingle Message Initiation
- bool already_accepted = mi_accepted_sid == session.sid && mi_accepted_call.account.equals(account) &&
- mi_accepted_call.counterpart.equals_bare(session.peer_full_jid) &&
- mi_accepted_video == counterpart_wants_video;
+ bool already_accepted = jmi_sid.contains(account) &&
+ jmi_sid[account] == session.sid && jmi_call[account].account.equals(account) &&
+ jmi_call[account].counterpart.equals_bare(session.peer_full_jid) &&
+ jmi_video[account] == counterpart_wants_video;
Call? call = null;
if (already_accepted) {
- call = mi_accepted_call;
+ call = jmi_call[account];
} else {
call = create_received_call(account, session.peer_full_jid, account.full_jid, counterpart_wants_video);
}
@@ -334,16 +373,15 @@ namespace Dino {
}
if (call.state == Call.State.IN_PROGRESS) {
call.state = Call.State.ENDED;
- call_terminated(call, reason_name, reason_text);
} else if (call.state == Call.State.RINGING || call.state == Call.State.ESTABLISHING) {
if (reason_name == Xep.Jingle.ReasonElement.DECLINE) {
call.state = Call.State.DECLINED;
} else {
call.state = Call.State.FAILED;
}
- call_terminated(call, reason_name, reason_text);
}
+ call_terminated(call, reason_name, reason_text);
remove_call_from_datastructures(call);
}
@@ -478,11 +516,21 @@ namespace Dino {
mi_module.session_accepted.connect((from, sid) => {
if (!call_by_sid[account].has_key(sid)) return;
- // Ignore session-accepted from ourselves
- if (!from.equals(account.full_jid)) {
+ if (from.equals_bare(account.bare_jid)) { // Carboned message from our account
+ // Ignore carbon from ourselves
+ if (from.equals(account.full_jid)) return;
+
Call call = call_by_sid[account][sid];
call.state = Call.State.OTHER_DEVICE_ACCEPTED;
remove_call_from_datastructures(call);
+ } else if (from.equals_bare(call_by_sid[account][sid].counterpart)) { // Message from our peer
+ // We proposed the call
+ if (jmi_sid.has_key(account) && jmi_sid[account] == sid) {
+ call_resource(account, from, jmi_call[account], jmi_video[account], jmi_sid[account]);
+ jmi_call.unset(account);
+ jmi_sid.unset(account);
+ jmi_video.unset(account);
+ }
}
});
mi_module.session_rejected.connect((from, to, sid) => {