基于WebRTC开源框架的实时视频聊天项目,搭建私人实时通信服务

Posted 邓大帅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于WebRTC开源框架的实时视频聊天项目,搭建私人实时通信服务相关的知识,希望对你有一定的参考价值。

本项目是一个 Web 应用,使用安全且可扩展的 WebRTC 构建,提供视频和音频通信、文件共享、屏幕共享、白板和实时 P2P 和群聊消息的实时通信。WebRTC 是一个开源框架,可在 Web 和本机应用程序中实现音频、视频和数据的实时通信。 它使用户能够通过视频和音频会议、网络研讨会、播客等与他人交流。


尽管任何 WebRTC 应用程序都使用 4 种类型的服务器:

  1. 应用程序服务器- 托管 Connect Web 应用程序!
  2. 信令服务器- WebRTC 信令是指建立、控制和终止通信会话的过程。为了让两个端点开始相互交谈,必须交换三种类型的信息: 会话控制信息确定何时初始化、关闭和修改通信会话。
  3. NAT Traversal Server (STUN & TURN) - WebRTC 在连接同一本地网络中的浏览器时表现出色。但是,一旦您开始到达您的网络之外——例如,进入企业防火墙——您将需要更多的火力。如果不使用 STUN(用于 NAT 的会话遍历实用程序)或 TURN(使用围绕 NAT 的中继的遍历)协议,防火墙配置不会让 WebRTC 进入。这就是您需要 STUN/TURN 服务器的原因。
  4. 媒体服务器- 媒体服务器派上用场,因为它有助于减少客户端需要发送的流的数量,通常为一个,甚至可以减少客户端需要接收的流的数量,具体取决于媒体服务器的功能。

但是目前 Connect 默认提供免费信令服务器,因此 Application Server 和 NAT Traversal Server 两台服务器即可。


一、环境依赖

由于应用程序是使用 Laravel 7 构建的,因此它需要具备 Laravel 7 的所有先决条件。 Laravel 的依赖项之一是Composer。以下是脚本的核心依赖项:

php >= 7.4
mysql >= 8.0
Apache / nginx
Node.js、npm、Composer

此外,还有一些文件和文件夹需要脚本的“写”权限。

.env 文件位于根文件夹中
storage/framework 文件夹及其子文件夹
storage/logs 文件夹及其子文件夹
bootstrap/cache 文件夹及其子文件夹
resources/lang文件夹及其子文件夹


二、基本部署

以宝塔环境为例,首先宝塔面板直接安装: PM2管理器以及对应版本的数据库、php。安装完成后重启服务器以生效:

# 1.安装宝塔
yum install -y wget && wget -O install.sh http://download.bt.cn/install/install.sh && sh install.sh

# 2.配置宝塔
(a) 安装Fileinfo扩展  # 如果Fileinfo拓展安装失败,就是内存太小,添加swap
(b) 禁用函数: proc_open、symlink、putenv、pcntl_signal、passthru
(c) 创建站点 

其中值得注意的是,宝塔面板默认安装MySQL 8.0要求2核4G内存,低配服务器可以通过如下方式安装MySQL 8.0:

wget http://download.bt.cn/install/0/mysql.sh;
bash mysql.sh install 8.0

这样低内存服务器在宝塔面板安装Mysql8.0就实现了!

请注意:如果你已经安装了数据库,上面的命令会卸载删除当前数据库及数据,请提前备份,一定要提前备份!

# 3.安装Composer
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer

# 4.编辑环境变量:修改.env文件数据库连接内容
# 5.创建数据库、创建站点、申请ssl证书、进入域名目录拉取项目
git clone https://github.com/DXJian/P2PChat.git -b master 

# 6.创建上传目录软链接
php artisan storage:link

期间报错按照错误提示安装PHP拓展以及解除禁用函数即可,如:

# 7.设置storage目录权限为777
chmod -R  0777 storage

# 8.修改网站运行目录public ,取消防跨站攻击

# 9.添加网站伪静态如下:
location /   
    try_files $uri $uri/ /index.php$is_args$query_string;  


