H2S133: Investigating intermediate paths of mp4 from mediaRecorder.start() to download:


Hu: Following<WP.MIC-H2S123,H3S4.H4S1-H5S4,H6S4>-><WP.MIC-H2S131,H3S1.H4S4>, we were able to turn on camera, and display its feedback in the user’s browser. We have established the capacity and grounds for media capture, but no recording is made yet, to our local files. There are existing libraries for recording, but our requirement is 30/s, and automated, such that the user need not click to download, as, obviously, this is unfeasible for that volume of output/s. We also need an objective | intermediate path<Turing>such that the recipient side can intercept, capture, and stream the files in real | time.

// snippet 1:
navigator.mediaDevices
    .getUserMedia(constraints)
    .then((stream) => {
      const mediaRecorder = new MediaRecorder(stream);
// snippet 2:
      record.onclick = () => {
        mediaRecorder.start();
      }

// snippet 3:
stop.onclick = () => {
        mediaRecorder.stop();
}

Hu: Above, a sequence of code-snippets from dev-Mozilla<a-r>, on the MediaRecorder main; I’m not currently interesting in using their chunk | blog setup, just to produce a single mp4 output, even if I need to manually click to download.

CM H4S1: Snippet 1:

Hu: Chained to the perm-line is the passage of the media.stream-obj to be acted upon by the MediaRecorder function, which<WP.MIC-H2S123><Mozilla, a-r>: creates a new MediaRecorder object, given a MediaStream to record. However, note that the recording has not started yet. This obj is assigned to the variable ‘mediaRecorder’, name-invented # Const is a standard JS.var-declr,keyw, only stipulating additionally  cannot be reassigned: W3-Schools<a-r>.

function start_record() {
	stream.then(function (value) { mediaRecorder = new MediaRecorder(value); }); 
}	

Hu: Here’s our code, appended to the file we created<WP.MIC-H2S131,H3S1.H4S4-H5S1,H6S4><long-ref>, which, paired with a new button-trigger, will create a MediaRecorder-obj, using the stream generated from the getUserMedia call.

CM H4S2: Snippet-2:

Hu: If we have a working version of mediaRecorder.state nested in console-log:

console.log(mediaRecorder.state);

that can be elicited after the mediaRecorder-obj is declared, then we can run mediaRecorder.start(), and test if the recording has started, without knowing the recording path yet. Our-code now looks like:

function start_record() {
	stream.then(function (value) { mediaRecorder = new MediaRecorder(value); 
	mediaRecorder.start();}); 	
}

^ Generated 11/28/22 3:14 AM, after reaching H3S2.H4S2-very,beginniner<console-log><fbno>; occasionally my q.tum-brain is able to synthesize 2 incomplete proteins, by generating the other amino acids quantumly, once I can q.tum-identify what’s missing, but I deserve the trademark #

H3S2: Live-record<miguelao>:

7-s, 308-kb in .webm format, a bit-rate of 44-kb/s, which is healthy for video. Hu: Miguel Casas-Sanchez, in affiliation with W3C<a-r>is notable for generating a working demo that 1) shows webcam feedback 2) starts recording and 3) successfully generates a downloadable .webm file, from a browser.embedded-web,page<Turing>. The sample-video is embedded<-, and the full-code in a-r. His innovative catch-implementation will be covered<WP.MIC-H2S123,H3S4.H4S4>

H4S1: .then(gotmedia):

Hu:<miguelao>uses a button named “Grab video & start recording” to trigger function startFunction(), over on js, and this function contains a getUserMedia | line that triggers a secondary | func, upon .then, named gotMedia; this function, unlike my srcObject declaration in cam-record.php, is defined separately.

.then(gotMedia)

Hu: Note that here, gotMedia is called without a param, but the definition contains an argument, a clear violation, but when in JS 🤷

H5S1: gotMedia func-def:

In this function, in the first internal | line, miguelao declares a variable theStream, analogous to mine stream in cam_on, and assigns it the stream-obj, extracted by getUserMedia. In the next 2 lines, he associates that stream-obj with the srcObject of the ‘video'<video>#. Then, a MediaRecorder-obj is declared, named ‘recorder’. #complexity-ceiling

After a spatial gap, but without the func-ending, just the try.catch-clause, a clause that remains perplexing #:

recorder.ondataavailable = 
      (event) => { recordedChunks.push(event.data); };
  recorder.start(100);

Hu: This snippet is almost impossible to understand unless I also read the next function, which is triggered by the other interface-element, “Download! (and stop video)”, a violation-2. There are several built.in-funcs here worthy of review, but will hold off:

