Record mic and audio from SIP call using sip.js
Asked Answered
Q

1

7

Good evening Stack Overflow! I really need help for a project of mine where I'm using sip.js and a VoIP to make real calls to a phone number.

The Goal

I want to allow the user to record the audio and microphone and save the data on a server (in base64 encoding or as a file). So I after the conversation can hear the conversation again and use it for what ever my purpose (employee training) was.

The Problem

I can't get the sound of the person speaking, which comes through and -HTML tag (working with the sip.js plugin). As of now I haven't found any way to successfully save the sound streaming through this audio tag.

What I've done so far

I've successfully figured out how to record the audio of the microphone using a plugin called AudioRecorder which allows me to record the audio through the microphone and saving it. I slightly changed the code so it got saved encoded as base64. This all work as expected, though I only get the audio of my own voice, and not the person I'm talking with.

Because I succeed to record the audio of my own voice I looked into the AudioRecorder plugin and tried to reverse the plugin to record from a audio tag. I found the "createMediaStreamSource" function inside AudioRecorder which I wanted to work with the -tag which did not work (as I suspected, because the -tag in it self isn't a stream (of which i understand).

The Code

I'm basically using the sip.js plugin to establish a call to a phone number by using below code (just using an example, matching my code, because my raw code contains some added values which doesn't need to be showed here):

// Create a user agent called bob, connect, and register to receive invitations.
var userAgent = new SIP.UA({
  uri: '[email protected]',
  wsServers: ['wss://sip-ws.example.com'],
  register: true
});
var options = { media: { constraints: { audio: true, video: false }, render: { remote: document.getElementById("audio") } } };

Then i use the build in invite function to call a phonenumber, which does the rest. Audio and microphone is now up and running.

userAgent.invite("+4512345678", options);

I can now talk with my new best friend Bob. But I can't record other than my own sound as of now.

Whats Next?

I would really like some help to understand how I can record the sound of "Bob" and store it, preferred in the same file as my own voice. If I have to record two separately files and play them synced, I won't mind, but else if preferred.

I know this might just be a call for help without showing anything real code of what I've tried to do it myself, but I have to admit I just fiddled with the code for hours without any good results and now I'm screaming for help.

Thank your all in advance and sorry for the bad grammar and (mis)use of language.

Quirita answered 22/8, 2017 at 21:17 Comment(0)
Q
10

Okay, so I after finally found a solution to my problem, which I though i wanted to share here.

What I did to solve the problem was to add ONE simple line of code to the "normal" recording script of a microphone. The script to record mic audio is:

window.AudioContext = window.AudioContext || window.webkitAudioContext;

var audioGlobalContext = new AudioContext();
var audioOutputAnalyser
var inputPoint = null,
    audioRecorder = null;
var recording = false;

// Controls the start and stop of recording
function toggleRecording( e ) {
    if (recording == true) {
        recording = false;
        audioRecorder.stop();
        audioRecorder.getBuffers( gotBuffers );
        console.log("Stop recording");
    } else {
        if (!audioRecorder)
            return;
        recording = true;
        audioRecorder.clear();
        audioRecorder.record();
        console.log("Start recording");
    }
}

function gotBuffers(buffers) {
    audioRecorder.exportWAV(doneEncoding);
}

function doneEncoding(blob) {
    document.getElementById("outputAudio").pause();
    Recorder.setupDownload(blob);
}

function gotAudioMicrophoneStream(stream) {
    var source = audioGlobalContext.createMediaStreamSource(stream);
    source.connect(inputPoint);
}

function initAudio() {
        if (!navigator.getUserMedia)
            navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
        if (!navigator.cancelAnimationFrame)
            navigator.cancelAnimationFrame = navigator.webkitCancelAnimationFrame || navigator.mozCancelAnimationFrame;
        if (!navigator.requestAnimationFrame)
            navigator.requestAnimationFrame = navigator.webkitRequestAnimationFrame || navigator.mozRequestAnimationFrame;

    inputPoint = audioGlobalContext.createGain();

    navigator.getUserMedia({
        "audio": {
            "mandatory": {
                "googEchoCancellation": "true",
                "googAutoGainControl": "false",
                "googNoiseSuppression": "true",
                "googHighpassFilter": "false"
            },
            "optional": []
        },
    }, gotAudioMicrophoneStream, function(e) {
        alert('Error recording microphone');
        console.log(e);
    });

    var analyserNode = audioGlobalContext.createAnalyser();
    analyserNode.fftSize = 2048;
    inputPoint.connect(analyserNode);
    var zeroGain = audioGlobalContext.createGain();
    zeroGain.gain.value = 0.0;
    inputPoint.connect(zeroGain);
    zeroGain.connect(audioGlobalContext.destination);

    audioRecorder = new Recorder(inputPoint);
}

