A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from http://developer.mozilla.org/en-US/docs/Web/API/MediaStream_Recording_API/Recording_a_media_element below:

Recording a media element - Web APIs

Recording a media element

While the article Using the MediaStream Recording API demonstrates using the MediaRecorder interface to capture a MediaStream generated by a hardware device, as returned by navigator.mediaDevices.getUserMedia(), you can also use an HTML media element (namely <audio> or <video>) as the source of the MediaStream to be recorded. In this article, we'll look at an example that does just that.

HTML
<p>
  Click the "Start Recording" button to begin video recording for a few seconds.
  You can stop recording by clicking the "Stop Recording" button. The "Download"
  button will download the received data (although it's in a raw, unwrapped form
  that isn't very useful).
</p>
<br />

Let's start by looking at the key bits of the HTML. There's a little more than this, but it's just informational rather than being part of the core operation of the app.

<div class="left">
  <div id="startButton" class="button">Start Recording</div>
  <h2>Preview</h2>
  <video id="preview" width="160" height="120" autoplay muted></video>
</div>

We present our main interface in two columns. On the left is a start button and a <video> element which displays the video preview; this is the video the user's camera sees. Note that the autoplay attribute is used so that as soon as the stream starts to arrive from the camera, it immediately gets displayed, and the muted attribute is specified to ensure that the sound from the user's microphone isn't output to their speakers, causing an ugly feedback loop.

<div class="right">
  <div id="stopButton" class="button">Stop Recording</div>
  <h2>Recording</h2>
  <video id="recording" width="160" height="120" controls></video>
  <a id="downloadButton" class="button">Download</a>
</div>

On the right we see a stop button and the <video> element which will be used to play back the recorded video. Notice that the playback panel doesn't have autoplay set (so the playback doesn't start as soon as media arrives), and it has controls set, which tells it to show the user controls to play, pause, and so forth.

Below the playback element is a button for downloading the recorded video.

<div class="bottom">
  <pre id="log"></pre>
</div>
body {
  font:
    14px "Open Sans",
    "Arial",
    sans-serif;
}

video {
  margin-top: 2px;
  border: 1px solid black;
}

.button {
  cursor: pointer;
  display: block;
  width: 160px;
  border: 1px solid black;
  font-size: 16px;
  text-align: center;
  padding-top: 2px;
  padding-bottom: 4px;
  color: white;
  background-color: darkgreen;
  text-decoration: none;
}

h2 {
  margin-bottom: 4px;
}

.left {
  margin-right: 10px;
  float: left;
  width: 160px;
  padding: 0px;
}

.right {
  margin-left: 10px;
  float: left;
  width: 160px;
  padding: 0px;
}

.bottom {
  clear: both;
  padding-top: 10px;
}

Now let's have a look at the JavaScript code; this is where the majority of the action happens, after all!

Setting up global variables

We start by establishing some global variables we'll need.

let preview = document.getElementById("preview");
let recording = document.getElementById("recording");
let startButton = document.getElementById("startButton");
let stopButton = document.getElementById("stopButton");
let downloadButton = document.getElementById("downloadButton");
let logElement = document.getElementById("log");

let recordingTimeMS = 5000;

Most of these are references to elements we need to work with. The last, recordingTimeMS, is set to 5000 milliseconds (5 seconds); this specifies the length of the videos we'll record.

Utility functions

Next, we create some utility functions that will get used later.

function log(msg) {
  logElement.innerText += `${msg}\n`;
}

The log() function is used to output text strings to a <div> so we can share information with the user. Not very pretty but it gets the job done for our purposes.

function wait(delayInMS) {
  return new Promise((resolve) => setTimeout(resolve, delayInMS));
}

The wait() function returns a new Promise which resolves once the specified number of milliseconds have elapsed. It works by using an arrow function which calls setTimeout(), specifying the promise's resolution handler as the timeout handler function. That lets us use promise syntax when using timeouts, which can be very handy when chaining promises, as we'll see later.

Starting media recording

The startRecording() function handles starting the recording process:

function startRecording(stream, lengthInMS) {
  let recorder = new MediaRecorder(stream);
  let data = [];

  recorder.ondataavailable = (event) => data.push(event.data);
  recorder.start();
  log(`${recorder.state} for ${lengthInMS / 1000} seconds…`);

  let stopped = new Promise((resolve, reject) => {
    recorder.onstop = resolve;
    recorder.onerror = (event) => reject(event.name);
  });

  let recorded = wait(lengthInMS).then(() => {
    if (recorder.state === "recording") {
      recorder.stop();
    }
  });

  return Promise.all([stopped, recorded]).then(() => data);
}

startRecording() takes two input parameters: a MediaStream to record from and the length in milliseconds of the recording to make. We always record no more than the specified number of milliseconds of media, although if the media stops before that time is reached, MediaRecorder automatically stops recording as well.

Stopping the input stream

The stop() function stops the input media:

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

This works by calling MediaStream.getTracks(), using forEach() to call MediaStreamTrack.stop() on each track in the stream.

Getting an input stream and setting up the recorder

Now let's look at the most intricate piece of code in this example: our event handler for clicks on the start button:

startButton.addEventListener(
  "click",
  () => {
    navigator.mediaDevices
      .getUserMedia({
        video: true,
        audio: true,
      })
      .then((stream) => {
        preview.srcObject = stream;
        downloadButton.href = stream;
        preview.captureStream =
          preview.captureStream || preview.mozCaptureStream;
        return new Promise((resolve) => {
          preview.onplaying = resolve;
        });
      })
      .then(() => startRecording(preview.captureStream(), recordingTimeMS))
      .then((recordedChunks) => {
        let recordedBlob = new Blob(recordedChunks, { type: "video/webm" });
        recording.src = URL.createObjectURL(recordedBlob);
        downloadButton.href = recording.src;
        downloadButton.download = "RecordedVideo.webm";

        log(
          `Successfully recorded ${recordedBlob.size} bytes of ${recordedBlob.type} media.`,
        );
      })
      .catch((error) => {
        if (error.name === "NotFoundError") {
          log("Camera or microphone not found. Can't record.");
        } else {
          log(error);
        }
      });
  },
  false,
);

When a click event occurs, here's what happens:

Handling the stop button

The last bit of code adds a handler for the click event on the stop button using addEventListener():

stopButton.addEventListener(
  "click",
  () => {
    stop(preview.srcObject);
  },
  false,
);

This calls the stop() function we covered earlier.

Result

When put all together with the rest of the HTML and the CSS not shown above, it looks and works like this:

You can also open this example in the playground using the "Play" button, which allows you to look at the combined code, including the parts hidden above because they aren't critical to the explanation of how the APIs are being used.

See also

RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4