I have a UI which is connecting to webrtc via PeerJs. I have a backend in NodeJS which has 2 servers running on it, 1 is in 9000 i.e. WebSocket Messaging server and the 2nd is in 9001 i.e. Peer server. I also have a server running on same machine i.e. asterisk server. I want to transfer my WebRTC PeerJS UI call to asterisk extension 8888. I have tested making call inbound and outbound to extension 8888 and that works fine. Zoiper Call showing inbound to 8888 And this is log for outbound call(asterisk CLI log).
<virtual-machine*CLI> originate PJSIP/8888 application playback hello-world [Mar 22 18:33:09] ERROR[108454]: res_pjsip.c:903 ast_sip_set_tpselector_from_transport_name: Unable to retrieve PJSIP transport 'transport-wss' -- Called 8888 -- PJSIP/8888-00000014 is ringing > 0x61ab32a10020 -- Strict RTP learning after remote address set to: 192.168.37.1:8000 -- PJSIP/8888-00000014 answered > Launching playback(hello-world) on PJSIP/8888-00000014 -- <PJSIP/8888-00000014> Playing 'hello-world.gsm' (language 'en') > 0x61ab32a10020 -- Strict RTP switching to RTP target address 192.168.37.1:8000 as source
But I am having problem in transferring the stream from WebRTC to Asterisk. I am using "asterisk-manager" node library for the same but I don't see a direct function to transfer the stream.
I am very new to this Asterisk and WebRTC so sorry in advance if the question is a bit stupid.
I am running my UI in Vite which is written in pure javascirpt and html. main.js looks like this
import './style.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap-icons/font/bootstrap-icons.css';
import Peer from 'peerjs';
document.querySelector('#header').innerHTML = `
<h2>WebRTC User</h2>
`;
document.querySelector('#footer').innerHTML = `
<p>Powered by PeerJS, Janus, Asterisk & Genesys</p>
`;
document.querySelector('#app').innerHTML = `
<h3>
<button type="button" class="btn btn-outline-primary" id="callButton">Connect with support <image src="public/telephone.svg"></button>
<button type="button" class="btn btn-outline-primary" id="cancelCallButton" style="display:none">Disconnect Call <image src="public/telephone-x.svg"></button>
</h3>
<div id="status"></div>
`;
const statusDisplay = document.getElementById('status');
const callButton = document.getElementById('callButton');
const cancelCallButton = document.getElementById('cancelCallButton');
let localStream;
let peer;
let globalPeerId = '';
const supportId = '582e9208-9da5-4e4c-91f9-cfefcfc7f602';
const signalingServerUrl = 'ws://127.0.0.1:9000';
cancelCallButton.addEventListener('click', async () => {
endCall();
});
callButton.addEventListener('click', async () => {
try {
localStream = await navigator.mediaDevices.getUserMedia({ audio: true });
statusDisplay.innerHTML = 'Connecting to support <div class="spinner-border text-primary" role="status"> <span class="visually-hidden">Loading...</span> </div>';
startCall();
} catch (error) {
console.error('Error accessing microphone:', error);
statusDisplay.textContent = 'Error accessing microphone';
}
});
function endCall() {
if (localStream) {
localStream.getTracks().forEach(track => track.stop());
localStream = null;
}
cancelCallButton.style.display = 'none';
callButton.style.display = 'inline';
statusDisplay.innerHTML = '<p>Thank you</p>';
peer.disconnect();
}
function showError(message) {
cancelCallButton.style.display = 'none';
callButton.style.display = 'inline';
statusDisplay.innerHTML = '<p>' + message + '</p>';
peer.disconnect();
}
function startCall() {
cancelCallButton.style.display = 'block';
callButton.style.display = 'none';
// Custom WebSocket connection
const customSocket = new WebSocket(`${signalingServerUrl}?id=${globalPeerId}`);
customSocket.onopen = () => {
console.log('WebSocket connection established');
// Create Peer object after WebSocket connection is open
peer = new Peer(globalPeerId, {
host: '192.168.37.129',
port: 9001,
path: '/myapp',
socket: customSocket
});
peer.on('open', (peerId) => {
console.log('User Peer ID', peerId);
globalPeerId = peerId;
const offerMessage = {
type: 'offer',
peerId: supportId
};
customSocket.send(JSON.stringify(offerMessage)); // Send the 'offer' message to the WebSocket server
const call = peer.call(supportId, localStream);
call.on('stream', (remoteStream) => {
statusDisplay.innerHTML = 'Call in progress ';
// Instead of playing remoteStream, we send it to the backend
sendAudioStreamToBackend(remoteStream);
});
call.on('close', () => {
endCall();
});
call.on('error', (error) => {
showError('Error occurred while connecting to support: ' + error);
});
});
peer.on('error', (error) => {
console.error('PeerJS error:', error);
});
};
customSocket.onclose = () => {
console.log('WebSocket connection closed');
showError('WebSocket connection closed unexpectedly');
};
customSocket.onerror = (error) => {
console.error('WebSocket error:', error);
showError('WebSocket error occurred');
};
}
function sendAudioStreamToBackend(stream) {
const audioContext = new AudioContext();
const mediaStreamSource = audioContext.createMediaStreamSource(stream);
const scriptProcessor = audioContext.createScriptProcessor(4096, 1, 1);
scriptProcessor.onaudioprocess = (audioProcessingEvent) => {
const inputBuffer = audioProcessingEvent.inputBuffer;
const inputData = inputBuffer.getChannelData(0); // Get audio data from the first channel
// Send audio data to the backend through WebSocket
customSocket.send(inputData.buffer);
};
mediaStreamSource.connect(scriptProcessor);
scriptProcessor.connect(audioContext.destination);
}
And backend nodejs code looks like this
const WebSocket = require('ws');
const http = require('http'); // Import the 'http' module
const server = http.createServer((req, res) => {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
res.setHeader('Access-Control-Allow-Credentials', 'true');
if (req.method === 'OPTIONS') {
res.writeHead(200);
res.end();
return;
}
});
const AMI = require('asterisk-manager')('5038', 'localhost', 'test', 'password', true);
const { PeerServer } = require('peer');
const wsServer = new WebSocket.Server({ server });
AMI.keepConnected();
const peerExtensionMap = {
'582e9208-9da5-4e4c-91f9-cfefcfc7f602': '8888'
};
wsServer.on('connection', (socket) => {
console.log('New WebSocket connection');
socket.on('message', (message) => {
console.log('Received message from PeerJS client:', message.toString('utf-8'));
const parsedMessage = JSON.parse(message);
switch (parsedMessage.type) {
case 'register':
registerPeer(parsedMessage.peerId, socket);
break;
case 'offer':
initiateCallToAsteriskExtension(parsedMessage.peerId);
break;
case 'hangup':
hangupCallInAsterisk();
break;
}
});
socket.on('close', () => {
console.log('WebSocket connection closed');
});
});
function registerPeer(peerId, socket) {
console.log('Registering Peer ID:', peerId);
}
function initiateCallToAsteriskExtension(peerId) {
const extension = peerExtensionMap[peerId];
console.log("Calling extension "+extension);
if (extension) {
const action = {
action: 'Originate',
channel: 'PJSIP/' + extension,
context: 'from-internal',
exten: extension,
priority: 1,
timeout: 30000,
CallerID: 'PeerJS Caller'
};
AMI.action(action, (err, res) => {
if (err) {
console.error('Error initiating call to Asterisk:', err);
} else {
console.log('Call initiated to Asterisk extension', extension);
}
});
} else {
console.error('No Asterisk extension mapped for Peer ID:', peerId);
}
}
function hangupCallInAsterisk() {
// Implement hangup logic if needed
}
const peerServer = PeerServer({ port: 9001, path: '/myapp' });
console.log('PeerJS server started on port 9001');
// Start the HTTP server
server.listen(9000, () => {
console.log('HTTP server listening on port 9000');
});
How do I connect my stream and is there any way to directly transfer it?