Files
caller/templates/call.html
Egor Matveev bfe163243c
All checks were successful
Deploy Dev / Build (pull_request) Successful in 6s
Deploy Dev / Push (pull_request) Successful in 8s
Deploy Dev / Deploy dev (pull_request) Successful in 10s
fix
2025-11-24 10:00:28 +03:00

129 lines
5.1 KiB
HTML

<html>
<head>
<script>
microOn = false;
function changeMicro() {
microOn = !microOn;
const micOn = document.getElementById("micOn");
const micOff = document.getElementById("micOff");
if (microOn) {
micOn.hidden = false;
micOff.hidden = true;
} else {
micOn.hidden = true;
micOff.hidden = false;
}
}
async function getMicrophoneStream() {
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: false
});
console.log("Microphone access granted, stream object:", stream);
return stream;
} catch (err) {
console.error(`Error accessing microphone: ${err}`);
// Handle cases where the user denies permission or no mic is available
}
}
async function connect() {
const urlParams = new URLSearchParams(window.location.search);
const callId = urlParams.get('call_id');
const socket = new WebSocket("/connect?call_id=" + callId);
socket.addEventListener("open", async () => {
console.log("WebSocket открыт");
// Запрашиваем доступ к микрофону
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
// Создаём AudioContext
const audioContext = new AudioContext();
// Создаём источник из микрофона
const source = audioContext.createMediaStreamSource(stream);
// ScriptProcessorNode является устаревшим, но всё ещё работает
const processor = audioContext.createScriptProcessor(4096, 1, 1);
source.connect(processor);
processor.connect(audioContext.destination); // необязательно, но нужно для запуска
processor.onaudioprocess = (audioEvent) => {
const input = audioEvent.inputBuffer.getChannelData(0); // Float32Array
// Конвертируем в 16-bit PCM (часто требуется серверам)
const pcm = floatTo16BitPCM(input);
// Отправляем через WebSocket
if (socket.readyState === WebSocket.OPEN) {
socket.send(pcm);
}
};
});
socket.binaryType = "arraybuffer";
socket.onmessage = (msg) => {
const int16 = new Int16Array(msg.data);
playPCM(int16);
};
// Функция конвертации Float32 → Int16
function floatTo16BitPCM(float32Array) {
const buffer = new ArrayBuffer(float32Array.length * 2);
const view = new DataView(buffer);
for (let i = 0; i < float32Array.length; i++) {
let s = Math.max(-1, Math.min(1, float32Array[i]));
view.setInt16(i * 2, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
}
return buffer;
}
const audioContext = new AudioContext();
// Очередь буферов, чтобы звук шел плавно
const playQueue = [];
// Функция для воспроизведения PCM
function playPCM(int16Array) {
const float32 = new Float32Array(int16Array.length);
for (let i = 0; i < int16Array.length; i++) {
float32[i] = int16Array[i] / 0x7fff;
}
const audioBuffer = audioContext.createBuffer(1, float32.length, audioContext.sampleRate);
audioBuffer.getChannelData(0).set(float32);
playQueue.push(audioBuffer);
if (playQueue.length === 1) {
playNext();
}
}
function playNext() {
if (playQueue.length === 0) return;
const buffer = playQueue[0];
const source = audioContext.createBufferSource();
source.buffer = buffer;
source.connect(audioContext.destination);
source.onended = () => {
playQueue.shift();
playNext();
};
source.start();
}
}
connect();
</script>
</head>
<body>
<p id="micOff">Call active</p>
</body>
</html>