【WebRTC系列一】WebRTC 两个基础 API 实现视频通话和桌面分享

安全源

使用 API 的前提条件:在 安全源 访问,调用 API 才不会有任何阻碍。

安全源由三部分组成:1、scheme 访问协议 2、host 主机 IP 3、port 端口。eg:(HTTP、example.com、80)

同时,它必须匹配下面的几种模式中的一种:

  • (_https, _, *)
  • (_wss, _, *)
  • (*_, localhost, _)
  • (*_, 127/8, _)
  • (*_, ::1/128, _)
  • (_file, _, —)
  • (_chrome-extension, _, —)
getUserMedia

这个接口用于获取用户层面的媒体(视频设备、音频设备、虚拟音频等),当我们的计算机通过 USB 或者其他形式接入了多个摄像头或虚拟设备时,都时可以通过这个 API 获取到。同时,它不但可以获取设备,还可以控制获取到媒体的分辨率等可选项。

MDN 使用默认媒体设备示例

<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta
            name="viewport"
            content="width=device-width,initial-scale=1.0,user-scalable=0,maximum-scale=1.0,minimum-scale=1.0"
        />
        <title>独往独来银粟地,一行一步玉沙声</title>
    </head>
    <body>
        <video
            autoplay
            controls
            muted
            style="width: 500px; height: 300px"
        ></video>
        <div id="errorMsg"></div>
        <!-- <script src="./getUserMedia01.js"></script> -->
        <script>
            'use strict';

            // Put variables in global scope to make them available to the browser console.
            // 将变量置于全局作用域中,使它们对浏览器控制台可用。
            let video = document.querySelector('video');
            let constraints = (window.constraints = {
                audio: false,
                video: true,
            });
            let errorElement = document.querySelector('#errorMsg');

            navigator.mediaDevices
                .getUserMedia(constraints)
                .then(function (stream) {
                    let videoTracks = stream.getVideoTracks();

                    console.log('Got stream with constraints:', constraints);
                    console.log('Using video device: ' + videoTracks[0].label);

                    stream.onended = function () {
                        console.log('Stream ended');
                    };
                    // make variable available to browser console
                    window.stream = stream;
                    video.srcObject = stream;
                })
                .catch(function (error) {
                    if (error.name === 'ConstraintNotSatisfiedError') {
                        errorMsg(
                            'The resolution ' +
                                constraints.video.width.exact +
                                'x' +
                                constraints.video.width.exact +
                                ' px is not supported by your device.',
                        );
                    } else if (error.name === 'PermissionDeniedError') {
                        errorMsg(
                            'Permissions have not been granted to use your camera and ' +
                                'microphone, you need to allow the page access to your devices in ' +
                                'order for the demo to work.',
                        );
                    }
                    errorMsg('getUserMedia error: ' + error.name, error);
                });

            function errorMsg(msg, error) {
                errorElement.innerHTML += '<p>' + msg + '</p>';
                if (typeof error !== 'undefined') {
                    console.error(error);
                }
            }
        </script>
    </body>
</html>

另外,它还可以解决在有些特殊的场景中,可能需要在设备上的多个摄像头同时使用的问题。

实现自定义连接摄像头或麦克风

一般情况下,我们使用这个 API 默认会获取到 PC 的默认设备(笔记本上的摄像头和麦克风),但在用户有连接蓝牙、USB 摄像头的场景的时候(用户想要自己选择外接设备)就上升了一定的难度了。

思路:
1、获取当前设备所有摄像头和麦克风信息
2、从获取到的媒体设备进行遍历筛选出需要的媒体设备
3、将需要使用的媒体设备以某种参数的形式传递给浏览器 API
4、浏览器 API 执行

设备信息一般分成了三类:音频输入、视频输入、音频输出。每一个类型都有固定的字段,eg:ID、kind(类型)、label(标签),而 kind 中的固定值用于区分它们,然后最核心的就是 ID 字段。

<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta
            name="viewport"
            content="width=device-width,initial-scale=1.0,user-scalable=0,maximum-scale=1.0,minimum-scale=1.0"
        />
        <title>独往独来银粟地,一行一步玉沙声</title>
    </head>
    <body>
        <script>
            function handleError(error) {
                alert('摄像头无法正常使用,请检查是否占用或缺失');
                console.error(
                    'navigator.MediaDevices.getUserMedia error: ',
                    error.message,
                    error.name,
                );
            }

            (function getUserMediaTest() {
                if (
                    !navigator.mediaDevices ||
                    !navigator.mediaDevices.enumerateDevices
                ) {
                    console.log('浏览器不支持获取媒体设备');
                    return;
                }

                // 媒体约束
                let constraints = {video: true, audio: true};
                navigator.mediaDevices
                    .getUserMedia(constraints)
                    .then(function (stream) {
                        stream.getTracks().forEach((trick) => {
                            trick.stop();
                        });
                    })
                    .catch(handleError);
                navigator.mediaDevices
                    .enumerateDevices()
                    .then(function (devices) {
                        console.log('当前设备所有媒体设备列表:', devices);
                        devices.forEach(function (device) {
                            // 遍历所有设备筛选出需要设备
                            // console.log(device);
                        });
                    })
                    .catch(handleError);
            })();
        </script>
    </body>
</html>

控制台打印信息:

image-20231117165335224

这里的 getTracks() 方法会返回 MediaStreamTrack 对象的数组,那 MediaStreamTrack 又是什么?这就回到了 MediaStream 对象了,它包含零个或多个 MediaStreamTrack 对象,代表着各种声轨和视频轨。每一个 MediaStreamTrack 可能有一个或多个代表媒体流的最小单位。比如一个音频信号对应着一个对应的扬声器,像是在立体声音轨中的左通道或右通道。MediaTrack 由 source 与 sink 组成。WebRTC 不能直接访问或控制源,都是通过轨道控制 MediaTrackConstrains 进行控制的。

