eaglercraft-1.8/sources/wasm-gc-teavm/js/platformVoiceClient.js

198 lines
6.1 KiB
JavaScript

/*
* Copyright (c) 2024 lax1dude, ayunami2000. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
const EVENT_VOICE_ICE = 0;
const EVENT_VOICE_DESC = 1;
const EVENT_VOICE_OPEN = 2;
const EVENT_VOICE_CLOSE = 3;
const platfVoiceClientName = "platformVoiceClient";
function initializePlatfVoiceClient(voiceClientImports) {
/**
* @return {boolean}
*/
voiceClientImports["isSupported"] = function() {
return typeof navigator.mediaDevices !== "undefined" && typeof navigator.mediaDevices.getUserMedia !== "undefined" && "srcObject" in HTMLAudioElement.prototype;
};
/**
* @param {string} desc
* @suppress {globalThis}
*/
function setRemoteDescriptionImpl(desc) {
try {
const remoteDesc = JSON.parse(desc);
this["_peerConnection"].setRemoteDescription(remoteDesc).then(() => {
if (remoteDesc.hasOwnProperty("type") && "offer" === remoteDesc["type"]) {
this["_peerConnection"].createAnswer().then((desc) => {
this["_peerConnection"].setLocalDescription(desc).then(() => {
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_DESC, {
"objId": this["objId"],
"data": JSON.stringify(desc)
});
}).catch((err) => {
eagError("Failed to set local description for \"{}\"! {}", this["objId"], err.message);
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_CLOSE, {
"objId": this["objId"]
});
});
}).catch((err) => {
eagError("Failed to create answer for \"{}\"! {}", this["objId"], err.message);
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_CLOSE, {
"objId": this["objId"]
});
});
}
}).catch((err) => {
eagError("Failed to set remote description for \"{}\"! {}", this["objId"], err.message);
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_CLOSE, {
"objId": this["objId"]
});
});
} catch (e) {
eagError(e.message);
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_CLOSE, {
"objId": this["objId"]
});
}
}
/**
* @param {string} ice
* @suppress {globalThis}
*/
function addRemoteICECandidateImpl(ice) {
try {
this["_peerConnection"].addIceCandidate(new RTCIceCandidate(/** @type {!RTCIceCandidateInit} */ (JSON.parse(ice)))).catch((err) => {
eagError("Failed to parse ice candidate for \"{}\"! {}", this["objId"], err.message);
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_CLOSE, {
"objId": this["objId"]
});
});
} catch (e) {
eagError(e.message);
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_CLOSE, {
"objId": this["objId"]
});
}
}
/**
* @suppress {globalThis}
*/
function closeImpl() {
this["_peerConnection"].close();
}
let idCounter = 0;
/**
* @param {string} iceServers
* @param {number} offer
* @param {!MediaStream} localStream
* @return {Object}
*/
voiceClientImports["createRTCPeerConnection"] = function(iceServers, offer, localStream) {
try {
const peerId = idCounter++;
var ret;
const peerConnection = new RTCPeerConnection(/** @type {!RTCConfiguration} */ ({
"iceServers": JSON.parse(iceServers),
"optional": [
{
"DtlsSrtpKeyAgreement": true
}
]
}));
peerConnection.addEventListener("icecandidate", /** @type {function(Event)} */ ((/** RTCPeerConnectionIceEvent */ evt) => {
if (evt.candidate) {
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_ICE, {
"objId": peerId,
"data": JSON.stringify({
"sdpMLineIndex": "" + evt.candidate.sdpMLineIndex,
"candidate": evt.candidate.candidate
})
});
}
}));
peerConnection.addEventListener("track", /** @type {function(Event)} */ ((/** RTCTrackEvent */ evt) => {
const rawStream = evt.streams[0];
ret["_aud"] = document.createElement("audio");
ret["_aud"]["autoplay"] = true;
ret["_aud"]["muted"] = true;
ret["_aud"]["srcObject"] = rawStream;
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_OPEN, {
"objId": peerId,
"stream": rawStream
});
}));
localStream.getTracks().forEach(function(track) {
peerConnection.addTrack(track, localStream);
});
if (offer) {
peerConnection.createOffer().then((desc) => {
peerConnection.setLocalDescription(desc).then(() => {
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_DESC, {
"objId": peerId,
"data": JSON.stringify(desc)
});
}).catch((err) => {
eagError("Failed to set local description for \"{}\"! {}", peerId, err.message);
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_CLOSE, {
"objId": peerId
});
});
}).catch((err) => {
eagError("Failed to set create offer for \"{}\"! {}", peerId, err.message);
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_CLOSE, {
"objId": peerId
});
});
}
peerConnection.addEventListener("connectionstatechange", () => {
const cs = peerConnection.connectionState;
if ("disconnected" === cs || "failed" === cs) {
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_CLOSE, {
"objId": peerId
});
}
});
return ret = {
"objId": peerId,
"_peerConnection": peerConnection,
"setRemoteDescription": setRemoteDescriptionImpl,
"addRemoteICECandidate": addRemoteICECandidateImpl,
"closeHandle": closeImpl
};
} catch (e) {
eagError(e.message);
return null;
}
};
}
function initializeNoPlatfVoiceClient(voiceClientImports) {
setUnsupportedFunc(voiceClientImports, platfVoiceClientName, "isSupported");
setUnsupportedFunc(voiceClientImports, platfVoiceClientName, "createRTCPeerConnection");
}