aboutsummaryrefslogtreecommitdiff
path: root/client-web/source/user/remote.ts
diff options
context:
space:
mode:
Diffstat (limited to 'client-web/source/user/remote.ts')
-rw-r--r--client-web/source/user/remote.ts133
1 files changed, 68 insertions, 65 deletions
diff --git a/client-web/source/user/remote.ts b/client-web/source/user/remote.ts
index fbab9c4..677d362 100644
--- a/client-web/source/user/remote.ts
+++ b/client-web/source/user/remote.ts
@@ -6,72 +6,72 @@
/// <reference lib="dom" />
import { RelayMessage } from "../../../common/packets.d.ts";
-import { Resource } from "../resource/mod.ts";
import { notify } from "../helper.ts";
import { ROOM_CONTAINER, RTC_CONFIG } from "../index.ts"
import { log } from "../logger.ts"
import { PREFS } from "../preferences/mod.ts";
+import { new_remote_resource, RemoteResource } from "../resource/mod.ts";
import { Room } from "../room.ts"
import { TrackHandle } from "../track_handle.ts";
import { User } from "./mod.ts";
-import { TrackResource } from "../resource/track.ts";
export class RemoteUser extends User {
- peer: RTCPeerConnection
+ pc: RTCPeerConnection
senders: Map<string, RTCRtpSender> = new Map()
data_channels: Map<string, RTCDataChannel> = new Map()
+ resources: Map<string, RemoteResource> = new Map()
negotiation_busy = false
constructor(room: Room, id: number) {
super(room, id)
- room.remote_users.set(this.id, this)
+ room.remote_users.set(id, this)
log("usermodel", `added remote user: ${this.display_name}`)
- this.peer = new RTCPeerConnection(RTC_CONFIG)
- this.peer.onicecandidate = ev => {
+ this.pc = new RTCPeerConnection(RTC_CONFIG)
+ this.pc.onicecandidate = ev => {
if (!ev.candidate) return
room.signaling.send_relay({ ice_candidate: ev.candidate.toJSON() }, this.id)
log("webrtc", `ICE candidate set`, ev.candidate)
this.update_stats()
}
- this.peer.ontrack = ev => {
+ this.pc.ontrack = ev => {
console.log(ev)
const t = ev.track
const id = ev.streams[0]?.id
if (!id) { ev.transceiver.stop(); return log({ scope: "media", warn: true }, "got a track without stream") }
const r = this.resources.get(id)
if (!r) { ev.transceiver.stop(); return log({ scope: "media", warn: true }, "got an unassociated track") }
- if (r instanceof TrackResource) r.track = new TrackHandle(t);
- else { ev.transceiver.stop(); return log({ scope: "media", warn: true }, "got a track for a resource that should use data channel") }
+ r.on_enable(new TrackHandle(t), () => {
+ this.request_resource_stop(r)
+ })
+ // else { ev.transceiver.stop(); return log({ scope: "media", warn: true }, "got a track for a resource that should use data channel") }
log("media", `remote track: ${this.display_name}`, t)
this.update_stats()
}
- this.peer.onnegotiationneeded = () => {
+ this.pc.onnegotiationneeded = () => {
log("webrtc", `negotiation needed: ${this.display_name}`)
- if (this.negotiation_busy && this.peer.signalingState == "stable") return
+ if (this.negotiation_busy && this.pc.signalingState == "stable") return
this.offer()
this.update_stats()
}
- this.peer.onicecandidateerror = () => {
+ this.pc.onicecandidateerror = () => {
log({ scope: "webrtc", warn: true }, "ICE error")
this.update_stats()
}
- this.peer.oniceconnectionstatechange = () => { this.update_stats() }
- this.peer.onicegatheringstatechange = () => { this.update_stats() }
- this.peer.onsignalingstatechange = () => { this.update_stats() }
- this.peer.onconnectionstatechange = () => { this.update_stats() }
+ this.pc.oniceconnectionstatechange = () => { this.update_stats() }
+ this.pc.onicegatheringstatechange = () => { this.update_stats() }
+ this.pc.onsignalingstatechange = () => { this.update_stats() }
+ this.pc.onconnectionstatechange = () => { this.update_stats() }
this.update_stats()
}
leave() {
log("usermodel", `remove remote user: ${this.display_name}`)
- this.peer.close()
+ this.pc.close()
this.room.remote_users.delete(this.id)
- super.leave()
ROOM_CONTAINER.removeChild(this.el)
if (PREFS.notify_leave) notify(`${this.display_name} left`)
}
-
on_relay(message: RelayMessage) {
if (message.chat) this.room.chat.add_message(this, message.chat)
if (message.ice_candidate) this.add_ice_candidate(message.ice_candidate)
@@ -83,12 +83,10 @@ export class RemoteUser extends User {
}
if (message.provide) {
console.log(message.provide.id);
- const d = Resource.create(this, message.provide)
+ const d = new_remote_resource(this, message.provide)
if (!d) return
- if (d instanceof TrackResource) {
- if (d.info.kind == "video" && PREFS.optional_video_default_enable) d.request()
- if (d.info.kind == "audio" && PREFS.optional_audio_default_enable) d.request()
- }
+ if (d.info.kind == "track" && d.info.track_kind == "audio" && PREFS.optional_audio_default_enable) this.request_resource(d)
+ if (d.info.kind == "track" && d.info.track_kind == "video" && PREFS.optional_video_default_enable) this.request_resource(d)
this.el.append(d.el)
this.resources.set(message.provide.id, d)
}
@@ -99,53 +97,36 @@ export class RemoteUser extends User {
if (message.request) {
const r = this.room.local_user.resources.get(message.request.id)
if (!r) return log({ scope: "*", warn: true }, "somebody requested an unknown resource")
- if (r instanceof TrackResource) {
- if (!r.track) throw new Error("local resources not avail");
- const sender = this.peer.addTrack(r.track.track, r.track.stream)
- this.senders.set(r.track.id, sender)
- r.track.addEventListener("end", () => { this.senders.delete(r.track?.id ?? "") })
- }
+ const channel = r.on_request(this, label => this.pc.createDataChannel(label))
+ if (channel instanceof TrackHandle) {
+ const sender = this.pc.addTrack(channel.track, channel.stream)
+ this.senders.set(channel.id, sender)
+ channel.addEventListener("end", () => { this.senders.delete(r.info.id) })
+ } else if (channel instanceof RTCDataChannel) {
+ this.data_channels.set(r.info.id, channel)
+ channel.addEventListener("close", () => this.data_channels.delete(r.info.id))
+ } else throw new Error("unreachable");
}
if (message.request_stop) {
const sender = this.senders.get(message.request_stop.id)
if (!sender) return log({ scope: "*", warn: true }, "somebody requested us to stop transmitting an unknown resource")
- this.peer.removeTrack(sender)
+ this.pc.removeTrack(sender)
}
}
send_to(message: RelayMessage) {
this.room.signaling.send_relay(message, this.id)
}
+ request_resource(r: RemoteResource) { this.send_to({ request: { id: r.info.id } }) }
+ request_resource_stop(r: RemoteResource) { this.send_to({ request_stop: { id: r.info.id } }) }
- async update_stats() {
- if (!PREFS.webrtc_debug) return
- try {
- const stats = await this.peer.getStats()
- let stuff = "";
- stuff += `ice-conn=${this.peer.iceConnectionState}; ice-gathering=${this.peer.iceGatheringState}; ice-trickle=${this.peer.canTrickleIceCandidates}; signaling=${this.peer.signalingState};\n`
- stats.forEach(s => {
- console.log("stat", s);
- if (s.type == "candidate-pair" && s.selected) {
- //@ts-ignore spec is weird....
- if (!stats.get) return console.warn("no RTCStatsReport.get");
- //@ts-ignore spec is weird....
- const cpstat = stats.get(s.localCandidateId)
- if (!cpstat) return console.warn("no stats");
- console.log("cp", cpstat);
- stuff += `via ${cpstat.candidateType}:${cpstat.protocol}:${cpstat.address}\n`
- } else if (s.type == "codec") {
- stuff += `using ${s.codecType ?? "dec/enc"}:${s.mimeType}(${s.sdpFmtpLine})\n`
- }
- })
- this.stats_el.textContent = stuff
- } catch (e) {
- console.warn(e);
- }
+ add_ice_candidate(candidate: RTCIceCandidateInit) {
+ this.pc.addIceCandidate(new RTCIceCandidate(candidate))
+ this.update_stats()
}
-
async offer() {
this.negotiation_busy = true
- const offer_description = await this.peer.createOffer()
- await this.peer.setLocalDescription(offer_description)
+ const offer_description = await this.pc.createOffer()
+ await this.pc.setLocalDescription(offer_description)
log("webrtc", `sent offer: ${this.display_name}`, { offer: offer_description.sdp })
this.send_to({ offer: offer_description.sdp })
}
@@ -153,12 +134,12 @@ export class RemoteUser extends User {
this.negotiation_busy = true
log("webrtc", `got offer: ${this.display_name}`, { offer })
const offer_description = new RTCSessionDescription({ sdp: offer, type: "offer" })
- await this.peer.setRemoteDescription(offer_description)
+ await this.pc.setRemoteDescription(offer_description)
this.answer()
}
async answer() {
- const answer_description = await this.peer.createAnswer()
- await this.peer.setLocalDescription(answer_description)
+ const answer_description = await this.pc.createAnswer()
+ await this.pc.setLocalDescription(answer_description)
log("webrtc", `sent answer: ${this.display_name}`, { answer: answer_description.sdp })
this.send_to({ answer: answer_description.sdp })
this.negotiation_busy = false
@@ -166,12 +147,34 @@ export class RemoteUser extends User {
async on_answer(answer: string) {
log("webrtc", `got answer: ${this.display_name}`, { answer })
const answer_description = new RTCSessionDescription({ sdp: answer, type: "answer" })
- await this.peer.setRemoteDescription(answer_description)
+ await this.pc.setRemoteDescription(answer_description)
this.negotiation_busy = false
}
- add_ice_candidate(candidate: RTCIceCandidateInit) {
- this.peer.addIceCandidate(new RTCIceCandidate(candidate))
- this.update_stats()
+ async update_stats() {
+ if (!PREFS.webrtc_debug) return
+ try {
+ const stats = await this.pc.getStats()
+ let stuff = "";
+ stuff += `ice-conn=${this.pc.iceConnectionState}; ice-gathering=${this.pc.iceGatheringState}; ice-trickle=${this.pc.canTrickleIceCandidates}; signaling=${this.pc.signalingState};\n`
+ stats.forEach(s => {
+ console.log("stat", s);
+ if (s.type == "candidate-pair" && s.selected) {
+ //@ts-ignore trust me, this works
+ if (!stats.get) return console.warn("no RTCStatsReport.get");
+ //@ts-ignore trust me, this works
+ const cpstat = stats.get(s.localCandidateId)
+ if (!cpstat) return console.warn("no stats");
+ console.log("cp", cpstat);
+ stuff += `via ${cpstat.candidateType}:${cpstat.protocol}:${cpstat.address}\n`
+ } else if (s.type == "codec") {
+ stuff += `using ${s.codecType ?? "dec/enc"}:${s.mimeType}(${s.sdpFmtpLine})\n`
+ }
+ })
+ this.stats_el.textContent = stuff
+ } catch (e) {
+ console.warn(e);
+ }
}
+
} \ No newline at end of file