使用JavaScript MediaRecorder API创建视频和音频录制器

使用JavaScript MediaRecorder API创建视频和音频录制器

WebRTC非常流行,用于访问设备摄像头和设备麦克风,并在浏览器中实时流式传输视频或音频媒体。但在许多情况下,我们可能需要记录流式传输以供将来使用或为用户(例如用户可能希望下载流式传输等)。在这种情况下,我们可以使用MediaRecorder API来记录媒体流。

在本文中,我们将使用纯JavaScript和其MediaRecorder API创建一个基本的视频和音频录制器网站。

项目描述: 我们正在构建的网站将包含:

  • 一个选择选项,让用户选择要记录的媒体类型(音频或带音频的视频)。
  • 如果用户选择录制视频,则浏览器将询问权限以访问设备摄像头和麦克风,如果用户允许,则:
    • 视频元素将显示摄像头媒体流
    • “开始录制” 按钮将开始录制
    • “停止录制” 按钮将停止录制
    • 录制完成后,将显示一个包含已录制媒体的新视频元素
    • 提供一个链接,让用户下载录制的视频
  • 如果用户选择只录制音频,则浏览器将询问权限以访问麦克风,并且如果用户允许,则:
    • “开始录制” 按钮将开始录制
    • “停止录制” 按钮将停止录制
    • 录制完成后,将显示一个包含已录制音频的新音频元素
    • 提供一个链接,让用户下载录制的音频

因此,让我们首先设置我们简单的HTML页面:

index.html

<!DOCTYPE html> 
<html lang="en"> 
  
<head> 
    <meta charset="UTF-8"> 
    <meta http-equiv="X-UA-Compatible" 
        content="IE=edge"> 
    <meta name="viewport" content= 
        "width=device-width, initial-scale=1.0"> 
    <link rel="stylesheet" href="index.css"> 
    <title>Video & Audio Recorder</title> 
</head> 
  
<body> 
    <h1> Video & Audio Recorder </h1> 
    <label for="media"> 
        Select what you want to record: 
    </label> 
  
    <select id="media"> 
        <option value="choose-an-option">  
            Choose an option 
        </option> 
        <option value="vid">Video</option> 
        <option value="aud">Audio</option> 
    </select> 
  
    <div class="display-none" id="vid-recorder"> 
        <h3>Record Video </h3> 
        <video autoplay id="web-cam-container" 
            style="background-color: black;"> 
            Your browser doesn't support  
            the video tag 
        </video> 
  
        <div class="recording" id="vid-record-status"> 
            Click the "Start video Recording"  
            button to start recording 
        </div> 
  
        <!-- This button will start the video recording -->
        <button type="button" id="start-vid-recording" 
            onclick="startRecording(this,  
            document.getElementById('stop-vid-recording'))"> 
            Start video recording 
        </button> 
  
        <!-- This button will stop the video recording -->
        <button type="button" id="stop-vid-recording" 
            disabled onclick="stopRecording(this,  
            document.getElementById('start-vid-recording'))"> 
            Stop video recording 
        </button> 
  
        <!--The video element will be created using  
            JavaScript and contains recorded video-->
        <!-- <video id="recorded-video"  controls> 
            Your browser doesn't support the video tag 
        </video> -->
  
        <!-- The below link will let the 
             users download the recorded video -->
        <!-- <a href="" > Download it! </a> -->
    </div> 
  
    <div class="display-none" id="aud-recorder"> 
        <h3> Record Audio</h3> 
  
        <div class="recording" id="aud-record-status"> 
            Click the "Start Recording"  
            button to start recording 
        </div> 
  
        <button type="button" id="start-aud-recording" 
            onclick="startRecording(this,  
            document.getElementById('stop-aud-recording'))"> 
            Start recording  
        </button> 
  
        <button type="button" id="stop-aud-recording" 
            disabled onclick="stopRecording(this,  
            document.getElementById('start-aud-recording'))"> 
            Stop recording 
        </button> 
  
        <!-- The audio element will contain the  
            recorded audio and will be created  
            using Javascript -->
        <!-- <audio id="recorded-audio"  
            controls></audio> -->
  
        <!-- The below link will let the users  
             download the recorded audio -->
        <!-- <a href="" > Download it! </a> -->
    </div> 
  
    <script src="index.js"></script> 
</body> 
  
</html> 

输出:

