美文网首页
WebRTC学习(三)

WebRTC学习(三)

作者: 强某某 | 来源:发表于2020-04-08 13:30 被阅读0次

    SDP规范

    • 会话层(全局的,类似于全局变量)
    • 媒体层(局部的,类似于局部变量)

    会话层

    • 会话的名称与目的
    • 会话的存活时间
    • 会话中包括多个媒体信息

    SDP媒体信息

    • 媒体格式
    • 传输协议
    • 传输IP和端口(价值不大,因为webrtc中会使用ice中收集的IP和端口)
    • 媒体负载类型

    SDP格式

    • 由多个<type>=<value>组成
    • 一个会话级描述
    • 多个媒体级描述

    SDP结构

    • Session Description
    • Time Description
    • Media Description

    Session Description

    • v=(protocol version)
    • 0=(owner/create and session identifiter)
    • s=(session name)
    • c=*(conn info - optional if included at session-level)
    • a=*(zero or more session attribute lines)

    Time Description

    • t=(time the session is active)
    • r=*(zero or more repeat times)

    Media Description

    • m=(media name and transport address)
    • c=*(conn info - optional if included at session-level)
    • b=*(bandwidth information)
    • a=*(zero or more session attribute lines)

    字段含义

    • Version必选

    v=0 SDP的版本号,不包括次版本号

    • Session Name 必选

    s=<session name> 会话名,s=- 表示忽略会话名

    • Origion/Owner 必选

    o=<username><session id ><version><network type><address type><address>
    例子:0=- 701812417903490901324124 2 IN IP4 127.0.0.1

    • Connection Data可选

    c=<network type><address type><connection address>
    例子: c=IN IP4 0.0.0.0

    • Media Announcements 必选

    m=<media><port><transport><fmt/payload type list>
    例子: m= audio 1024 UDP/TLS/RTP/SAVF 111103 104 9 0 8 106 105 13 126

    • Suggested Attributes 可选

    a=<TYPE>或a=<TYPE>:<VALUES>

    例子: a=framerate:《帧速率》

    • rtpmap 可选

    a=rtpmap:<fmt/payload type><encoding name>/<clockrate>[/</encodingparameters>]
    例子: a=rtpmap:103 ISAC/16000

    • fmtp可选

    a=fmtp:<format/payload type> parameters
    例子:a=fmtp:103 apt=106

    1.png

    STUN/TURN服务器选型

    • rfc5766-turn-server:谷歌开源,但是比较老了
    • coTurn :rfc的升级版,比较活跃,选这个
    • ResTurn :也是比较流行的,但是比较老

    coTurn服务器搭建与部署

    • 下载coTurn
    • ./configure --prefix=/usr/local/turn
    • 编译 make&&make install

    一、下载corturn

    //将安装的文件克隆到本地
    git clone https://github.com/coturn/coturn
    //安装依赖libevent-dev
    sudo apt-get install libevent-dev
    //进入corturn执行下面的代码。注意此处后面的路径是安装的路径,可以自选;执行成功如下
     ./configure --prefix=/usr/local/coturn
    
    2.png

    执行成功之后查看makefile

    ls -alt Makefile


    3.png

    二、使用make工具,可以使用多线程进行并行的编译(一般后面是内核的两倍,例如:电脑内核为6,这里-j后面为12)
    make -j 12


    4.png

    三、最后install

    sudo make install

    5.png

    安装完毕,在/usr/local/corturn下面就可以看到安装的corturn了 /bin下面就是可执行的服务 /etc下面就是一些配置 /include下面是一些头文件 /lib下面是一些库文件

    coTurn服务器配置

    6.png

    修改turnserver.conf.default文件

    smileyqp@smileyqp:/usr/local/coturn/etc$ sudo vim turnserver.conf.default 
    //简单设置几个参数
    turnserver.conf.default 
    user=smileyqp:123456
    listening-port=3478
    

    环境变量中添加coturn

    sudo vim ~/.bashrc 
    export PATH=/usr/local/coturn/bin
    source  ~/.bashrc
    

    开启服务

    //如果没添加到环境变量种种就用,加了就直接turnserver
    ./bin/turnserver -c ./etc/turnserver.conf.default
    //查看turn服务是否启动
    ps -ef | grep turn
    

    PTCPeerConnection

    7.png 8.png 9.png 10.png 11.png 12.png 13.png

    客户端信令消息

    • message中分为三个:offer、answer、candidate


      14.png
    15.png 16.png 17.png

    音视频实时互动信令服务器

    'use strict'
    
    var log4js = require('log4js');
    var http = require('http');
    var https = require('https');
    var fs = require('fs');
    var socketIo = require('socket.io');
    
    var express = require('express');
    var serveIndex = require('serve-index');
    
    var USERCOUNT = 3;
    
    log4js.configure({
        appenders: {
            file: {
                type: 'file',
                filename: 'app.log',
                layout: {
                    type: 'pattern',
                    pattern: '%r %p - %m',
                }
            }
        },
        categories: {
           default: {
              appenders: ['file'],
              level: 'debug'
           }
        }
    });
    
    var logger = log4js.getLogger();
    
    var app = express();
    app.use(serveIndex('./public'));
    app.use(express.static('./public'));
    
    
    
    //http server
    var http_server = http.createServer(app);
    http_server.listen(80, '0.0.0.0');
    
    var options = {
        key : fs.readFileSync('./cert/1557605_www.learningrtc.cn.key'),
        cert: fs.readFileSync('./cert/1557605_www.learningrtc.cn.pem')
    }
    
    //https server
    var https_server = https.createServer(options, app);
    var io = socketIo.listen(https_server);
    
    io.sockets.on('connection', (socket)=> {
    
        socket.on('message', (room, data)=>{
            socket.to(room).emit('message',room, data);
        });
    
        socket.on('join', (room)=>{
            socket.join(room);
            var myRoom = io.sockets.adapter.rooms[room]; 
            var users = (myRoom)? Object.keys(myRoom.sockets).length : 0;
            logger.debug('the user number of room is: ' + users);
    
            if(users < USERCOUNT){
                socket.emit('joined', room, socket.id); //发给除自己之外的房间内的所有人
                if(users > 1){
                    socket.to(room).emit('otherjoin', room, socket.id);
                }
            
            }else{
                socket.leave(room); 
                socket.emit('full', room, socket.id);
            }
            //socket.emit('joined', room, socket.id); //发给自己
            //socket.broadcast.emit('joined', room, socket.id); //发给除自己之外的这个节点上的所有人
            //io.in(room).emit('joined', room, socket.id); //发给房间内的所有人
        });
    
        socket.on('leave', (room)=>{
            var myRoom = io.sockets.adapter.rooms[room]; 
            var users = (myRoom)? Object.keys(myRoom.sockets).length : 0;
            logger.debug('the user number of room is: ' + (users-1));
            //socket.emit('leaved', room, socket.id);
            //socket.broadcast.emit('leaved', room, socket.id);
            socket.to(room).emit('bye', room, socket.id);
            socket.emit('leaved', room, socket.id);
            //io.in(room).emit('leaved', room, socket.id);
        });
    
    });
    
    https_server.listen(443, '0.0.0.0');
    

    客户状态机及处理逻辑

    18.png 19.png 20.png 21.png 22.png 23.png
    //main.js
    'use strict'
    
    
    var start = document.querySelector('button#start');
    var restart = document.querySelector('button#restart');
    
    var pc1 = new RTCPeerConnection();
    var pc2 = new RTCPeerConnection();
    
    function handleError(err){
    
        console.log('Failed to create offer', err);
    }
    
    function getPc1Answer(desc){
    
        console.log('getPc1Answer', desc.sdp);
        pc2.setLocalDescription(desc);
        pc1.setRemoteDescription(desc);
    
        /*
        pc2.createOffer({offerToRecieveAudio:1, offerToReceiveVideo:1})
            .then(getPc2Offer)
            .catch(handleError);
            */
    }
    
    function getPc1Offer(desc){
    
        console.log('getPc1Offer', desc.sdp);
        pc1.setLocalDescription(desc);
        pc2.setRemoteDescription(desc);
        pc2.createAnswer().then(getPc1Answer).catch(handleError);
    
    }
    
    function getPc2Answer(desc){
        console.log('getPc2Answer');
    
        pc1.setLocalDescription(desc);
        pc2.setRemoteDescription(desc);
    }
    
    function getPc2Offer(desc){
    
        console.log('getPc2Offer');
        pc2.setLocalDescription(desc);
        pc1.setRemoteDescription(desc);
        pc1.createAnswer().then(getPc2Answer).catch(handleError);
    
    }
    
    function startTest(){
    
    
        pc1.createOffer({offerToReceiveAudio:1, offerToRecieveVideo:1})
            .then(getPc1Offer)
            .catch(handleError);
    
    
    
    
    }
    
    function getMediaStream(stream){
    
        stream.getTracks().forEach((track) => {
            pc1.addTrack(track, stream);    
        });
    
        var offerConstraints = {
            offerToReceiveAudio: 1,
            offerToRecieveVideo: 1,
            iceRestart:false 
        }
    
        pc1.createOffer(offerConstraints)
            .then(getPc1Offer)
            .catch(handleError);
    
    }
    
    function startICE(){
    
        var constraints = {
            audio: true,
            video: true
        }
    
        navigator.mediaDevices.getUserMedia(constraints)
                    .then(getMediaStream)
                    .catch(handleError);
    }
    
    start.onclick = startTest;
    restart.onclick = startICE;
    
    <html>
        <head>
            <title>
                test createOffer from different client
            </title>
        </head>
    
        <body>
            <button id="start">Start</button>
            <button id="restart">reStart ICE</button>
            <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
            <script src="js/main.js"></script>
        </body>
    </html>
    

    实战demo

    //main.js
    'use strict'
    
    var localVideo = document.querySelector('video#localvideo');
    var remoteVideo = document.querySelector('video#remotevideo');
    
    var btnConn =  document.querySelector('button#connserver');
    var btnLeave = document.querySelector('button#leave');
    
    var offer = document.querySelector('textarea#offer');
    var answer = document.querySelector('textarea#answer');
    
    var shareDeskBox  = document.querySelector('input#shareDesk');
    
    var pcConfig = {
      'iceServers': [{
        'urls': 'turn:stun.al.learningrtc.cn:3478',
        'credential': "mypasswd",
        'username': "garrylea"
      }]
    };
    
    var localStream = null;
    var remoteStream = null;
    
    var pc = null;
    
    var roomid;
    var socket = null;
    
    var offerdesc = null;
    var state = 'init';
    
    // 以下代码是从网上找的
    //=========================================================================================
    
    //如果返回的是false说明当前操作系统是手机端,如果返回的是true则说明当前的操作系统是电脑端
    function IsPC() {
        var userAgentInfo = navigator.userAgent;
        var Agents = ["Android", "iPhone","SymbianOS", "Windows Phone","iPad", "iPod"];
        var flag = true;
    
        for (var v = 0; v < Agents.length; v++) {
            if (userAgentInfo.indexOf(Agents[v]) > 0) {
                flag = false;
                break;
            }
        }
    
        return flag;
    }
    
    //如果返回true 则说明是Android  false是ios
    function is_android() {
        var u = navigator.userAgent, app = navigator.appVersion;
        var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1; //g
        var isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
        if (isAndroid) {
            //这个是安卓操作系统
            return true;
        }
    
        if (isIOS) {
            //这个是ios操作系统
            return false;
        }
    }
    
    //获取url参数
    function getQueryVariable(variable)
    {
           var query = window.location.search.substring(1);
           var vars = query.split("&");
           for (var i=0;i<vars.length;i++) {
                   var pair = vars[i].split("=");
                   if(pair[0] == variable){return pair[1];}
           }
           return(false);
    }
    
    //=======================================================================
    
    function sendMessage(roomid, data){
    
        console.log('send message to other end', roomid, data);
        if(!socket){
            console.log('socket is null');
        }
        socket.emit('message', roomid, data);
    }
    
    function conn(){
    
        socket = io.connect();
    
        socket.on('joined', (roomid, id) => {
            console.log('receive joined message!', roomid, id);
            state = 'joined'
    
            //如果是多人的话,第一个人不该在这里创建peerConnection
            //都等到收到一个otherjoin时再创建
            //所以,在这个消息里应该带当前房间的用户数
            //
            //create conn and bind media track
            createPeerConnection();
            bindTracks();
    
            btnConn.disabled = true;
            btnLeave.disabled = false;
            console.log('receive joined message, state=', state);
        });
    
        socket.on('otherjoin', (roomid) => {
            console.log('receive joined message:', roomid, state);
    
            //如果是多人的话,每上来一个人都要创建一个新的 peerConnection
            //
            if(state === 'joined_unbind'){
                createPeerConnection();
                bindTracks();
            }
    
            state = 'joined_conn';
            call();
    
            console.log('receive other_join message, state=', state);
        });
    
        socket.on('full', (roomid, id) => {
            console.log('receive full message', roomid, id);
            hangup();
            closeLocalMedia();
            state = 'leaved';
            console.log('receive full message, state=', state);
            alert('the room is full!');
        });
    
        socket.on('leaved', (roomid, id) => {
            console.log('receive leaved message', roomid, id);
            state='leaved'
            socket.disconnect();
            console.log('receive leaved message, state=', state);
    
            btnConn.disabled = false;
            btnLeave.disabled = true;
        });
    
        socket.on('bye', (room, id) => {
            console.log('receive bye message', roomid, id);
            //state = 'created';
            //当是多人通话时,应该带上当前房间的用户数
            //如果当前房间用户不小于 2, 则不用修改状态
            //并且,关闭的应该是对应用户的peerconnection
            //在客户端应该维护一张peerconnection表,它是
            //一个key:value的格式,key=userid, value=peerconnection
            state = 'joined_unbind';
            hangup();
            offer.value = '';
            answer.value = '';
            console.log('receive bye message, state=', state);
        });
    
        socket.on('disconnect', (socket) => {
            console.log('receive disconnect message!', roomid);
            if(!(state === 'leaved')){
                hangup();
                closeLocalMedia();
    
            }
            state = 'leaved';
        
        });
    
        socket.on('message', (roomid, data) => {
            console.log('receive message!', roomid, data);
    
            if(data === null || data === undefined){
                console.error('the message is invalid!');
                return; 
            }
    
            if(data.hasOwnProperty('type') && data.type === 'offer') {
                
                offer.value = data.sdp;
    
                pc.setRemoteDescription(new RTCSessionDescription(data));
    
                //create answer
                pc.createAnswer()
                    .then(getAnswer)
                    .catch(handleAnswerError);
    
            }else if(data.hasOwnProperty('type') && data.type == 'answer'){
                answer.value = data.sdp;
                pc.setRemoteDescription(new RTCSessionDescription(data));
            
            }else if (data.hasOwnProperty('type') && data.type === 'candidate'){
                var candidate = new RTCIceCandidate({
                    sdpMLineIndex: data.label,
                    candidate: data.candidate
                });
                pc.addIceCandidate(candidate);  
            
            }else{
                console.log('the message is invalid!', data);
            
            }
        
        });
    
    
        roomid = getQueryVariable('room');
        socket.emit('join', roomid);
    
        return true;
    }
    
    function connSignalServer(){
        
        //开启本地视频
        start();
    
        return true;
    }
    
    function getMediaStream(stream){
    
        if(localStream){
            stream.getAudioTracks().forEach((track)=>{
                localStream.addTrack(track);    
                stream.removeTrack(track);
            });
        }else{
            localStream = stream;   
        }
    
        localVideo.srcObject = localStream;
    
        //这个函数的位置特别重要,
        //一定要放到getMediaStream之后再调用
        //否则就会出现绑定失败的情况
        //
        //setup connection
        conn();
    
        //btnStart.disabled = true;
        //btnCall.disabled = true;
        //btnHangup.disabled = true;
    }
    
    function getDeskStream(stream){
        localStream = stream;
    }
    
    function handleError(err){
        console.error('Failed to get Media Stream!', err);
    }
    
    function shareDesk(){
    
        if(IsPC()){
            navigator.mediaDevices.getDisplayMedia({video: true})
                .then(getDeskStream)
                .catch(handleError);
    
            return true;
        }
    
        return false;
    
    }
    
    function start(){
    
        if(!navigator.mediaDevices ||
            !navigator.mediaDevices.getUserMedia){
            console.error('the getUserMedia is not supported!');
            return;
        }else {
    
            var constraints;
    
            if( shareDeskBox.checked && shareDesk()){
    
                constraints = {
                    video: false,
                    audio:  {
                        echoCancellation: true,
                        noiseSuppression: true,
                        autoGainControl: true
                    }
                }
    
            }else{
                constraints = {
                    video: true,
                    audio:  {
                        echoCancellation: true,
                        noiseSuppression: true,
                        autoGainControl: true
                    }
                }
            }
    
            navigator.mediaDevices.getUserMedia(constraints)
                        .then(getMediaStream)
                        .catch(handleError);
        }
    
    }
    
    function getRemoteStream(e){
        remoteStream = e.streams[0];
        remoteVideo.srcObject = e.streams[0];
    }
    
    function handleOfferError(err){
        console.error('Failed to create offer:', err);
    }
    
    function handleAnswerError(err){
        console.error('Failed to create answer:', err);
    }
    
    function getAnswer(desc){
        pc.setLocalDescription(desc);
        answer.value = desc.sdp;
    
        //send answer sdp
        sendMessage(roomid, desc);
    }
    
    function getOffer(desc){
        pc.setLocalDescription(desc);
        offer.value = desc.sdp;
        offerdesc = desc;
    
        //send offer sdp
        sendMessage(roomid, offerdesc); 
    
    }
    
    function createPeerConnection(){
    
        //如果是多人的话,在这里要创建一个新的连接.
        //新创建好的要放到一个map表中。
        //key=userid, value=peerconnection
        console.log('create RTCPeerConnection!');
        if(!pc){
            pc = new RTCPeerConnection(pcConfig);
    
            pc.onicecandidate = (e)=>{
    
                if(e.candidate) {
                    sendMessage(roomid, {
                        type: 'candidate',
                        label:event.candidate.sdpMLineIndex, 
                        id:event.candidate.sdpMid, 
                        candidate: event.candidate.candidate
                    });
                }else{
                    console.log('this is the end candidate');
                }
            }
    
            pc.ontrack = getRemoteStream;
        }else {
            console.warning('the pc have be created!');
        }
    
        return; 
    }
    
    //绑定永远与 peerconnection在一起,
    //所以没必要再单独做成一个函数
    function bindTracks(){
    
        console.log('bind tracks into RTCPeerConnection!');
    
        if( pc === null || pc === undefined) {
            console.error('pc is null or undefined!');
            return;
        }
    
        if(localStream === null || localStream === undefined) {
            console.error('localstream is null or undefined!');
            return;
        }
    
        //add all track into peer connection
        localStream.getTracks().forEach((track)=>{
            pc.addTrack(track, localStream);    
        });
    
    }
    
    function call(){
        
        if(state === 'joined_conn'){
    
            var offerOptions = {
                offerToRecieveAudio: 1,
                offerToRecieveVideo: 1
            }
    
            pc.createOffer(offerOptions)
                .then(getOffer)
                .catch(handleOfferError);
        }
    }
    
    function hangup(){
    
        if(pc) {
    
            offerdesc = null;
            
            pc.close();
            pc = null;
        }
    
    }
    
    function closeLocalMedia(){
    
        if(localStream && localStream.getTracks()){
            localStream.getTracks().forEach((track)=>{
                track.stop();
            });
        }
        localStream = null;
    }
    
    function leave() {
    
        if(socket){
            socket.emit('leave', roomid); //notify server
        }
    
        hangup();
        closeLocalMedia();
    
        offer.value = '';
        answer.value = '';
        btnConn.disabled = false;
        btnLeave.disabled = true;
    }
    
    btnConn.onclick = connSignalServer
    btnLeave.onclick = leave;
    
    
    //main_signal.js
    'use strict'
    
    var localVideo = document.querySelector('video#localvideo');
    var remoteVideo = document.querySelector('video#remotevideo');
    
    var btnConn =  document.querySelector('button#connserver');
    var btnLeave = document.querySelector('button#leave');
    
    var localStream = null;
    
    var roomid;
    var socket = null;
    
    var state = 'init';
    
    
    function sendMessage(roomid, data){
    
        console.log('send message to other end', roomid, data);
        if(!socket){
            console.log('socket is null');
        }
        socket.emit('message', roomid, data);
    }
    
    function conn(){
    
        socket = io.connect();
    
        socket.on('joined', (roomid, id) => {
            console.log('receive joined message!', roomid, id);
            state = 'joined'
    
            btnConn.disabled = true;
            btnLeave.disabled = false;
            console.log('receive joined message, state=', state);
        });
    
        socket.on('otherjoin', (roomid) => {
            console.log('receive joined message:', roomid, state);
            state = 'joined_conn';
            console.log('receive other_join message, state=', state);
        });
    
        socket.on('full', (roomid, id) => {
            console.log('receive full message', roomid, id);
            state = 'leaved';
            console.log('receive full message, state=', state);
            alert('the room is full!');
        });
    
        socket.on('leaved', (roomid, id) => {
            console.log('receive leaved message', roomid, id);
            state='leaved'
            btnConn.disabled = false;
            btnLeave.disabled = true;
        });
    
        socket.on('bye', (room, id) => {
            console.log('receive bye message', roomid, id);
            state = 'joined_unbind';
            console.log('receive bye message, state=', state);
        });
    
        socket.on('disconnect', (socket) => {
            console.log('receive disconnect message!', roomid);
            state = 'leaved';
            console.log('receive disconnect message, state=', state);
        });
    
        socket.on('message', (roomid, data) => {
            console.log('receive message!', roomid, data);
    
            if(data === null || data === undefined){
                console.error('the message is invalid!');
                return; 
            }
    
            if(data.hasOwnProperty('type') && data.type === 'offer') {
                
    
            }else if(data.hasOwnProperty('type') && data.type == 'answer'){
            
            }else if (data.hasOwnProperty('type') && data.type === 'candidate'){
            
            }else{
                console.log('the message is invalid!', data);
            }
        
        });
    
        roomid = getQueryVariable('room');
        socket.emit('join', roomid);
    
        return true;
    }
    
    function connSignalServer(){
        
        //开启本地视频
        start();
    
        return true;
    }
    
    function getMediaStream(stream){
    
        localStream = stream;   
        localVideo.srcObject = localStream;
    
        conn();
    }
    
    function handleError(err){
        if(err){
            console.error('Failed to get Media Stream:', err);
        }else {
            console.error('Failed to get Media Stream!', );
        }
    
        return;
    }
    
    
    function start(){
    
        if(!navigator.mediaDevices ||
            !navigator.mediaDevices.getUserMedia){
            console.error('the getUserMedia is not supported!');
            return;
        }else {
    
            var constraints = {
                video: true,
                audio: false 
            }
    
            navigator.mediaDevices.getUserMedia(constraints)
                        .then(getMediaStream)
                        .catch(handleError);
        }
    
    }
    
    function closeLocalMedia(){
    
        if(localStream && localStream.getTracks()){
            localStream.getTracks().forEach((track)=>{
                track.stop();
            });
        }
        localStream = null;
    }
    
    function leave() {
    
        if(socket){
            socket.emit('leave', roomid); //notify server
        }
    
        btnConn.disabled = false;
        btnLeave.disabled = true;
    }
    
    btnConn.onclick = connSignalServer
    btnLeave.onclick = leave;
    
    //main_sdp.js
    'use strict'
    
    var localVideo = document.querySelector('video#localvideo');
    var remoteVideo = document.querySelector('video#remotevideo');
    
    var btnConn =  document.querySelector('button#connserver');
    var btnLeave = document.querySelector('button#leave');
    
    var pcConfig = {
      'iceServers': [{
        'urls': 'turn:stun.al.learningrtc.cn:3478',
        'credential': "mypasswd",
        'username': "garrylea"
      }]
    };
    
    var localStream = null;
    var remoteStream = null;
    
    var pc = null;
    
    var roomid;
    var socket = null;
    
    var offerdesc = null;
    var state = 'init';
    
    
    function conn(){
    
        socket = io.connect();
    
        socket.on('joined', (roomid, id) => {
            console.log('receive joined message!', roomid, id);
            state = 'joined'
    
            createPeerConnection();
    
            btnConn.disabled = true;
            btnLeave.disabled = false;
            console.log('receive joined message, state=', state);
        });
    
        socket.on('otherjoin', (roomid) => {
            console.log('receive joined message:', roomid, state);
    
            if(state === 'joined_unbind'){
                createPeerConnection();
            }
    
            state = 'joined_conn';
            call();
    
            console.log('receive other_join message, state=', state);
        });
    
        socket.on('full', (roomid, id) => {
            console.log('receive full message', roomid, id);
            state = 'leaved';
            console.log('receive full message, state=', state);
            alert('the room is full!');
        });
    
        socket.on('leaved', (roomid, id) => {
            console.log('receive leaved message', roomid, id);
            state='leaved'
            socket.disconnect();
            console.log('receive leaved message, state=', state);
    
            btnConn.disabled = false;
            btnLeave.disabled = true;
        });
    
        socket.on('bye', (room, id) => {
            console.log('receive bye message', roomid, id);
            state = 'joined_unbind';
            hangup();
            console.log('receive bye message, state=', state);
        });
    
        socket.on('disconnect', (socket) => {
            console.log('receive disconnect message!', roomid);
            state = 'leaved';
        
            console.log('receive disconnect message, state=', state);
        });
    
        socket.on('message', (roomid, data) => {
            console.log('receive message!', roomid, data);
    
            if(data === null || data === undefined){
                console.error('the message is invalid!');
                return; 
            }
    
            if(data.hasOwnProperty('type') && data.type === 'offer') {
    
                pc.setRemoteDescription(new RTCSessionDescription(data));
                //create answer
                pc.createAnswer()
                    .then(getAnswer)
                    .catch(handleAnswerError);
    
            }else if(data.hasOwnProperty('type') && data.type == 'answer'){
                answer.value = data.sdp;
                pc.setRemoteDescription(new RTCSessionDescription(data));
            
            }else if (data.hasOwnProperty('type') && data.type === 'candidate'){
                var candidate = new RTCIceCandidate({
                    sdpMLineIndex: data.label,
                    candidate: data.candidate
                });
                pc.addIceCandidate(candidate);  
            
            }else{
                console.log('the message is invalid!', data);
            
            }
        
        });
    
    
        roomid = getQueryVariable('room');
        socket.emit('join', roomid);
    
        return true;
    }
    
    function connSignalServer(){
        
        //开启本地视频
        start();
    
        return true;
    }
    
    function getMediaStream(stream){
    
        localStream = stream;   
        localVideo.srcObject = localStream;
    
        //setup connection
        conn();
    }
    
    function handleError(err){
        if(err){
            console.error('Failed to get Media Stream!', err);  
        }else {
            console.error('Failed to get Media Stream!');
        }
    }
    
    function start(){
    
        if(!navigator.mediaDevices ||
            !navigator.mediaDevices.getUserMedia){
            console.error('the getUserMedia is not supported!');
            return;
        }else {
    
            var constraints = {
                video: true,
                audio: false
            }
    
            navigator.mediaDevices.getUserMedia(constraints)
                        .then(getMediaStream)
                        .catch(handleError);
        }
    
    }
    
    function getRemoteStream(e){
        remoteStream = e.streams[0];
        remoteVideo.srcObject = e.streams[0];
    }
    
    function sendMessage(roomid, data){
    
        console.log('send message to other end', roomid, data);
        if(!socket){
            console.log('socket is null');
        }
        socket.emit('message', roomid, data);
    }
    
    function handleOfferError(err){
        console.error('Failed to create offer:', err);
    }
    
    function handleAnswerError(err){
        console.error('Failed to create answer:', err);
    }
    
    function getAnswer(desc){
        pc.setLocalDescription(desc);
    
        //send answer sdp
        sendMessage(roomid, desc);
    }
    
    function getOffer(desc){
        pc.setLocalDescription(desc);
    
        sendMessage(roomid, offerdesc); 
    }
    
    function call(){
        
        if(state === 'joined_conn'){
    
            var offerOptions = {
                offerToRecieveAudio: 1,
                offerToRecieveVideo: 1
            }
    
            pc.createOffer(offerOptions)
                .then(getOffer)
                .catch(handleOfferError);
        }
    }
    
    function createPeerConnection(){
    
        console.log('create RTCPeerConnection!');
        if(!pc){
            pc = new RTCPeerConnection(pcConfig);
    
            pc.onicecandidate = (e)=>{
    
                if(e.candidate) {
                    sendMessage(roomid, {
                        type: 'candidate',
                        label:event.candidate.sdpMLineIndex, 
                        id:event.candidate.sdpMid, 
                        candidate: event.candidate.candidate
                    });
                }else{
                    console.log('this is the end candidate');
                }
            }
    
            pc.ontrack = getRemoteStream;
        }else {
            console.warning('the pc have be created!');
        }
    
        console.log('bind tracks into RTCPeerConnection!');
    
        if(localStream === null || localStream === undefined) {
            console.error('localstream is null or undefined!');
            return false;
        }
    
        //add all track into peer connection
        localStream.getTracks().forEach((track)=>{
            pc.addTrack(track, localStream);    
        });
    
        return true;    
    }
    
    
    function hangup(){
    
        if(pc) {
            pc.close();
            pc = null;
        }
    
    }
    
    function closeLocalMedia(){
    
        if(localStream && localStream.getTracks()){
            localStream.getTracks().forEach((track)=>{
                track.stop();
            });
        }
        localStream = null;
    }
    
    function leave() {
    
        if(socket){
            socket.emit('leave', roomid); //notify server
        }
    
        hangup();
        closeLocalMedia();
    
        btnConn.disabled = false;
        btnLeave.disabled = true;
    }
    
    btnConn.onclick = connSignalServer
    btnLeave.onclick = leave;
    
    //main_connection.js
    'use strict'
    
    var localVideo = document.querySelector('video#localvideo');
    var remoteVideo = document.querySelector('video#remotevideo');
    
    var btnConn =  document.querySelector('button#connserver');
    var btnLeave = document.querySelector('button#leave');
    
    var pcConfig = {
      'iceServers': [{
        'urls': 'turn:stun.al.learningrtc.cn:3478',
        'credential': "mypasswd",
        'username': "garrylea"
      }]
    };
    
    var localStream = null;
    var remoteStream = null;
    
    var pc = null;
    
    var roomid;
    var socket = null;
    
    var state = 'init';
    
    function conn(){
    
        socket = io.connect();
    
        socket.on('joined', (roomid, id) => {
            console.log('receive joined message!', roomid, id);
            state = 'joined'
    
            createPeerConnection();
    
            btnConn.disabled = true;
            btnLeave.disabled = false;
            console.log('receive joined message, state=', state);
        });
    
        socket.on('otherjoin', (roomid) => {
            console.log('receive joined message:', roomid, state);
    
            if(state === 'joined_unbind'){
                createPeerConnection();
            }
    
            state = 'joined_conn';
            call();
    
            console.log('receive other_join message, state=', state);
        });
    
        socket.on('full', (roomid, id) => {
            console.log('receive full message', roomid, id);
            state = 'leaved';
            console.log('receive full message, state=', state);
            alert('the room is full!');
        });
    
        socket.on('leaved', (roomid, id) => {
            console.log('receive leaved message', roomid, id);
            state='leaved'
            socket.disconnect();
            console.log('receive leaved message, state=', state);
    
            btnConn.disabled = false;
            btnLeave.disabled = true;
        });
    
        socket.on('bye', (room, id) => {
            console.log('receive bye message', roomid, id);
            state = 'joined_unbind';
            hangup();
            console.log('receive bye message, state=', state);
        });
    
        socket.on('disconnect', (socket) => {
            console.log('receive disconnect message!', roomid);
            state = 'leaved';
        
            console.log('receive disconnect message, state=', state);
        });
    
        socket.on('message', (roomid, data) => {
            console.log('receive message!', roomid, data);
    
            if(data === null || data === undefined){
                console.error('the message is invalid!');
                return; 
            }
    
            if(data.hasOwnProperty('type') && data.type === 'offer') {
    
            }else if(data.hasOwnProperty('type') && data.type == 'answer'){
            
            }else if (data.hasOwnProperty('type') && data.type === 'candidate'){
            
            }else{
                console.log('the message is invalid!', data);
            
            }
        
        });
    
    
        roomid = getQueryVariable('room');
        socket.emit('join', roomid);
    
        return true;
    }
    
    function connSignalServer(){
        
        //开启本地视频
        start();
    
        return true;
    }
    
    function getMediaStream(stream){
    
        localStream = stream;   
        localVideo.srcObject = localStream;
    
        //setup connection
        conn();
    }
    
    function handleError(err){
        if(err){
            console.error('Failed to get Media Stream!', err);  
        }else {
            console.error('Failed to get Media Stream!');
        }
    }
    
    function start(){
    
        if(!navigator.mediaDevices ||
            !navigator.mediaDevices.getUserMedia){
            console.error('the getUserMedia is not supported!');
            return;
        }else {
    
            var constraints = {
                video: true,
                audio: false
            }
    
            navigator.mediaDevices.getUserMedia(constraints)
                        .then(getMediaStream)
                        .catch(handleError);
        }
    
    }
    
    function getRemoteStream(e){
        remoteStream = e.streams[0];
        remoteVideo.srcObject = e.streams[0];
    }
    
    function createPeerConnection(){
    
        console.log('create RTCPeerConnection!');
        if(!pc){
            pc = new RTCPeerConnection(pcConfig);
    
            pc.onicecandidate = (e)=>{
    
                if(e.candidate) {
                    sendMessage(roomid, {
                        type: 'candidate',
                        label:event.candidate.sdpMLineIndex, 
                        id:event.candidate.sdpMid, 
                        candidate: event.candidate.candidate
                    });
                }else{
                    console.log('this is the end candidate');
                }
            }
    
            pc.ontrack = getRemoteStream;
        }else {
            console.warning('the pc have be created!');
        }
    
        console.log('bind tracks into RTCPeerConnection!');
    
        if(localStream === null || localStream === undefined) {
            console.error('localstream is null or undefined!');
            return false;
        }
    
        //add all track into peer connection
        localStream.getTracks().forEach((track)=>{
            pc.addTrack(track, localStream);    
        });
    
        return true;    
    }
    
    function call(){
        
        if(state === 'joined_conn'){
    
            var offerOptions = {
                offerToRecieveAudio: 1,
                offerToRecieveVideo: 1
            }
    
            pc.createOffer(offerOptions)
                .then(getOffer)
                .catch(handleOfferError);
        }
    }
    
    function hangup(){
    
        if(pc) {
            pc.close();
            pc = null;
        }
    
    }
    
    function closeLocalMedia(){
    
        if(localStream && localStream.getTracks()){
            localStream.getTracks().forEach((track)=>{
                track.stop();
            });
        }
        localStream = null;
    }
    
    function sendMessage(roomid, data){
    
        console.log('send message to other end', roomid, data);
        if(!socket){
            console.log('socket is null');
        }
        socket.emit('message', roomid, data);
    }
    
    
    function leave() {
    
        if(socket){
            socket.emit('leave', roomid); //notify server
        }
    
        hangup();
        closeLocalMedia();
    
        btnConn.disabled = false;
        btnLeave.disabled = true;
    }
    
    btnConn.onclick = connSignalServer
    btnLeave.onclick = leave;
    
    //index.html
    <html>
        <head>
            <title>really peer connection</title>
            <link rel="stylesheet" href="./css/main.css">
            <script language="javascript" type="text/javascript">
                function gotoNextPage(){
                    var roomid = document.querySelector('input#room');
                    if(roomid.value === null || roomid.value === ''){
                        alert('roomid is null');
                    }else {
                        window.location.href="room.html?room="+ roomid.value;
                    }
                }
            </script>
        </head>
        <body>
            <table align="center">
                <tr><td><div>
                        <label>roomid:</label>
                        <input type="input" id="room">
                </div></td></tr>
                <tr><td><div>
                            <button id="join" onclick="gotoNextPage()">Join</button>
                </div></td></tr>
            </table>
        </body>
    </html>
    
    //room.html
    <html>
        <head>
            <title>WebRTC PeerConnection</title>
            <link href="./css/main.css" rel="stylesheet" />
        </head>
    
        <body>
            <div>
    
    
                <div>
                    <button id="connserver">Connect Sig Server</button>
                    <!--<button id="start" disabled>Start</button>  
                    <button id="call" disabled>Call</button>    
                    <button id="hangup" disabled>HangUp</button>    
                    -->
                    <button id="leave" disabled>Leave</button>  
                </div>
    
                <div>
                    <input id="shareDesk" type="checkbox"/><label for="shareDesk">Share Desktop</label>
                </div>
    
    
                <div id="preview">
                    <div >
                        <h2>Local:</h2>
                        <video id="localvideo" autoplay playsinline muted></video>
                        <h2>Offer SDP:</h2>
                        <textarea id="offer"></textarea>
                    </div>
                    <div>
                        <h2>Remote:</h2>
                        <video id="remotevideo" autoplay playsinline></video>
                        <h2>Answer SDP:</h2>
                        <textarea id="answer"></textarea>
                    </div>
                </div>
            </div>
    
            <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js"></script>
            <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
            <script src="js/main.js"></script>
        </body>
    </html>
    

    如果远程共享桌面

    //只是提几个重要api
    var promise=navigator.mediaDevices.getDisplayMedia(contraints);
    
    //constraints可选
    //constraints中约束与getUserMedia函数中的基本一致
    
    • getDisplayMedia无法同时采集音频和桌面,一般都是采集桌面
    • 桌面采集时候分辨率是否可调整

    总结

    • 网络连接要在音视频数据获取到之后,否则有可能绑定音视频失败
    • 当一段退出房间后,另一端的PeerConnection要关闭重建,否则与新用户互通时媒体协商会失败
    • 异步事件处理

    相关文章

      网友评论

          本文标题:WebRTC学习(三)

          本文链接:https://www.haomeiwen.com/subject/jzqtmhtx.html