三、线上部署

  • 第一步检测环境是否合格,如若不合格按照不合格的选项整改即可。
  • 第二步连接数据库
  • 第三步创建管理员账户
  • 第四步要求填入访问代码,随意输入即可

项目上线后,进入设置(config)页面,(任意)填入Pusher Credential选项中的信息。


四、建立通信

instant meeting——start a meeting——Live Class

之后的报错不需要理会,instant meeting——meeting history

此时可以看到之前创建的代码为444的聊天室





五、新用户加入

新用户通过注册页面注册成功后,需要管理员激活新用户。


六、项目展示

激动人心的实时通信:




其他可以辅助参考的信息:

  1. codecanyon-27525559-connect-live-class-meeting-webinar-online-training-web-conference
  2. https://kodemintserviceshelp.freshdesk.com/support/solutions/81000097241
  3. https://www.youtube.com/watch?v=QY_VdHvBsSI

至此,本文也就进入尾声了。希望本文能够起到抛砖引玉之效,也欢迎大家的批评交流。


如果您有任何疑问或者好的建议,期待你的留言、评论与关注!

基于WebRtc实现安卓视频一对一聊天

WebRtc是谷歌2010年收购GlobalIPSolutions公司而获得的一项实时语音对话或视频对话的技术。之后谷歌将其开源,有很好的跨平台性。官方网址:https://webrtc.org/

最近由于公司项目需求,刚刚接触webrtc,由于国内这方面的资料少之又少,学习起来也有点困难。这一个月来对webrtc也稍微有点了解吧,特此写个博客纪念下,结合自己写的小Demo给刚入坑的新人一点建议。

基本流程

 

 

使用webrtc###
1. Maven

<dependency>
<groupId>org.webrtc</groupId>
<artifactId>google-webrtc</artifactId>
<version>1.0.20723</version>
<type>pom</type>
</dependency>

 

注意:安卓6.0以上请自行处理CAMERA、RECORD_AUDIO、WRITE_EXTERNAL_STORAGE等危险权限。

介绍Webrtc一些关键类###
1. PeerConnectionFactory
webrtc核心类,用于创建其他关键类,稍后在做介绍。在使用PeerConnectionFactory之前,请先初始化,类似这样。

PeerConnectionFactory.initialize(
PeerConnectionFactory.InitializationOptions.builder(getApplicationContext())
.setEnableVideoHwAcceleration(true)
.createInitializationOptions());

PeerConnectionFactory.InitializationOptions作为PeerConnectionFactory初始化传入参数,该类采用构造模式,可以对初始化参数进行一些配置。初始化之后就可以创建PeerConnectionFactory实例了。

PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
mPeerConnectionFactory = new PeerConnectionFactory(options);

 


2. VideoCapturer
视频捕捉器的一个顶级接口,它的的子接口为CameraVideoCapturer,封装了安卓相机的使用方法,使用它们可以轻松的获取设备相机数据,切换摄像头,获取摄像头数量等。该对象的创建如下。

private CameraVideoCapturer createVideoCapture(Context context) {
CameraEnumerator enumerator;
if (Camera2Enumerator.isSupported(context)) {
enumerator = new Camera2Enumerator(context);
} else {
enumerator = new Camera1Enumerator(true);

final String[] deviceNames = enumerator.getDeviceNames();

for (String deviceName : deviceNames) {
if (enumerator.isFrontFacing(deviceName)) {
CameraVideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);

if (videoCapturer != null) {
return videoCapturer;
}
}
}

for (String deviceName : deviceNames) {
if (!enumerator.isFrontFacing(deviceName)) {
CameraVideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
if (videoCapturer != null) {
return videoCapturer;
}
}
}
return null;
}

 

3. VideoSource/VideoTrack
VideoSource为视频源,通过核心类PeerConnectionFactory创建,VideoTrack是对VideoSource的包装,可以方便的将视频源在本地进行播放,添加到MediaStream中进行网络传输。

