This content originally appeared on Modern Web Development with Chrome and was authored by Paul Kinlan
<p>I have a goal of building the worlds simplest screen recording software and I've been slowly noodling around on the project for the last couple of months (I mean really slowly).</p>
<p>In previous posts I had got the <a href="https://paul.kinlan.me/building-a-video-editor-on-the-web-screencasting/">screen recording and a voice overlay</a> by futzing about with the streams from all the input sources. One area of frustration though was that I could not work out how to get the audio from the desktop <em>and</em> overlay the audio from the speaker. I finally worked out how to do it.</p>
<p>Firstly, <code>getDisplayMedia</code> in Chrome now allows audio capture, there seems like an odd oversight in the Spec in that it did not allow you to specify <code>audio: true</code> in the function call, now you can.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-javascript" data-lang="javascript"><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">audio</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">audioToggle</span>.<span style="color:#a6e22e">checked</span> <span style="color:#f92672">||</span> <span style="color:#66d9ef">false</span>;
<span style="color:#a6e22e">desktopStream</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">await</span> <span style="color:#a6e22e">navigator</span>.<span style="color:#a6e22e">mediaDevices</span>.<span style="color:#a6e22e">getDisplayMedia</span>({ <span style="color:#a6e22e">video</span><span style="color:#f92672">:</span><span style="color:#66d9ef">true</span>, <span style="color:#a6e22e">audio</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">audio</span> });
</code></pre></div><p>Secondly, I had originally thought that by creating two tracks in the audio stream I would be able to get what I wanted, however I learnt that Chrome's <code>MediaRecorder</code> API can only output one track, and 2nd, it wouldn't have worked anyway because tracks are like the DVD mutliple audio tracks in that only one can play at a time.</p>
<p>The solution is probably simple to a lot of people, but it was new to me: Use Web Audio.</p>
<p>It turns out that WebAudio API has <code>createMediaStreamSource</code> and <code>createMediaStreamDestination</code>, both of which are API's needed to solve the problem. The <code>createMediaStreamSource</code> can take streams from my desktop audio and microphone, and by connecting the two together into the object created by <code>createMediaStreamDestination</code> it gives me the ability to pipe this one stream into the <code>MediaRecorder</code> API.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-javascript" data-lang="javascript"><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">mergeAudioStreams</span> <span style="color:#f92672">=</span> (<span style="color:#a6e22e">desktopStream</span>, <span style="color:#a6e22e">voiceStream</span>) => {
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">context</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">AudioContext</span>();
<span style="color:#75715e">// Create a couple of sources
</span><span style="color:#75715e"></span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">source1</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">createMediaStreamSource</span>(<span style="color:#a6e22e">desktopStream</span>);
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">source2</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">createMediaStreamSource</span>(<span style="color:#a6e22e">voiceStream</span>);
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">destination</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">createMediaStreamDestination</span>();
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">desktopGain</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">createGain</span>();
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">voiceGain</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">createGain</span>();
<span style="color:#a6e22e">desktopGain</span>.<span style="color:#a6e22e">gain</span>.<span style="color:#a6e22e">value</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.7</span>;
<span style="color:#a6e22e">voiceGain</span>.<span style="color:#a6e22e">gain</span>.<span style="color:#a6e22e">value</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.7</span>;
<span style="color:#a6e22e">source1</span>.<span style="color:#a6e22e">connect</span>(<span style="color:#a6e22e">desktopGain</span>).<span style="color:#a6e22e">connect</span>(<span style="color:#a6e22e">destination</span>);
<span style="color:#75715e">// Connect source2
</span><span style="color:#75715e"></span> <span style="color:#a6e22e">source2</span>.<span style="color:#a6e22e">connect</span>(<span style="color:#a6e22e">voiceGain</span>).<span style="color:#a6e22e">connect</span>(<span style="color:#a6e22e">destination</span>);
<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">destination</span>.<span style="color:#a6e22e">stream</span>.<span style="color:#a6e22e">getAudioTracks</span>();
};
</code></pre></div><p>Simples.</p>
<p>The full code can be found on <a href="https://glitch.com/edit/#!/screen-record-voice">my glitch</a>, and the demo can be found here: <a href="https://screen-record-voice.glitch.me/">https://screen-record-voice.glitch.me/</a></p>
<div class="yt-embed" style="position:relative;padding-bottom: 56.25%; padding-top: 25px; height: 0;">
<iframe
data-src="//www.youtube-nocookie.com/embed/oGIdqcMFKlA?autoplay=1"
style="position: absolute; left: 0; top: 0; width: 100%; height: 100%; border:0;"
allowfullscreen
title="YouTube Video"></iframe>
</div>
This content originally appeared on Modern Web Development with Chrome and was authored by Paul Kinlan