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 相关文章推荐
javascript 网页跳转的方法
Dec 24 Javascript
通过判断JavaScript的版本实现执行不同的代码
May 11 Javascript
事件冒泡是什么如何用jquery阻止事件冒泡
Mar 20 Javascript
原生javascript兼容性测试实例
Jul 01 Javascript
分享28款免费实用的 JQuery 图片和内容滑块插件
Dec 15 Javascript
javascript获取四位数字或者字母的随机数
Jan 09 Javascript
jQuery插件slicebox实现3D动画图片轮播切换特效
Apr 12 Javascript
JS使用正则表达式实现关键字替换加粗功能示例
Aug 03 Javascript
使用prop解决一个checkbox选中后再次选中失效的问题
Jul 05 Javascript
JavaScript实现简单轮播图效果
Dec 01 Javascript
angular6 填坑之sdk的方法
Dec 27 Javascript
vue+elementUI 实现内容区域高度自适应的示例
Sep 26 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环境下利用session防止页面重复刷新的具体实现
2014/01/09 PHP
php实现的pdo公共类定义与用法示例
2017/07/19 PHP
PHP判断是否微信访问的方法示例
2019/03/27 PHP
基于jQuery的实现简单的分页控件
2010/10/10 Javascript
jQuery 版元素拖拽原型代码
2011/04/25 Javascript
JS中toFixed()方法引起的问题如何解决
2012/11/20 Javascript
extjs表格文本启用选择复制功能具体实现
2013/10/11 Javascript
SuperSlide2实现图片滚动特效
2014/06/20 Javascript
JavaScript中使用Callback控制流程介绍
2015/03/16 Javascript
javascript实现链接单选效果的方法
2015/05/13 Javascript
JS实现给对象动态添加属性的方法
2017/01/05 Javascript
vue与bootstrap实现时间选择器的示例代码
2017/08/26 Javascript
JS简单实现父子窗口传值功能示例【未使用iframe框架】
2017/09/20 Javascript
AngularJS实现动态切换样式的方法分析
2018/06/26 Javascript
jQuery解析json格式数据示例
2018/09/01 jQuery
vue中多个倒计时实现代码实例
2019/03/27 Javascript
详解a标签添加onclick事件的几种方式
2019/03/29 Javascript
详解如何使用router-link对象方式传递参数?
2019/05/02 Javascript
python实现迭代法求方程组的根过程解析
2019/11/25 Javascript
python线程池的实现实例
2013/11/18 Python
python3制作捧腹网段子页爬虫
2017/02/12 Python
python 限制函数执行时间,自己实现timeout的实例
2019/01/12 Python
Python3.5基础之变量、数据结构、条件和循环语句、break与continue语句实例详解
2019/04/26 Python
Python基于stuck实现scoket文件传输
2020/04/02 Python
HTML5之tabindex属性全面解析
2016/07/07 HTML / CSS
naturalizer加拿大官网:美国娜然女鞋
2017/04/04 全球购物
StubHub新西兰:购买和出售你的门票
2019/04/22 全球购物
精致的手工皮鞋:Shoe Embassy
2019/11/08 全球购物
狼和鹿教学反思
2014/02/05 职场文书
应届中专生自荐书范文
2014/02/13 职场文书
小学生优秀评语大全
2014/04/22 职场文书
企业法人代表证明书
2014/09/27 职场文书
优秀教师申报材料
2014/12/16 职场文书
检讨书怎么写
2015/01/23 职场文书
祝福语集锦:给百岁老人祝寿贺词
2019/11/19 职场文书
Java实现房屋出租系统详解
2021/10/05 Java/Android