WebRTC音视频开发读书笔记(三)

ops/2024/10/15 20:18:23/

当采集音频或视频时,设备会源源不断地产生媒体数据,这些数据就是媒体流,从Canvas,桌面,摄像头捕获的流为视频流,从麦克风捕获的的流称为音频流,媒体流中混入的可能是多种数据 ,因此WebRTC又将其划分成多个轨道,每个轨道对应于具体的设备。

五、媒体流与轨道

本文涉及的API如表所示:

1、媒体流

通过MediaStream接口处理媒体流,一个流包含多个视频流轨道和音频轨道,有两种方法可以输出 媒体流,其一,可以将输出显示为视频或者音频元素; 其二,可以将输出发送到RTCPeerConection对象,然后将其发送到远程计算机。媒体流可以通过摄像头、麦克风、屏幕、画布、视频源、远端流等方式获取。下面简要介绍属性、事件、方法。

        (1)属性

                active: 流的活动状态,活动为true ,否则为false

                ended: 结束状态,结束为true,否则为false

               id:  唯一标识符

        (2)事件

              onactive: 当MediaStream对象变为活动状态时触发活动状态。

             onended: 当媒体流终止时触发此事件。

            oninactive: 当流变为非活动状态时,触发此事件。

           onremovetrack: 从它移除MediaStreamTrack 对象时触发此事件。

       (3)方法

              addTrack :增加MediaStreamTrack

              clone: 使用新id 返回MediaStream对象的克隆。

              getAudioTracks: 获取音频轨道列表

               getTrackById :    通过id返回媒体流轨道。

         2、媒体轨道

                     MediaStreamTrack表示一段媒体源,是WebRTC的基本媒体单元,每一个轨道都有一个源与之关联。媒体流有两个输出渠道,一是video标签,二是通过RTCPeerConnection发送到远端。 

(1)属性

        enabled:  布尔值, 轨道有效性

       kind:  字符值,audio为音频,video为视频。

      muted: 布尔值 :静音状态,true为静音。

      readyState: 布尔值,轨道当前状态。

           remote:  布尔值,数据通过RTCPeerConnection提供状态,true是远端传输数据。

(2) 事件

            started:  轨道开始事件

           ended:   l轨道结束事件

 (3) 方法 

           getConstraints : 获取轨道采用的约束条件。

          applyConstraints: 应用约束条件至轨道。        

     3、流与轨道API测试示例

此示例用于测试音视频轨道的获取和删除等方法,主要有以下步骤:

          (1)设置约束条件,启用音频和视频。

          (2)使用 getUserMedia访问设备并获取到MediaStream对象,然后添加操作流的方法,如下所示:

                获取音频轨道列表、根据id获取轨道列表、删除音频轨道、获取所有轨道、获取视频轨道列表、删除轨道列表。 

           (3)添加每个方法的测试按钮。

完整代码如下所示:

   