window.addEventListener('load', initAudio );

The function I was looking for to convert the Audio-tag sound into an Audio Source was createMediaElementSource() so what I did was adding this function:

function gotAudioOutputStream() {
    var source = audioGlobalContext.createMediaElementSource(document.getElementById("outputAudio"));
    source.connect(inputPoint);
    source.connect(audioGlobalContext.destination);
}

And in the initAudio() function just after navigator.getUserMedia added a call to the function. To the finished code (with HTML) would look like this

window.AudioContext = window.AudioContext || window.webkitAudioContext;

var audioGlobalContext = new AudioContext();
var audioOutputAnalyser
var inputPoint = null,
    audioRecorder = null;
var recording = false;

// Controls the start and stop of recording
function toggleRecording( e ) {
    if (recording == true) {
        recording = false;
        audioRecorder.stop();
        audioRecorder.getBuffers( gotBuffers );
        console.log("Stop recording");
    } else {
        if (!audioRecorder)
            return;
        recording = true;
        audioRecorder.clear();
        audioRecorder.record();
        console.log("Start recording");
    }
}

function gotBuffers(buffers) {
    audioRecorder.exportWAV(doneEncoding);
}

function doneEncoding(blob) {
    document.getElementById("outputAudio").pause();
    Recorder.setupDownload(blob);
}

function gotAudioMicrophoneStream(stream) {
    var source = audioGlobalContext.createMediaStreamSource(stream);
    source.connect(inputPoint);
}

function gotAudioOutputStream() {
    var source = audioGlobalContext.createMediaElementSource(document.getElementById("outputAudio"));
    source.connect(inputPoint);
    source.connect(audioGlobalContext.destination);
}

function initAudio() {
        if (!navigator.getUserMedia)
            navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
        if (!navigator.cancelAnimationFrame)
            navigator.cancelAnimationFrame = navigator.webkitCancelAnimationFrame || navigator.mozCancelAnimationFrame;
        if (!navigator.requestAnimationFrame)
            navigator.requestAnimationFrame = navigator.webkitRequestAnimationFrame || navigator.mozRequestAnimationFrame;

    inputPoint = audioGlobalContext.createGain();

    navigator.getUserMedia({
        "audio": {
            "mandatory": {
                "googEchoCancellation": "true",
                "googAutoGainControl": "false",
                "googNoiseSuppression": "true",
                "googHighpassFilter": "false"
            },
            "optional": []
        },
    }, gotAudioMicrophoneStream, function(e) {
        alert('Error recording microphone');
        console.log(e);
    });

    gotAudioOutputStream();

    var analyserNode = audioGlobalContext.createAnalyser();
    analyserNode.fftSize = 2048;
    inputPoint.connect(analyserNode);
    var zeroGain = audioGlobalContext.createGain();
    zeroGain.gain.value = 0.0;
    inputPoint.connect(zeroGain);
    zeroGain.connect(audioGlobalContext.destination);

    audioRecorder = new Recorder(inputPoint);
}

window.addEventListener('load', initAudio );

<!doctype html>
<html>
<head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Audio Recorder</title>
    <script src="assets/js/AudioRecorder/js/recorderjs/recorder.js"></script>
    <script src="assets/js/AudioRecorder/js/main.js"></script>
</head>
<body>
    <audio id="outputAudio" autoplay="true" src="test.mp3" type="audio/mpeg"></audio>
    <audio id="playBack"></audio>
    <div id="controls">
        <img id="record" src="assets/js/AudioRecorder/img/mic128.png" onclick="toggleRecording(this);">
    </div>
</body>
</html>

This records your voice and the sound coming from the audio element tag. Simple. Hope everyone out there who had the same problem as me to "rewind" your head around Audio API will find this helpful.

This code snippets shown above require Recorder.js to work.

Quirita answered 29/8, 2017 at 20:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.