web技術分享| WebRTC記錄音影片流

anyRTC發表於2022-02-17

監聽開始事件

  • EventTarget.addEventListener() 方法將指定的監聽器註冊到 EventTarget 上,當該物件觸發指定的事件時,指定的回撥函式就會被執行。 事件目標可以是一個文件上的元素 Element , Document Window 或者任何其他支援事件的物件 (比如 XMLHttpRequest )

  • addEventListener() 的工作原理是將實現 EventListener 的函式或物件新增到呼叫它的 EventTarget 上的指定事件型別的事件偵聽器列表中。

document.querySelector('button#start').addEventListener('click', async () => {

   document.querySelector('button#start').disabled = true;
   const constraints = {
       audio: {},
       video: {
           width: 1280, height: 720
       }
   };
   await init(constraints);
});

獲取音影片軌道

  • MediaDevices.getUserMedia() 會提示使用者給予使用媒體輸入的許可,媒體輸入會產生一個 MediaStream ,裡面包含了請求的媒體型別的軌道。此流可以包含一個影片軌道(來自硬體或者虛擬影片源,比如相機、影片採集裝置和螢幕共享服務等等)、一個音訊軌道(同樣來自硬體或虛擬音訊源,比如麥克風、A/D轉換器等等),也可能是其它軌道型別。

  • 它返回一個 Promise 物件,成功後會 resolve 回撥一個 MediaStream 物件。若使用者拒絕了使用許可權,或者需要的媒體源不可用, promise reject 回撥一個   PermissionDeniedError 或者 NotFoundError

async function init(constraints) {

   try {
       const stream = await navigator.mediaDevices.getUserMedia(constraints);
       handleSuccess(stream);
   } catch (e) {
       console.error('navigator.getUserMedia error:', e);
   }
}
  • HTMLMediaElement 介面的 srcObject 屬性設定或返回一個物件,這個物件提供了一個與 HTMLMediaElement 關聯的媒體源,這個物件通常是 MediaStream ,但根據規範可以是 MediaSource Blob 或者 File

function handleSuccess(stream) {

   recordButton.disabled = false;
   window.stream = stream;
   const gumVideo = document.querySelector('video#gum');
   gumVideo.srcObject = stream;
}

錄製媒體流

  • MediaRecorder()  建構函式會建立一個對指定的 MediaStream 進行錄製的 MediaRecorder 物件

  • MediaRecorder.ondataavailable 事件處理程式API處理 dataavailable 事件,在響應執行程式碼 Blob 資料被提供使用。

  • dataavailable 當MediaRecorder將媒體資料傳遞到您的應用程式以供使用時,將觸發該事件。資料在包含資料的 Blob 物件中提供。這在四種情況下發生:

    • 媒體流結束時,所有尚未傳遞到 ondataavailable 處理程式的媒體資料都將在單個 Blob 中傳遞。

    • 當呼叫 MediaRecorder.stop() (en-US)時,自記錄開始或 dataavailable 事件最後一次發生以來已捕         獲的所有媒體資料都將傳遞到 Blob 中;此後,捕獲結束。

    • 呼叫 MediaRecorder.requestData() (en-US) dataavailable 時,將傳遞自記錄開始或事件最後一次發生以來捕獲的所有媒體資料;然後 Blob 建立一個新檔案,並將媒體捕獲繼續到該blob中。

    • 如果將 timeslice 屬性傳遞到開始媒體捕獲的 MediaRecorder.start() (en-US)方法中, dataavailable 則每 timeslice 毫秒觸發一次事件。這意味著每個Blob都有特定的持續時間(最後一個Blob除外,後者可能更短,因為它將是自上次事件以來剩下的所有東西)。

let mediaRecorder;

const recordButton = document.querySelector('button#record');

recordButton.addEventListener('click', () => {
   if (recordButton.textContent === '開始記錄') {
       startRecording();
   } else {
       stopRecording();
       recordButton.textContent = '開始記錄';
       playButton.disabled = false;
   }
});