import React from "react";
import { Button } from "antd";//MediaStream对象
let stream;
/*** 摄像头使用示例*/
class MediaStreamAPI extends React.Component {constructor() {super();}componentDidMount() {this.openDevice();}//打开音视频设备openDevice = async () => {try {//根据约束条件获取媒体stream = await navigator.mediaDevices.getUserMedia({//启用音频audio: true,//启用视频video: true});let video = document.getElementById("myVideo");video.srcObject = stream;} catch (e) {console.log(`getUserMedia错误:` + error);}}//获取音频轨道列表btnGetAudioTracks = () => {console.log("getAudioTracks");//返回一个数据console.log(stream.getAudioTracks());}//根据Id获取音频轨道btnGetTrackById = () => {console.log("getTrackById");console.log(stream.getTrackById(stream.getAudioTracks()[0].id));}//删除音频轨道btnRemoveAudioTrack = () => {console.log("removeAudioTrack()");stream.removeTrack(stream.getAudioTracks()[0]);}//获取所有轨道,包括音频及视频btnGetTracks = () => {console.log("getTracks()");console.log(stream.getTracks());}//获取视频轨道列表btnGetVideoTracks = () => {console.log("getVideoTracks()");console.log(stream.getVideoTracks());}//删除视频轨道btnRemoveVideoTrack = () => {console.log("removeVideoTrack()");stream.removeTrack(stream.getVideoTracks()[0]);}render() {return (<div className="container"><h1><span>MediaStreamAPI测试</span></h1><video className="video" id="myVideo" autoPlay playsInline></video><Button onClick={this.btnGetTracks} style={{width:'120px'}}>获取所有轨道</Button><Button onClick={this.btnGetAudioTracks} style={{width:'120px'}}>获取音频轨道</Button><Button onClick={this.btnGetTrackById} style={{width:'200px'}}>根据Id获取音频轨道</Button><Button onClick={this.btnRemoveAudioTrack} style={{width:'120px'}}>删除音频轨道</Button><Button onClick={this.btnGetVideoTracks} style={{width:'120px'}}>获取视频轨道</Button><Button onClick={this.btnRemoveVideoTrack} style={{width:'120px'}}>删除视频轨道</Button></div>);}
}
//导出组件
export default MediaStreamAPI;

六、媒体录制

 1、MediaRecorder

影像及声音保存在某些场景下是必要的,目的是便于日后回放,在W3C制定的标准中,MediaRecorder是控制媒体录制的API,它给我们的网页赋予了录制音频和视频的能力,使得web可以脱离服务器,客户端辅助,独立进行媒体的录制。

MediaRecorder的语法如下所示:

var mediaRecorder=new MediaRecorder(stream,options)

参数 stream是媒体流数据源,可以从 getUserMedia获取,也可以从<video>、<audio>、<canvas>标签获取。

参数options是限制选项,表示一个字典对象,包含下列属性:

        mineType:  指定录制的媒体类型,音频还是视频,编码方式。

        audioBitsPerSound: 指定音频的比特率

        videoBitsPerSound: 指定视频的比特率

        bitsPerSecond:  指定音频和视频比特率

注意: 默认视频比特率为:  2.5Mbps,   音频的比特率为自适应.

mineType指定录制容器的MIME类型,在应用中通过调用MediaRecorder.isTypeSuupported()检查浏览器是否支持此种mineType.   mineType的类型及说明如图所示:

mineType示例代码如下所示:

var options={mineType:  'video/webm;codecs=vp8'}

此代码表示:设置成webm格式视频,编码格式为vp8。

常用API如下所示:

       start  开始录制媒体

       stop: 停止录制媒体

      ondataavailable: 数据有效时触发此事件。

       onerror:   当有错误时触发此事件。

        2、录制音频示例

         此示例主要有以下步骤

       (1)定义状态和变量

       (2)获取音频数据

        (3)录制音频

         (4) 播放音频

完整示例代码如下所示:

