1. 程式人生 > 其它 >web技術分享| webRTC 媒體流錄製

web技術分享| webRTC 媒體流錄製

音視訊的錄製音視訊的分為服務端錄製和客戶端錄製,下面主要講的是利用webrtc進行客戶端錄製的方式。
因為WebRTC 錄製音視訊流之後,最終是通過 Blob 物件將資料儲存成多媒體檔案的,

  • Blob 物件表示一個不可變、原始資料的類檔案物件。它的資料可以按文字或二進位制的格式進行讀取,也可以轉換成 ReadableStream 來用於資料操作。

  • Blob 表示的不一定是JavaScript原生格式的資料。File介面基於Blob,繼承了 blob 的功能並將其擴充套件使其支援使用者系統上的檔案。

  • 要從其他非blob物件和資料構造一個 Blob,請使用 Blob() 建構函式。要建立一個 blob 資料的子集 blob,請使用 slice()

    方法。要獲取使用者檔案系統上的檔案對應的 Blob 物件。

  • 接受 Blob 物件的API也被列在 File 文件中。

瞭解完Blob的特性之後呢, 我們進入正題。

獲取元素宣告變數

let mediaRecorder;
let recordedBlobs;

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

繫結點選事件

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

  • URL.revokeObjectURL() 靜態方法用來釋放一個之前已經存在的、通過呼叫 URL.createObjectURL() 建立的 URL 物件。當你結束使用某個 URL 物件之後,應該通過呼叫這個方法來讓瀏覽器知道不用在記憶體中繼續保留對這個檔案的引用了。你可以在 sourceopen 被處理之後的任何時候呼叫 revokeObjectURL()

    。這是因為 createObjectURL() 僅僅意味著將一個媒體元素的 src 屬性關聯到一個 MediaSource 物件上去。呼叫revokeObjectURL() 使這個潛在的物件回到原來的地方,允許平臺在合適的時機進行垃圾收集。

recordButton.addEventListener('click', () => {
    if (recordButton.textContent === 'Start Recording') {
        startRecording();
    } else {
        stopRecording();
        recordButton.textContent = 'Start Recording';
        playButton.disabled = false;
        downloadButton.disabled = false;
    }
});

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

downloadButton.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 = 'test.webm';
    document.body.appendChild(a);
    a.click();
    setTimeout(() => {
        document.body.removeChild(a);
        window.URL.revokeObjectURL(url);
    }, 100);
});

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

點選開始初始化

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

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

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

function handleSuccess(stream) {
    recordButton.disabled = false;
    console.log('getUserMedia() got stream:', stream);
    window.stream = stream;
    const gumVideo = document.querySelector('video#gum');
    gumVideo.srcObject = stream;
}

開始記錄方法

function startRecording() {
    recordedBlobs = [];
    try {
        mediaRecorder = new MediaRecorder(window.stream);
    } catch (e) {
        console.error('Exception while creating MediaRecorder:', e);
        return;
    }

    recordButton.textContent = 'Stop Recording';
    playButton.disabled = true;
    downloadButton.disabled = true;
    mediaRecorder.onstop = (event) => {
        console.log('Recorder stopped: ', event);
        console.log('Recorded Blobs: ', recordedBlobs);
    };
    mediaRecorder.ondataavailable = handleDataAvailable;
    mediaRecorder.start();
}

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

HTML

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta id="theme-color" name="theme-color" content="#ffffff">
    <link rel="stylesheet" href="./index.css">
</head>

<body>
    <div id="container">
        <video id="gum" playsinline autoplay muted></video>
        <video id="recorded" playsinline loop></video>
        <div>
            <button id="start">Start camera</button>
            <button id="record" disabled>Start Recording</button>
            <button id="play" disabled>Play</button>
            <button id="download" disabled>Download</button>
        </div>
    </div>
    <script src="./main.js" async></script>
</body>
</html>

CSS

button {
    background-color: #d84a38;
    border: none;
    border-radius: 2px;
    color: white;
    font-family: 'Roboto', sans-serif;
    font-size: 0.8em;
    margin: 0 0 1em 0;
    padding: 0.5em 0.7em 0.6em 0.7em;
}

button:active {
    background-color: #cf402f;
}

button:hover {
    background-color: #cf402f;
}

button[disabled] {
    color: #ccc;
}

button[disabled]:hover {
    background-color: #d84a38;
}

div#container {
    margin: 0 auto 0 auto;
    max-width: 60em;
    padding: 1em 1.5em 1.3em 1.5em;
}

video {
    background: #222;
    margin: 0 0 20px 0;
    --width: 100%;
    width: var(--width);
    height: calc(var(--width) * 0.75);
}

webRTC功能是非常強大的,關於webrtc和直播還有很多技術需要我們去研究,上續呢是DEMO的示例程式碼,感興趣的小夥伴可以親自試一試。