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 相关文章推荐
去除链接虚线全面分析总结
Aug 15 Javascript
基于jquery实现的可以编辑选择的下拉框的代码
Nov 19 Javascript
Jquery判断IE6等浏览器的代码
Apr 05 Javascript
js改变img标签的src属性在IE下没反应的解决方法
Jul 23 Javascript
JavaScript拆分字符串时产生空字符的解决方案
Sep 26 Javascript
javascript对象的创建和访问
Mar 08 Javascript
angular中实现li或者某个元素点击变色的两种方法
Jul 27 Javascript
js 实现复选框只能选择一项的示例代码
Jan 23 Javascript
结合Vue控制字符和字节的显示个数的示例
May 17 Javascript
vue实现多条件和模糊搜索功能
May 28 Javascript
在Layui中实现开关按钮的效果实例
Sep 29 Javascript
vue实现移动端拖动排序
Aug 21 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 高手之路(三)
2006/10/09 PHP
PHP版本如何选择?应该使用哪个版本?
2015/05/13 PHP
postfixadmin忘记密码后的修改密码方法详解
2016/07/20 PHP
php实现不通过扩展名准确判断文件类型的方法【finfo_file方法与二进制流】
2017/04/18 PHP
PHP dirname简单使用代码实例
2020/11/13 PHP
JavaScript 调试器简介
2009/02/21 Javascript
JavaScript高级程序设计 读书笔记之九 本地对象Array
2012/02/27 Javascript
javascript加号&quot;+&quot;的二义性说明
2013/03/04 Javascript
javascript:;与javascript:void(0)使用介绍
2013/06/05 Javascript
open 动态修改img的onclick事件示例代码
2013/11/13 Javascript
js判断IE浏览器版本过低示例代码
2013/11/22 Javascript
基于jQuery的判断iPad、iPhone、Android是横屏还是竖屏的代码
2014/05/11 Javascript
jQuery插件jFade实现鼠标经过的图片高亮其它变暗
2015/03/14 Javascript
关于JavaScript作用域你想知道的一切
2016/02/04 Javascript
Node.js的项目构建工具Grunt的安装与配置教程
2016/05/12 Javascript
jquery动态切换背景图片的简单实现方法
2016/05/14 Javascript
Easyui的组合框的取值与赋值
2016/10/28 Javascript
详解jQuery中基本的动画方法
2016/12/14 Javascript
DWR3 访问WEB元素的两种方法实例详解
2017/01/03 Javascript
layui 优化button按钮和弹出框的方法
2018/08/15 Javascript
nodejs aes 加解密实例
2018/10/10 NodeJs
基于JavaScript实现十五拼图代码实例
2020/04/26 Javascript
图解JS原型和原型链实现原理
2020/09/15 Javascript
Bootstrap FileInput实现图片上传功能
2021/01/28 Javascript
[09:37]2018DOTA2国际邀请赛寻真——不懈追梦的Team Serenity
2018/08/13 DOTA
合并Excel工作薄中成绩表的VBA代码,非常适合教育一线的朋友
2009/04/09 Python
Python collections模块实例讲解
2014/04/07 Python
对Pandas MultiIndex(多重索引)详解
2018/11/16 Python
Python八皇后问题解答过程详解
2019/07/29 Python
深入浅析CSS3中的Flex布局整理
2020/04/27 HTML / CSS
加拿大奢华时装品牌:Mackage
2018/01/10 全球购物
英国行业制服供应商:Alexandra
2019/09/14 全球购物
秸秆管理实施方案
2014/03/15 职场文书
2015年机关党建工作总结
2015/05/22 职场文书
2016教师国培研修感言
2015/12/08 职场文书
python内置模块之上下文管理contextlib
2022/06/14 Python