| author | Stephen Paul Weber
<singpolyma@singpolyma.net> 2025-10-29 02:21:46 UTC |
| committer | Stephen Paul Weber
<singpolyma@singpolyma.net> 2025-10-29 02:23:59 UTC |
| parent | a50c707984da049d1a65deac6825e1eae4e26f9e |
| borogove/Chat.hx | +1 | -0 |
| borogove/Client.hx | +4 | -4 |
| borogove/calls/Session.hx | +55 | -20 |
diff --git a/borogove/Chat.hx b/borogove/Chat.hx index a0e6ddd..3781554 100644 --- a/borogove/Chat.hx +++ b/borogove/Chat.hx @@ -618,6 +618,7 @@ abstract class Chat { final session = new OutgoingProposedSession(client, JID.parse(chatId)); jingleSessions.set(session.sid, session); session.propose(audio, video); + return session; } @HaxeCBridge.noemit diff --git a/borogove/Client.hx b/borogove/Client.hx index 33355ec..777abcf 100644 --- a/borogove/Client.hx +++ b/borogove/Client.hx @@ -1278,9 +1278,9 @@ class Client extends EventEmitter { @param handler takes two arguments, the associated Chat ID and Session ID @returns token for use with removeEventListener **/ - public function addCallRingingListener(handler:(String,String)->Void) { + public function addCallRingingListener(handler:(Session)->Void) { return this.on("call/ringing", (data) -> { - handler(data.chatId, data.sid); + handler(data); return EventHandled; }); } @@ -1320,9 +1320,9 @@ class Client extends EventEmitter { the new MediaStreamTrack, and an array of any associated MediaStreams @returns token for use with removeEventListener **/ - public function addCallTrackListener(handler:(String,MediaStreamTrack,Array<MediaStream>)->Void) { + public function addCallTrackListener(handler:(InitiatedSession,MediaStreamTrack,Array<MediaStream>)->Void) { return this.on("call/track", (data) -> { - handler(data.chatId, data.track, data.streams); + handler(data.session, data.track, data.streams); return EventHandled; }); } diff --git a/borogove/calls/Session.hx b/borogove/calls/Session.hx index 7358814..5a4e1d7 100644 --- a/borogove/calls/Session.hx +++ b/borogove/calls/Session.hx @@ -44,6 +44,7 @@ interface Session { private function transportInfo(stanza: Stanza): Promise<Any>; public function addMedia(streams: Array<MediaStream>): Void; public function callStatus():CallStatus; + public function audioTracks():Array<MediaStreamTrack>; public function videoTracks():Array<MediaStreamTrack>; public function dtmf():Null<DTMFSender>; } @@ -103,6 +104,7 @@ class IncomingProposedSession implements Session { client.notifyMessageHandlers(stored[0], CorrectionEvent); }); client.getDirectChat(from.asBare().asString(), false).jingleSessions.remove(sid); + client.trigger("call/retract", { chatId: chatId, sid: sid }); } public function retract() { @@ -143,7 +145,8 @@ class IncomingProposedSession implements Session { }); } - public function initiate(stanza: Stanza) { + @:allow(borogove) + private function initiate(stanza: Stanza) { // TODO: check if new session has corrent media final session = InitiatedSession.fromSessionInitiate(client, stanza); if (session.sid != sid) throw "id mismatch"; @@ -153,18 +156,27 @@ class IncomingProposedSession implements Session { return session; } + @HaxeCBridge.noemit public function addMedia(_) { throw "Cannot add media before call starts"; } + @HaxeCBridge.noemit public function callStatus() { return Incoming; } + @HaxeCBridge.noemit + public function audioTracks() { + return []; + } + + @HaxeCBridge.noemit public function videoTracks() { return []; } + @HaxeCBridge.noemit public function dtmf() { return null; } @@ -187,13 +199,15 @@ class OutgoingProposedSession implements Session { private var audio = false; private var video = false; - public function new(client: Client, to: JID) { + @:allow(borogove) + private function new(client: Client, to: JID) { this.client = client; this.to = to; this._sid = ID.long(); } - public function propose(audio: Bool, video: Bool) { + @:allow(borogove) + private function propose(audio: Bool, video: Bool) { this.audio = audio; this.video = video; final event = new Stanza("propose", { xmlns: "urn:xmpp:jingle-message:0", id: sid }); @@ -210,14 +224,10 @@ class OutgoingProposedSession implements Session { .tag("store", { xmlns: "urn:xmpp:hints" }); client.sendStanza(stanza); client.notifyMessageHandlers(stored[0], DeliveryEvent); - client.trigger("call/ringing", { chatId: chatId, sid: sid }); + client.trigger("call/ringing", this); }); } - public function ring() { - trace("Tried to accept before initiate: " + sid, this); - } - public function hangup() { final event = new Stanza("retract", { xmlns: "urn:xmpp:jingle-message:0", id: sid }); final msg = mkCallMessage(to, client.jid, event); @@ -230,35 +240,43 @@ class OutgoingProposedSession implements Session { client.notifyMessageHandlers(stored[0], CorrectionEvent); }); client.getDirectChat(to.asBare().asString(), false).jingleSessions.remove(sid); + client.trigger("call/retract", { chatId: chatId, sid: sid }); } - public function retract() { + @:allow(borogove) + private function retract() { // Other side rejected the call client.trigger("call/retract", { chatId: chatId, sid: sid }); } - public function terminate() { + @:allow(borogove) + private function terminate() { trace("Tried to terminate before session-initiate: " + sid, this); } - public function contentAdd(_) { + @:allow(borogove) + private function contentAdd(_) { trace("Got content-add before session-initiate: " + sid, this); } - public function contentAccept(_) { + @:allow(borogove) + private function contentAccept(_) { trace("Got content-accept before session-initiate: " + sid, this); } - public function transportInfo(_) { + @:allow(borogove) + private function transportInfo(_) { trace("Got transport-info before session-initiate: " + sid, this); return Promise.resolve(null); } + @HaxeCBridge.noemit public function accept() { trace("Tried to accept before initiate: " + sid, this); } - public function initiate(stanza: Stanza) { + @:allow(borogove) + private function initiate(stanza: Stanza) { final jmi = stanza.getChild("proceed", "urn:xmpp:jingle-message:0"); if (jmi == null) throw "no jmi: " + stanza; if (jmi.attr.get("id") != sid) throw "sid doesn't match: " + jmi.attr.get("id") + " vs " + sid; @@ -269,6 +287,7 @@ class OutgoingProposedSession implements Session { return session; } + @HaxeCBridge.noemit public function addMedia(_) { throw "Cannot add media before call starts"; } @@ -277,10 +296,17 @@ class OutgoingProposedSession implements Session { return Outgoing; } + @HaxeCBridge.noemit + public function audioTracks() { + return []; + } + + @HaxeCBridge.noemit public function videoTracks() { return []; } + @HaxeCBridge.noemit public function dtmf() { return null; } @@ -381,15 +407,17 @@ class InitiatedSession implements Session { @:allow(borogove) private function terminate() { + client.trigger("call/retract", { chatId: chatId, sid: sid }); + if (pc == null) return; - pc.close(); - for (tranceiver in pc.getTransceivers()) { + final oldPc = pc; + pc = null; + oldPc.close(); + for (tranceiver in oldPc.getTransceivers()) { if (tranceiver.sender != null && tranceiver.sender.track != null) { tranceiver.sender.track.stop(); } } - pc = null; - client.trigger("call/retract", { chatId: chatId, sid: sid }); final event = new Stanza("finish", { xmlns: "urn:xmpp:jingle-message:0", id: sid }); final msg = mkCallMessage(counterpart, client.jid, event); @@ -487,7 +515,7 @@ class InitiatedSession implements Session { } public function callStatus() { - return if (pc == null || pc.connectionState == "connecting") { + return if (pc == null || pc.connectionState == "connecting" || pc.connectionState == "new") { Connecting; } else if (pc.connectionState == "failed" || pc.connectionState == "closed") { Failed; @@ -496,6 +524,13 @@ class InitiatedSession implements Session { } } + public function audioTracks(): Array<MediaStreamTrack> { + if (pc == null) return []; + return pc.getTransceivers() + .filter((t) -> t.receiver != null && t.receiver.track != null && t.receiver.track.kind == "audio" && !t.receiver.track.muted) + .map((t) -> t.receiver.track); + } + public function videoTracks(): Array<MediaStreamTrack> { if (pc == null) return []; return pc.getTransceivers() @@ -571,7 +606,7 @@ class InitiatedSession implements Session { client.getIceServers((servers) -> { pc = new PeerConnection({ iceServers: cast servers }, null); pc.addEventListener("track", (event) -> { - client.trigger("call/track", { chatId: chatId, track: event.track, streams: event.streams }); + client.trigger("call/track", { session: this, track: event.track, streams: event.streams }); }); pc.addEventListener("negotiationneeded", (event) -> { trace("renegotiate", event); return; }); pc.addEventListener("icecandidate", (event) -> {