WebRTC简介(二)

WebRTC简介(二)

Webrtc架构

image-20211129151851667.png

Webrtc协议栈

image-20211129151916955.png

WebRTC API

WebRTC 虽然底层实现极其复杂,但是面向开发者的API还是非常简洁的,主要分为三个方面:

MediaStream

MediaStream — MediaStream用来表示一个媒体数据流(通过getUserMedia接口获取),允许你访问输入设备,如麦克风和 Web摄像机,该 API 允许从其中任意一个获取媒体流。

以下是示例代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div>
        <button id="start">开始录制</button>
        <button id="stop">停止录制</button>
    </div>
    <div>
        <video autoplay controls id="stream"></video>
    </div>
    <script>
        // 只获取视频
        let constraints = {audio: false, video: true}; 
        let startBtn = document.getElementById('start')
        let stopBtn = document.getElementById('stop')
        let video = document.getElementById('stream')
        startBtn.onclick = function() {
            navigator.getUserMedia(constraints, function(stream) {
                video.srcObject = stream;
                window.stream = stream;
            }, function(err) {
                console.log(err)
            })
        }
        stopBtn.onclick = function() {
            video.pause();
        }
    </script>
</body>
</html>

RTCPeerConnection

RTCPeerConnection:一个RTCPeerConnection对象允许用户在两个浏览器之间直接通讯。
SDP: 用来描述当前连接者想要传输的内容,支持的协议类型,支持的编解码类型等。
RTCIceCandidate:表示一个ICE协议的候选者,简单的说,就是目标节点的IP以及端口。
RTCIceServer:表示一个ICE Server,其主要用于当前主机的IP发现,通过和ICE Server通讯,我们会得到一组可供连接使用的IP:Port候选值,双方通过交换ICE候选值来建立起连接。

RTCDataChannel

非音视频数据都是通过RTCDataChannel进行传输
DataChannel:数据通道( DataChannel)接口表示一个在两个节点之间的双向的数据通道,该通道可以设置成可靠传输或非可靠传输。

更多API与示例请参考官方示例,此处不再赘述, 官方示例地址:https://webrtc.github.io/samples/

Stun服务器和Signal服务器

WebRTC并不提供Stun服务器和Signal服务器,服务器端需要自己实现。

Stun服务器可以用google提供的实现stun协议的测试服务器(stun:stun.l.google.com:19302),Signal服务器则完全需要自己实现了,它需要在ClientA和ClientB之间传送彼此的SDP信息和candidate信息,ClientA和ClientB通过这些信息建立P2P连接来传送音视频数据。由于网络环境的复杂性,并不是所有的客户端之间都能够建立P2P连接,这种情况下就需要有个trun服务器做音视频数据的中转。

Stun/turn、Signal服务器的实现在WebRTC源码中都有示例。

Stun/turn有开源实现:coturn
image-20211129152734274.png

PeerJS

使用WebRTC原生API还是有一些难度的,但是有开源项目对其进行了二次封装,此处就说说peerjs

PeerJS提供了一个完整的、可配置的、易于使用的点对点 API,它构建在 WebRTC 之上,支持数据通道和媒体流。
Github地址:https://github.com/peers/peerjs
Star:9.4k
PeerServer 一个PeerJS的服务端,有助于 PeerJS 客户端之间建立连接
Github地址:https://github.com/peers/peerjs-server
Star:3.2k
coturn:TURN 和 STUN Server 的免费开源实现
Github地址:https://github.com/coturn/coturn
Star:6.6k

服务端代码示例:

const express = require('express');
const { ExpressPeerServer } = require('peer');

const app = express();

app.get('/', (req, res, next) => res.send('Hello world!'));

const server = app.listen(9000);

const peerServer = ExpressPeerServer(server, {
  path: '/peer-server'
});

app.use('/', peerServer);
app.use(express.static('public'));

学生端代码示例:

<!DOCTYPE html>
<html>
<head>
  <title>Realtime communication with WebRTC</title>
  <style>
    body {
      font-family: sans-serif;
    }

    video {
      max-width: 100%;
      width: 320px;
    }
  </style>
</head>

<body>
    <video id="video" autoplay playsinline></video>

    <div>
      <button id="readyButton">准备</button>
    </div>

    <script src="https://unpkg.com/peerjs@1.3.1/dist/peerjs.min.js"></script>
    <script>
        const video = document.getElementById('video');
        const readyButton = document.getElementById('readyButton');
        
        readyButton.addEventListener('click', e => {
            const peer = new Peer("dc8a395f-76c2-4607-a071-416ed4086d87", {
              host: 'localhost',
              port: 9000,
              path: '/peer-server'
            });
            
            /**
            const conn = peer.connect('e3c6447d-182d-4b80-a975-2dd13dbc1721');
            
            peer.on('connection', (conn) => {
              conn.on('data', (data) => {
                console.log(data);
              });
            });*/
            
            peer.on('call', call => {
                /**
                const getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
                getUserMedia({video: true, audio: false}, (stream) => {
                    call.on('stream', (remoteStream) => {
                      video.srcObject = remoteStream;
                    });
                  }, (err) => {
                    console.log('Failed to get local stream' ,err);
                  });*/
                console.log('---');
                call.on('stream', (remoteStream) => {
                    video.srcObject = remoteStream;
                });
            });

        });
        
    </script>
</body>
</html>

老师端代码示例:

<!DOCTYPE html>
<html>
<head>
  <title>Realtime communication with WebRTC</title>
  <style>
    body {
      font-family: sans-serif;
    }

    video {
      max-width: 100%;
      width: 320px;
    }
  </style>
</head>

<body>
    <video id="video" autoplay playsinline></video>

    <div>
      <button id="startButton">创建对等端</button>
      <button id="sendButton">发送</button>
      <button id="stopButton">停止</button>
    </div>

    <script src="https://unpkg.com/peerjs@1.3.1/dist/peerjs.min.js"></script>
    <script>
        const video = document.getElementById('video');
        const startButton = document.getElementById('startButton');
        const sendButton = document.getElementById('sendButton');
        
        startButton.addEventListener('click', e => {
            
        })
        
        sendButton.addEventListener('click', e => {
        
            const peer = new Peer("e3c6447d-182d-4b80-a975-2dd13dbc1721", {
              host: 'localhost',
              port: 9000,
              path: '/peer-server'
            });
            
            /**
            const conn = peer.connect('dc8a395f-76c2-4607-a071-416ed4086d87');
            
            conn.on('open', () => {
              conn.send('hi!');
              console.log('teacher has open !');
            });*/
            /**
            const getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
            getUserMedia({video: true, audio: false}, (stream) => {
              video.srcObject = stream;
              peer.call('dc8a395f-76c2-4607-a071-416ed4086d87', stream);
            }, (err) => {
              console.log('Failed to get local stream' ,err);
            });*/
        })
    </script>
</body>
</html>

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,084评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,623评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,450评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,322评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,370评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,274评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,126评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,980评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,414评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,599评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,773评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,470评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,080评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,713评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,852评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,865评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,689评论 2 354

推荐阅读更多精彩内容