{"id":56349,"date":"2022-07-08T03:01:20","date_gmt":"2022-07-07T17:01:20","guid":{"rendered":"http:\/\/www.rjmprogramming.com.au\/ITblog\/?p=56349"},"modified":"2022-07-08T08:20:18","modified_gmt":"2022-07-07T22:20:18","slug":"screen-capture-api-download-multitrack-video-tutorial","status":"publish","type":"post","link":"https:\/\/www.rjmprogramming.com.au\/ITblog\/screen-capture-api-download-multitrack-video-tutorial\/","title":{"rendered":"Screen Capture API Download Multitrack Video Tutorial"},"content":{"rendered":"<div style=\"width: 230px\" class=\"wp-caption alignnone\"><a target=_blank href=\"https:\/\/www.rjmprogramming.com.au\/HTMLCSS\/screen_capture_api_va.htm\"><img decoding=\"async\" style=\"border: 15px solid pink;\" alt=\"Screen Capture API Download Multitrack Video Tutorial\" src=\"http:\/\/www.rjmprogramming.com.au\/HTMLCSS\/screen_capture_apiva.jpg\" title=\"Screen Capture API Download Multitrack Video Tutorial\"  style=\"float:left;\" \/><\/a><p class=\"wp-caption-text\">Screen Capture API Download Multitrack Video Tutorial<\/p><\/div>\n<p>We were inspired, stumbling upon the great <a target=_blank title='Screen Recorder: recording microphone and the desktop audio at the same time' href='https:\/\/paul.kinlan.me\/screen-recorderrecording-microphone-and-the-desktop-audio-at-the-same-time\/'>Screen Recorder: recording microphone and the desktop audio at the same time<\/a> webpage, leading us to think that we could improve the scope of functionality of the recent <a title='Screen Capture API Download Audio Video Tutorial' href='#scapidavt'>Screen Capture API Download Audio Video Tutorial<\/a> where we left off &#8230;<\/p>\n<ul>\n<li>being able to separately, but not synchronously, setting in play, a video and an audio stream separately playable and &#8220;kludgily synchronizable&#8221; via a timer mechanism to start the audio after the video &#8230; with, today &#8230;<\/li>\n<li>the <a target=_blank href='https:\/\/glitch.com\/edit\/#!\/screen-record-voice?path=script.js%3A67%3A0'title='Code behind ... Screen Recorder: recording microphone and the desktop audio at the same time'>screen-record-voice glitch.com<\/a> inspired, thanks, ability to synchronously record the video along with the audio, and be able to download them, optionally, into a single webm video with a video track and an optional audio track<\/li>\n<\/ul>\n<p>As far as user interaction goes, we only add one new checkbox element as per &#8230;<\/p>\n<p><code><br \/>\n&lt;input title='Leave checked and capture of button to left (which should be clicked first for any screen capturing) is a single webm video with audio and video tracks synchronized whereas unchecking this checkbox and button to right usage has timer synchronization of a video (with only video tracks) with an audio (only with audio tracks) element playing so as to finish at the same time.' type=checkbox id=caudio checked&gt;&lt;\/input&gt;<br \/>\n<\/code><\/p>\n<p> &#8230; to offer several more combinations of functionalities now, with a single web video containing video and audio a step forward in sharing and collaboration functionalities, with <a target=_blank href=\"http:\/\/www.rjmprogramming.com.au\/PHP\/Geographicals\/diff.php?one=http:\/\/www.rjmprogramming.com.au\/HTMLCSS\/screen_capture_api_va.html---GETME\" title=\"screen_capture_api_va.htm\">the changed<\/a> <a target=_blank href=\"https:\/\/www.rjmprogramming.com.au\/HTMLCSS\/screen_capture_api_va.html---GETME\" title=\"screen_capture_api_va.htm\">screen_capture_api_va.htm<\/a> <a target=_blank href=\"https:\/\/www.rjmprogramming.com.au\/HTMLCSS\/screen_capture_api_va.htm\">Screen Capture API using video and audio downloadable content (including single video with audio and video track option)<\/a> web application for you to try <a href='#ifsc'>below, maybe<\/a>?<\/p>\n<p><!--p>You can also see this play out at WordPress 4.1.1's <a target=_blank  href='\/\/www.rjmprogramming.com.au\/ITblog\/screen-capture-api-download-audio-video-tutorial\/'>Screen Capture API Download Multitrack Video Tutorial<\/a>.<\/p-->\n<hr>\n<p id='scapidavt'>Previous relevant <a target=_blank title='Screen Capture API Download Audio Video Tutorial' href='\/\/www.rjmprogramming.com.au\/ITblog\/screen-capture-api-download-audio-video-tutorial\/'>Screen Capture API Download Audio Video Tutorial<\/a> is shown below.<\/p>\n<div style=\"width: 230px\" class=\"wp-caption alignnone\"><a target=_blank href=\"https:\/\/www.rjmprogramming.com.au\/HTMLCSS\/screen_capture_api_va.htm\"><img decoding=\"async\" style=\"border: 15px solid pink;\" alt=\"Screen Capture API Download Audio Video Tutorial\" src=\"http:\/\/www.rjmprogramming.com.au\/HTMLCSS\/screen_capture_api_va.gif\" title=\"Screen Capture API Download Audio Video Tutorial\"  style=\"float:left;\" \/><\/a><p class=\"wp-caption-text\">Screen Capture API Download Audio Video Tutorial<\/p><\/div>\n<p>The recent <a title='Screen Capture API Download Video Tutorial' href='#scapidvt'>Screen Capture API Download Video Tutorial<\/a> &#8230;<\/p>\n<ul>\n<li><a target=_blank title='Screen Capture API' href='https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Screen_Capture_API'>Screen Capture API<\/a> inspired &#8220;streamed video&#8221; screen capturing logic &#8230; benefitted from &#8230;<\/li>\n<li>download capabilities via <a target=_blank title='https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/MediaStream_Recording_API' href='\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/MediaStream_Recording_API'>MediaRecorder<\/a> &#8220;video&#8221; functionality &#8230; and, today, we add into the mix &#8230;<\/li>\n<li>recording and download of &#8220;audio&#8221; content via the device&#8217;s &#8220;microphone&#8221; (permissions granted, <a target=_blank title='?' href='https:\/\/www.youtube.com\/watch?v=9x_FzCWl2nc'>that is<\/a>)<\/li>\n<\/ul>\n<p>This logic we thought required its own independent <a target=_blank href=\"https:\/\/www.rjmprogramming.com.au\/HTMLCSS\/audio_idea.html_GETME\">audio_idea.html<\/a> <a target=_blank href='https:\/\/www.rjmprogramming.com.au\/HTMLCSS\/audio_idea.html'>&#8220;proof of Audio only MediaRecorder usage concept&#8221;<\/a> web application, the reason being that very little in the &#8220;media&#8221; wooooorrrrlllllddd passes muster these days if it does not get preceded by a real user &#8220;onclick&#8221; event induced click of a relevant HTML element (usually a button), and this infers a need to synchronize, and have Javascript var<font size=1>iable<\/font> names that do not overlap many of the &#8220;video&#8221; ones.<\/p>\n<p><code><br \/>\n&lt;html&gt;<br \/>\n&lt;head&gt;<br \/>\n&lt;style&gt;<br \/>\nbody {<br \/>\n  padding: 0;<br \/>\n  margin: 0;<br \/>\n}<br \/>\n<br \/>\n#amytable {<br \/>\n  margin-top: 0px;<br \/>\n  margin-left: 0px;<br \/>\n}<br \/>\n<br \/>\ntd {<br \/>\n  vertical-align: top;<br \/>\n}<br \/>\n<br \/>\nbutton {<br \/>\n  vertical-align: top;<br \/>\n}<br \/>\n&lt;\/style&gt;<br \/>\n&lt;\/head&gt;<br \/>\n&lt;body&gt;<br \/>\n&lt;table id=amytable&gt;&lt;tr&gt;&lt;td&gt;&lt;button id=astart&gt;Audio &amp;#9654;&lt;\/button&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;button onclick=\"adownload();\" id=adownload&gt;Download &amp;#127908;&lt;\/button&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;button id=astop&gt;Stop &amp;#9209;&lt;\/button&gt;&lt;br&gt;&lt;br&gt;<br \/>\n&lt;audio id=audio autoplay&gt;&lt;\/audio&gt;<br \/>\n<br \/>\n&lt;!--strong id=stronge&gt;Log:&lt;\/strong&gt;<br \/>\n&lt;br&gt;<br \/>\n&lt;pre id=\"log\"&gt;&lt;\/pre--&gt;&lt;\/td&gt;&lt;td style=\"width:20%;\"&gt;<br \/>\n&lt;!--h2&gt;Using Screen Capture API&lt;\/h2&gt;&lt;h3&gt;RJM Programming - June, 2022&lt;\/h3&gt;&lt;h4&gt;Thanks to &lt;a target=_blank href='\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Screen_Capture_API\/Using_Screen_Capture' title='https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Screen_Capture_API\/Using_Screen_Capture'&gt;https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Screen_Capture_API\/Using_Screen_Capture&lt;\/a&gt; and &lt;a target=_blank title='https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/MediaStream_Recording_API' href='\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/MediaStream_Recording_API'&gt;https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/MediaStream_Recording_API&amp;nbsp;&amp;nbsp;&lt;\/a&gt;&lt;\/h4&gt;&lt;br&gt;&lt;br&gt;&lt;strong id=stronge&gt;Log:&lt;\/strong--&gt;<br \/>\n&lt;br&gt;<br \/>\n&lt;pre id=\"alog\"&gt;&lt;\/pre&gt;&lt;canvas id=acanvas&gt;&lt;\/canvas&gt;&lt;\/td&gt;&lt;\/tr&gt;&lt;\/table&gt;<br \/>\n<br \/>\n&lt;script type='text\/javascript'&gt;<br \/>\nvar audioElem=document.getElementById('audio');<br \/>\nvar amediaRecorder=null, xblob=null, chunks=[], xaudio=null, xaudioURL=null, astream=null, microphone=null, audioCtx=null, acanvas=null;<br \/>\nconst astartElem = document.getElementById(\"astart\");<br \/>\nconst astopElem = document.getElementById(\"astop\");<br \/>\n<br \/>\n\/\/console.log = msg =&gt; logElem.innerHTML += `${msg}&lt;br&gt;`;<br \/>\n\/\/console.error = msg =&gt; logElem.innerHTML += `&lt;span class=\"error\"&gt;${msg}&lt;\/span&gt;&lt;br&gt;`;<br \/>\n\/\/console.warn = msg =&gt; logElem.innerHTML += `&lt;span class=\"warn\"&gt;${msg}&lt;span&gt;&lt;br&gt;`;<br \/>\n\/\/console.info = msg =&gt; logElem.innerHTML += `&lt;span class=\"info\"&gt;${msg}&lt;\/span&gt;&lt;br&gt;`;<br \/>\n<br \/>\n\/\/ Set event listeners for the start and stop buttons ... thanks to https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Screen_Capture_API\/Using_Screen_Capture<br \/>\nastartElem.addEventListener(\"click\", function(evt) {<br \/>\n  astartCapture();<br \/>\n}, false);<br \/>\n<br \/>\nastopElem.addEventListener(\"click\", function(evt) {<br \/>\n  astopCapture();<br \/>\n}, false);<br \/>\n<br \/>\nasync function astartCapture() {<br \/>\naudioCtx = new AudioContext();<br \/>\nif (navigator.mediaDevices) {<br \/>\n<br \/>\n\/\/console.log('precanvas');<br \/>\nacanvas = document.querySelector(\"acanvas\");<br \/>\n\/\/console.log('canvas=' + canvas);<br \/>\n<br \/>\n\/\/ Optional frames per second argument.<br \/>\n\/\/const stream = canvas.captureStream(25);<br \/>\n<br \/>\n\/\/console.log(stream);<br \/>\n \/\/window.stream = stream;<br \/>\n<br \/>\n  navigator.mediaDevices.getUserMedia({\"audio\": true}).then((stream) =&gt; {<br \/>\n    audioElem.srcObject = stream;<br \/>\n    microphone = audioCtx.createMediaStreamSource(stream);<br \/>\n<br \/>\n\/\/ Instantiate the media recorder.<br \/>\namediaRecorder = new MediaRecorder(audioElem.srcObject); \/\/audioElem.srcObject);<br \/>\n<br \/>\n\/\/ Create a buffer to store the incoming data.<br \/>\nchunks = [];<br \/>\namediaRecorder.ondataavailable = (event) =&gt; {<br \/>\n  chunks.push(event.data);<br \/>\n}<br \/>\n<br \/>\namediaRecorder.start(1000);<br \/>\n<br \/>\namediaRecorder.onstop = () =&gt; {<br \/>\n  \/\/ A \"blob\" combines all the audio chunks into a single entity<br \/>\n  xblob = new Blob(chunks, {\"type\": \"audio\/ogg; codecs=opus\"});<br \/>\n  chunks = []; \/\/ clear buffer<br \/>\n<br \/>\n  \/\/ One of many ways to use the blob<br \/>\n  xaudio = new Audio();<br \/>\n  xaudioURL = window.URL.createObjectURL(xblob);<br \/>\n  xaudio.src = xaudioURL;<br \/>\n}<br \/>\n    \/\/ `microphone` can now act like any other AudioNode<br \/>\n  }).catch((err) =&gt; {<br \/>\n    \/\/ browser unable to access microphone<br \/>\n    \/\/ (check to see if microphone is attached)<br \/>\n  });<br \/>\n} else {<br \/>\n  \/\/ browser unable to access media devices<br \/>\n  \/\/ (update your browser)<br \/>\n}<br \/>\n}<br \/>\n<br \/>\nfunction astopCapture() {<br \/>\n  let atracks = audioElem.srcObject.getTracks();<br \/>\n<br \/>\n  \/\/tracks.forEach(track =&gt; amediaRecorder.addTrack(track, stream));<br \/>\n  atracks.forEach(track =&gt; track.stop());<br \/>\n  audioElem.srcObject = null;<br \/>\n  if (amediaRecorder) {<br \/>\n   amediaRecorder.stop();<br \/>\n  }<br \/>\n}<br \/>\n<br \/>\nfunction adownload() {<br \/>\n  \/\/console.log(\"downloading 0\");<br \/>\n\/\/  let dtracks = videoElem.srcObject.getTracks();<br \/>\n  \/\/tracks.forEach(track =&gt; mediaRecorder.addTrack(track, stream));<br \/>\n\/\/  dtracks.forEach(track =&gt; window.stream.addTrack(track));<br \/>\n  \/\/console.log(\"recording1 vs \" + mediaRecorder.state);<br \/>\n  \/\/mediaRecorder.stop();<br \/>\n  \/\/console.log(\"recording2 vs \" + mediaRecorder.state);<br \/>\n  if (amediaRecorder) {<br \/>\n  if (('' + amediaRecorder.state) != 'inactive') {<br \/>\n  amediaRecorder.requestData();<br \/>\n  }<br \/>\n  \/\/console.log(\"recording3 vs \" + amediaRecorder.state);<br \/>\n  xblob = new Blob(chunks, {<br \/>\n    type: \"audio\/ogg; codecs=opus\"<br \/>\n  });<br \/>\n  url = URL.createObjectURL(xblob);<br \/>\n  a = document.createElement(\"a\");<br \/>\n  document.body.appendChild(a);<br \/>\n  a.style = \"display: none\";<br \/>\n  a.href = url;<br \/>\n  a.download = \"audio_idea.ogg\";<br \/>\n  a.click();<br \/>\n    setTimeout(() =&gt; {<br \/>\n        document.body.removeChild(a);<br \/>\n        window.URL.revokeObjectURL(url);<br \/>\n    }, 100);<br \/>\n  \/\/window.URL.revokeObjectURL(url);<br \/>\n  \/\/console.log(\"downloading 1\");<br \/>\n  \/\/amediaRecorder.start(1000);<br \/>\n  \/\/console.log(\"recording4 vs \" + amediaRecorder.state);<br \/>\n  }<br \/>\n}<br \/>\n<br \/>\nfunction dostopit() {<br \/>\n  astopCapture();<br \/>\n}<br \/>\n<br \/>\n\/\/setTimeout(dostopit, 9000);<br \/>\n&lt;\/script&gt;<br \/>\n&lt;\/body&gt;<br \/>\n&lt;\/html&gt;<br \/>\n<\/code><\/p>\n<p>This proved a good way to then (have that code, largely) ease into <a target=_blank href=\"http:\/\/www.rjmprogramming.com.au\/PHP\/Geographicals\/diff.php?one=http:\/\/www.rjmprogramming.com.au\/HTMLCSS\/screen_capture_api_test.html--GETME\" title=\"screen_capture_api_va.htm\">the changed<\/a> <a target=_blank href=\"https:\/\/www.rjmprogramming.com.au\/HTMLCSS\/screen_capture_api_va.html--GETME\" title=\"screen_capture_api_va.htm\">screen_capture_api_va.htm<\/a> <a target=_blank href=\"https:\/\/www.rjmprogramming.com.au\/HTMLCSS\/screen_capture_api_va.htm\">Screen Capture API using video and audio downloadable content<\/a> web application for you to try <a href='#ifsc'>below, maybe<\/a>?<\/p>\n<p>In order to assist with user understanding of functionality we do a bit of &#8230;<\/p>\n<p><code><br \/>\n&lt;button onclick=\"download();\" id=download disabled&gt;Download &amp;#128249;&lt;\/button&gt;<br \/>\n<\/code><\/p>\n<p> &#8230; HTML (design based) button disabling, teamed with Javascript, dynamic &#8230;<\/p>\n<p><code><br \/>\n    document.getElementById('download').disabled=false;<br \/>\n<\/code><\/p>\n<p> &#8230; button re-enabling, as required.<\/p>\n<p>In the HTML &#8220;static design&#8221; side of &#8220;media play back&#8221; things we add to the bottom of the document.body element &#8230;<\/p>\n<p><code><br \/>\n&lt;hr id=myhr&gt;<br \/>\n&lt;table id=myplayt style=\"width:96%;\"&gt;&lt;tr&gt;&lt;td style=\"width:70%;\"&gt;<br \/>\n&lt;video id=playvideo style='display:none;width:95%;' type='video\/webm' onloadeddata='playa();' onclick='playa();' controls&gt;<br \/>\n&lt;!--source id=playvideov type='video\/webm' style='display:none;' src=''&gt;&lt;\/source--&gt;<br \/>\n&lt;!--source id=playvideoas type='audio\/ogg' style='display:none;' src=''&gt;&lt;\/source--&gt;<br \/>\n&lt;\/video&gt;&lt;\/td&gt;&lt;td style=\"width:30%;\"&gt;<br \/>\n&lt;audio id=playvideoa style='display:none;width:95%;' type='audio\/ogg' controls&gt;&lt;\/audio&gt;&lt;br&gt;&lt;div id=divsync&gt;&lt;\/div&gt;&lt;\/td&gt;&lt;\/tr&gt;&lt;\/table&gt;&lt;br&gt;&lt;br&gt;&lt;p id=lastp&gt;&lt;\/p&gt;<br \/>\n<\/code><\/p>\n<p> &#8230; is used by reworked &#8220;download&#8221; logic as per &#8230;<\/p>\n<p><code><br \/>\nfunction download() {<br \/>\n  \/\/console.log(\"downloading 0\");<br \/>\n\/\/  let dtracks = videoElem.srcObject.getTracks();<br \/>\n  \/\/tracks.forEach(track =&gt; mediaRecorder.addTrack(track, stream));<br \/>\n\/\/  dtracks.forEach(track =&gt; window.stream.addTrack(track));<br \/>\n  \/\/console.log(\"recording1 vs \" + mediaRecorder.state);<br \/>\n  \/\/mediaRecorder.stop();<br \/>\n  \/\/console.log(\"recording2 vs \" + mediaRecorder.state);<br \/>\n  if (mediaRecorder) {<br \/>\n  if (('' + mediaRecorder.state) != 'inactive') {<br \/>\n  mediaRecorder.requestData();<br \/>\n  }<br \/>\n  \/\/console.log(\"recording3 vs \" + mediaRecorder.state);<br \/>\n  blob = new Blob(recordedChunks, {<br \/>\n    type: \"video\/webm\"<br \/>\n  });<br \/>\n  url = URL.createObjectURL(blob);<br \/>\n<br \/> <br \/>\n  if (1 == 11) {<br \/>\nRequestAsBlob(url,<br \/>\nfunction(vblob)<br \/>\n{<br \/>\n var vurl = URL.createObjectURL(vblob);<br \/>\n<br \/>\n document.getElementById('playvideo').src = vurl;<br \/>\n document.getElementById('playvideo').play();<br \/>\n});<br \/>\n  } else {<br \/>\n  \/\/document.getElementById('playvideov').src=url;<br \/>\n document.getElementById('playvideo').src = url;<br \/>\n document.getElementById('playvideo').play();<br \/>\n  }<br \/>\n  if (1 == 1) {<br \/>\n  \/\/document.getElementById('playvideov').style.display='block';<br \/>\n  document.getElementById('playvideo').style.display='block';<br \/>\n  document.getElementById('myplayt').style.backgroundColor='yellow';<br \/>\n  document.getElementById('myplayt').scrollIntoView(); \/\/location.href='#lastp'; \/\/document.getElementById('lastp').scrollIntoView(); \/\/document.getElementById('playvideo').scrollIntoView(); \/\/location.href='#myhr';<br \/>\n  window.scrollBy(0,120);<br \/>\n  }<br \/>\n  a = document.createElement(\"a\");<br \/>\n  document.body.appendChild(a);<br \/>\n  a.style = \"display: none\";<br \/>\n  a.href = url;<br \/>\n  a.download = \"screen_capture_api_va.webm\";<br \/>\n  a.click();<br \/>\n    setTimeout(() =&gt; {<br \/>\n        document.body.removeChild(a);<br \/>\n        window.URL.revokeObjectURL(url);<br \/>\n        window.scrollTo(0,document.body.scrollHeight);<br \/>\n    }, 100);<br \/>\n  \/\/window.URL.revokeObjectURL(url);<br \/>\n  \/\/console.log(\"downloading 1\");<br \/>\n  \/\/mediaRecorder.start(1000);<br \/>\n  \/\/console.log(\"recording4 vs \" + mediaRecorder.state);<br \/>\n  }<br \/>\n  if (document.getElementById('adownload')) { adownload(); }<br \/>\n}<br \/>\n<br \/>\nfunction adownload() {<br \/>\n  \/\/console.log(\"downloading 0\");<br \/>\n\/\/  let dtracks = videoElem.srcObject.getTracks();<br \/>\n  \/\/tracks.forEach(track =&gt; mediaRecorder.addTrack(track, stream));<br \/>\n\/\/  dtracks.forEach(track =&gt; window.stream.addTrack(track));<br \/>\n  \/\/console.log(\"recording1 vs \" + mediaRecorder.state);<br \/>\n  \/\/mediaRecorder.stop();<br \/>\n  \/\/console.log(\"recording2 vs \" + mediaRecorder.state);<br \/>\n  if (amediaRecorder) {<br \/>\n  if (('' + amediaRecorder.state) != 'inactive') {<br \/>\n  amediaRecorder.requestData();<br \/>\n  }<br \/>\n  \/\/console.log(\"recording3 vs \" + amediaRecorder.state);<br \/>\n  xblob = new Blob(chunks, {<br \/>\n    type: \"audio\/ogg; codecs=opus\"<br \/>\n  });<br \/>\n  aaurl = URL.createObjectURL(xblob);<br \/>\n  if (1 == 11) {<br \/>\nRequestAsBlob(aaurl,<br \/>\nfunction(aablob)<br \/>\n{<br \/>\n var aaaurl = URL.createObjectURL(aablob);<br \/>\n<br \/>\n document.getElementById('playvideoa').src = aaaurl;<br \/>\n document.getElementById('playvideoa').play();<br \/>\n});<br \/>\n  } else {<br \/>\n  \/\/document.getElementById('playvideoa').src=url;<br \/>\n document.getElementById('playvideoa').src = aaurl;<br \/>\n if (delaya == 0) {<br \/>\n document.getElementById('playvideoa').play();<br \/>\n } else {<br \/>\n document.getElementById('divsync').innerHTML='&lt;br&gt;&lt;br&gt;Clicking within video synchronizes an audiovisual replay';<br \/>\n document.getElementById('playvideo').title='Clicking within this video (not clicking play button) synchronizes an appropriate audiovisual replay';<br \/>\n playa(); \/\/setTimeout(function(){ document.getElementById('playvideoa').play(); }, delaya);<br \/>\n \/\/document.getElementById('playvideo').addEventListener('click', playa);<br \/>\n }<br \/>\n  }<br \/>\n  if (8 == 8) {<br \/>\n  document.getElementById('playvideoa').style.display='block';<br \/>\n  document.getElementById('playvideo').style.display='block';<br \/>\n  }<br \/>\n  aa = document.createElement(\"a\");<br \/>\n  document.body.appendChild(aa);<br \/>\n  aa.style = \"display: none\";<br \/>\n  aa.href = aaurl;<br \/>\n  aa.download = \"screen_capture_api_va.ogg\";<br \/>\n  aa.click();<br \/>\n    setTimeout(() =&gt; {<br \/>\n        document.body.removeChild(aa);<br \/>\n        window.URL.revokeObjectURL(aaurl);<br \/>\n    }, 100);<br \/>\n  \/\/window.URL.revokeObjectURL(url);<br \/>\n  \/\/console.log(\"downloading 1\");<br \/>\n  \/\/amediaRecorder.start(1000);<br \/>\n  \/\/console.log(\"recording4 vs \" + amediaRecorder.state);<br \/>\n  }<br \/>\n}<br \/>\n<br \/>\nfunction playa() {<br \/>\n  setTimeout(function(){ document.getElementById('playvideoa').play(); }, delaya);<br \/>\n}<br \/>\n<\/code><\/p>\n<p> &#8230; the Javascript var<font size=1>iable<\/font> &#8220;delaya&#8221; containing the milliseconds between the &#8220;video start click&#8221; and the &#8220;audio start click&#8221; used in a <a target=_blank title='Javascript setTimeout() method information from w3schools' href='http:\/\/www.w3schools.com\/jsref\/met_win_settimeout.asp'>setTimeout<\/a> timer approach to synchronizing the &#8220;audio stream&#8221; play endpoint line up with the &#8220;video stream&#8221; play endpoint in these media elements above, even if a replay is achieved via a click &#8220;within&#8221; the video element (rather than using that video&#8217;s play button, where behaviour is as per usual).<\/p>\n<p><!--p>You can also see this play out at WordPress 4.1.1's <a target=_blank  href='\/\/www.rjmprogramming.com.au\/ITblog\/screen-capture-api-download-audio-video-tutorial\/'>Screen Capture API Download Audio Video Tutorial<\/a>.<\/p-->\n<hr>\n<p id='scapidvt'>Previous relevant <a target=_blank title='Screen Capture API Download Video Tutorial' href='\/\/www.rjmprogramming.com.au\/ITblog\/screen-capture-api-download-video-tutorial\/'>Screen Capture API Download Video Tutorial<\/a> is shown below.<\/p>\n<div style=\"width: 230px\" class=\"wp-caption alignnone\"><a target=_blank href=\"https:\/\/www.rjmprogramming.com.au\/HTMLCSS\/screen_capture_api_test.htm\"><img decoding=\"async\" style=\"border: 15px solid pink;\" alt=\"Screen Capture API Download Video Tutorial\" src=\"http:\/\/www.rjmprogramming.com.au\/HTMLCSS\/screen_capture_api_test_video_download.jpg\" title=\"Screen Capture API Download Video Tutorial\"  style=\"float:left;\" \/><\/a><p class=\"wp-caption-text\">Screen Capture API Download Video Tutorial<\/p><\/div>\n<p>Lots of users will have noticed regarding yesterday&#8217;s <a title='Screen Capture API Primer Tutorial' href='#scapipt'>Screen Capture API Primer Tutorial<\/a> creation of a &#8230;<\/p>\n<ul>\n<li><a target=_blank title='Screen Capture API' href='https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Screen_Capture_API'>Screen Capture API<\/a> inspired &#8220;streamed video&#8221; &#8230; the downside is that &#8230;<\/li>\n<li>context (or right-click or two finger gesture) menu over that &#8220;streamed video&#8221; has a grayed out &#8220;Save Video As&#8230;&#8221; option &#8230; hampering sharing, except that, today, we add &#8230;<\/li>\n<li>&#8220;streamed video&#8221; sharing mechanism, featuring &#8230;\n<ol>\n<li>one new HTML &#8220;Download &#128249;&#8221; button and canvas &#8230;<br \/>\n<code><br \/>\n&lt;button onclick=\"download();\" id=download&gt;Download &amp;#128249;&lt;\/button&gt;<br \/>\n&lt;canvas id=canvas&gt;&lt;\/canvas&gt;<br \/>\n<\/code><br \/>\n &#8230; linking to &#8220;onclick&#8221; logic &#8230;\n<\/li>\n<li>Javascript event logic for download of a &#8220;webm&#8221; video &#8230;<br \/>\n<code><br \/>\nvar mediaRecorder=null;<br \/>\nvar blob=null, url=null, a=null;<br \/>\nvar recorderChunks=[];<br \/>\n<br \/>\nfunction download() {<br \/>\n  if (mediaRecorder) {<br \/>\n  if (('' + mediaRecorder.state) != 'inactive') {<br \/>\n  mediaRecorder.requestData();<br \/>\n  }<br \/>\n  blob = new Blob(recordedChunks, {<br \/>\n    type: \"video\/webm\"<br \/>\n  });<br \/>\n  url = URL.createObjectURL(blob);<br \/>\n  a = document.createElement(\"a\");<br \/>\n  document.body.appendChild(a);<br \/>\n  a.style = \"display: none\";<br \/>\n  a.href = url;<br \/>\n  a.download = \"screen_capture_api_test.webm\";<br \/>\n  a.click();<br \/>\n    setTimeout(() => {<br \/>\n        document.body.removeChild(a);<br \/>\n        window.URL.revokeObjectURL(url);<br \/>\n    }, 100);<br \/>\n  }<br \/>\n}<br \/>\n<\/code><br \/>\n &#8230; using  &#8230;\n<\/li>\n<li><a target=_blank title='https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/MediaStream_Recording_API' href='\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/MediaStream_Recording_API'>Media Recorder<\/a> <font color=blue>object and method logics<\/font> &#8230;<br \/>\n<code><br \/>\nasync function startCapture() {<br \/>\n  <font color=blue>recordedChunks = [];<\/font><br \/>\n<br \/> <br \/>\n  if (typeof(logElem) !== 'undefined') {<br \/>\n  logElem.innerHTML = \"\";<br \/>\n  }<br \/>\n<br \/>\n  try {<br \/>\n    if (typeof(videoElem) !== 'undefined') {<br \/>\n    videoElem.srcObject = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions);<br \/>\n    dumpOptionsInfo();<br \/>\n    }<br \/>\n  } catch(err) {<br \/>\n    console.error(\"Error: \" + err);<br \/>\n  }<br \/>\n<br \/>\n  <font color=blue>if (typeof(stream) === 'undefined') {<br \/>\nconst canvas = document.querySelector(\"canvas\");<br \/>\nconst stream = canvas.captureStream(25);<br \/>\n<br \/>\n window.stream = stream;<br \/>\n const options = { mimeType: \"video\/webm;codecs=vp9\" };  \/\/ vs 9<br \/>\n mediaRecorder = new MediaRecorder(videoElem.srcObject, options);<br \/>\n<br \/>\n mediaRecorder.ondataavailable = function(e) {<br \/>\n  if (e.data.size &gt; 0){<br \/>\n    recordedChunks.push(e.data);<br \/>\n  }<br \/>\n };<br \/>\n<br \/>\n mediaRecorder.start(1000);<br \/>\n<br \/> <br \/>\n mediaRecorder.onstop = function(e) {<br \/>\n  if (recordedChunks) {<br \/>\n    if(1 == 5 && recordedChunks.length &gt; 0 && document.getElementById('audio')) {<br \/>\n      \/\/const audio = document.querySelector('audio');<br \/>\n      audio.controls = true;<br \/>\n      bloba = new Blob(recordedChunks, { 'type' : 'audio\/ogg; codecs=opus'      });<br \/>\n      audioURL = window.URL.createObjectURL(bloba);<br \/>\n      audio.src = audioURL;<br \/>\n    }<br \/>\n   }<br \/>\n   location.href=document.URL;<br \/>\n  };<br \/>\n<br \/>\n  }<\/font><br \/>\n<br \/> <br \/>\n  if (document.getElementById('mybody')) {<br \/>\n  document.getElementById('mybody').style.backgroundImage='none';<br \/>\n  }<br \/>\n}<br \/>\n<\/code>\n<\/li>\n<\/ol>\n<\/li>\n<\/ul>\n<p> &#8230; in <a target=_blank href=\"https:\/\/www.rjmprogramming.com.au\/PHP\/Geographicals\/diff.php?one=https:\/\/www.rjmprogramming.com.au\/HTMLCSS\/screen_capture_api_test.html-GETME\" title=\"screen_capture_api_test.htm\">a changed<\/a> <a target=_blank href=\"https:\/\/www.rjmprogramming.com.au\/HTMLCSS\/screen_capture_api_test.html-GETME\" title=\"screen_capture_api_test.htm\">screen_capture_api_test.htm<\/a>&#8216;s <a target=_blank href=\"https:\/\/www.rjmprogramming.com.au\/HTMLCSS\/screen_capture_api_test.html\" title=\"Click picture\">live run<\/a>, that has a sharing component now, which you <a href='#ifsc'>can also try below<\/a>.<\/p>\n<p><!--p>You can also see this play out at WordPress 4.1.1's <a target=_blank  href='\/\/www.rjmprogramming.com.au\/ITblog\/screen-capture-api-download-video-tutorial\/'>Screen Capture API Download Video Tutorial<\/a>.<\/p-->\n<hr>\n<p id='scapipt'>Previous relevant <a target=_blank title='Screen Capture API Primer Tutorial' href='\/\/www.rjmprogramming.com.au\/ITblog\/screen-capture-api-primer-tutorial\/'>Screen Capture API Primer Tutorial<\/a> is shown below.<\/p>\n<div style=\"width: 230px\" class=\"wp-caption alignnone\"><a target=_blank href=\"https:\/\/www.rjmprogramming.com.au\/HTMLCSS\/screen_capture_api_test.html\"><img decoding=\"async\" style=\"border: 15px solid pink;\" alt=\"Screen Capture API Primer Tutorial\" src=\"http:\/\/www.rjmprogramming.com.au\/HTMLCSS\/screen_capture_api_test.gif\" title=\"Screen Capture API Primer Tutorial\"  style=\"float:left;\" \/><\/a><p class=\"wp-caption-text\">Screen Capture API Primer Tutorial<\/p><\/div>\n<p>We&#8217;ve spent another day researching the web <a target=_blank title='W3 Spec for Screen Capture API' href='https:\/\/www.w3.org\/TR\/screen-capture\/'>Screen Capture<\/a> <a target=_blank title='Screen Capture API' href='https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Screen_Capture_API'>API<\/a> whereby &#8230;<\/p>\n<blockquote cite='https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Screen_Capture_API'><p>\nThe Screen Capture API introduces additions to the existing Media Capture and Streams API to let the user select a screen or portion of a screen (such as a window) to capture as a media stream. This stream can then be recorded or shared with others over the network.\n<\/p><\/blockquote>\n<p>Teamed with this great resource was the tutorial, we followed to a letter, called <a target=_blank href='https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Screen_Capture_API\/Using_Screen_Capture' title='Using the Screen Capture API'>Using the Screen Capture API<\/a> that had us being able to display a video of the Screen Capturing, including cursor movement, as applicable.  Cute, huh?!<\/p>\n<p>Again, as with many web API functionality, permissions may need to be tweaked allowing the web browsers involved, permission to access &#8220;Screen Capturing&#8221;, <a target=_blank href=\"http:\/\/www.rjmprogramming.com.au\/HTMLCSS\/screen_capture_api_test.gif\" title=\"Tutorial picture\">at least in the case of the macOS environment we tested this on today<\/a>.<\/p>\n<p>Luckily, we can show you the fruits of following a tutorial assiduously with <a target=_blank href=\"https:\/\/www.rjmprogramming.com.au\/HTMLCSS\/screen_capture_api_test.html_GETME\" title=\"screen_capture_api_test.html\">screen_capture_api_test.html<\/a>&#8216;s <a target=_blank href=\"https:\/\/www.rjmprogramming.com.au\/HTMLCSS\/screen_capture_api_test.html\" title=\"Click picture\">live run<\/a> you can also try below &#8230;<\/p>\n<p><iframe id=ifsc src=\"https:\/\/www.rjmprogramming.com.au\/HTMLCSS\/screen_capture_api_test.html?iframe=y\" style=\"width:100%;height:900px;\"><\/iframe><\/p>\n<p>If this was interesting you may be interested in <a title='Click here to see topics in which you might be interested' href='#d56278' onclick='var dv=document.getElementById(\"d56278\"); dv.innerHTML = \"&lt;iframe width=670 height=600 src=\" + \"https:\/\/www.rjmprogramming.com.au\/ITblog\/tag\/video\" + \"&gt;&lt;\/iframe&gt;\"; dv.style.display = \"block\";'>this<\/a> too.<\/p>\n<div id='d56278' style='display: none; border-left: 2px solid green; border-top: 2px solid green;'><\/div>\n<hr>\n<p>If this was interesting you may be interested in <a title='Click here to see topics in which you might be interested' href='#d56286' onclick='var dv=document.getElementById(\"d56286\"); dv.innerHTML = \"&lt;iframe width=670 height=600 src=\" + \"https:\/\/www.rjmprogramming.com.au\/ITblog\/tag\/download\" + \"&gt;&lt;\/iframe&gt;\"; dv.style.display = \"block\";'>this<\/a> too.<\/p>\n<div id='d56286' style='display: none; border-left: 2px solid green; border-top: 2px solid green;'><\/div>\n<hr>\n<p>If this was interesting you may be interested in <a title='Click here to see topics in which you might be interested' href='#d56330' onclick='var dv=document.getElementById(\"d56330\"); dv.innerHTML = \"&lt;iframe width=670 height=600 src=\" + \"https:\/\/www.rjmprogramming.com.au\/ITblog\/tag\/microphone\" + \"&gt;&lt;\/iframe&gt;\"; dv.style.display = \"block\";'>this<\/a> too.<\/p>\n<div id='d56330' style='display: none; border-left: 2px solid green; border-top: 2px solid green;'><\/div>\n<hr>\n<p>If this was interesting you may be interested in <a title='Click here to see topics in which you might be interested' href='#d56349' onclick='var dv=document.getElementById(\"d56349\"); dv.innerHTML = \"&lt;iframe width=670 height=600 src=\" + \"https:\/\/www.rjmprogramming.com.au\/ITblog\/tag\/audio\" + \"&gt;&lt;\/iframe&gt;\"; dv.style.display = \"block\";'>this<\/a> too.<\/p>\n<div id='d56349' style='display: none; border-left: 2px solid green; border-top: 2px solid green;'><\/div>\n","protected":false},"excerpt":{"rendered":"<p>We were inspired, stumbling upon the great Screen Recorder: recording microphone and the desktop audio at the same time webpage, leading us to think that we could improve the scope of functionality of the recent Screen Capture API Download Audio &hellip; <a href=\"https:\/\/www.rjmprogramming.com.au\/ITblog\/screen-capture-api-download-multitrack-video-tutorial\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,12,14,37],"tags":[88,2360,113,2361,174,1604,360,4008,576,652,760,4010,4024,4023,2189,849,861,4022,4021,2976,997,2256,1103,1114,1126,1133,1137,3224,1670,1282,1296,1319,1369],"class_list":["post-56349","post","type-post","status-publish","format-standard","hentry","category-animation","category-elearning","category-event-driven-programming","category-tutorials","tag-api","tag-async","tag-audio","tag-await","tag-button","tag-collaboration","tag-download","tag-getdisplaymedia","tag-html","tag-javascript","tag-media","tag-mediarecorder","tag-multi-track","tag-multitrack","tag-navigator","tag-object","tag-onclick","tag-permission","tag-permissions","tag-privacy","tag-programming","tag-screen","tag-screen-capture","tag-security","tag-settimeout","tag-share","tag-sharing","tag-stream","tag-synchronize","tag-timer","tag-track","tag-tutorial","tag-video"],"_links":{"self":[{"href":"https:\/\/www.rjmprogramming.com.au\/ITblog\/wp-json\/wp\/v2\/posts\/56349"}],"collection":[{"href":"https:\/\/www.rjmprogramming.com.au\/ITblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.rjmprogramming.com.au\/ITblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.rjmprogramming.com.au\/ITblog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.rjmprogramming.com.au\/ITblog\/wp-json\/wp\/v2\/comments?post=56349"}],"version-history":[{"count":5,"href":"https:\/\/www.rjmprogramming.com.au\/ITblog\/wp-json\/wp\/v2\/posts\/56349\/revisions"}],"predecessor-version":[{"id":56361,"href":"https:\/\/www.rjmprogramming.com.au\/ITblog\/wp-json\/wp\/v2\/posts\/56349\/revisions\/56361"}],"wp:attachment":[{"href":"https:\/\/www.rjmprogramming.com.au\/ITblog\/wp-json\/wp\/v2\/media?parent=56349"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.rjmprogramming.com.au\/ITblog\/wp-json\/wp\/v2\/categories?post=56349"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.rjmprogramming.com.au\/ITblog\/wp-json\/wp\/v2\/tags?post=56349"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}