SRS流媒体服务器(3)视频通话环境搭建和源码分析

1. 概述

        本文档介绍如何通过SRS 4.0搭建 WebRTC视频通话环境,涵盖环境配置、服务器编译启动、逻辑分析及测试方法。SRS 源码包提供了信令服务器以及显示UI静态web页面分别在3rdparty/signaling 3rdparty/httpx-static。信令服务器是基于go语言编写,所以需要搭建go环境。

2. 环境搭建

2.1 安装go语⾔环境

#下载安装包
cd /usr/local/
wget https://dl.google.com/go/go1.16.5.linux-amd64.tar.gz --no-check-certificate
tar -C /usr/local -xzf go1.16.5.linux-amd64.tar.gz

#配置环境变量
vim /etc/profile
export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin
source /etc/profile

2.2 编译启动 SRS 流媒体服务器 

编译可参考SRS流媒体服务器(1)概述和环境搭建-优快云博客


#vim conf/rtc.conf 
rtc_server {
    enabled on;
    listen 8000; 
    #改成自己云服务器ip
    candidate xxx.81;
}

# 启动 SRS
./objs/srs -c conf/rtc.conf  

2.3 编译启动信令服务器 

cd 3rdparty/signaling
make && ./objs/signaling  # 默认端口 1989

2.4 编译启动 Web 服务器(支持 HTTPS/WSS)

#编译
cd 3rdparty/httpx-static
make
#生成 SSL 证书
openssl genrsa -out server.key 2048
openssl req -new -x509 -key server.key -out server.crt -days 3650
#启动
./objs/httpx-static -http 80 -https 443 -ssk server.key -ssc server.crt \
  --proxy http://127.0.0.1:1989/sig \
  --proxy http://127.0.0.1:1985/rtc \
  --proxy http://127.0.0.1:8080/

3. 测试方法

访问以下 URL 进行测试(替换 localhost 为实际 IP):

  1. https://localhost/demos/

  2. https://192.168.3.6/demos/

3.1 排查失败之云服务器端口开放 

   当你在公网部署且访问一直在转圈失败,连摄像头都无法打开,那可能是端口未开放。这里排查了很久,居然需要打开3306这个端口!以及8000(rtp配置文件端口)

除此之外SRS 流媒体服务器其他端口说明如图:

 4. 源码分析

入口代码 one2one.html:297 $("#btn_start").click(startDemo);

SrsRtcSignalingParse:

  1. 解析出连接信令服务器的地址
  2. 房间号:可填或随机
  3. 显示名:可填或随机
  4. 主机

