今天我们来实战实现一个直播客户端,那么在开始之前我们有几个要点要注意。
我们建立这个网络连接要在音视频数据获取之后,否则就有可能绑定音视频流失败,那就是说我们与信令服务器建立连接如果在获取数据之前建立的连接,那么这个时候由于我们在通讯的双方有可能音频数据和视频数据还没准备好那么这时候就已经可以开始传输信令了,你一加入的时候马上就要发送join消息相应的后面一系列的信令都开始触发,那么这时候如果数据没有准备好,所以我们在创建这个协商的时候它的整个媒体通道都是没有准备好的,那么协商的时候肯定会失败。
第二点是当一端退出房间后,那么另一端的PeerConnection,也就是说媒体的这个通道也要关闭,然后在用户进来的时候进行重建,这样它才能始终保持一个比较干净的一个环境,否的话当我们反复的进出这个房间的时候,那么这个状态实际是很难控制的,非常容易导致新用户互通的时候也是媒体协商但是协商不成功。这也是经常遇到的错误 。
那再有就是我们整个的逻辑包括上节我们所讲的状态机处理的流程,信令之间的交互,那么这些都是异步处理的,大家一定要有一个异步的概念,而不是同步;它是异步的,也就是我做完这件事要等待一个通知过来,那这个通知过来之后我才能做下一件事。这是大家一定要注意的一个方面,因为对于很多同学来说,理解同步的关系比较容易,那异步的话还是要理解一下的。对于很多开发服务器端的同学来说,这个比较简单,因为服务器端都是异步的,就是我要通过事件去触发通过消息去触发。但是对于客户端的同学来说就会理解的稍微难一些。不过也没关系,随着课程的深入和我们对基础知识的讲解,这样大家反复的结合的看,就会对这个有很深刻的印象。这个大家一定要去实践,只有实践的时候才能知道自己哪里理解的不到位。
下面我就来具体写一下代码,打开终端服务器,进入public/peerconnection目录下,这个目录下之前我们已经实现了一些基本的功能,首先是room.html
在room.html里,我们有几个标签,第一个button就是id为connnserver表示我连接服务器,连接服务器之后我们所有服务的运转都是在底层完成的,与界面没有任何关系了,这个交互性要好一些 ,对于用于来说我只要点击一个button我就可以建立通讯了;再有一个 button就是id为leave表示离开,比如说我现在不想进行音视频通话了,所有我就可以离开,这是两个最基本的button
那另外是有两个视频video标签,第一个是展示localvideo的,也就是说我们从音视频设备中获取的数据可以在这个localvideo标签里展示,另外一个remotevideo是远程的这个标签,远程的这边标签就是当我们整个通讯都连接好了之后,协商已经好了,通道连接性检测也检测完了,数据开始发送了,这个 时候我们从远端获取的音视频数据,那么会从这个remotevideo标签里展示出来。ok
另外我们有三个js脚本
socket.io是用于连接信令服务器的,这个之前已经给大家讲过了,第二个adapter-latest.js是我们用于适配的,各个浏览器之间去调用webrtcAPI的时候,它的适配层js,最后一个是我们自己的main.js
另外一个css样式是用来美化展示的,我现在看一下 js
实际我们要为这两个button添加一些事件,当连接事件触发的时候会调用connSignalServer,当我们点击leave这个button的时候实际就会触发leave这个函数。
对于connSignalServer函数实际我要的第一件事就是打开本地的音视频设备,
就是通过这个navigator.mediaDevices.getUserMedia来获取我们的音视频数据,我们这里因为参数的限制我只获取这个视频,
获取到视频之后我们会通getMediaStream来获取到流,拿到这个流之后我们就可以将它设置到localVideo这个视频标签里,通过srcObject,这样我们本地的视频就能展示出来,我们
下面我们来实现信令相关的,因为信令是我们这里的核心,当信令完成之后后面我们就可以逐步的去添加我们的功能了。
首先在getMediaStream里面有个conn函数,这个函数就是用于和socket.io进行连接接收服务端的消息,下面我们就来实现这个函数,创建这个函数之后我们首先就是要实现和socket连接,也就是socket = io.connect(); 就这样一行语句就与信令服务器建立连接了,非常简单,建立连接之后,要注册我们接收服务端消息的这些函数。
第一个就是joined,当客户端发送一个join的时候那服务端会回一个响应,那这个时候我们就能拿到join,那它回来的时候实际是带了两个参数,一个是roomid一个用户id, 所以这个是和服务端所匹配的,这样我们joined方法就算注册成功了,这里具体逻辑没有写后面我们将它补上。
第二个是otherjoin,它也有两个参数,一个是roomid一个用户id,
第三个是full,当有两个以上用户时,就会回一个full,这个时候我们就会对这个full进行逻辑处理,
第四个是leaved,
第六个是bye,
这样我们就实现了这几个函数的注册,我们这里打印一些console,这样我们5个消息就算注册完成了,非常简单,但是我们逻辑还没写,逻辑的话,我们放到后面去处理,
紧接着我们要接收一个消息,就是做一个端对端的,就是当其中一端发送Offer和Answer和Candidate消息时候,通过服务端去中转,那这时候我们也要监听这个message,在message里我们也有两个参数,第一个是roomid,第二个是data, 这个数据实际服务器端不关心,要有我们这个客户端去做相应的处理,
这样我们就完成了所有函数接收消息的注册,那下面我们就要发送这个消息,当我们建立这个连接之后,我们要发送一个join,后面111111是房间号,这样就完成 了我们建立连接之后我们首先从服务端接收消息注册,消息来的时候就会对他们进行触发,另外接收了消息我们紧接着发送一个消息join,加入的房间是111111,当然更好的写法是我们可以在界面层去控制 ,我们可以让用户输入一个房间号,我们现在为了实现方便,我们就写111111就好,这个时候我们就返回return。
这样我们这个连接就算做完了。也就说当我们点击这个connSignalServer的时候,在这里面首先打开本地的音视频设备获取音视频数据,那在获取到这个音视频数据之后呢,让本地的这个localVideo去展示本地音视频,之后去调用这个conn方法,然后去建立socket连接,连接上之后就会接收消息方法的注册,之后发送一个消息,当我发送完这个消息当服务端收到这个消息返回到这个join之后呢,我们就可以在console里面看到join这条日志了。这就是连接。
另外我们要实现一个这个leave。这个函数比较简单,最开始的时候我们只要对信令做一下判断就好了,当socket已经创建好了我们才能进行发送,发送的就是leave.离开的房间号我们暂时就写111111就好。当我离开了这个房间,那说明这个连接已经断掉了,那断掉连接之后我们还要能够继续连接,所以这个leave就处于关闭状态,连接这个leave就处于打开状态。我们断掉这个连接之后我们要不要关闭这个连接呢?我们不应该在这个leave函数里实现,应该在conn函数的leaved里面,当这个leaved收到消息的时候我们才能真正的端口连接。因为我们虽然离开了,但是我们还想收到服务端给我们返回的消息,所以这个时候不能断开连接。
当我们实现连接进入之后,用户就不能再实现连接了,
以上我们信令相关的注册就完成了。后面我们再逐步完善这个功能。