MDN 示例

<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta
            name="viewport"
            content="width=device-width,initial-scale=1.0,user-scalable=0,maximum-scale=1.0,minimum-scale=1.0"
        />
        <title>独往独来银粟地,一行一步玉沙声</title>
    </head>
    <body>
        <video
            autoplay
            controls
            muted
            style="width: 500px; height: 300px"
        ></video>
        <div id="errorMsg"></div>
        <script>
            navigator.mediaDevices
                .getUserMedia({audio: false, video: true})
                .then((mediaStream) => {
                    document.querySelector('video').srcObject = mediaStream;
                    // 5 秒后将停止使用摄像头
                    setTimeout(() => {
                        const tracks = mediaStream.getTracks();
                        console.log('track', tracks);
                        tracks[0].stop();
                    }, 5 * 1000);
                });
        </script>
    </body>
</html>

当然,在这里可以利用媒体设备的 kind 类型,在遍历的时候根据 kind 属性值的不同来进行分类。

在获取到当前设备的所有媒体设备以后,我们就需要选择一个来作为使用设备了,想要指定的使用所需要的设备就需要 getUserMedia 的约束参数 constraints 了。

constraints 媒体约束
1、媒体源输入判断

在上面的代码中 { audio: true, video: true} 是一个媒体约束,我们通常使用这个对象在调用 enumerateDevices 返回结果主动判断有无输入源,如果没有的话就需要将这个对象中响应类型更改为 false。eg:在没有获取到音频输入的时候,就需要将 audio 改为 false,否则将会报错。

2、对媒体源指定分辨率

在会议带宽充足和流媒体传输合理的时候,就需要考虑用户的摄像头分配率返回,因为并不是所有的用户都是同一个分辨率,所以我们还需要指定一个分辨率区间。所以我们在设置 video 的时候常常可以设置三个值:minmaxideal(最理想的),ideal 有着最高的权重,浏览器在设定的时候就会优先设置这个值。

当然,很多时候我们常常将分辨率降低这样就可以容纳更多的人数

{
    audio:true,
    video:{
        width:{min:320,ideal:1200,max:1920}
        height:{min:240,ideal:720,max:1080}
    }
}
// or
{audio:true,video:{width:720,height:480}}
3、facingMode 指定移动设备的前后置

这个属性可接受两个值:user(前置)environment(后置),当然这个值只能在移动端使用。

4、frameRate 指定帧速率

帧速率(FPS)越高就代表着画质越清晰,它对视频质量和带宽都有很大的影响。一般来说视频质量越好,FPS 越高,带宽需求也高。同时,视频是通过一定速率的连续多张图像形成的。

{
    audio: true,
    video: {
        width:{min:320,ideal:1200,max:1920}
        height:{min:240,ideal:720,max:1080}
        frameRate: { ideal: 10, max: 15 }
    }
};
getDisplayMedia

getDisplayMedia() 方法提示用户去选择和授权捕获展示的内容或部分内容(如一个窗口)在一个 MediaStream 里。然后,这个媒体流可以通过使用 MediaStream Recording API 被记录或者作为 WebRTC 会话的一部分被传输。

MDN 示例

async function startCapture(displayMediaOptions) {
    let captureStream = null;

    try {
        captureStream = await navigator.mediaDevices.getDisplayMedia(
            displayMediaOptions,
        );
    } catch (err) {
        console.error('Error: ' + err);
    }
    return captureStream;
}

当然上面的示例并不能将获取到的屏幕分享映射到我们的 video 中,所以,请看下面这个比较完整的

<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta
            name="viewport"
            content="width=device-width,initial-scale=1.0,user-scalable=0,maximum-scale=1.0,minimum-scale=1.0"
        />
        <title>独往独来银粟地,一行一步玉沙声</title>
    </head>
    <body>
        <video
            autoplay
            controls
            muted
            style="width: 500px; height: 300px"
        ></video>
        <div id="errorMsg"></div>
        <!-- <script src="./getDisplayMedia01.js"></script> -->
        <script>
            function handleError(error) {
                alert('摄像头无法正常使用,请检查是否占用或缺失或未同意');
                console.error(
                    'navigator.MediaDevices.getUserMedia error: ',
                    error.message,
                    error.name,
                );
            }

            (function getDisplayMediaTest() {
                if (
                    !navigator.mediaDevices ||
                    !navigator.mediaDevices.enumerateDevices
                ) {
                    console.log('浏览器不支持获取媒体设备');
                    return;
                }
                // 媒体约束
                let constraints = {video: true, audio: true};
                navigator.mediaDevices
                    // 主要是这里换成了 getDisplayMedia API
                    .getDisplayMedia(constraints)
                    .then(function (stream) {
                        let video = document.querySelector('video');
                        let videoTracks = stream.getVideoTracks();

                        console.log(
                            'Got stream with constraints:',
                            constraints,
                        );
                        console.log(
                            'Using video device: ' + videoTracks[0].label,
                        );

                        stream.onended = function () {
                            console.log('Stream ended');
                        };
                        // make variable available to browser console
                        window.stream = stream;
                        video.srcObject = stream;
                    })
                    .catch(handleError);
            })();
        </script>
    </body>
</html>
constraint

同样,getDisplayMedia 也是有 constraint 媒体约束的,不过与 getUserMedia 不同的是它这里 video 是不能设置为 false 的,但 audioframeRate 还是可以用的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

画一个圆_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值