WebRTC 采集音视频数据

WebRTC 采集音视频数据

  • WebRTC 采集音视频数据
    • getUserMedia API 简介
      • 浏览器兼容性
      • getUserMedia 接口格式
      • MediaStreamConstraints
      • MediaTrackConstraints
    • 采集音频数据
    • MediaStream 和 MediaStreamTrack
    • 本地视频预览
    • 切换摄像头显示
    • 参考

WebRTC 采集音视频数据

getUserMedia API 简介

HTML5 的 getUserMedia API为用户提供访问硬件设备媒体(摄像头、视频、音频、地理位置等)的接口,基于该接口,开发者可以在不依赖任何浏览器插件的条件下访问硬件媒体设备。

getUserMedia API 最初是 navigator.getUserMedia,目前已被最新Web标准废除,变更为 navigator.mediaDevices.getUserMedia,但浏览器支持情况不如旧版 API 普及。

MediaDevices.getUserMedia 方法提示用户允许使用一个视频和/或一个音频输入设备,例如相机或屏幕共享和/或麦克风。如果用户给予许可,就返回一个 Promise 对象,MediaStream 对象作为此 Promise 对象的 Resolved[成功]状态的回调函数参数,相应的,如果用户拒绝了许可,或者没有媒体可用的情况下PermissionDeniedError 或者 NotFoundError 作为此 Promise 的 Rejected[失败]状态的回调函数参数。注意,由于用户不会被要求必须作出允许或者拒绝的选择,所以返回的 Promise 对象可能既不会触发 resolve 也不会触发 reject。

浏览器兼容性

在这里插入图片描述

getUserMedia 接口格式

navigator.mediaDevices.getUserMedia(constraints)
.then(function(mediaStream) { ... })
.catch(function(error) { ... })

该接口有一个 MediaStreamConstraints 类型的输入参数,指定请求的媒体类型,主要包含 video 和 audio,必须至少一个类型或者两个同时可以被指定。如果浏览器无法找到指定的媒体类型或者无法满足相对应的参数要求,那么返回的 Promise 对象就会处于 rejected[失败]状态,NotFoundError 作为 rejected[失败]回调的参数。

成功回调函数seccessCallback的参数mediaStream:mediaStream是MediaStream的对象,表示媒体内容的数据流,可以通过URL.createObjectURL转换后设置为Video或Audio元素的src属性来使用,部分较新的浏览器也可以直接设置为srcObject属性来使用。

失败回调函数errorCallback的参数error,可能的异常有:

  • AbortError:硬件问题
  • NotAllowedError:用户拒绝了当前的浏览器实例的访问请求;或者用户拒绝了当前会话的访问;或者用户在全局范围内拒绝了所有媒体访问请求。
  • NotFoundError:找不到满足请求参数的媒体类型。
  • NotReadableError:操作系统上某个硬件、浏览器或者网页层面发生的错误导致设备无法被访问。
  • OverConstrainedError:指定的要求无法被设备满足。
  • SecurityError:安全错误,在getUserMedia() 被调用的 Document
    上面,使用设备媒体被禁止。这个机制是否开启或者关闭取决于单个用户的偏好设置。
  • TypeError:类型错误,constraints对象未设置[空],或者都被设置为false。

MediaStreamConstraints

MediaStreamConstraints 结构体:

dictionary MediaStreamConstraints {
    (boolean) or (MediaTrackConstraints) video = true;
    (boolean) or (MediaTrackConstraints) audio = true;
}

video 和 audio 属性可以是 boolean 类型,也可以是 MediaTrackConstraints 类型,只有像 JavaScript 这种弱类型语言才可以做到这一点。因此,我们既可以直接给 video 和 audio 赋值 true/false,简单地指明是否采集视频/音频数据,此时浏览器会采用默认设备和默认参数采集音视频数据;也可以赋值一个 MediaTrackConstraints 类型值,对音视频设备做更精准的设置。

MediaTrackConstraints

MediaTrackConstraints 结构体:

