WebRTC網頁開啟攝像頭並錄製影片

語言: CN / TW / HK

前面我們能 開啟本地攝像頭 ,並且在網頁上看到攝像頭的預覽影象。

本文我們使用 MediaRecorder 來錄製影片。在網頁上播放錄製好的影片,並能提供下載功能。

html

首先建立一個html介面,放上一些元素

<video id="v1" playsinline autoplay muted></video>
    <video id="v2" playsinline loop></video>

    <div>
        <button id="startCamera">開啟攝像頭</button>
        <button id="stopCamera">停止攝像頭</button>
        <button id="record" disabled>錄製</button>
        <button id="play" disabled>播放</button>
        <button id="download" disabled>下載影片</button>
    </div>
    <div> 錄製使用的影片格式: <select id="codecSelect" disabled></select> </div>
    <div>
        <h4>影片設定</h4>
        <p>回聲消除: <input type="checkbox" id="echoCancellation"></p>
    </div>
    <div> <span id="msg" style="font-size:smaller"></span> </div>

    <!-- 使用本地的介面卡 -->
    <script src="../js/adapter-latest.js" async></script>
  • video
    • v1 用來預覽
    • v2 用來播放錄製好的影片
  • button 控制攝像頭開啟、錄製,下載等等
  • select 選擇錄製用的影片格式
  • input 選擇回聲消除

js

準備

先把介面上的元素拿到

'use strict';

let mediaRecorder;
let recordedBlobs; // 錄製下來的內容
let isRecording = false;

// 先把頁面元素拿到
const startCameraBtn = document.querySelector('button#startCamera'); // 啟動攝像頭按鈕
const stopCameraBtn = document.querySelector('button#stopCamera');
const recordBtn = document.querySelector('button#record'); // 開始錄製按鈕
const playBtn = document.querySelector('button#play');     // 播放按鈕
const downloadBtn = document.querySelector('button#download'); // 下載影片按鈕
const codecSelector = document.querySelector('#codecSelect'); // 選擇格式
const msgEle = document.querySelector('span#msg');         // 顯示訊息
const previewV1 = document.querySelector('video#v1'); // 預覽用的
const recordedV2 = document.querySelector('video#v2');  // 用來播放錄製好的影片

影片支援的格式

先預定幾個可能的格式,然後一個個來判斷是否支援。找到支援的格式。

function getSupportedMimeTypes() {
  const possibleTypes = [
    'video/webm;codecs=vp9,opus',
    'video/webm;codecs=vp8,opus',
    'video/webm;codecs=h264,opus',
    'video/mp4;codecs=h264,aac',
  ];
  return possibleTypes.filter(mimeType => {
    return MediaRecorder.isTypeSupported(mimeType);
  });
}

開啟攝像頭

同樣要使用 getUserMedia 方法。這裡給影片指定了寬高。回聲消除是可選項。

// 啟動攝像頭
startCameraBtn.addEventListener('click', async () => {
  startCameraBtn.disabled = true;
  const isEchoCancellation = document.querySelector('#echoCancellation').checked;
  const constraints = {
    audio: {
      echoCancellation: { exact: isEchoCancellation }
    },
    video: {
      width: 1280, height: 720
    }
  };
  await init(constraints);
});

async function init(constraints) {
  try {
    const stream = await navigator.mediaDevices.getUserMedia(constraints);
    gotStream(stream);
  } catch (e) {
    showMsg(`navigator.getUserMedia error:${e.toString()}`);
  }
}

function gotStream(stream) {
  recordBtn.disabled = false;
  showMsg('拿到了 stream:', stream);
  window.stream = stream;
  previewV1.srcObject = stream;

  // 重置
  var codecOption = codecSelector.lastChild;
  while (codecOption != null) {
    codecSelector.removeChild(codecOption);
    codecOption = codecSelector.lastChild;
  }

  getSupportedMimeTypes().forEach(mimeType => {
    const option = document.createElement('option');
    option.value = mimeType;
    option.innerText = option.value;
    codecSelector.appendChild(option);
  });
  codecSelector.disabled = false; // 可以進行選擇了
}