拉流和推流函数对象主要做:

  • 1.确定协议
  • 2.addtrack
  • 3. offer/answer协商
  • 5.RTCPeerConnection  
  • 6.parseUrl xxx + '/rtc/v1/play/  如: 'https://117.72.13.81:443/rtc/v1/push or play/'
	var startDemo = async function () {
		
		sig = new SrsRtcSignalingAsync();   //srs.sig.js:28  创建SrsRtcSignalingAsync,RTC信令
		sig.onmessage = function (msg) {    //onmessage订阅新的信令消息
			console.log('Notify: ', msg);

			if (msg.event === 'leave') {
				$('#player').hide();
			}

			if (msg.event === 'publish') {//房间已经存在的参与者,收到publish信令后再去订阅新加入者
				if (msg.peer && msg.peer.publishing && msg.peer.display !== display) {
					startPlay(host, room, msg.peer.display);
				}
			}
            
		};
		await sig.connect(conf.wsSchema, conf.wsHost, room, display);//连接websock,见下面SrsRtcSignalingAsync代码


		let r0 = await sig.send({action:'join', room:room, display:display}); //向信令服务器发送join信令,会返回房间列表。
		console.log('Signaling: join ok', r0);

		// 对于一对一演示,当房间已满时发出警报并忽略。
		if (r0.participants.length > 2) {
			alert('Room is full, already ' + (r0.participants.length - 1) + ' participants');
			sig.close();
			return;
		}

		// 如果信令正常,开始推流
		await startPublish(host, room, display);     //向srs流媒体服务器开始推流
		let r1 = await sig.send({action:'publish', room:room, display:display}); //向信令服务器发送publish信令
		console.log('Signaling: publish ok', r1);

		// 拉其他人的流
		r0.participants.forEach(function(participant) {
			if (participant.display === display || !participant.publishing) return;
			startPlay(host, room, participant.display);// 对于新进的房间,会拉取房间内另一个人的流
		});

		if (r0.participants.length >= 2) {
			$('.srs_merge').show();
		}
	};

	var startPublish = function (host, room, display) {
		$(".ff_first").each(function(i,e) {
			$(e).text(display);
		});

		var url = 'webrtc://' + host + '/' + room + '/' + display + conf.query;
		$('#rtc_media_publisher').show();
		$('#publisher').show();

		if (publisher) {
			publisher.close();
		}
		publisher = new SrsRtcPublisherAsync(); //创建RTC异步推流 srs.sdk.js:20 与下文类似
		$('#rtc_media_publisher').prop('srcObject', publisher.stream);

		
	};

	var startPlay = function (host, room, display) { //向srs服务器拉流
		$(".ff_second").each(function(i,e) {
			$(e).text(display);
		});
		//拼接url "webrtc://117.72.13.81/63838c2/1907a7c",其中webrtc://是协议头,"117.72.13.81"是srs服务器ip,"63838c2"是房间号,"1907a7c"是参与者昵称
		var url = 'webrtc://' + host + '/' + room + '/' + display + conf.query;
		$('#rtc_media_player').show();
		$('#player').show();

		if (player) {
			player.close();
		}

		player = new SrsRtcPlayerAsync();//创建RTC异步拉流 srs.sdk.js:278 1.确定协议 2.addtrack 3. offer/answer协商 5.RTCPeerConnection  6.parseUrl xxx + '/rtc/v1/play/  如: 'https://117.72.13.81:443/rtc/v1/publish/',
		$('#rtc_media_player').prop('srcObject', player.stream);

		
	};

	// Pass-by to SRS url. 用于解析出连接信令服务器的地址
	let conf = SrsRtcSignalingParse(window.location);
	
	
	$("#btn_start").click(startDemo);
	// Never play util windows loaded @see https://github.com/ossrs/srs/issues/2732
	if (conf.autostart) {
		window.addEventListener("load", function(){ startDemo(); });
	}
});

 SrsRtcSignalingAsync 信令函数对象主要做:

  1. connect:连接信令服务器 wss://xxxx/sig/v1/rtc
  2. onmessage:处理接收到的消息
  3. send : 把对象序列化后发送到服务器
  4. close: 关闭
    function SrsRtcSignalingAsync() { //信令函数
        var self = {};
    
        // The schema is ws or wss, host is ip or ip:port, display is nickname
        // of user to join the room.
        self.connect = async function (schema, host, room, display) {
            var url = schema + '://' + host + '/sig/v1/rtc'; //如:api:"wss://117.72.13.81/sig/v1/rtc"
            self.ws = new WebSocket(url + '?room=' + room + '&display=' + display); //与url建立连接 参数:房间号,昵称
    
            self.ws.onmessage = function(event) { //收到消息的处理函数
                var r = JSON.parse(event.data);
                var promise = self._internals.msgs[r.tid];
                if (promise) {
                    promise.resolve(r.msg);
                    delete self._internals.msgs[r.tid];
                } else {
                    self.onmessage(r.msg);
                }
            };
        };
    
        //消息是一个json对象。
        self.send = async function (message) {
            return new Promise(function (resolve, reject) {
                var r = {tid: Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).substr(0, 7), msg: message};
                self._internals.msgs[r.tid] = {resolve: resolve, reject: reject};
                self.ws.send(JSON.stringify(r));
            });
        };
    
        self.close = function () {
            self.ws && self.ws.close();
            self.ws = null;
    
            for (const tid in self._internals.msgs) {
                var promise = self._internals.msgs[tid];
                promise.reject('close');
            }
        };
    
    
        return self;
    }

    4.1 总体流程

    学习资料分享

    40voice · GitHub

    评论
    成就一亿技术人!
    拼手气红包6.0元
    还能输入1000个字符
     
    红包 添加红包
    表情包 插入表情
     条评论被折叠 查看
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

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

    余额充值