H4S2: function download()

H5S1: Line.1-internal: stops the recording, using the same-obj

H5S2:

theStream.getTracks().forEach(track => { track.stop(); });

Hu: Probably the best line in this function, and likely, the only one that does anything<God>, as well as the most aesthetic, obviously, by virtue of the previous # H6S1: theStream is the JS-var that miguelao assigned the stream-obj, and now he’s acting upon it for the second time, after creating the MediaRecorder, using .getTracks: H6S2: .getTracks: The getTracks() method of the MediaStream interface returns a sequence that represents all the MediaStreamTrack objects in this stream’s track set, regardless of MediaStreamTrack.kind. H7S1: Since .getTracks returns a list | representation, it cannot be acted upon directly, to affect the stream<Turing>; forEach loops through this list, and extracts the name of the track, and stops the track, using track.stop<MediaStreamTrack.stop(), a-r>; note that this is redundant<80%, #v-t 11/28/22>with having already stopped the Recording-obj.

It has also occurred to me, at this point, that stopping the stream, is independent from compiling a blob, in this implementation, to output the download, which is what I’m looking for<Turing>

The JS-compiler is about 1,000x worse than PHP’s.

Indeed, when I remove the first 2 lines of internal, containing the stop-tracks, I am able to click the download button, and download files repeatedly, without the cam, or recording, stopping<Turing>. However, each track does not stop from the end of the previous | track, because in the fallacious | implementation, the recordedChunks array continues to build, and the blob, from which the download file is generated, is continued to be bloated. This can be resolved later, by emptying the Chunk<idea>every time download completes, but simply adding this line causes a 0-kb download, so I likely need to associate the emptying with an event, to circumvent JS’s broken-compiler<Turing>; will investigate more, later.

H5S3:

(event) => { recordedChunks.push(event.data); };

Hu: Going back to the startFunction, this line, then, is what builds the recordedChunks array, defined pre-funcs<OK>, upon the ondataavailable event, which, I believe, occurs every 100-ms, as set by the start(interval), but will investigate this<80% 11/28/22>

H5S4:

var blob = new Blob(recordedChunks, {type: "video/webm"});

Hu: The blob-stuff in JS is actually quite complex, and utile, but the JS-fauxes that work on MediaStreams like to use this as a one-stop to generate the file for download. The blob, here, takes a param that specifies the video in webm format. webm was previously set as the mimeType<a-r>, and

some research will need to be done 1) on how to set the type: param in blob, and 2) how to change the type to mp4<Turing>

H6S1: In general, the notion of the blob as a transport | wrapper<Turing>is compelling, but at most, I will be using an array of count=1<Turing-2>

H5S5:

var url = URL.createObjectURL(blob);

<dev-Mozilla>: The URL.createObjectURL() static | method creates a string containing a URL representing the object given in the parameter. Hu: Seems like a shoddy implementation, of whatever miguelao is trying to do #<dev-Mozilla>: The URL lifetime is tied to the document in the window on which it was created. The new object URL represents the specified File object or Blob object. Hu: I’m guessing this line is used, just to generate the download pop-up window for the Blog-obj, that is passed as a param.

H5S6:

var a = document.createElement("a");

Hu: Devolving into greater | levels of incomprehensibility:<dev-Moz>: In an HTML document, the document.createElement() method creates the HTML element specified by tagName, or an HTMLUnknownElement if tagName isn’t recognized. Hu: OK, so “a” specifies an<a>tag, not a tag id=”a”;<dev-Moz>: Parameters: tagName: A string that specifies the type of element to be created. Hu: Stunningly, miguelao names the variable, to which he assigns this createElement-obj, ‘a’.

H5S7:

a.download = 'test.webm';

<W3>: The download attribute | specifies that the target (the file specified in the href | attribute) will be downloaded when a user clicks on the hyperlink. == The optional | value of the download attribute will be the new name of the file after it is downloaded. There are no | restrictions on allowed | values, and the browser will automatically | detect the correct file extension and add it to the file (.img, .pdf, .txt, .html, etc.). == If the value is omitted, the original | filename is used. Hu: This | text<anthro>suggests that # if a download is possible, it must exist somewhere in the server, as a file, with a destination. This seems fairly intuitive.

However, after downloading to my computer, this code does not create any noticeable | intermediate-file, which means it’s auto-deleting, and occupying an unspecified | RAM<illegal!>Moreover, most of the code subsequent document.createElement will be useless, except any code that can set a custom file name, for when I need to specify that name, to the recipient side. I need a solution that saves the file automatically, so this part will probably not be useful to me #

