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


Posted in Javascript onFebruary 24, 2022

一、监听开始事件

  • 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 对象。若用户拒绝了使用权限,或者需要的媒体源不可用,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数据被提供使用。
  • dataavailableMediaRecorder将媒体数据传递到您的应用程序以供使用时,将触发该事件。数据在包含数据的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);

});

到此这篇关于WebRTC记录音视频流(web技术分享)的文章就介绍到这了,更多相关WebRTC记录音视频流内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章,希望大家以后多多支持三水点靠木!

 
Javascript 相关文章推荐
Prototype Class对象学习
Jul 19 Javascript
通过js获取div的background-image属性
Oct 15 Javascript
js判断设备是否为PC并调整图片大小
Feb 12 Javascript
jquery live()调用不存在的解决方法
Feb 26 Javascript
基于JavaScript将表单序列化类型的数据转化成对象的处理(允许对象中包含对象)
Dec 28 Javascript
JavaScript中解决多浏览器兼容性23个问题的快速解决方法
May 19 Javascript
使用React实现轮播效果组件示例代码
Sep 05 Javascript
Vue自定义图片懒加载指令v-lazyload详解
Dec 31 Javascript
JS原生数据双向绑定实现代码
Aug 14 Javascript
vue a标签点击实现赋值方式
Sep 07 Javascript
vant组件中 dialog的确认按钮的回调事件操作
Nov 04 Javascript
用javascript制作qq注册动态页面
Apr 14 Javascript
Vue3如何理解ref toRef和toRefs的区别
Feb 18 #Vue.js
JavaScript实现酷炫的鼠标拖尾特效
Vue h函数的使用详解
Feb 18 #Vue.js
详解Vue中$props、$attrs和$listeners的使用方法
Feb 18 #Vue.js
详解JSON.parse和JSON.stringify用法
Feb 18 #Javascript
前端vue+express实现文件的上传下载示例
详解JavaScript的计时器和按钮效果设置
You might like
PHP实现将MySQL重复ID二维数组重组为三维数组的方法
2016/08/01 PHP
Ubuntu 16.04中Laravel5.4升级到5.6的步骤
2018/12/07 PHP
Smarty缓存机制实例详解【三种缓存方式】
2019/07/20 PHP
PHP框架实现WebSocket在线聊天通讯系统
2019/11/21 PHP
javascript的对话框详解与参数
2007/03/08 Javascript
jQuery源码分析-05异步队列 Deferred 使用介绍
2011/11/14 Javascript
简单的代码实现jquery定时器
2014/01/03 Javascript
JavaScript获得当前网页来源页面(即上一页)的方法
2015/04/03 Javascript
JavaScript判断前缀、后缀是否是空格的方法
2015/04/15 Javascript
require.js+vue开发微信上传图片组件
2016/10/27 Javascript
JS实现的添加弹出层并完成锁屏操作示例
2017/04/07 Javascript
js实现左右两侧浮动广告
2018/07/09 Javascript
bootstrap里bootstrap动态加载下拉框的实例讲解
2018/08/10 Javascript
mock.js模拟数据实现前后端分离
2019/07/24 Javascript
原生js实现随机点餐效果
2019/12/10 Javascript
Vue+Node服务器查询Mongo数据库及页面数据传递操作实例分析
2019/12/20 Javascript
JQuery获得内容和属性方法解析
2020/05/30 jQuery
[11:12]2018DOTA2国际邀请赛寻真——绿色长城OpTic
2018/08/10 DOTA
Python元组及文件核心对象类型详解
2018/02/11 Python
python和shell监控linux服务器的详细代码
2018/06/22 Python
Python3.5文件修改操作实例分析
2019/05/01 Python
Python3之不使用第三方变量,实现交换两个变量的值
2019/06/26 Python
python调用支付宝支付接口流程
2019/08/15 Python
pyhton中__pycache__文件夹的产生与作用详解
2019/11/24 Python
使用Python测试Ping主机IP和某端口是否开放的实例
2019/12/17 Python
PyTorch学习:动态图和静态图的例子
2020/01/06 Python
Python生成六万个随机,唯一的8位数字和数字组成的随机字符串实例
2020/03/03 Python
HTML5的语法变化介绍
2013/08/13 HTML / CSS
利用HTML5的新特点实现图片文件异步上传
2014/05/29 HTML / CSS
C语言中break与continue的区别
2012/07/12 面试题
大学生职业生涯规划大赛作品(精品)
2014/09/17 职场文书
暑假安全保证书
2015/02/28 职场文书
试用期自我评价范文
2015/03/10 职场文书
导游词之苏州盘门景区
2019/11/12 职场文书
解决Nginx 配置 proxy_pass 后 返回404问题
2021/03/31 Servers
CSS中理解层叠性及权重如何分配
2022/12/24 HTML / CSS