下面是停止攝像頭的方法

stopCameraBtn.addEventListener('click', () => {
  var stream = previewV1.srcObject;
  if (stream == null) {
    return;
  }
  const tracks = stream.getTracks();
  tracks.forEach(function (track) {
    track.stop();
  });
  previewV1.srcObject = null;
  window.stream = null;
  codecSelector.disabled = true;
  startCameraBtn.disabled = false;
});

開始錄製

開始錄製影片

function startRecording() {
  recordedBlobs = [];
  const mimeType = codecSelector.options[codecSelector.selectedIndex].value;
  const options = { mimeType };

  try {
    mediaRecorder = new MediaRecorder(window.stream, options);
  } catch (e) {
    showMsg(`建立MediaRecorder出錯: ${JSON.stringify(e)}`);
    return;
  }

  showMsg('建立MediaRecorder', mediaRecorder, ' -> options', options);
  recordBtn.textContent = '停止錄製';
  isRecording = true;
  playBtn.disabled = true;
  downloadBtn.disabled = true;
  codecSelector.disabled = true;
  mediaRecorder.onstop = (event) => {
    showMsg('錄製停止了: ' + event);
    showMsg('錄製的資料Blobs: ' + recordedBlobs);
  };
  mediaRecorder.ondataavailable = handleDataAvailable;
  mediaRecorder.start();
  showMsg('錄製開始 mediaRecorder: ' + mediaRecorder);
}

function handleDataAvailable(event) {
  if (event.data && event.data.size > 0) {
    recordedBlobs.push(event.data);
  }
}

recordBtn.addEventListener('click', () => {
  if (isRecording == false) {
    startRecording();
  } else {
    stopRecording();
    recordBtn.textContent = '開始錄製';
    playBtn.disabled = false;
    downloadBtn.disabled = false;
    codecSelector.disabled = false;
  }
});
  • 重置錄製內容 recordedBlobs = []
  • 拿到選定的影片格式 mimeType
  • 新建 MediaRecorder 物件,傳入前面獲取到的流
  • 處理各個按鈕(ui)的狀態
  • mediaRecorder
    • 設定停止監聽器 onstop
    • 監聽錄製資料 ondataavailable ,有資料來的時候存放在 recordedBlobs
    • 啟動錄製 mediaRecorder.start()

停止錄製

function stopRecording() {
  mediaRecorder.stop();
}

播放錄製好的影片

錄製好的影片內容存放在 recordedBlobs 。新建 Blob ,交給video(recordedV2)來播放

playBtn.addEventListener('click', () => {
  const mimeType = codecSelector.options[codecSelector.selectedIndex].value.split(';', 1)[0];
  const superBuffer = new Blob(recordedBlobs, { type: mimeType });
  recordedV2.src = null;
  recordedV2.srcObject = null;
  recordedV2.src = window.URL.createObjectURL(superBuffer);
  recordedV2.controls = true;
  recordedV2.play();
});

下載影片

錄製好的影片內容存放在 recordedBlobs

downloadBtn.addEventListener('click', () => {
  const blob = new Blob(recordedBlobs, { type: 'video/webm' });
  const url = window.URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.style.display = 'none';
  a.href = url;
  a.download = '影片_' + new Date().getTime() + '.webm';
  document.body.appendChild(a);
  a.click();
  setTimeout(() => {
    document.body.removeChild(a);
    window.URL.revokeObjectURL(url);
  }, 100);
});

新建 Blob 和一個 a 元素。根據blob建立ObjectURL,並傳給 a 元素的href。

修改下載檔案的預設名字 a.download

觸發 a 元素的 click() ,即能讓瀏覽器下載這個檔案。

延遲把這個 a 移除掉。

小結

getUserMedia() 開啟影片拿到影片流。 MediaRecorder 錄製影片。用 Blob 來播放和下載。

實現一個小的錄製影片效果。影片資料快取在物件裡。

完整的效果請參考 影片錄製

原文連結