import React from "react";
import { Button, } from "antd";//录制对象
let mediaRecorder;
//录制数据
let recordedBlobs;
//音频播放对象
let audioPlayer;
/*** 录制音频示例*/
class RecordAudio extends React.Component {constructor() {super();//初始操作状态this.state = {status: 'start',}}componentDidMount() {//获取音频播放器audioPlayer =document.getElementById('audioPlayer');}//点击打开麦克风按钮startClickHandler = async (e) => {try {//获取音频数据流const stream = await navigator.mediaDevices.getUserMedia({ audio: true });console.log('获取音频stream:', stream);//将stream与window.stream绑定window.stream = stream;//设置当前状态为startRecordthis.setState({status: 'startRecord',});} catch (e) {//发生错误console.error('navigator.getUserMedia error:', e);}}//开始录制startRecordButtonClickHandler = (e) => {recordedBlobs = [];//媒体类型let options = { mineType: 'audio/ogg;' };try {//初始化MediaRecorder对象,传入音频流及媒体类型mediaRecorder = new MediaRecorder(window.stream, options);} catch (e) {console.error('MediaRecorder创建失败:', e);return;}//录制停止事件回调mediaRecorder.onstop = (event) => {console.log('Recorder stopped: ', event);console.log('Recorded Blobs: ', recordedBlobs);};//当数据有效时触发的事件,可以把数据存储到缓存区里mediaRecorder.ondataavailable = this.handleDataAvailable;//录制10秒mediaRecorder.start(10);console.log('MediaRecorder started', mediaRecorder);//设置当前状态为stopRecordthis.setState({status: 'stopRecord',});}//停止录制stopRecordButtonClickHandler = (e) => {mediaRecorder.stop();//设置当前状态为playthis.setState({status: 'play',});}//播放录制数据playButtonClickHandler = (e) => {//生成blob文件,类型为audio/oggconst blob = new Blob(recordedBlobs, { type: 'audio/ogg' });audioPlayer.src = null;//根据blob文件生成播放器的数据源audioPlayer.src = window.URL.createObjectURL(blob);//播放声音audioPlayer.play();//设置当前状态为downloadthis.setState({status: 'download',});}//下载录制文件downloadButtonClickHandler = (e) => {//生成blob文件,类型为audio/oggconst blob = new Blob(recordedBlobs, { type: 'audio/ogg' });//URL.createObjectURL()方法会根据传入的参数创建一个指向该参数对象的URLconst url = window.URL.createObjectURL(blob);//创建a标签const a = document.createElement('a');a.style.display = 'none';a.href = url;//设置下载文件a.download = 'test.ogg';//将a标签添加至网页上去document.body.appendChild(a);a.click();setTimeout(() => {document.body.removeChild(a);//URL.revokeObjectURL()方法会释放一个通过URL.createObjectURL()创建的对象URL.window.URL.revokeObjectURL(url);}, 100);//设置当前状态为startthis.setState({status: 'start',});}//录制数据回调事件handleDataAvailable = (event) => {console.log('handleDataAvailable', event);//判断是否有数据if (event.data && event.data.size > 0) {//将数据记录起来recordedBlobs.push(event.data);}}render() {return (<div className="container"><h1><span>音频录制</span></h1>{/* 音频播放器,播放录制音频 */}<audio id="audioPlayer" controls autoPlay></audio><div><ButtonclassName="button"onClick={this.startClickHandler}disabled={this.state.status != 'start'}>打开麦克风</Button><ButtonclassName="button"disabled={this.state.status != 'startRecord'}onClick={this.startRecordButtonClickHandler}>开始录制</Button><ButtonclassName="button"disabled={this.state.status != 'stopRecord'}onClick={this.stopRecordButtonClickHandler}>停止录制</Button><ButtonclassName="button"disabled={this.state.status != 'play'}onClick={this.playButtonClickHandler}>播放</Button><ButtonclassName="button"disabled={this.state.status != 'download'}onClick={this.downloadButtonClickHandler}>下载</Button></div></div>);}
}
//导出组件
export default RecordAudio;

3、其它媒体的录制

(1)视频的录制

获取媒体流的区别

//约束条件let constraints = {//开启音频audio: true,//设置视频分辨率为1280*720video: {width: 1280, height: 720}//获取音视频流const stream = await navigator.mediaDevices.getUserMedia(constraints);

播放器的区别

  {/* 视频预览 muted表示默认静音 */}<video className="small-video" id="videoPreview" playsInline autoPlay muted></video>{/* 视频回放 loop表示循环播放 */}<video className="small-video" id="videoPlayer" playsInline loop></video>

mineType区别

let options = { mimeType: 'video/webm;codecs=vp9' };
(2)录制屏幕

        获取媒体流的区别  

//调用getDisplayMedia方法,约束设置成{video:true}即可stream = await navigator.mediaDevices.getDisplayMedia({//设置屏幕分辨率video: {width: 2880, height: 1800}});

        播放器的区别

       

 {/* 捕获屏幕数据渲染 */}<video className="video" ref="myVideo" autoPlay playsInline></video>