dictionary MediaTrackConstraints {
	// Instance properties of video tracks
	ConstrainULong width;
	ConstrainULong height;
	ConstrainDouble aspectRatio; // 宽高比
	ConstrainDouble frameRate;
	ConstrainDOMString facingMode; // 前置/后置摄像头
	ConstrainDOMString resizeMode; // 缩放或裁剪
	// Instance properties of audio tracks
	ConstrainULong sampleRate;
	ConstrainULong sampleSize
	ConstrainULong channelCount;
	ConstrainDouble latency; // 目标延迟
	ConstrainDouble volume; // 已废弃
	ConstrainBoolean autoGainControl; // 自动增益
	ConstrainBoolean echoCancellation; // 回声消除
	ConstrainBoolean noiseSuppression; // 降噪
    // Instance properties of all media tracks
    ConstrainDOMString deviceId;
    ConstrainDOMString groupId;
}

MediaTrackConstraints官方文档:https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints

采集音频数据

通用模板:

// 从设备选项栏里选择某个设备
var deviceId = ...;
// 设置采集限制
var constraints = {
	// 音频约束
	audio: {
	deviceId: deviceId?{exact:deviceId}:undefined,
	sampleRate: 16000, // 采样率
    sampleSize: 16, // 每个采样点大小的位数
    channelCount: 1, // 通道数
    volume: 1, // 从 0(静音)到 1(最大音量)取值,被用作每个样本值的乘数
    echoCancellation: true, // 开启回音消除
    noiseSuppression: true, // 开启降噪功能
    },
    // 视频约束
    video: false
}
// 开始采集数据
navigator.mediaDevices.getUserMedia(constraints)
.then(gotMediaStream)
.catch(handleError);
// 采集到某路流
function gotMediaStream(stream) {
	...
}
// 处理错误
function handleError {
	...
}

更详细的例子:HTML + JavaScript 实现网页录制音频与下载

MediaStream 和 MediaStreamTrack

  • MediaStreamTrack 称为“轨”,表示单一类型的媒体源。
  • MediaStream 称为“流”,它包含 0 个或多个 MediaStreamTrack。

MediaStream 有两个作用:

  1. 作为录制或者渲染的源。可以录制成文件,也可以通过浏览器的<video>标签播放出来。
  2. 同一 MediaStream 中的 MediaStreamTrack 会进行同步。

本地视频预览

当使用 getUserMedia() 接口获得本地音视频流 MediaStream 后,赋值给 H5 的 <video> 标签的 srcObject 属性,就可以显示出来。

index.html:

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Camera Video Display</title>
    </head>

    <body>
        <video id="video" width="640" height="480" autoplay="autoplay"></video>

        <script src="index.js"></script>
    </body>

</html>

video 标签设置 autoplay,可以自动显示采集的数据。

index.js:

// 从 HTML 页面获得 video 标签
var videoElement = document.getElementById('video');
// getUserMedia 的采集限制
var constraints = {
    video: true,
    audio: false
};

// 检查浏览器是否支持 getUserMedia API
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
    console.error("您的浏览器不支持 getUserMedia API");
} else {
    navigator.mediaDevices.getUserMedia(constraints)
        .then(gotLocalStream)
        .catch(handleError);
}

function gotLocalStream(mediaStream) {
    // 将采集到的视频信息显示在 video 标签中
    videoElement.srcObject = mediaStream;
}

function handleError() {
    // 处理错误
    console.error("获取摄像头错误:", err);
}

切换摄像头显示

结合前一篇文章 WebRTC 遍历音视频设备,我们可以用一个 select 选择不同的摄像头,切换摄像头后,select.value 就是当前的 deviceId,重新设置 constraints 中 video 的 deviceId,使用 getUserMedia() 接口获得本地音视频流 MediaStream 后,赋值给 H5 的 <video> 标签的 srcObject 属性,就可以实现切换摄像头的效果。

index.html:

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Camera Video Display</title>
        <link rel="stylesheet" href="styles.css">
    </head>

    <body>
        <div class="center-select">
            <label>请选择你的采集设备:</label>
            <select id="cameraSelect"></select>
        </div>

        <div class="center-video">
            <video id="video" width="640" height="480" autoplay="autoplay"></video>
        </div>

        <script src="index.js"></script>
    </body>

</html>

styles.css:

.center-select {
    display: flex;
    justify-content: center;
    align-items: center;
}

.center-video {
    display: flex;
    justify-content: center;
    align-items: center;
    /* 使用视口高度来使容器填满整个屏幕 */
    height: 100vh;
}

index.js:

// 使用严格语法 
'use strict'
var cameraSelect = document.getElementById('cameraSelect');
// 从 HTML 页面获得 video 标签
var videoElement = document.getElementById('video');