<Mozilla>: The HTMLAnchorElement.download property is a string indicating that the linked | resource is intended to be downloaded rather than displayed in the browser.

a.click();

<dev-Mozilla>: The HTMLElement.click() method simulates a mouse click on an element<oh-no!>. When click() is used with supported elements (such as an <input>), it fires the element’s click | event. This event then bubbles up to elements higher in the document | tree (or event chain) and fires their click events<Macros><chain><macro-chain!>

H5S8: Mozilla: The href attribute specifies the URL of the page the link goes to.

a.href = url;

Hu: This line is obviously important, because it indicates that the<a>element has a URL, and this is the URL that is accessed by the .click event, and its URL #<Biblical>is also the location of the video-file, that can be downloaded. The file, itself, was instantiated with new Blob( while the URL assignment to the file occurred using createObjectURL; if the object has a URL, then this URL, itself, can be used to point to the obj, absent an intermediate action by the user; the URL, for instance, can be written to a db, and a separate file, can call that URL, across any of a public.private-TURN<Turing>, to read the file(s), in real-time. The specifics of the URL are not even important, as long as the URLs are read in order of their creation, and this can be done, in MySQL, at the level of indexing.

If the URL can be created, passed, and used to download the file, then the file must exist<Schrodinger’s!>, and therefore, if it exists, and has a URL-association<fbno>, I can also access it, by H6S1: document.write(url) H6S2: XMLHttpRequest POST to PHP, to write to db; if the URL has a temporary existence, then it needs to be accessed, for example, before the stream is stopped, or at which ever time point, before so, of its lifetime-termination, which is feasible, during a real.time-call<Turings>H6S3:

<emergency-intermezzo>: Let’s construct a test # to intercept<Turing><fbno>the URL by writing:

document.write(url);

Immediately following createObjectURL, and wipe the<a>tag mish-mosh, and run the file again. Now, we see here as the display | output:

blob:https://flare/7717bea1-31ee-45da-a8e8-91e85ddf926c
// refresh-2: blob:https://flare/a1c3e66d-d616-4467-81ae-c35a1fb3b001

Hu: These seem to be cryptographically.secure-hashes, indicating an intention for web-transport, and some basic interception-prevention<high.level-anthro><fbno>; each link has this unique URL, which provides an added level, although not necessarily unnecessary, of complexity in transport<Turing>

// 5:55 AM 11/27/28, whereas the rest of this H6S was written 28 hours later #p-f<this-kind><fbno>:

Push: Reverse engineer the download button from Coding Shiksha and see where that file is coming from ^ to see the cached storage, location. I'm pretty sure it's the blob. The blob is just an array of mp4s: where are they? The JS-docs have a time interval for the start for packet creation that nobody complies with. == Key problem: the minions have never done video editing, and they don't understand what an MP4 file is. They think a browser can render a 1 hour video instantly. No complexity estimation function here, by the non.core-devs. == On the other hand, the core-devs must have encoded MP4's directly, making this the only such implementation, and it's fast, like the Vegas preview feature, before render. == In Vegas, the preview of a clip, even with all the added effects, is real time, even though the rendering of the same clip could take hours. == I want to skip the rendering, and just allow the recipient to also see the preview<fbno><Turing> == Javascript core devs actually built the blob array chunking for preview, not render, which download would be, so the non-devs are fallacious.

Takeaways from all of this, after which, fin.v-1, and I’m moving to<WP.MIC-H2S135>:

H7S1: My original intuition was correct: JS-devs built blob to be a transient | transport protocol, as evidenced by the fact that H8S1: the URL and file are wiped, after browser session ends<#p>, H8S2: the URL is prefixed by a protocol wrapper<Turing>

H7S2: This transient | transport protocol, by virtue of its protocol-nature, not its ephemeral-nature, explains its ability to be a real.time-preview, and this is consistent, with the quality<video.editor-10,yrs>of the file generated, even for download.

H7S3: The URL is objective, since it can be clicked from this page; if it can be clicked from this page, then it can be clicked from any other page; what is of concern is not whether the URL can be accessed, from the recipient, thus, but rather its duration; the video call must be ongoing, and the page open, by definition, before the recipient can download<Turing><high.level-verbals>