        mineType区别

 //创建MediaRecorder对象,准备录制mediaRecorder = new MediaRecorder(window.stream, { mimeType: 'video/webm' });
(3)录制canvas

        获取媒体流的区别

stream = canvas.captureStream(10);

        播放器的区别

 {/* 画布Canvas容器 */}<div className="small-canvas">{/* Canvas不设置样式 */}<canvas ref='canvas'></canvas></div><video className="small-video" ref='video' playsInline autoPlay></video>

        mineType区别

//创建MediaRecorder对象,准备录制mediaRecorder = new MediaRecorder(window.stream, { mimeType: 'video/webm' });


http://www.ppmy.cn/ops/94982.html

相关文章

IOS 06 OC调用Swift第三方框架

前面文章05讲的是在OC项目中&#xff0c;调用Swift代码&#xff0c;而在真实开发过程中&#xff0c;在OC项目中调用Swift第三方框架场景用的是非常多的&#xff0c;所以我们也了解在OC项目如何使用Swift写的三方框架。 实现流程&#xff1a; 1、OCUseSwiftTest&#xff1b;在…

力扣 3152. 特殊数字Ⅱ

题目描述 queries二维数组是nums数组待判断的索引区间&#xff08;左闭右闭&#xff09;。需要判断每个索引区间中的nums相邻元素奇偶性是否不同&#xff0c;如果都不同则该索引区间的搜索结果为True&#xff0c;否则为False。 暴力推演&#xff1a;也是我最开始的思路 遍历q…

景联文科技:图像标注的类型有哪些?

图像标注是计算机视觉领域中一个非常重要的步骤&#xff0c;它是创建训练数据集的关键组成部分&#xff0c;主要用于帮助机器学习算法理解图像内容。 以下是图像标注的一些主要类型&#xff1a; 1. 边界框标注&#xff1a; • 这是最常见的标注方式之一&#xff0c;通常用于…

android framework Display屏幕相关实战作业探讨

背景&#xff1a; 近来学员vip群里讨论屏幕相关的需求比较多&#xff0c;有2个需求属于粉丝朋友都比较感兴趣一起讨论的&#xff0c;这里刚好做一个记录&#xff0c;方便其他粉丝朋友看看。很多学员朋友学习马哥投屏和sf课程后也很想来做一些实战项目练手&#xff0c;刚好下面…

怎么直接在PDF上修改内容?随心编辑PDF内容

PDF(Portable Document Format)作为一种专用于阅读而非编辑的文档格式&#xff0c;其设计的核心目的是保持文档格式的一致性&#xff0c;确保文档在不同平台和设备上都能以相同的布局和格式呈现。然而&#xff0c;在实际工作和生活中&#xff0c;我们经常需要对PDF文档进行编辑…

Linux速成入门教程——从零基础开始快速入门,一文了解Linux

1.1 什么是Linux&#xff1f; Linux的起源与历史 Linux是一个开源的、基于UNIX操作系统的操作系统内核&#xff0c;由芬兰大学生林纳斯托瓦兹&#xff08;Linus Torvalds&#xff09;于1991年首次发布。最初的Linux只是一个小型项目&#xff0c;旨在创建一个免费的UNIX替代品…

Python酷库之旅-第三方库Pandas(084)

目录 一、用法精讲 351、pandas.Series.str.isdigit方法 351-1、语法 351-2、参数 351-3、功能 351-4、返回值 351-5、说明 351-6、用法 351-6-1、数据准备 351-6-2、代码示例 351-6-3、结果输出 352、pandas.Series.str.isspace方法 352-1、语法 352-2、参数 3…

C/C++中奇妙的类型转换

1.引言 大家在学习C语言的时候&#xff0c;有没有遇见过类似于下面这样的代码呢&#xff1f; // 整形转bool int count 10; while(count--) {cout << count << endl; }// 指针转bool int* ptr cur; while(ptr) {//…… } 众所周知&#xff0c;while循环的判断…