写点什么

web 技术分享| WebRTC 记录音视频流

作者:anyRTC开发者
  • 2022 年 2 月 17 日
  • 本文字数:3505 字

    阅读完需:约 11 分钟

监听开始事件

  • EventTarget.addEventListener() 方法将指定的监听器注册到 EventTarget 上,当该对象触发指定的事件时,指定的回调函数就会被执行。 事件目标可以是一个文档上的元素 Element,DocumentWindow或者任何其他支持事件的对象 (比如 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 对象。若用户拒绝了使用权限,或者需要的媒体源不可用,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);    }}
复制代码


  • HTMLMediaElement 接口的 srcObject 属性设定或返回一个对象,这个对象提供了一个与HTMLMediaElement关联的媒体源,这个对象通常是 MediaStream ,但根据规范可以是 MediaSourceBlob 或者 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.ondataavailable = handleDataAvailable; 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.ondataavailable = handleDataAvailable; 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);});
复制代码



发布于: 刚刚阅读数: 2
用户头像

实时交互,万物互联! 2020.08.10 加入

实时交互,万物互联,全球实时互动云服务商领跑者!

评论

发布
暂无评论
web技术分享| WebRTC记录音视频流