H7S4: Whereas H7S1-3 were derived before, and confirmed now, this is derived now: the JS-devs clearly intended H8S1: Blob( and createObjectURL to work together , which I’ll give miguel for figuring out H8S2: These to be use, most explicitly, for real.time-transport, and secondarily, as a video.editor-preview<YouTube><Twitch>, and tertiarily, as a stand.alone-protocol, for such purposes, which means it’s the most native-RTC<Turing-dirty>

References:

https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder

https://www.w3schools.com/js/js_const.asp

Code-inspo:

https://gist.github.com/prof3ssorSt3v3/48621be79794a8a3adeed7971786d4d8

https://webninjadeveloper.com/javascript/javascript-mediarecorder-webrtc-api-project-to-record-video-and-audio-from-camera-in-browser/

const chunks = [];

mediaRecorder.onstop = (e) => {
  console.log("data available after MediaRecorder.stop() called.");

  const audio = document.createElement('audio');
  audio.controls = true;
  const blob = new Blob(chunks, { 'type' : 'audio/ogg; codecs=opus' });
  const audioURL = window.URL.createObjectURL(blob);
  audio.src = audioURL;
  console.log("recorder stopped");
}

mediaRecorder.ondataavailable = (e) => {
  chunks.push(e.data);
}

^Above: https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/dataavailable_event

if (navigator.mediaDevices) {
  console.log('getUserMedia supported.');

  const constraints = { audio: true, video: true };
  const chunks = [];

  navigator.mediaDevices.getUserMedia(constraints)
    .then((stream) => {
      const options = {
        audioBitsPerSecond: 128000,
        videoBitsPerSecond: 2500000,
        mimeType: 'video/mp4'
      }
      const mediaRecorder = new MediaRecorder(stream, options);
      m = mediaRecorder;

      m.mimeType; // would return 'video/mp4'
      // …
    })
    .catch((error) => {
      console.error(error.message);
    });
}

^Above: https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/mimeType

record.onclick = () => {
  mediaRecorder.start();
  console.log(mediaRecorder.state);
  // Will return "recording"
  console.log("recorder started");
}

^Above: https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/state

if (navigator.mediaDevices) {
  console.log("getUserMedia supported.");

  const constraints = { audio: true };
  let chunks = [];

  navigator.mediaDevices
    .getUserMedia(constraints)
    .then((stream) => {
      const mediaRecorder = new MediaRecorder(stream);

      visualize(stream);

      record.onclick = () => {
        mediaRecorder.start();
        console.log(mediaRecorder.state);
        console.log("recorder started");
        record.style.background = "red";
        record.style.color = "black";
      };

      stop.onclick = () => {
        mediaRecorder.stop();
        console.log(mediaRecorder.state);
        console.log("recorder stopped");
        record.style.background = "";
        record.style.color = "";
      };

      mediaRecorder.onstop = (e) => {
        console.log("data available after MediaRecorder.stop() called.");

        const clipName = prompt("Enter a name for your sound clip");

        const clipContainer = document.createElement("article");
        const clipLabel = document.createElement("p");
        const audio = document.createElement("audio");
        const deleteButton = document.createElement("button");

        clipContainer.classList.add("clip");
        audio.setAttribute("controls", "");
        deleteButton.textContent = "Delete";
        clipLabel.textContent = clipName;

        clipContainer.appendChild(audio);
        clipContainer.appendChild(clipLabel);
        clipContainer.appendChild(deleteButton);
        soundClips.appendChild(clipContainer);

        audio.controls = true;
        const blob = new Blob(chunks, { type: "audio/ogg; codecs=opus" });
        chunks = [];
        const audioURL = URL.createObjectURL(blob);
        audio.src = audioURL;
        console.log("recorder stopped");

        deleteButton.onclick = (e) => {
          const evtTgt = e.target;
          evtTgt.parentNode.parentNode.removeChild(evtTgt.parentNode);
        };
      };

      mediaRecorder.ondataavailable = (e) => {
        chunks.push(e.data);
      };
    })
    .catch((err) => {
      console.error(`The following error occurred: ${err}`);
    });
}

^Above: https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder

if (navigator.getUserMedia) {
  console.log('getUserMedia supported.');
  navigator.getUserMedia(
    // constraints - only audio needed for this app
    {
      audio: true,
    },

    // Success callback
    (stream) => {
      const mediaRecorder = new MediaRecorder(stream);

      const myStream = mediaRecorder.stream;
      console.log(myStream);
    }
  );
}

^Above: https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/stream


<html>
<body>
<video autoplay/>
<script>
  var recordedChunks = [];

  function gotMedia(stream) {
    // |video| shows a live view of the captured MediaStream.
    var video = document.querySelector('video');
    video.src = URL.createObjectURL(stream);

    var recorder = null;
    try {
      recorder = new MediaRecorder(stream, {mimeType: "video/webm"});
    } catch (e) {
      console.error('Exception while creating MediaRecorder: ' + e);
      return;
    }

    recorder.ondataavailable = (event) => {
      console.log(' Recorded chunk of size ' + event.data.size + "B");
      recordedChunks.push(event.data);
    };

    recorder.start(100);
  }

  navigator.mediaDevices.getUserMedia({video: true, audio: true})
      .then(gotMedia)
      .catch(e => { console.error('getUserMedia() failed: ' + e); });
</script>
</body>
</html>

https://www.w3.org/TR/mediastream-recording/: The recordedChunks can be saved to a file using e.g. the function download() in the MediaRecorder Web Fundamentals article. Cont-below:

<!--HTML:-->
<p><video id="video" autoplay width=320/><p>
<p><button onclick="startFunction()">Grab video & start recording</button></p>
<p><button onclick="download()">Download! (and stop video)</button></p>
const constraints = { "video": { width: { max: 320 } }, "audio" : true };

var theStream;
var theRecorder;
var recordedChunks = [];

function startFunction() {
  navigator.mediaDevices.getUserMedia(constraints)
      .then(gotMedia)
      .catch(e => { console.error('getUserMedia() failed: ' + e); });
}

function gotMedia(stream) {
  theStream = stream;
  var video = document.querySelector('video');
  video.srcObject = stream;
  try {
    recorder = new MediaRecorder(stream, {mimeType : "video/webm"});
  } catch (e) {
    console.error('Exception while creating MediaRecorder: ' + e);
    return;
  }
  
  theRecorder = recorder;
  recorder.ondataavailable = 
      (event) => { recordedChunks.push(event.data); };
  recorder.start(100);
}

// From @samdutton's "Record Audio and Video with MediaRecorder"
// https://developers.google.com/web/updates/2016/01/mediarecorder
function download() {
  theRecorder.stop();
  theStream.getTracks().forEach(track => { track.stop(); });

  var blob = new Blob(recordedChunks, {type: "video/webm"});
  var url =  URL.createObjectURL(blob);
  var a = document.createElement("a");
  document.body.appendChild(a);
  a.style = "display: none";
  a.href = url;
  a.download = 'test.webm';
  a.click();
  // setTimeout() here is needed for Firefox.
  setTimeout(function() { URL.revokeObjectURL(url); }, 100); 
}

Testable version: https://codepen.io/miguelao/pen/wzVMJb?editors=0010: 7.2. Recording webcam video and audio: This example captures an video+audio MediaStream using getUserMedia(), plugs it into a <video> tag and tries to record it, retrieving the recorded chunks via the ondataavailable event. Note that the recording will go on forever until either MediaRecorder is stop()ed or all the MediaStreamTracks of the recorded MediaStream are ended. https://www.w3.org/Consortium/: The World Wide Web Consortium (W3C) is an international community where Member organizations, a full-time staff, and the public work together to develop Web standards. Led by Web inventor and Director Tim Berners-Lee and CEO Jeffrey Jaffe, W3C’s mission is to lead the Web to its full potential.

https://www.w3schools.com/html/html_media.asp

// pull code snips from these:

https://web.dev/getusermedia-intro/

https://www.digitalocean.com/community/tutorials/front-and-rear-camera-access-with-javascripts-getusermedia

https://developers.google.com/web/updates/2016/01/mediarecorder

https://developer.mozilla.org/en-US/docs/Web/API/MediaStream/getTracks

navigator.mediaDevices.getUserMedia({audio: false, video: true})
.then((mediaStream) => {
  document.querySelector('video').srcObject = mediaStream;
  // Stop the stream after 5 seconds
  setTimeout(() => {
    const tracks = mediaStream.getTracks()
    tracks[0].stop()
  }, 5000)
})

^https://developer.mozilla.org/en-US/docs/Web/API/MediaStream/getTracks

http://students.cs.ucl.ac.uk/schoolslab/projects/HT6/cooking/HT6/JavaScript_Seq_Sel_Itr.html#:~:text=A%20sequence%20is%20a%20set,the%20code%20as%20it%20goes.

https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack/stop

https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL

https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click

https://www.w3schools.com/tags/att_a_download.asp

https://www.w3schools.com/tags/tryit.asp?filename=tryhtml5_a_download

https://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement/download

https://www.w3schools.com/jsref/prop_anchor_download.asp

Coding Shiksha 26.5K subscribers 7/20: https://www.youtube.com/watch?v=ZgMOxkVM8Js

^ https://webninjadeveloper.com/javascript/javascript-mediarecorder-webrtc-api-project-to-record-video-and-audio-from-camera-in-browser/


Leave a Reply

Your email address will not be published. Required fields are marked *