function startRecording() {
   recordedBlobs = [];
   try {
       mediaRecorder = new MediaRecorder(window.stream);
   } catch (e) {
       console.error('建立MediaRecorder時異常:', e);
   }
   recordButton.textContent = '停止記錄';
   playButton.disabled = true;
   mediaRecorder.>
   mediaRecorder.start();
}

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

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

播放媒體流

  • URL.createObjectURL()  靜態方法會建立一個 DOMString ,其中包含一個表示引數中給出的物件的URL。這個 URL 的生命週期和建立它的視窗中的 document 繫結。這個新的URL 物件表示指定的 File 物件或 Blob 物件。

let recordedBlobs;

const recordedVideo = document.querySelector('video#recorded');
const playButton = document.querySelector('button#play');

playButton.addEventListener('click', () => {
   const superBuffer = new Blob(recordedBlobs, { type: 'video/webm' });
   recordedVideo.src = null;
   recordedVideo.srcObject = null;
   recordedVideo.src = window.URL.createObjectURL(superBuffer);
   recordedVideo.controls = true;
   recordedVideo.play();
});

HTML

<link rel="stylesheet" href="./index.css">


<video id="gum" autoplay></video>
<video id="recorded"></video>
<div>
   <button id="start">開始</button>
   <button id="record" disabled>開始記錄</button>
   <button id="play" disabled>Play</button>
</div>

<script src="./index.js"></script>

CSS

button {

   margin: 0 3px 10px 0;
   padding-left: 2px;
   padding-right: 2px;
   width: 99px;
}
 
button:last-of-type {
   margin: 0;
}
 
video {
   vertical-align: top;
   --width: 25vw;
   width: var(--width);
   height: calc(var(--width) * 0.5625);
}
 
video:last-of-type {
   margin: 0 0 20px 0;
}
 
video#gumVideo {
   margin: 0 20px 20px 0;
}

JavaScript

let mediaRecorder;

let recordedBlobs;

const recordedVideo = document.querySelector('video#recorded');
const recordButton = document.querySelector('button#record');
recordButton.addEventListener('click', () => {
   if (recordButton.textContent === '開始記錄') {
       startRecording();
   } else {
       stopRecording();
       recordButton.textContent = '開始記錄';
       playButton.disabled = false;
   }
});

const playButton = document.querySelector('button#play');
playButton.addEventListener('click', () => {
   const superBuffer = new Blob(recordedBlobs, { type: 'video/webm' });
   recordedVideo.src = null;
   recordedVideo.srcObject = null;
   recordedVideo.src = window.URL.createObjectURL(superBuffer);
   recordedVideo.controls = true;
   recordedVideo.play();
});

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

function startRecording() {
   recordedBlobs = [];
   try {
       mediaRecorder = new MediaRecorder(window.stream);
   } catch (e) {
       console.error('建立MediaRecorder時異常:', e);
   }
   recordButton.textContent = '停止記錄';
   playButton.disabled = true;
   mediaRecorder.>
   mediaRecorder.start();
}

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

function handleSuccess(stream) {
   recordButton.disabled = false;
   window.stream = stream;
   const gumVideo = document.querySelector('video#gum');
   gumVideo.srcObject = stream;
}

async function init(constraints) {
   try {
       const stream = await navigator.mediaDevices.getUserMedia(constraints);
       handleSuccess(stream);
   } catch (e) {
       console.error('navigator.getUserMedia error:', e);
   }
}

document.querySelector('button#start').addEventListener('click', async () => {
   document.querySelector('button#start').disabled = true;
   const constraints = {
       audio: {},
       video: {
           width: 1280, height: 720
       }
   };
   await init(constraints);
});

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70013909/viewspace-2856074/,如需轉載,請註明出處,否則將追究法律責任。

相關文章