使用JavaScript MediaRecorder API创建视频和音频录制器

如果你仔细检查index.html,你会发现视频和音频标签没有给出任何源,我们稍后会使用JavaScript添加源。目前,我们有一个选择选项,让用户选择要录制的媒体类型。位于“vid-recorder” div元素中的第一个视频元素将包含网络摄像头流,注释中的视频元素将包含录制的视频。请注意,只有最后一个视频元素具有“controls”属性,因为第一个视频元素将包含流,并不需要任何控件。

在“aud-recorder” div内部,我们有两个按钮用于开始和停止录制,注释的音频元素将包含录制的音频。

现在,让我们为HTML页面添加一些CSS

index.css

body { 
    text-align: center; 
    color: green; 
    font-size: 1.2em; 
} 
  
.display-none { 
    display: none; 
} 
  
.recording { 
    color: red; 
    background-color: rgb(241 211 211); 
    padding: 5px; 
    margin: 6px auto; 
    width: fit-content; 
} 
  
video { 
    background-color: black; 
    display: block; 
    margin: 6px auto; 
    width: 420px; 
    height: 240px; 
} 
  
audio { 
    display: block; 
    margin: 6px auto; 
} 
  
a { 
    color: green; 
}

输出:

使用JavaScript MediaRecorder API创建视频和音频录制器

目前,我们已经将 “display-none” 类添加到 “vid-recorder”“aud-recorder” div元素中。因为我们想要根据用户的选择显示正确的录制器。

现在,让我们使用JavaScript实现仅显示用户选择的录制器的逻辑:

index.js

const mediaSelector = document.getElementById("media"); 
let selectedMedia = null; 
  
// Handler function to handle the "change" event 
// when the user selects some option 
mediaSelector.addEventListener("change", (e) => { 
    selectedMedia = e.target.value; 
    document.getElementById( 
      `{selectedMedia}-recorder`).style.display = "block"; 
    document.getElementById( 
      `{otherRecorder(selectedMedia)}-recorder`) 
      .style.display = "none"; 
}); 
  
function otherRecorder(selectedMedia) { 
    return selectedMedia === "vid" ? "aud" : "vid"; 
} 

输出: 当用户选择 “视频” 时,显示以下视频录制机。

使用JavaScript MediaRecorder API创建视频和音频录制器

类似地,当用户选择 “音频” 选项时,音频录制器将显示出来 –

使用JavaScript MediaRecorder API创建视频和音频录制器

以上代码仅显示用户选择的录音机,即音频或视频。我们为mediaSelector元素添加了“change”事件监听器,当select元素的值发生变化时,它会触发一个“change”事件,并由给定的回调函数处理。回调函数将所选媒体录制器的CSS“display”属性更改为“block”,而其他媒体录制器更改为“none”。

接入Web摄像头和麦克风:WebRTC的getUserMedia API允许您访问设备摄像头和麦克风。getUserMedia()方法返回一个Promise,该Promise解析为MediaStream,其中包含基于给定规格的媒体内容(一个媒体轨道流)。getUserMedia()方法以MediaStreamConstraints对象作为参数,该参数定义了所得到的媒体流应匹配的所有约束条件。

const mediaStreamConstraints = {
   audio: true,
   video: true
};
// The above MediaStreamConstraints object 
// specifies that the resulting media must have
// both the video and audio media content or tracks.

// The mediaStreamConstraints object is passed to 
// the getUserMedia method
navigator.mediaDevices.getUserMedia( MediaStreamConstraints )
.then( resultingMediaStream => {
   // Code to use the received media stream
});

当调用getUserMedia方法时,浏览器会提示用户是否允许使用设备的摄像头和麦克风。如果用户允许,则getUserMedia返回的Promise会解析为媒体流。否则,会抛出 NotAllowedError 异常。在上面的代码中,接收到的媒体流包含视频和音频数据。

所以,请将以下代码添加到index.js文件中:

index.js

const mediaSelector =  
    document.getElementById("media"); 
  
// Added code 
const webCamContainer = document 
    .getElementById('web-cam-container'); 
  
let selectedMedia = null; 
  
/* Previous code  
... 
Added code */
  
const audioMediaConstraints = { 
    audio: true, 
    video: false
}; 
  
const videoMediaConstraints = { 
    // or you can set audio to false  
    // to record only video 
    audio: true, 
    video: true
}; 
  