CameraVideoCapturer mVideoCapturer = createVideoCapture(this);
VideoSource videoSource = mPeerConnectionFactory.createVideoSource(mVideoCapturer);
VideoTrack mVideoTrack = mPeerConnectionFactory.createVideoTrack("videtrack", videoSource);


4. AudioSource/AudioTrack
AudioSource/AudioTrack和上面的VideoSource/VideoTrack类似,从名字上面就知道是对音频的获取和处理了,AudioSource的创建很简单,直接用PeerConnectionFactory创建就可以了。

AudioSource audioSource = mPeerConnectionFactory.createAudioSource(new MediaConstraints());
AudioTrack mAudioTrack = mPeerConnectionFactory.createAudioTrack("audiotrack", audioSource);


AudioSource 创建的时候需要传入MediaConstraints这个对象的实例,其用于对媒体的一些约束限制,创建的时候可以直接使用默认的。如果你想自己定义,就需要自己填入相应的键值对了。

MediaConstraints audioConstraints = new MediaConstraints();
//回声消除
audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googEchoCancellation", "true"));
//自动增益 
audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googAutoGainControl", "true"));
//高音过滤 
audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googHighpassFilter", "true"));
//噪音处理
audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googNoiseSuppression", "true"));

 


5. MediaStream
音视频的媒体流,通过PeerConnectionFactory创建,用于PeerConnection通过网络传输发送给另一方。在媒体流传输之前,需要将前面获取的VideoTrack和AudioTrack添加进去。

MediaStream mMediaStream = mPeerConnectionFactory.createLocalMediaStream("localstream");
mMediaStream.addTrack(mVideoTrack);
mMediaStream.addTrack(mAudioTrack);



6. PeerConnection
用于p2p网络传输,双方信令的交换。webrtc是基于p2p的,因此在双方通信之前需要服务器帮助传递信令,并且需要添加STUN和TURN服务器网络穿透。在双方通道打开之后就可以将媒体流发送给另一方了。下面是PeerConnection的创建,并将媒体流添加到其中用于网络传输。

PeerConnection peerConnection = mPeerConnectionFactory.createPeerConnection(iceServers, pcConstraints, this);
peerConnection.addStream(mMediaStream);



参数说明如下:
iceServers连接到外网和网络穿用到的,添加STUN和TURN服务器可以帮助你连接。
constraints是一个MediaConstrains的实例。应该包含offerToRecieveAudio和offerToRecieveVideo。
observer是一个PeerConnectionObserver的实例,对PeerConnection的一些连接状态的监听。

信令交换###
在实现媒体流的网络传输之前,需要交换双方信令,将连接通道打开,下面介绍一下webrtc的信令交换机制。

A向B发起建立连接的请求,通过PeerConnection的createOffer()方法创建一个offer信令,创建成功后调用SdpObserver监听中的onCreateSuccess()回调函数,调用PeerConnection的setLocalDescription方法设置自己的offer信令,同时将offer信令通过服务器转发给B。

peerConnection.createOffer(sdpObserver, sdpMediaConstraints);

//SdpObserver
@Override
public void onCreateSuccess(SessionDescription sessionDescription) {
peerConnection.setLocalDescription(this, sessionDescription);
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("type", sessionDescription.type.canonicalForm());
jsonObject.put("description", sessionDescription.description);
} catch (JSONException e) {
e.printStackTrace();
}
mSocket.emit("SdpInfo", jsonObject.toString());
}

 


B收到A的offer信令后,创建一个SessionDescription(SDP描述符包含媒体信息,如分辨率、编解码能力等)对象将A的offer信令解析出来,并调用PeerConnection的setRemoteDescription方法设置A的SDP描述符,然后B通过PeerConnection的createAnswer()方法创建一个answer信令,创建成功后调用SdpObserver监听中的onCreateSuccess()回调函数,调用PeerConnection的setLocalDescription方法设置自己的answer信令,同时将answer信令通过服务器转发给A。