// 获取摄像头列表并填充到 select 元素中
async function getCameraDevices() {
    try {
        const devices = await navigator.mediaDevices.enumerateDevices();
        const cameras = devices.filter(device => device.kind === 'videoinput');
        cameras.forEach(camera => {
            const option = document.createElement('option');
            option.value = camera.deviceId;
            option.text = camera.label || `Camera ${cameraSelect.length + 1}`;
            cameraSelect.appendChild(option);
        });
    } catch (error) {
        console.error('Error enumerating devices:', error);
    }
}

// 切换摄像头
async function switchCamera() {
    const deviceId = cameraSelect.value;
    const constraints = {
        video: {
            deviceId: deviceId ? { exact: deviceId } : undefined
        },
        audio: false
    };

    try {
        const mediaStream = await navigator.mediaDevices.getUserMedia(constraints);
        videoElement.srcObject = mediaStream;
    } catch (error) {
        console.error('Error switching camera:', error);
    }
}

// 初始化页面
async function init() {
    await getCameraDevices();
    if (cameraSelect.length > 0) {
        // 默认选择第一个摄像头
        await switchCamera();
    }
}

cameraSelect.addEventListener('change', switchCamera);

// 初始化
init();

运行效果:

在这里插入图片描述

切换采集设备后,下面的显示内容也会跟着切换。

参考

  1. https://www.cnblogs.com/cangqinglang/p/10210826.html
  2. https://webrtc.org.cn/20200318-api/
  3. https://blog.csdn.net/xiehuanbin/article/details/131512316

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/605241.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言 | Leetcode C语言题解之第77题组合

题目&#xff1a; 题解&#xff1a; int** combine(int n, int k, int* returnSize, int** returnColumnSizes) {int* temp malloc(sizeof(int) * (k 1));int tempSize 0;int** ans malloc(sizeof(int*) * 200001);int ansSize 0;// 初始化// 将 temp 中 [0, k - 1] 每个…

跟我学做零售数据分析报表-商品滞销分析

商品滞销的情况很常见&#xff0c;因此商品滞销分析也是基本属于零售数据分析标配内容之一。那么&#xff0c;商品滞销分析报表该怎么做&#xff1f;要做计算哪些指标&#xff0c;怎么分析滞销趋势&#xff1f;别急&#xff0c;奥威BI零售数据分析方案预设了一张BI商品滞销分析…

C语言 | Leetcode C语言题解之第76题最小覆盖子串

题目&#xff1a; 题解&#xff1a; char* minWindow(char* s, char* t) {int tLen strlen(t);int hash[256] { 0 };for (int i 0; i < tLen; i)hash[t[i]];for (int i 0; i < 256; i) {if (0 hash[i])hash[i] INT_MIN;}int left, right, count, start, minLen, s…

Java | Leetcode Java题解之第75题颜色分类

题目&#xff1a; 题解&#xff1a; class Solution {public void sortColors(int[] nums) {int n nums.length;int p0 0, p2 n - 1;for (int i 0; i < p2; i) {while (i < p2 && nums[i] 2) {int temp nums[i];nums[i] nums[p2];nums[p2] temp;--p2;}i…

【OceanBase 系列】—— OceanBase v4.3 特性解读:查询性能提升之利器列存储引擎

原文链接&#xff1a;OceanBase 社区 对于分析类查询&#xff0c;列存可以极大地提升查询性能&#xff0c;也是 OceanBase 做好 HTAP 和 OLAP 的一项不可缺少的特性。本文介绍 OceanBase 列存的实现特色。 OceanBase从诞生起就一直坚持LSM-Tree架构&#xff0c;不断打磨功能支…

web API设计笔记

Hello , 我是小恒。今晚就讲讲我在开发维护API后的经验分享&#xff0c;当然我知识有限&#xff0c;暂时也不会写实际操作。GitHub项目仓库有一堆还在前期开发&#xff0c;我的时间很多时间投在了开源上。 推荐书籍 我认为一个好的 API 设计是面向用户的&#xff0c;充分隐藏底…