function startRecording(thisButton, otherButton) { 
  
    navigator.mediaDevices.getUserMedia( 
            selectedMedia === "vid" ?  
            videoMediaConstraints :  
            audioMediaConstraints) 
        .then(mediaStream => { 
            // Use the mediaStream in  
            // your application 
  
            // Make the mediaStream global 
            window.mediaStream = mediaStream; 
  
            if (selectedMedia === 'vid') { 
  
                // Remember to use the "srcObject"  
                // attribute since the "src" attribute  
                // doesn't support media stream as a value 
                webCamContainer.srcObject = mediaStream; 
            } 
  
            document.getElementById( 
                `{selectedMedia}-record-status`) 
                .innerText = "Recording"; 
            thisButton.disabled = true; 
            otherButton.disabled = false; 
        }); 
  
} 
  
function stopRecording(thisButton, otherButton) { 
  
    // Stop all the tracks in the received  
    // media stream i.e. close the camera 
    // and microphone 
    window.mediaStream.getTracks().forEach(track => { 
        track.stop(); 
    }); 
  
    document.getElementById( 
        `{selectedMedia}-record-status`) 
        .innerText = "Recording done!"; 
          
    thisButton.disabled = true; 
    otherButton.disabled = false; 
} 

startRecording函数调用navigator.mediaDevices.getUserMedia()方法以访问设备摄像头和麦克风,禁用 “开始录制” 按钮,启用 “停止录制” 按钮。而stopRecording函数通过调用媒体流中使用的每个媒体轨道的 “stop()” 方法关闭摄像头和麦克风,禁用 “停止录制” 按钮,启用 “开始录制” 按钮。

实现录制器:到目前为止,我们只是访问了网络摄像头和麦克风,但没有进行任何记录媒体的操作。

要记录媒体流,我们首先需要使用MediaRecorder构造函数创建一个MediaRecorder实例(用于记录媒体流的接口)。

MediaRecorder构造函数接受两个参数:

  • stream: 流就像是数据的流动(任何类型的数据)。在本文中,我们将使用MediaStream,它基本上是一个包含媒体(视频或音频或两者)数据或媒体内容的流。
  • Options(可选): 一个包含一些关于录制的规格的对象。您可以设置记录媒体的MIME类型、音频比特率、视频比特率等。MIME类型是表示记录的媒体文件的格式的标准(例如,两个MIME类型 “audio/webm”、“video/mp4” 分别表示音频webm文件和视频mp4文件)。

语法:

const mediaRecorder = new MediaRecorder(
stream, { mimeType: "audio/webm" });

上述代码行创建了一个新的MediaRecorder实例,可以录制给定的流并将其存储为音频WebM文件。

所以,请修改你的index.js文件:

index.js文件

/* Previous code  
... */
  
function startRecording(thisButton, otherButton) { 
  
    navigator.mediaDevices.getUserMedia( 
        selectedMedia === "vid" ? 
        videoMediaConstraints : 
        audioMediaConstraints) 
  
    .then(mediaStream => { 
  
        /* New code */
        // Create a new MediaRecorder  
        // instance that records the  
        // received mediaStream 
        const mediaRecorder =  
            new MediaRecorder(mediaStream); 
  
        // Make the mediaStream global 
        window.mediaStream = mediaStream; 
  
        // Make the mediaRecorder global 
        // New line of code 
        window.mediaRecorder = mediaRecorder; 
  
        if (selectedMedia === 'vid') { 
  
            // Remember to use the srcObject  
            // attribute since the src attribute  
            // doesn't support media stream as a value 
            webCamContainer.srcObject = mediaStream; 
        } 
  
        document.getElementById( 
            `${selectedMedia}-record-status`) 
            .innerText = "Recording"; 
  
        thisButton.disabled = true; 
        otherButton.disabled = false; 
    }); 
} 
  
/* Remaining code  
...*/

当调用startRecording()函数时,它会创建一个MediaRecorder实例来录制接收到的媒体流。现在,我们需要使用创建的MediaRecorder实例。MediaRecorder提供了一些有用的方法,我们可以在这里使用:

  • start(): 当调用此方法时,MediaRecorder实例开始录制给定的媒体流。可选地以“timeslice”作为参数,指定的话将导致以该时间片段持续时间将给定的媒体录制为小的单独块。
  • pause(): 调用时暂停录制。
  • resume(): 在调用pause()方法后调用,恢复录制。
  • stop(): 调用时停止录制并触发一个“dataavailable”事件,其中包含保存数据的最终Blob。
  • requestData(): 调用时请求一个包含到目前为止保存的数据的Blob。

