Anirudh
Anirudh

Reputation: 3428

How to save a Video recorded from webcam in mp4 format using mediaDevices in HTML

I'm capturing video using the webcam. Below is my code. I'm able to save the video as an mp4 file, but none of the video players are able to play it. I keep getting an unsupported file error.

What could be the issue here?

Here is the error screenshot

enter image description here

Here is the HTML source code

  <!DOCTYPE html>
  <html lang="en">
  <head>
      <meta charset="UTF-8">
      <title>MediaCapture and Streams API</title>
      <meta name="viewport" content="width=device-width">
      <link rel="stylesheet" href="main.css">
  </head>
  <body>
      <header>
          <h1>MediaCapture, MediaRecorder and Streams API</h1>
      </header>
      <main>
          <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Impedit molestiae itaque facere totam saepe tempore esse temporibus, quae reprehenderit aliquid iusto ea laborum, iure eligendi odio exercitationem sapiente illum quos.</p>

          <p><button id="btnStart">START RECORDING</button><br/>
          <button id="btnStop">STOP RECORDING</button></p>

          <video controls></video>

          <video id="vid2" controls></video>

          <!-- could save to canvas and do image manipulation and saving too -->
      </main>
      <script>

          let constraintObj = {
              audio: false,
              video: {
                  facingMode: "user",
                  width: { min: 640, ideal: 1280, max: 1920 },
                  height: { min: 480, ideal: 720, max: 1080 }
              }
          };
          // width: 1280, height: 720  -- preference only
          // facingMode: {exact: "user"}
          // facingMode: "environment"

          //handle older browsers that might implement getUserMedia in some way
          if (navigator.mediaDevices === undefined) {
              navigator.mediaDevices = {};
              navigator.mediaDevices.getUserMedia = function(constraintObj) {
                  let getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
                  if (!getUserMedia) {
                      return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
                  }
                  return new Promise(function(resolve, reject) {
                      getUserMedia.call(navigator, constraintObj, resolve, reject);
                  });
              }
          }else{
              navigator.mediaDevices.enumerateDevices()
              .then(devices => {
                  devices.forEach(device=>{
                      console.log(device.kind.toUpperCase(), device.label);
                      //, device.deviceId
                  })
              })
              .catch(err=>{
                  console.log(err.name, err.message);
              })
          }

          navigator.mediaDevices.getUserMedia(constraintObj)
          .then(function(mediaStreamObj) {
              //connect the media stream to the first video element
              let video = document.querySelector('video');
              if ("srcObject" in video) {
                  video.srcObject = mediaStreamObj;
              } else {
                  //old version
                  video.src = window.URL.createObjectURL(mediaStreamObj);
              }

              video.onloadedmetadata = function(ev) {
                  //show in the video element what is being captured by the webcam
                  video.play();
              };

              //add listeners for saving video/audio
              let start = document.getElementById('btnStart');
              let stop = document.getElementById('btnStop');
              let vidSave = document.getElementById('vid2');
              let mediaRecorder = new MediaRecorder(mediaStreamObj);
              let chunks = [];

              start.addEventListener('click', (ev)=>{
                  mediaRecorder.start();
                  console.log(mediaRecorder.state);
              })
              stop.addEventListener('click', (ev)=>{
                  mediaRecorder.stop();
                  console.log(mediaRecorder.state);
              });
              mediaRecorder.ondataavailable = function(ev) {
                  chunks.push(ev.data);
              }
              mediaRecorder.onstop = (ev)=>{
                  let blob = new Blob(chunks, { 'type' : 'video/mp4;' });
                  chunks = [];
                  let videoURL = window.URL.createObjectURL(blob);
                  vidSave.src = videoURL;
              }
          })
          .catch(function(err) {
              console.log(err.name, err.message);
          });

          /*********************************
          getUserMedia returns a Promise
          resolve - returns a MediaStream Object
          reject returns one of the following errors
          AbortError - generic unknown cause
          NotAllowedError (SecurityError) - user rejected permissions
          NotFoundError - missing media track
          NotReadableError - user permissions given but hardware/OS error
          OverconstrainedError - constraint video settings preventing
          TypeError - audio: false, video: false
          *********************************/
      </script>
  </body>
  </html>

Upvotes: 2

Views: 2835

Answers (2)

Malcolm Swaine
Malcolm Swaine

Reputation: 2268

I was having the same issue too. From my analysis, even if you set the chunk type like you have done here ...

let blob = new Blob(chunks, { 'type' : 'video/mp4;' });

... the actual content will still be webm.

I proved this by running an FFProbe on the uploaded data

enter image description here

Accepted answer I think is to re-mux it on the server.

For reference here is my .NET code using the Xabe FFMpeg Nuget package

// Convert .webm media to mp4
        public async Task<string> ConvertWebmToMp4(string ffMpegPath, string srcWebm)
        {
            FFmpeg.SetExecutablesPath(ffMpegPath);
            string outputFileName = Path.ChangeExtension(srcWebm, ".mp4");
            var conversion = await FFmpeg.Conversions.FromSnippet.Convert(srcWebm, outputFileName);
            await conversion.Start();

            return outputFileName;
        }

Upvotes: 0

Tarun Lalwani
Tarun Lalwani

Reputation: 146630

There is nothing wrong with your code. The format is actually not supported by QuickTime player.

If you use VLC or Chrome/Firefox browser to play the file it will work.

Upvotes: 7

Related Questions