java项目之校园失物招领系统(springboot+vue+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的校园失物招领系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 校园失物招领系统的主要…

UML类图之间的关系及其符号表示

UML是什么 UML&#xff08;Unified Modeling Language&#xff0c;统一建模语言&#xff09;是一种为面向对象系统的产品进行说明、可视化和编制文档的一种标准语言。UML分静态图和动态图两种&#xff0c;常用的静态图有&#xff1a;用例图、类图、包图、对象图、部署图&#…

基于BP神经网络的16QAM解调算法matlab性能仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022a 3.部分核心程序 ........................................................... % 第一部分&#xff1a;加载并…

C++聊天服务器数据库创建

创建数据库chat show databases&#xff1a;展示所有的数据库 create database chat&#xff1a;创建一个数据库chat use chat&#xff1a;使用数据库 创建表User、Friend、AllGroup、GroupUser、OfflineMessage 表User包含&#xff1a;用户id、用户名、用户密码、当前登录…

安卓自定义View

部分效果如下&#xff1a; 完整项目地址&#xff1a;https://download.csdn.net/download/qq_41733851/89282771?spm1001.2101.3001.9500

itext5.5.13 PDF预览权限问题

PdfUtils.htFile.createNewFile&#xff08;&#xff09; createNewFile 创建文件错误错误原因方式一方式二实例代码-生成PDF表格数据 createNewFile 创建文件错误 ht getResourceBasePath() "\\templates\\ht.pdf"; htFile new File(ht);代码含义是创建源文件路…

LeetCode例题讲解:快乐数

编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1&#xff0c;也可能是 无限循环 但始终变不到 1。如果这个过程 结果为 1&#xff0c…

Linux 操作系统网络编程1

目录 1、网络编程 1.1 OSI 网络七层模型 1.1.1 OSI 参考模型 1.1.2 网络数据传输过程 2 传输层通信协议 2.1 TCP 2.1.1 TCP的3次握手过程 2.1.2 TCP四次挥手过程 2.2 UDP 3 网络编程的IP地址 4 端口 5 套接字 1、网络编程 1.1 OSI 网络七层模型 1.1.1 OSI 参考模型…

日志审计系统在提高网络安全方面具有哪些重要的作用

随着信息技术的飞速发展&#xff0c;我们正处于一个高度互联、数据驱动的网络时代。在这个时代&#xff0c;日志审计系统作为网络安全和信息管理的重要工具&#xff0c;发挥着至关重要的作用。下面德迅云安全就详细介绍下关于日志审计系统在当今网络时代的重要性。 一、什么是日…

第41天:WEB攻防-ASP应用HTTP.SYS短文件文件解析Access注入数据库泄漏

第四十一天 一、ASP-SQL注入-Access数据库 1.解释 ACCESS数据库无管理帐号密码&#xff0c;顶级架构为表名&#xff0c;列名&#xff08;字段&#xff09;&#xff0c;数据&#xff0c;所以在注入猜解中一般采用字典猜解表和列再获取数据&#xff0c;猜解简单但又可能出现猜解…

Vue中使用$t(‘xxx‘)实现中英文切换;

&#xff08;原文链接&#xff09; 介绍 {{$t(key)}} &#xff1a;是VueI18n插件提供的函数&#xff0c;主要用于根据当前语言环境返回对应的翻译文本&#xff0c;以便在页面上显示多语言内容。 key&#xff1a;作为参数传递给函数$t()的字符串&#xff0c;用于指定需要翻译的…

uni-app实战在线教育类app开发

随着移动互联网的快速发展&#xff0c;教育行业也在不断向在线化、数字化方向转型。开发一款功能丰富、用户体验优秀的在线教育类 App 对于满足学习者需求、促进教育行业的发展至关重要。本文将介绍如何利用 Uni-App 进行在线教育类 App 的开发&#xff0c;让您快速上手&#x…

svg画扇形进度动画

有人问下面这种图好怎么画&#xff1f;svg 想了下&#xff0c;确实用svg可以&#xff0c;可以这么设计 外层是一个容器放置内容&#xff0c;并且设置overflow:hidden&#xff0c; 内层放一个半径大于容器宽高一半的svg&#xff0c;并定位居中&#xff0c;然后svg画扇形&#x…

如何评估大模型音频理解能力-从Gemini说起

Gemini家族包含Ultra、Pro和Nano三种大小的模型是谷歌开发的大型多模态人工智能模型&#xff0c;它在人工智能的多模态领域实现了重大突破&#xff0c;结合了语言、图像、音频和视频的理解能力。 Gemini的性能评估情况如下&#xff1a; Gemini模型的评估的具体指标从文本理解能…