@Override
public void call(Object... args) {
if (mPeer == null) {
mPeer = new Peer();
}
try {
JSONObject jsonObject = new JSONObject(args[0].toString());
SessionDescription description = new SessionDescription
(SessionDescription.Type.fromCanonicalForm(jsonObject.getString("type")),
jsonObject.getString("description"));
mPeer.peerConnection.setRemoteDescription(mPeer, description);
if (!isOffer) {
mPeer.peerConnection.createAnswer(mPeer, sdpConstraints);
}
} catch (JSONException e) {
e.printStackTrace();
}
}



A收到B的answer信令后,解析B的answer信令再调用PeerConnection的setRemoteDescription方法设置B的SDP描述符。这样双方的信令交换就算完成了。
在非局域网下,信令的交换还需要借助于STUN和TURN服务器网络穿透,创建PeerConnection的时候需要传入iceServers这个参数,这里面存放的就是穿透地址变换的服务器地址了,类似的,Ice穿透也需要信令的交换,过程大致如下。

当A和B创建好配置了iceServers的PeerConnection实例后,当网络候可用时,回调PeerConnection.Observer的onIceCandidate()函数,在回调函数里面将IceCandidate对象发送给对方。
在收到对方的IceCandidate信令后,解析出来并用PeerConnection的addIceCandidate()方法设置对方的信令。

@Override
public void onIceCandidate(IceCandidate iceCandidate) {
try {
JSONObject jsonObject = new JSONObject();
jsonObject.put("label", iceCandidate.sdpMLineIndex);
jsonObject.put("id", iceCandidate.sdpMid);
jsonObject.put("candidate", iceCandidate.sdp);
mSocket.emit("IceInfo", jsonObject.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}

@Override
public void call(Object... args) {
try {
JSONObject jsonObject = new JSONObject(args[0].toString());
IceCandidate candidate = null;
candidate = new IceCandidate(
jsonObject.getString("id"),
jsonObject.getInt("label"),
jsonObject.getString("candidate")
);
mPeer.peerConnection.addIceCandidate(candidate);
} catch (JSONException e) {
e.printStackTrace();
}
}

 

现在,双方的连接通道就完全打开了,PeerConnection.Observer就会调用onAddStream()响应函数,里面包含对方的媒体流Mediastream,将媒体流播放就可以了。

@Override
public void onAddStream(MediaStream mediaStream) {
remoteVideoTrack = mediaStream.videoTracks.get(0);
remoteVideoTrack.addRenderer(new VideoRenderer(remoteView));
}


播放媒体流###
媒体流的播放需要用到webrtc封装的控件SurfaceViewRenderer,它继承于安卓的SurfaceView。
在播放之前,需要对该控件初始化和配置一些属性。

localView = findViewById(R.id.localVideoView);
//创建EglBase对象
mEglBase = EglBase.create();
//初始化localView
localView.init(mEglBase.getEglBaseContext(), null);
localView.setKeepScreenOn(true);
localView.setMirror(true);
localView.setZOrderMediaOverlay(true);
localView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);
localView.setEnableHardwareScaler(false);



注意:SurfaceViewRenderer的初始化需要在主线程

初始化完成后,将localView包装成VideoRenderer对象并添加到VideoTrack中就可以进行播放了。

mVideoTrack.addRenderer(new VideoRenderer(localView));

Demo###
demo里面包含了用IoSocket简单写的java服务器(webrtc文件夹),里面的地址改成自己电脑的本机ip4地址即可测试。

demo地址:demo传送门

附上demo运行效果图

 

原文链接:https://blog.csdn.net/qq_35054800/article/details/78647545

以上是关于基于WebRTC开源框架的实时视频聊天项目,搭建私人实时通信服务的主要内容,如果未能解决你的问题,请参考以下文章

实时音视频互动系列(下):基于 WebRTC 技术的实战解析

google为啥要开源webrtc

尚硅谷基于阿里云搭建数据仓库(实时)项目视频发布!

实时音视频入门学习:开源工程WebRTC的技术原理和使用浅析

EasyRTC的Web开发过程中如何创建新的空分支?

iOS开发之WebRTC和SIP(转载)