I'm building a component that shows information about a video's audio. I use the AudioContext interface to get audio samples from a HTML5 video element. It works fine the first time I create the component, but when the component is unmounted and then recreated at a later point, I get the following error message:
Uncaught InvalidStateError: Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode.
Here's how I get the audio:
const video = document.querySelectorAll('video')[0]
if (!window.audioContext) {
window.audioContext = new (window.AudioContext || window.webkitAudioContext)
}
if (!this.source && !this.scriptNode) {
this.source = window.audioContext.createMediaElementSource(video)
this.scriptNode = window.audioContext.createScriptProcessor(4096, 1, 1)
}
this.scriptNode.onaudioprocess = (evt) => {
// Processing audio works fine...
}
this.source.connect(this.scriptNode)
this.scriptNode.connect(window.audioContext.destination)
And when the component is unmounted I do:
if (this.source && this.scriptNode) {
this.source.disconnect(this.scriptNode)
this.scriptNode.disconnect(window.audioContext.destination)
}
I thought this would put me in a state where I can safely create and connect new nodes. But the next time the component is mounted, this block throws the error mentioned earlier:
if (!this.source && !this.scriptNode) {
this.source = window.audioContext.createMediaElementSource(video) // this throws the error
this.scriptNode = window.audioContext.createScriptProcessor(4096, 1, 1)
}
I could get it to work by making everything global, i.e. putting source
and scriptNode
on window
rather than this
. But that won't work if my video element changes. What's the correct way to do this?
You're not destroying the node you created with context.createMediaSourceElement. You have your on page video element which hasn't changed, all you've done is disconnected the video audio stream from your audio graph. Therefore the video element is still bound to an AudioNode, in this case 'this.source'. Instead of trying to recreate source just detect if the source is already defined.
if (this.source == undefined) {
// Build element
this.source = window.audioContext.createMediaElementSource(video);
}
this.scriptNode = window.audioContext.createScriptProcessor(4096, 1, 1)
this.source.connect(this.scriptNode);
this.scriptNode.connect(window.audioContext.destination);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With