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连接access数据库的方法
Nov 17 Javascript
用javascript实现的支持lrc歌词的播放器
May 17 Javascript
JavaScript DOM 学习第九章 选取范围的介绍
Feb 19 Javascript
Jquery倒数计时按钮setTimeout的实例代码
Jul 04 Javascript
让元素在网页中可拖动示例代码
Aug 13 Javascript
使用JavaScript 编写简单计算器
Nov 24 Javascript
以Python代码实例展示kNN算法的实际运用
Oct 26 Javascript
微信小程序 swiper组件详解及实例代码
Oct 25 Javascript
ajax+node+request爬取网络图片的实例(宅男福利)
Aug 28 Javascript
微信小程序自定义组件的实现方法及自定义组件与页面间的数据传递问题
Oct 09 Javascript
简化版的vue-router实现思路详解
Oct 19 Javascript
JavaScript中跨域问题的深入理解
Mar 04 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继承的一个应用
2011/09/06 PHP
深入了解PHP类Class的概念
2012/06/14 PHP
Function eregi is deprecated (解决方法)
2013/06/21 PHP
php中require和require_once的区别说明
2014/02/27 PHP
php+MySQL判断update语句是否执行成功的方法
2014/08/28 PHP
ThinkPHP框架实现的邮箱激活功能示例
2018/06/15 PHP
Laravel5.5 手动分页和自定义分页样式的简单实现
2019/10/15 PHP
再谈javascript图片预加载技术(详细演示)
2011/03/12 Javascript
jquery动画1.加载指示器
2012/08/24 Javascript
解决jquery1.9不支持browser对象的问题
2013/11/13 Javascript
angular源码学习第一篇 setupModuleLoader方法
2016/10/20 Javascript
使用vue + less 实现简单换肤功能的示例
2018/02/21 Javascript
css配合JavaScript实现tab标签切换效果
2018/10/11 Javascript
详解关于element el-button使用$attrs的一个注意要点
2018/11/09 Javascript
微信小程序导航栏滑动定位功能示例(实现CSS3的positionsticky效果)
2019/01/24 Javascript
使用Vue父子组件通信实现todolist的功能示例代码
2019/04/11 Javascript
javascript实现异形滚动轮播
2019/11/28 Javascript
利用PHP实现递归删除链表元素的方法示例
2020/10/23 Javascript
react-native 实现购物车滑动删除效果的示例代码
2021/01/15 Javascript
[16:56]教你分分钟做大人:司夜刺客
2014/10/30 DOTA
wxpython中利用线程防止假死的实现方法
2014/08/11 Python
用Python制作简单的朴素基数估计器的教程
2015/04/01 Python
按日期打印Python的Tornado框架中的日志的方法
2015/05/02 Python
Python增量循环删除MySQL表数据的方法
2016/09/23 Python
Python实现求两个csv文件交集的方法
2017/09/06 Python
利用Python找出序列中出现最多的元素示例代码
2017/12/08 Python
员工自我鉴定范文
2013/10/06 职场文书
图书室管理制度
2014/01/19 职场文书
法律六进活动方案
2014/03/13 职场文书
会计员岗位职责
2014/03/15 职场文书
烈士陵园扫墓感想
2015/08/07 职场文书
Django 如何实现文件上传下载
2021/04/08 Python
如何用JS实现网页瀑布流布局
2021/04/24 Javascript
Python数据可视化之绘制柱状图和条形图
2021/05/25 Python
Python实战之大鱼吃小鱼游戏的实现
2022/04/01 Python
【海涛dota解说】海涛小满开黑4v5被破两路翻盘潮汐第一视角解说
2022/04/01 DOTA