类似地,MediaRecorder还提供了一些有用的事件处理程序:
* ondataavailable: 用于“dataavailable”事件的事件处理程序。每当记录了 timeslice(如果指定了)毫秒的媒体数据或录制完成(如果未指定 timeslice)时,MediaRecorder会发出一个事件,并包含记录的Blob数据。可以从“event”的“data”属性中获取这些数据。

mediaRecorder.ondataavailable = ( event ) => {
  const recordedData = event.data;
}
  • onstop: MediaRecorder的“stop”事件的事件处理程序。当调用MediaRecorder.stop()方法或停止相应的MediaStream时,会发出此事件。
  • onerror: 使用MediaRecorder时发生错误时发出的“error”事件的处理程序。事件的“error”属性包含错误的详细信息。
mediaRecorder.onerror = ( event ) => {
  console.log(event.error);
}
  • onstart : 当媒体录制器开始录制时触发的“start”事件的处理程序。
  • onpause: 当录制暂停时触发的“pause”事件的处理程序。
  • onresume : 当录制暂停后再次恢复时触发的“resume”事件的处理程序。

现在,我们需要使用其中一些方法和事件处理程序来使我们的项目运行起来。

index.js

const mediaSelector = document.getElementById("media"); 
  
const webCamContainer = 
    document.getElementById("web-cam-container"); 
  
let selectedMedia = null; 
  
// This array stores the recorded media data 
let chunks = []; 
  
// Handler function to handle the "change" event 
// when the user selects some option 
mediaSelector.addEventListener("change", (e) => { 
  
    // Takes the current value of the mediaSeletor 
    selectedMedia = e.target.value; 
  
    document.getElementById( 
        `{selectedMedia}-recorder`) 
            .style.display = "block"; 
  
    document.getElementById( 
            `{otherRecorderContainer( 
            selectedMedia)}-recorder`) 
        .style.display = "none"; 
}); 
  
function otherRecorderContainer( 
    selectedMedia) { 
  
    return selectedMedia === "vid" ?  
        "aud" : "vid"; 
} 
  
// This constraints object tells  
// the browser to include only  
// the audio Media Track 
const audioMediaConstraints = { 
    audio: true, 
    video: false, 
}; 
  
// This constraints object tells  
// the browser to include 
// both the audio and video 
// Media Tracks 
const videoMediaConstraints = { 
  
    // or you can set audio to 
    // false to record 
    // only video 
    audio: true, 
    video: true, 
}; 
  
