fixes
this makes chromium happy, but firefox isn't, can't test webkit, not sure on that
This commit is contained in:
parent
472031f9bc
commit
2ffc0ca94c
1 changed files with 144 additions and 93 deletions
|
@ -8,7 +8,10 @@ class VoiceFactory {
|
||||||
voices = new Map<string, Map<string, Voice>>();
|
voices = new Map<string, Map<string, Voice>>();
|
||||||
voiceChannels = new Map<string, Voice>();
|
voiceChannels = new Map<string, Voice>();
|
||||||
currentVoice?: Voice;
|
currentVoice?: Voice;
|
||||||
guildUrlMap = new Map<string, {url?: string; geturl: Promise<void>; gotUrl: () => void}>();
|
guildUrlMap = new Map<
|
||||||
|
string,
|
||||||
|
{url?: string; token?: string; geturl: Promise<void>; gotUrl: () => void}
|
||||||
|
>();
|
||||||
makeVoice(guildid: string, channelId: string, settings: Voice["settings"]) {
|
makeVoice(guildid: string, channelId: string, settings: Voice["settings"]) {
|
||||||
let guild = this.voices.get(guildid);
|
let guild = this.voices.get(guildid);
|
||||||
if (!guild) {
|
if (!guild) {
|
||||||
|
@ -26,10 +29,11 @@ class VoiceFactory {
|
||||||
onJoin = (_voice: Voice) => {};
|
onJoin = (_voice: Voice) => {};
|
||||||
onLeave = (_voice: Voice) => {};
|
onLeave = (_voice: Voice) => {};
|
||||||
joinVoice(channelId: string, guildId: string) {
|
joinVoice(channelId: string, guildId: string) {
|
||||||
if (this.currentVoice) {
|
const voice = this.voiceChannels.get(channelId);
|
||||||
|
if (this.currentVoice && this.currentVoice.ws) {
|
||||||
this.currentVoice.leave();
|
this.currentVoice.leave();
|
||||||
}
|
}
|
||||||
const voice = this.voiceChannels.get(channelId);
|
|
||||||
if (!voice) throw new Error(`Voice ${channelId} does not exist`);
|
if (!voice) throw new Error(`Voice ${channelId} does not exist`);
|
||||||
voice.join();
|
voice.join();
|
||||||
this.currentVoice = voice;
|
this.currentVoice = voice;
|
||||||
|
@ -38,7 +42,7 @@ class VoiceFactory {
|
||||||
d: {
|
d: {
|
||||||
guild_id: guildId,
|
guild_id: guildId,
|
||||||
channel_id: channelId,
|
channel_id: channelId,
|
||||||
self_mute: true, //todo
|
self_mute: false, //todo
|
||||||
self_deaf: false, //todo
|
self_deaf: false, //todo
|
||||||
self_video: false, //What is this? I have some guesses
|
self_video: false, //What is this? I have some guesses
|
||||||
flags: 2, //?????
|
flags: 2, //?????
|
||||||
|
@ -71,6 +75,7 @@ class VoiceFactory {
|
||||||
const obj = this.guildUrlMap.get(update.d.guild_id);
|
const obj = this.guildUrlMap.get(update.d.guild_id);
|
||||||
if (!obj) return;
|
if (!obj) return;
|
||||||
obj.url = update.d.endpoint;
|
obj.url = update.d.endpoint;
|
||||||
|
obj.token = update.d.token;
|
||||||
obj.gotUrl();
|
obj.gotUrl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +93,7 @@ class Voice {
|
||||||
}
|
}
|
||||||
readonly userid: string;
|
readonly userid: string;
|
||||||
settings: {bitrate: number};
|
settings: {bitrate: number};
|
||||||
urlobj: {url?: string; geturl: Promise<void>; gotUrl: () => void};
|
urlobj: {url?: string; token?: string; geturl: Promise<void>; gotUrl: () => void};
|
||||||
constructor(userid: string, settings: Voice["settings"], urlobj: Voice["urlobj"]) {
|
constructor(userid: string, settings: Voice["settings"], urlobj: Voice["urlobj"]) {
|
||||||
this.userid = userid;
|
this.userid = userid;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
@ -163,7 +168,13 @@ class Voice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
offer?: string;
|
hoffer?: string;
|
||||||
|
get offer() {
|
||||||
|
return this.hoffer;
|
||||||
|
}
|
||||||
|
set offer(e: string | undefined) {
|
||||||
|
this.hoffer = e;
|
||||||
|
}
|
||||||
cleanServerSDP(sdp: string): string {
|
cleanServerSDP(sdp: string): string {
|
||||||
const pc = this.pc;
|
const pc = this.pc;
|
||||||
if (!pc) throw new Error("pc isn't defined");
|
if (!pc) throw new Error("pc isn't defined");
|
||||||
|
@ -188,7 +199,7 @@ class Voice {
|
||||||
.value as string;
|
.value as string;
|
||||||
const candidate = (parsed1.atr.get("candidate") as Set<string>).values().next().value as string;
|
const candidate = (parsed1.atr.get("candidate") as Set<string>).values().next().value as string;
|
||||||
let build = `v=0\r
|
let build = `v=0\r
|
||||||
o=- 1420070400000 0 IN IP4 127.0.0.1\r
|
o=- 1420070400000 0 IN IP4 ${this.urlobj.url}\r
|
||||||
s=-\r
|
s=-\r
|
||||||
t=0 0\r
|
t=0 0\r
|
||||||
a=msid-semantic: WMS *\r
|
a=msid-semantic: WMS *\r
|
||||||
|
@ -210,7 +221,7 @@ a=fmtp:111 minptime=10;useinbandfec=1;usedtx=1\r
|
||||||
a=rtcp:${rtcport}\r
|
a=rtcp:${rtcport}\r
|
||||||
a=rtcp-fb:111 transport-cc\r
|
a=rtcp-fb:111 transport-cc\r
|
||||||
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r
|
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r
|
||||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01/r/n
|
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||||
a=setup:passive\r
|
a=setup:passive\r
|
||||||
a=mid:${bundles[i]}\r
|
a=mid:${bundles[i]}\r
|
||||||
a=maxptime:60\r
|
a=maxptime:60\r
|
||||||
|
@ -222,23 +233,24 @@ a=candidate:${candidate}\r
|
||||||
a=rtcp-mux\r`;
|
a=rtcp-mux\r`;
|
||||||
} else {
|
} else {
|
||||||
build += `
|
build += `
|
||||||
m=video ${rtcport} UDP/TLS/RTP/SAVPF 102 103\r
|
m=video ${rtcport} UDP/TLS/RTP/SAVPF 103 104\r
|
||||||
${cline}\r
|
${cline}\r
|
||||||
a=rtpmap:102 H264/90000\r
|
a=rtpmap:103 H264/90000\r
|
||||||
a=rtpmap:103 rtx/90000\r
|
a=rtpmap:104 rtx/90000\r
|
||||||
a=fmtp:102 x-google-max-bitrate=2500;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r
|
a=fmtp:103 x-google-max-bitrate=2500;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r
|
||||||
a=fmtp:103 apt=102\r
|
a=fmtp:104 apt=103\r
|
||||||
a=rtcp:${rtcport}\r
|
a=rtcp:${rtcport}\r
|
||||||
a=rtcp-fb:102 ccm fir\r
|
a=rtcp-fb:103 ccm fir\r
|
||||||
a=rtcp-fb:102 nack\r
|
a=rtcp-fb:103 nack\r
|
||||||
a=rtcp-fb:102 nack pli\r
|
a=rtcp-fb:103 nack pli\r
|
||||||
a=rtcp-fb:102 goog-remb\r
|
a=rtcp-fb:103 goog-remb\r
|
||||||
a=rtcp-fb:102 transport-cc\r
|
a=rtcp-fb:103 transport-cc\r
|
||||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time/r/n
|
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01/r/n
|
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||||
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset\r
|
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset\r
|
||||||
a=extmap:13 urn:3gpp:video-orientation\r
|
a=extmap:13 urn:3gpp:video-orientation\r
|
||||||
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay/r/na=setup:passive/r/n
|
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
|
||||||
|
a=setup:passive
|
||||||
a=mid:${bundles[i]}\r
|
a=mid:${bundles[i]}\r
|
||||||
a=${mode}\r
|
a=${mode}\r
|
||||||
a=ice-ufrag:${ICE_UFRAG}\r
|
a=ice-ufrag:${ICE_UFRAG}\r
|
||||||
|
@ -254,25 +266,13 @@ a=rtcp-mux\r`;
|
||||||
}
|
}
|
||||||
counter?: string;
|
counter?: string;
|
||||||
negotationneeded() {
|
negotationneeded() {
|
||||||
if (this.pc && this.offer) {
|
if (this.pc) {
|
||||||
const pc = this.pc;
|
const pc = this.pc;
|
||||||
pc.addEventListener("negotiationneeded", async () => {
|
const sendOffer = async () => {
|
||||||
this.offer = (
|
console.trace("neg need");
|
||||||
await pc.createOffer({
|
await pc.setLocalDescription();
|
||||||
offerToReceiveAudio: true,
|
console.warn(pc.localDescription?.sdp, this.offer);
|
||||||
offerToReceiveVideo: true,
|
|
||||||
})
|
|
||||||
).sdp;
|
|
||||||
await pc.setLocalDescription({sdp: this.offer});
|
|
||||||
|
|
||||||
if (!this.counter) throw new Error("counter isn't defined");
|
|
||||||
const counter = this.counter;
|
|
||||||
const remote: {sdp: string; type: RTCSdpType} = {
|
|
||||||
sdp: this.cleanServerSDP(counter),
|
|
||||||
type: "answer",
|
|
||||||
};
|
|
||||||
console.log(remote);
|
|
||||||
await pc.setRemoteDescription(remote);
|
|
||||||
const senders = this.senders.difference(this.ssrcMap);
|
const senders = this.senders.difference(this.ssrcMap);
|
||||||
for (const sender of senders) {
|
for (const sender of senders) {
|
||||||
for (const thing of (await sender.getStats()) as Map<string, any>) {
|
for (const thing of (await sender.getStats()) as Map<string, any>) {
|
||||||
|
@ -282,8 +282,44 @@ a=rtcp-mux\r`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
pc.addEventListener("negotiationneeded", async () => {
|
||||||
|
await sendOffer();
|
||||||
console.log(this.ssrcMap);
|
console.log(this.ssrcMap);
|
||||||
});
|
});
|
||||||
|
pc.addEventListener("signalingstatechange", async () => {
|
||||||
|
while (!this.counter) await new Promise((res) => setTimeout(res, 100));
|
||||||
|
if (this.pc && this.counter) {
|
||||||
|
if (pc.signalingState === "have-local-offer") {
|
||||||
|
const counter = this.counter;
|
||||||
|
const remote: {sdp: string; type: RTCSdpType} = {
|
||||||
|
sdp: this.cleanServerSDP(counter),
|
||||||
|
type: "answer",
|
||||||
|
};
|
||||||
|
console.log(remote);
|
||||||
|
await pc.setRemoteDescription(remote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
pc.addEventListener("connectionstatechange", async () => {
|
||||||
|
if (pc.connectionState === "connecting") {
|
||||||
|
await pc.setLocalDescription();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
pc.addEventListener("icegatheringstatechange", async () => {
|
||||||
|
console.log("icegatheringstatechange", pc.iceGatheringState, this.pc, this.counter);
|
||||||
|
if (this.pc && this.counter) {
|
||||||
|
if (pc.iceGatheringState === "complete") {
|
||||||
|
console.log("icegatheringstatechange");
|
||||||
|
const counter = this.counter;
|
||||||
|
const remote: {sdp: string; type: RTCSdpType} = {
|
||||||
|
sdp: this.cleanServerSDP(counter),
|
||||||
|
type: "answer",
|
||||||
|
};
|
||||||
|
await pc.setRemoteDescription(remote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async makeOp12(
|
async makeOp12(
|
||||||
|
@ -294,6 +330,7 @@ a=rtcp-mux\r`;
|
||||||
sender = sender[0];
|
sender = sender[0];
|
||||||
}
|
}
|
||||||
if (this.ws) {
|
if (this.ws) {
|
||||||
|
console.log(this.ssrcMap);
|
||||||
this.ws.send(
|
this.ws.send(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
op: 12,
|
op: 12,
|
||||||
|
@ -374,58 +411,26 @@ a=rtcp-mux\r`;
|
||||||
}
|
}
|
||||||
async continueWebRTC(data: sdpback) {
|
async continueWebRTC(data: sdpback) {
|
||||||
if (this.pc && this.offer) {
|
if (this.pc && this.offer) {
|
||||||
const pc = this.pc;
|
|
||||||
this.negotationneeded();
|
|
||||||
this.status = "Starting Audio streams";
|
|
||||||
const audioStream = await navigator.mediaDevices.getUserMedia({video: false, audio: true});
|
|
||||||
for (const track of audioStream.getAudioTracks()) {
|
|
||||||
//Add track
|
|
||||||
|
|
||||||
this.setupMic(audioStream);
|
|
||||||
const sender = pc.addTrack(track);
|
|
||||||
this.senders.add(sender);
|
|
||||||
console.log(sender);
|
|
||||||
}
|
|
||||||
for (let i = 0; i < 10; i++) {
|
|
||||||
pc.addTransceiver("audio", {
|
|
||||||
direction: "recvonly",
|
|
||||||
streams: [],
|
|
||||||
sendEncodings: [{active: true, maxBitrate: this.settings.bitrate}],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (let i = 0; i < 10; i++) {
|
|
||||||
pc.addTransceiver("video", {
|
|
||||||
direction: "recvonly",
|
|
||||||
streams: [],
|
|
||||||
sendEncodings: [{active: true, maxBitrate: this.settings.bitrate}],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.counter = data.d.sdp;
|
this.counter = data.d.sdp;
|
||||||
pc.ontrack = async (e) => {
|
|
||||||
this.status = "Done";
|
|
||||||
if (e.track.kind === "video") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const media = e.streams[0];
|
|
||||||
console.log("got audio:", e);
|
|
||||||
for (const track of media.getTracks()) {
|
|
||||||
console.log(track);
|
|
||||||
}
|
|
||||||
|
|
||||||
const context = new AudioContext();
|
|
||||||
await context.resume();
|
|
||||||
const ss = context.createMediaStreamSource(media);
|
|
||||||
console.log(media);
|
|
||||||
ss.connect(context.destination);
|
|
||||||
new Audio().srcObject = media; //weird I know, but it's for chromium/webkit bug
|
|
||||||
this.recivers.add(e.receiver);
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
this.status = "Connection failed";
|
this.status = "Connection failed";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reciverMap = new Map<number, RTCRtpReceiver>();
|
reciverMap = new Map<number, RTCRtpReceiver>();
|
||||||
|
off?: Promise<RTCSessionDescriptionInit>;
|
||||||
|
async makeOffer() {
|
||||||
|
if (this.pc?.localDescription?.sdp) return {sdp: this.pc?.localDescription?.sdp};
|
||||||
|
if (this.off) return this.off;
|
||||||
|
return (this.off = new Promise<RTCSessionDescriptionInit>(async (res) => {
|
||||||
|
if (!this.pc) throw new Error("stupid");
|
||||||
|
console.error("stupid!");
|
||||||
|
const offer = await this.pc.createOffer({
|
||||||
|
offerToReceiveAudio: true,
|
||||||
|
offerToReceiveVideo: true,
|
||||||
|
});
|
||||||
|
res(offer);
|
||||||
|
}));
|
||||||
|
}
|
||||||
async figureRecivers() {
|
async figureRecivers() {
|
||||||
await new Promise((res) => setTimeout(res, 500));
|
await new Promise((res) => setTimeout(res, 500));
|
||||||
for (const reciver of this.recivers) {
|
for (const reciver of this.recivers) {
|
||||||
|
@ -441,15 +446,61 @@ a=rtcp-mux\r`;
|
||||||
async startWebRTC() {
|
async startWebRTC() {
|
||||||
this.status = "Making offer";
|
this.status = "Making offer";
|
||||||
const pc = new RTCPeerConnection();
|
const pc = new RTCPeerConnection();
|
||||||
this.pc = pc;
|
pc.ontrack = async (e) => {
|
||||||
const offer = await pc.createOffer({
|
this.status = "Done";
|
||||||
offerToReceiveAudio: true,
|
if (e.track.kind === "video") {
|
||||||
offerToReceiveVideo: true,
|
return;
|
||||||
});
|
}
|
||||||
this.status = "Starting RTC connection";
|
|
||||||
const sdp = offer.sdp;
|
|
||||||
this.offer = sdp;
|
|
||||||
|
|
||||||
|
const media = e.streams[0];
|
||||||
|
console.log("got audio:", e);
|
||||||
|
for (const track of media.getTracks()) {
|
||||||
|
console.log(track);
|
||||||
|
}
|
||||||
|
|
||||||
|
const context = new AudioContext();
|
||||||
|
await context.resume();
|
||||||
|
const ss = context.createMediaStreamSource(media);
|
||||||
|
console.log(media);
|
||||||
|
ss.connect(context.destination);
|
||||||
|
new Audio().srcObject = media; //weird I know, but it's for chromium/webkit bug
|
||||||
|
this.recivers.add(e.receiver);
|
||||||
|
};
|
||||||
|
const audioStream = await navigator.mediaDevices.getUserMedia({video: false, audio: true});
|
||||||
|
for (const track of audioStream.getAudioTracks()) {
|
||||||
|
//Add track
|
||||||
|
|
||||||
|
this.setupMic(audioStream);
|
||||||
|
const sender = pc.addTrack(track);
|
||||||
|
this.senders.add(sender);
|
||||||
|
console.log(sender);
|
||||||
|
}
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
pc.addTransceiver("audio", {
|
||||||
|
direction: "recvonly",
|
||||||
|
streams: [],
|
||||||
|
sendEncodings: [{active: true, maxBitrate: this.settings.bitrate}],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
pc.addTransceiver("video", {
|
||||||
|
direction: "recvonly",
|
||||||
|
streams: [],
|
||||||
|
sendEncodings: [{active: true, maxBitrate: this.settings.bitrate}],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.pc = pc;
|
||||||
|
this.negotationneeded();
|
||||||
|
await new Promise((res) => setTimeout(res, 100));
|
||||||
|
let sdp = this.offer;
|
||||||
|
if (!sdp) {
|
||||||
|
const offer = await this.makeOffer();
|
||||||
|
this.status = "Starting RTC connection";
|
||||||
|
sdp = offer.sdp;
|
||||||
|
this.offer = sdp;
|
||||||
|
}
|
||||||
|
|
||||||
|
await pc.setLocalDescription();
|
||||||
if (!sdp) {
|
if (!sdp) {
|
||||||
this.status = "No SDP";
|
this.status = "No SDP";
|
||||||
this.ws?.close();
|
this.ws?.close();
|
||||||
|
@ -658,7 +709,7 @@ a=rtcp-mux\r`;
|
||||||
server_id: update.d.guild_id,
|
server_id: update.d.guild_id,
|
||||||
user_id: update.d.user_id,
|
user_id: update.d.user_id,
|
||||||
session_id: update.d.session_id,
|
session_id: update.d.session_id,
|
||||||
token: update.d.token,
|
token: this.urlobj.token,
|
||||||
video: false,
|
video: false,
|
||||||
streams: [
|
streams: [
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue