Need to connect my WebRTC stream(Handled by PeerJS) to my Asterisk server

34 Views Asked by At

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?

0

There are 0 best solutions below