Why does Chrome not output WebRTC audio with AudioContext? - javascript

I'm implementing a WebRTC Audio chat. I have everything working, and was initially using <audio> elements to output the audio, which worked fine.
But then I wanted to implement a "Speaking indicator" feature, and decided to go with AudioContext.
It works, in Safari + Firefox, but no Chrome. I just don't get any output.
This is my code:
const audioContext = new AudioContext();
// Create an audio source node from the stream received by the
// RTCPeerConnection with peerConnection.ontrack()
const audioSourceNode = audioContext.createMediaStreamSource(stream);
// Connect the audio source to the destination
audioSourceNode.connect(audioContext.destination);
Am I missing something? Do I need to somehow use an <audio> element to get sound on Chrome?

It's an old known bug in Chrome that wasn't fixed so far.
A common workaround is to create a muted <audio> element to make the audio flowing (it can be deleted after). See this answer for an example.

Related

AudioContext compromising app performance the more audio files I play

I'm developing an Elecron app (JavaScript) to audio visualization. There is a Playlist() instance which receives audio file paths the user wants to play. When the first audio finishes, it plays the next one. So far so good. The app does an intense computational work extracting audio features from each channel, re-rendering canvases and animating plots. It does it beautifully.
The problem is: each time the app plays a next file, the more slow it gets, as if all the audio data before is still somewhere. I've found in documentation the method close() from AudioContext():
"The close() method of the AudioContext Interface closes the audio context, releasing any system audio resources that it uses."
"An AudioContext can now be explicitly closed, thereby releasing any hardware resources associated with the AudioContext. Without this, developers had to depend on garbage collection of the AudioContext to release hardware resources."
I also have found this example of closing and restarting audio contexts:
https://github.com/mdn/webaudio-examples/blob/master/audiocontext-states/index.html
https://mdn.github.io/webaudio-examples/audiocontext-states/
The problem is that I use a audioContext.createMediaElementSource(HTMLelementID) and it doesn't allow me to restart everything recreating all the nodes like in the example. A simplified code that represents what I did before is:
class Audio() {
constructor(audioElementID, playlistObj) {
this.audioContext = new AudioContext();
this.audioElement = document.getElementById(audioElementID);
this.track = this.audioContext.createMediaElementSource(this.audioElement);
this.gainNode = this.audioContext.createGain();
this.track.connect(this.gainNode);
this.gainNode.connect(this.audioContext.destination);
this.audioElement.addEventListener('ended', () => {
playlistObj.playnextTrack() // changes the src from the html element (audioElementID) and sets this.audioElement.currentTime to 0
}
}
// everything is a property here for debugging reasons
}
const audio = new Audio('audioID', playlist);
// playlist defined somewhere else
To implement the close() method I had to change (just exactly the example, a function that recreates everything again):
class Audio() {
constructor(audioElementID, playlistObj) {
this.createAudioContext = () => {
this.audioContext = new AudioContext();
this.audioElement = document.getElementById(audioElementID);
this.track = this.audioContext.createMediaElementSource(this.audioElement);
this.gainNode = this.audioContext.createGain();
this.track.connect(this.gainNode);
this.gainNode.connect(this.audioContext.destination);
this.audioElement.addEventListener('ended', () => {
playlistObj.playNextTrack() // changes the src from the html element (audioElementID) and sets this.audioElement.currentTime to 0
}
}
this.createAudioContext();
}
}
and in playlist.playNextTrack() I pause the audioElement, call audio.audioContext.close(), wait for it (it's a promise), call audio.createAudioContext() to recreate everything and plays. The logic returns an error at this.track = this.audioContext.createMediaElementSource(this.audioElement) with:
"Failed to execute 'createMediaElementSource' on 'BaseAudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode, at Audio.createAudioContext"
In the example, the audio source is just a random oscillator and not a mp3 audio file.
I'm really stuck here. Don't know what to do. I'm not even sure if AudioContext() really holds data from all the audio files before causing this performance problem. And if so, how could I reconnect the HTMLMediaElement to a new node audio.createAudioContext() creates? I've already tried audio.track.disconnect()but it doesn't work (as it shouldn't because here I'm disconnecting track from gainNode). And also audioElement doesn't have a disconnect()method as It's just a html element.
Any idea?
UPDATE:
I passed over the problem of recreating the audio context deleting and creating again the html element. But the problem persist: the more new audio files are played, the app gets slower. More precisely now: the more new AudioContext() is created, the slower it gets (even if I close the previous one).
I'm really stuck here. Don't know what to do. I'm not even sure if AudioContext() really holds data from all the audio files before causing this performance problem.
No, it's really unlikely this is the case. The AudioContext sets up things like the sample rate, output destination, and the graph. That's all.
The close() method of the AudioContext Interface closes the audio context, releasing any system audio resources that it uses.
You're misunderstanding what this means. Those "system audio resources" are the sound devices. While the AudioContext is running, there is an audio device requested. This is particularly meaningful in low power environments, like mobile. Another example would be Bluetooth. If the AudioContext is kept running, your Bluetooth headset may just stay on. If the AudioContext is allowed to close, then the Bluetooth headset may go to sleep.
And if so, how could I recconect the HTMLMediaElement to a new node audio.createAudioContext() creates?
You don't. While it would be nice if the API supported this, it seems it doesn't. Simply create a new HTMLMediaElement.
What you should do is properly profile your application to figure out where the slowdown is occurring. Use your developer tools. Might be faster though just to start commenting out sections of things that are running. We certainly can't tell you where the problem is, specifically, from the code you've shown.

Web Audio API filter not working in Safari

I am in the process of developing an HTML5 canvas interactive piece that uses Createjs and the Web Audio API. I've managed to get audio working in Chrome/Firefox/Safari despite the deprecation of webkitAudioContext by Chrome and FF but not Safari. However, filters for some reason are not working in Safari, but sound still plays. Filters DO work in Chrome/FF.
I have my filters set up like this:
var sound = new Audio();
sound.src = './sounds/sound.mp3';
sound.autoplay = false;
sound.loop = true;
soundSource = context.createMediaElementSource(sound);
var soundFilter = context.createBiquadFilter();
soundFilter.type = "lowpass";
soundFilter.frequency.value = 500;
soundSource.connect(soundFilter);
soundFilter.connect(context.destination);
Am I unknowingly using a deprecated term or something? Live project can be found here. Cheers.
UPDATE: This has been recognised as a genuine bug by the WebKit team, and will be patched. Full details here
Apparently Safari doesn't implement createMediaElementSource correctly. So instead of redirecting the sound through your Web Audio nodes, it still just plays the sound directly to the audio device.
Is there any particular reason why you can't use a BufferSourceNode? It makes you jump through extra hoops to get the sound file and decode it, but it should work.

How to stop audio playback in Firefox & use source.connect() hack

I'm developing a webapp that (in part) records some audio using recorder.js, and sends it to a server. I'm trying to target Firefox, so I have to use this hack to keep the audio source from cutting off:
// Hack for a Firefox bug that stops input after a few seconds
window.source = audio_context.createMediaStreamSource(stream);
source.connect(audio_context.destination);
I think that this is causing audio to be played back through the computer speakers, but I'm not sure. I'm kind of a newbie when it comes to web audio. My goal is to eliminate the audio that is being played out of the speakers.
EDIT:
Here's a link to my JS file on Github: https://github.com/miller9904/Jonathan/blob/master/js/main.js#L87
you have to connect the source to the node( through which you retrieve data which you are going to record), replace this.node with what variable name you have assigned to yuor node used for processing.
window.source.connect(this.node);
//this.node.connect(this.context.destination);
edit: just checked, connecting to destination is not compulsory, also make sure your node variable does not get garbage collected( which i am assuming is happening in your case, since recording stops after few seconds.)

Change the VideoTrack of a MediaStream object

In a Nutshell: I'm trying to change the VideoTrack of a MediaStream object.
(Documentation: https://developer.mozilla.org/en-US/docs/WebRTC/MediaStream_API)
I have a MediaStream object __o_jsep_stream_audiovideo which is created by the sipml library.
__o_jsep_stream_audiovideo looks like this:
So it has one AudioTrack and one VideoTrack. At first the VideoTrack comes from the users camera (e.g label: "FaceTime Camera").
According to the Documentation:
A MediaStream consists of zero or more MediaStreamTrack objects, representing various audio or video tracks.
So we should be fine adding more Tracks to this Stream.
I'm trying to switch/exchange the VideoTrack with that from another stream. The other stream (streamB) originates from Chromes ScreenCapture api (label: "Screen")
I tried:
__o_jsep_stream_audiovideo.addTrack(streamB.getVideoTracks()[0])
which doesn't seem to have any effect.
I also tried assigning the videoTracks directly (which was desperate I know).
I must be missing something obvious could you point me in the right direction?
I'm running
Chrome (Version 34.0.1847.131) and
Canary (Version 36.0.1976.2 canary)
OSX 10.9.2
When you talk about change video track, we mean 2 areas:
change the remote video track (what the others can see from u)
WebRTC gets new version of doing that, since it deprecates addStream/removeStream.
However, the excelence is that they introduce new interface replaceTrack
stream.getTracks().forEach(function(track) {
// remote
qcClient.calls.values().forEach(function(call) {
var sender = call.pc.getSenders().find(function(s) {
return s.track.kind == track.kind;
});
sender.replaceTrack(track);
});
});
change your display video (You see yourself)
Better to just add a new video element (or using existing video element) But assign srcObject to the new captured stream
Adding and removing tracks on a MediaStream object do not signal a renegotiation and there are also issues with a MediaStream having two tracks of the same type in chrome.
You should probably just add the separate mediastream to the peer connection so that it can fire a re-negotiation and handle the streams. The Track add/remove functionality in chrome is very naive and not very granular and you should move away from it as much as you can.

Javascript audio not working in Firefox (x-unknown/unknown)

The following JavaScript running in canvas should play audio fine:
var audio = new Audio('tune.wav');
audio.play();
Most of the time it does work, the wav is 24bit 14100kbps and plays fine on several machines, but on my laptop (Win7, using Firefox 22.0) I get the error:
HTTP "Content-Type" of "x-unknown/unknown" is not supported. Load of media resource file:///C:/code/sound/tune.wav failed.
I'm aware that there are other libraries to play sound, but I want to keep this pure JavaScript and since it works fine on other machines it might be a hardware problem.
But I am able to play other audio files fine, so I'm not sure what's going wrong here. Any ideas?
Hmm. Based upon my experience with the JS Audio elements, you're missing a line.
var audio = new Audio('tune.wav');
audio.load();
audio.play();
I don't think that's causing the error though. Based upon the responses to this question:
Firefox won't play .WAV files using the HTML5 <audio> tag?
and the back-and-forth in this forum: https://bugzilla.mozilla.org/show_bug.cgi?id=524109 (comment 7)
It looks like Firefox simply doesn't support 24-bit WAVE files. 16-bit is probably a safer option.

Categories