{"id":56330,"date":"2022-07-06T03:01:15","date_gmt":"2022-07-05T17:01:15","guid":{"rendered":"http:\/\/www.rjmprogramming.com.au\/ITblog\/?p=56330"},"modified":"2022-07-06T15:51:26","modified_gmt":"2022-07-06T05:51:26","slug":"screen-capture-api-download-audio-video-tutorial","status":"publish","type":"post","link":"https:\/\/www.rjmprogramming.com.au\/ITblog\/screen-capture-api-download-audio-video-tutorial\/","title":{"rendered":"Screen Capture API Download Audio 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 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","protected":false},"excerpt":{"rendered":"<p>The recent Screen Capture API Download Video Tutorial &#8230; Screen Capture API inspired &#8220;streamed video&#8221; screen capturing logic &#8230; benefitted from &#8230; download capabilities via MediaRecorder &#8220;video&#8221; functionality &#8230; and, today, we add into the mix &#8230; recording and download &hellip; <a href=\"https:\/\/www.rjmprogramming.com.au\/ITblog\/screen-capture-api-download-audio-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,360,4008,576,652,4010,2189,849,861,4022,4021,2976,997,2256,1103,1114,1126,1133,1137,3224,1670,1282,1319,1369],"class_list":["post-56330","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-download","tag-getdisplaymedia","tag-html","tag-javascript","tag-mediarecorder","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-tutorial","tag-video"],"_links":{"self":[{"href":"https:\/\/www.rjmprogramming.com.au\/ITblog\/wp-json\/wp\/v2\/posts\/56330"}],"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=56330"}],"version-history":[{"count":8,"href":"https:\/\/www.rjmprogramming.com.au\/ITblog\/wp-json\/wp\/v2\/posts\/56330\/revisions"}],"predecessor-version":[{"id":56338,"href":"https:\/\/www.rjmprogramming.com.au\/ITblog\/wp-json\/wp\/v2\/posts\/56330\/revisions\/56338"}],"wp:attachment":[{"href":"https:\/\/www.rjmprogramming.com.au\/ITblog\/wp-json\/wp\/v2\/media?parent=56330"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.rjmprogramming.com.au\/ITblog\/wp-json\/wp\/v2\/categories?post=56330"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.rjmprogramming.com.au\/ITblog\/wp-json\/wp\/v2\/tags?post=56330"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}