| author | Stephen Paul Weber
<singpolyma@singpolyma.net> 2023-10-05 04:22:36 UTC |
| committer | Stephen Paul Weber
<singpolyma@singpolyma.net> 2023-10-05 04:22:36 UTC |
| parent | cbd0f383d8e16615d50b8d98f4737944d9f4a0c0 |
| xmpp/Chat.hx | +6 | -0 |
| xmpp/Client.hx | +4 | -0 |
| xmpp/jingle/Session.hx | +46 | -2 |
diff --git a/xmpp/Chat.hx b/xmpp/Chat.hx index 25b5943..123a8cd 100644 --- a/xmpp/Chat.hx +++ b/xmpp/Chat.hx @@ -7,6 +7,7 @@ import xmpp.Color; import xmpp.GenericStream; import xmpp.ID; import xmpp.MessageSync; +import xmpp.jingle.PeerConnection; import xmpp.jingle.Session; import xmpp.queries.MAMQuery; using Lambda; @@ -87,6 +88,11 @@ abstract class Chat { session.propose(audio, video); } + public function addMedia(streams: Array<MediaStream>) { + if (callStatus() != "ongoing") throw "cannot add media when no call ongoing"; + jingleSessions.iterator().next().addMedia(streams); + } + public function acceptCall() { for (session in jingleSessions) { session.accept(); diff --git a/xmpp/Client.hx b/xmpp/Client.hx index 39ea31c..69e4119 100644 --- a/xmpp/Client.hx +++ b/xmpp/Client.hx @@ -210,6 +210,10 @@ class Client extends xmpp.EventEmitter { session.contentAdd(stanza); } + if (session != null && jingle.attr.get("action") == "content-accept") { + session.contentAccept(stanza); + } + if (session != null && jingle.attr.get("action") == "transport-info") { session.transportInfo(stanza); } diff --git a/xmpp/jingle/Session.hx b/xmpp/jingle/Session.hx index a01f667..3eba09a 100644 --- a/xmpp/jingle/Session.hx +++ b/xmpp/jingle/Session.hx @@ -13,7 +13,9 @@ interface Session { public function retract(): Void; public function terminate(): Void; public function contentAdd(stanza: Stanza): Void; + public function contentAccept(stanza: Stanza): Void; public function transportInfo(stanza: Stanza): Promise<Void>; + public function addMedia(streams: Array<MediaStream>): Void; public function callStatus():String; public function videoTracks():Array<MediaStreamTrack>; } @@ -55,6 +57,10 @@ class IncomingProposedSession implements Session { trace("Got content-add before session-initiate: " + sid, this); } + public function contentAccept(_) { + trace("Got content-accept before session-initiate: " + sid, this); + } + public function transportInfo(_) { trace("Got transport-info before session-initiate: " + sid, this); return Promise.resolve(null); @@ -80,6 +86,10 @@ class IncomingProposedSession implements Session { return session; } + public function addMedia(_) { + throw "Cannot add media before call starts"; + } + public function callStatus() { return "incoming"; } @@ -148,6 +158,10 @@ class OutgoingProposedSession implements Session { trace("Got content-add before session-initiate: " + sid, this); } + public function contentAccept(_) { + trace("Got content-accept before session-initiate: " + sid, this); + } + public function transportInfo(_) { trace("Got transport-info before session-initiate: " + sid, this); return Promise.resolve(null); @@ -167,6 +181,10 @@ class OutgoingProposedSession implements Session { return session; } + public function addMedia(_) { + throw "Cannot add media before call starts"; + } + public function callStatus() { return "outgoing"; } @@ -276,6 +294,7 @@ class InitiatedSession implements Session { m.attributes.push(new Attribute("setup", peerDtlsSetup)); } remoteDescription = remoteDescription.addContent(addThis); + // TODO: tie-break with any in-flight content-add we sent? pc.setRemoteDescription({ type: SdpType.OFFER, sdp: remoteDescription.toSdp() }).then((_) -> { afterMedia = () -> { setupLocalDescription("content-accept", addThis.media.map((m) -> m.mid)); @@ -285,6 +304,18 @@ class InitiatedSession implements Session { }); } + public function contentAccept(stanza: Stanza) { + if (remoteDescription == null) throw "Got content-accept before session-accept"; + // TODO: check if matches a content-add we sent? + + final addThis = SessionDescription.fromStanza(stanza, !initiator, remoteDescription); + for (m in addThis.media) { + m.attributes.push(new Attribute("setup", peerDtlsSetup)); + } + remoteDescription = remoteDescription.addContent(addThis); + pc.setRemoteDescription({ type: SdpType.ANSWER, sdp: remoteDescription.toSdp() }); + } + public function transportInfo(stanza: Stanza) { if (pc == null) { queuedInboundTransportInfo.push(stanza); @@ -302,6 +333,19 @@ class InitiatedSession implements Session { })).then((_) -> {}); } + public function addMedia(streams: Array<MediaStream>) { + if (pc == null) throw "tried to add media before PeerConnection exists"; + + final oldMids = localDescription.media.map((m) -> m.mid); + for (stream in streams) { + for (track in stream.getTracks()) { + pc.addTrack(track, stream); + } + } + + setupLocalDescription("content-add", oldMids, true); + } + public function callStatus() { return "ongoing"; } @@ -380,7 +424,7 @@ class InitiatedSession implements Session { }); } - private function setupLocalDescription(type: String, ?filterMedia: Array<String>) { + private function setupLocalDescription(type: String, ?filterMedia: Array<String>, ?filterOut: Bool = false) { return pc.setLocalDescription(null).then((_) -> { localDescription = SessionDescription.parse(pc.localDescription.sdp); var descriptionToSend = localDescription; @@ -388,7 +432,7 @@ class InitiatedSession implements Session { descriptionToSend = new SessionDescription( descriptionToSend.version, descriptionToSend.name, - descriptionToSend.media.filter((m) -> filterMedia.contains(m.mid)), + descriptionToSend.media.filter((m) -> filterOut ? !filterMedia.contains(m.mid) : filterMedia.contains(m.mid)), descriptionToSend.attributes, descriptionToSend.identificationTags );