// When the user clicks the "Start  
// Recording" button this function 
// gets invoked 
function startRecording( 
    thisButton, otherButton) { 
  
    // Access the camera and microphone 
    navigator.mediaDevices.getUserMedia( 
        selectedMedia === "vid" ?  
        videoMediaConstraints : 
        audioMediaConstraints) 
        .then((mediaStream) => { 
  
        // Create a new MediaRecorder instance 
        const mediaRecorder =  
            new MediaRecorder(mediaStream); 
  
        //Make the mediaStream global 
        window.mediaStream = mediaStream; 
        //Make the mediaRecorder global 
        window.mediaRecorder = mediaRecorder; 
  
        mediaRecorder.start(); 
  
        // Whenever (here when the recorder 
        // stops recording) data is available 
        // the MediaRecorder emits a "dataavailable"  
        // event with the recorded media data. 
        mediaRecorder.ondataavailable = (e) => { 
  
            // Push the recorded media data to 
            // the chunks array 
            chunks.push(e.data); 
        }; 
  
        // When the MediaRecorder stops 
        // recording, it emits "stop" 
        // event 
        mediaRecorder.onstop = () => { 
  
            /* A Blob is a File like object. 
            In fact, the File interface is  
            based on Blob. File inherits the  
            Blob interface and expands it to 
            support the files on the user's  
            systemThe Blob constructor takes  
            the chunk of media data as the  
            first parameter and constructs  
            a Blob of the type given as the  
            second parameter*/
            const blob = new Blob( 
                chunks, { 
                    type: selectedMedia === "vid" ? 
                        "video/mp4" : "audio/mpeg"
                }); 
            chunks = []; 
  
            // Create a video or audio element 
            // that stores the recorded media 
            const recordedMedia = document.createElement( 
                selectedMedia === "vid" ? "video" : "audio"); 
            recordedMedia.controls = true; 
  
            // You can not directly set the blob as  
            // the source of the video or audio element 
            // Instead, you need to create a URL for blob 
            // using URL.createObjectURL() method. 
            const recordedMediaURL = URL.createObjectURL(blob); 
  
            // Now you can use the created URL as the 
            // source of the video or audio element 
            recordedMedia.src = recordedMediaURL; 
  
            // Create a download button that lets the  
            // user download the recorded media 
            const downloadButton = document.createElement("a"); 
  
            // Set the download attribute to true so that 
            // when the user clicks the link the recorded 
            // media is automatically gets downloaded. 
            downloadButton.download = "Recorded-Media"; 
  
            downloadButton.href = recordedMediaURL; 
            downloadButton.innerText = "Download it!"; 
  
            downloadButton.onclick = () => { 
  
                /* After download revoke the created URL 
                using URL.revokeObjectURL() method to  
                avoid possible memory leak. Though,  
                the browser automatically revokes the  
                created URL when the document is unloaded, 
                but still it is good to revoke the created  
                URLs */
                URL.revokeObjectURL(recordedMedia); 
            }; 
  
            document.getElementById( 
                `{selectedMedia}-recorder`).append( 
                recordedMedia, downloadButton); 
        }; 
  
        if (selectedMedia === "vid") { 
  
            // Remember to use the srcObject 
            // attribute since the src attribute 
            // doesn't support media stream as a value 
            webCamContainer.srcObject = mediaStream; 
        } 
  
        document.getElementById( 
                `{selectedMedia}-record-status`) 
                .innerText = "Recording"; 
  
        thisButton.disabled = true; 
        otherButton.disabled = false; 
    }); 
} 
  
function stopRecording(thisButton, otherButton) { 
  
    // Stop the recording 
    window.mediaRecorder.stop(); 
  
    // Stop all the tracks in the  
    // received media stream 
    window.mediaStream.getTracks() 
    .forEach((track) => { 
        track.stop(); 
    }); 
  
    document.getElementById( 
            `${selectedMedia}-record-status`) 
            .innerText = "Recording done!"; 
    thisButton.disabled = true; 
    otherButton.disabled = false; 
} 

输出:

假设用户选择音频记录器 —

使用JavaScript MediaRecorder API创建视频和音频录制器

现在,如果用户点击开始录制按钮,那么 –

使用JavaScript MediaRecorder API创建视频和音频录制器

当单击 “停止录制” 按钮时 —

使用JavaScript MediaRecorder API创建视频和音频录制器

它显示录制的音频并提供下载链接。

所以,startRecording()函数是做什么的?

  • 它调用navigator.mediaDevices.getUserMedia()方法并访问设备的摄像头或麦克风或两者。该方法返回一个解析为MediaStream的Promise。
  • 在接收到MediaStream后,它创建一个可以录制给定MediaStream的MediaRecorder实例,使得MediaStream和MediaRecorder都成为全局变量,以便我们可以在startRecording函数之外使用它们。
window.mediaStream = mediaStream;
window.mediaRecorder  = mediaRecorder;
  • 通过调用MediaRecorder.start()方法开始录制给定的MediaStream。
mediaRecorder.start();
  • 定义了用于创建MediaRecorder的事件处理程序。 “dataavailable” 事件处理程序将记录的数据推送到chunks数组(包含记录的媒体数据Blob的数组)。 “stop” 事件处理程序。
    • 使用Blob()构造函数从chunks数组创建一个新的Blob。
    • 重新初始化chunks数组。
    • 使用URL.createObjectURL()方法为创建的Blob创建URL。
    • 将新创建的 “video” / “audio” 元素的源设置为创建的URL。
    • 创建一个下载记录媒体的链接,并在点击链接后通过URL.revokeObjectURL()方法撤销创建的URL。
  • 禁用“开始录制”按钮,并启用 “停止录制” 按钮。

stopRecording()函数是用来做什么的?

  • 通过调用MediaRecorder.stop()方法停止录制。
  • 停止MediaStream的所有媒体轨道,即关闭相机和麦克风。
  • 禁用 “停止录制” 按钮,并启用 “开始录制” 按钮。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程