本篇不详细介绍websocket,只针对websocket整合rtc。

一、简单说下webrtc的流程

webrtc是P2P通信,也就是实际交流的只有两个人,而要建立通信,这两个人需要交换一些信息来保证通信安全。而且,webrtc必须通过ssh加密,也就是使用https协议、wss协议。

借用一幅图

1.1 创建端点的解析

以下解析不包括websockt,只针对stun做解析。与上图略有不同

  1. 首先,Client A创建端点(Create PeerConnection),并添加音视频流(Add Streams)。接下来通知Client B,让Client B也创建一个端点。

  2. Client B收到通知后,Client B创建端点(Create PeerConnection),并添加音视频流(Add Streams),

  3. 接下来,Client B创建一个用于answer的SDP对象(Create Answer),保存并发送给Client A。

  4. Client A收到用于answer的SDP后,保存下来。

  5. 然后, Client A创建一个用于offer的SDP对象(Create Office),保存并发送给Client B。

  6. 最后,Client B保存收到的用于offer的SDP对象

以上步骤完成之后:

1、rtc会自动收集Candidate信息,并通过回调函数通知你,用于交换Candidate信息。

2、交换完Candidate信息后,P2P连接就建立好了。并通过回调函数,将远程视频流给你

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

1.2 交换Candidate信息

Candidate信息是交换完SDP对象之后自动收集的信息。在创建端点(PeerConnection)的时候,添加回调函数(onIceCandidate

创建回调函数(onIceCandidate)

将Candidate信息发送给另一端(a发给b,b发给a)

保存发过来的 Candidate信息(addIceCandidate)。注意是保存发过来的,不是保存自己的!!!

交换完Candidate信息后,P2P连接就建立好了。

二、新建springboot项目,并开启ssh

因为rtc必须使用ssh,所以springboot需要使用https协议才可以

2.1 生成ssh自签名文件

在终端中执行

keytool -genkey -alias webrtc -dname "CN=Andy,OU=kfit,O=kfit,L=HaiDian,ST=BeiJing,C=CN" -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore webrtc.keystore -validity 36500

执行时,会要求输入密码;
执行后,会在根目录下生成一个webrtc.keystore的文件

2.2 配置ssh信息

webrtc.keystore文件放在resource目录下

application.yaml中填写配置信息

server:ssl:#证书的路径key-store: classpath:webrtc.keystore#证书密码key-store-password: 123456#秘钥库类型key-store-type: JKS

2.3 检测一下能不能跑起来

运行就行,能跑起来就OK。

三、编写websocket服务类

这个简单的demo只考虑一个房间,没有房间控制,所以websocket代码很少,主要代码都在js里面。

3.1 先放一下Message实体类

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Message
{private String operation;private Object msg;public Message setOperation(String operation){this.operation = operation;return this;}public Message setMsg(Object msg){this.msg = msg;return this;}public String getMsgStr(){return msg == null ? "" : msg.toString();}
}

3.2 服务类

主要有以下信息:

  • 加入房间(into)
  • 发送 sdp 对象(send-sdp)
  • 交换 candidate 信息(send-candidate)
package com.websocket.controller;import com.alibaba.fastjson.JSONObject;
import com.websocket.pojo.Message;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Controller;import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;@Log4j2
@Controller
@ServerEndpoint("/webrtc")
public class WebrtcController
{/*** 这里只做一个最简单的, 只有一个房间, 后面有需要自己可以改*/private static Session offer;private static Session answer;@OnMessagepublic void onMessage(Session session, String message){final Message data = JSONObject.parseObject(message, Message.class);final Message response = Message.builder().operation(data.getOperation()).build();switch (data.getOperation()) {//加入房间case "into": {if (offer == null) {offer = session;response.setMsg("offer");}else if (answer == null) {answer = session;response.setMsg("answer");}else {response.setMsg("none");}sendMessage(session, response);break;}case "start":sendMessage(offer, response);break;//发送 offer 的 SDP 对象case "send-offer"://发送 answer 的 SDP 对象case "send-answer"://交换 candidate 信息case "send-candidate": {sendOther(session, response.setMsg(data.getMsg()));break;}}}@OnClosepublic void onClose(Session session, CloseReason closeReason){if (offer != null && session.getId().equals(offer.getId())) {offer = null;}else if (answer != null && session.getId().equals(answer.getId())) {answer = null;}}public static void sendOther(Session session, Object msg){if (offer != null && session.getId().equals(offer.getId())) {sendMessage(answer, msg);}else if (answer != null && session.getId().equals(answer.getId())) {sendMessage(offer, msg);}}public static void sendMessage(Session session, Object msg){sendMessage(session, JSONObject.toJSONString(msg));}@SneakyThrowsprivate static void sendMessage(Session session, String msg){final RemoteEndpoint.Basic basic = session.getBasicRemote();basic.sendText(msg);}
}

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

四、页面

4.1 html

这部分不太重要,就直接放上来了

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head><meta charset="UTF-8"><title>websocket-demo</title><link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.2.1/css/bootstrap.min.css">
</head>
<body><div class="container py-3"><div class="row"><div class="col-12"><div id="addRoom" class="btn btn-primary">加入房间</div></div><div class="col-12 col-lg-6"><p>本地视频:</p><video id="localVideo" width="500px" height="300px" autoplay style="border: 1px solid black;"></video></div><div class="col-12 col-lg-6"><p>远程视频:</p><video id="remoteVideo" width="500px" height="300px" autoplay style="border: 1px solid black;"></video></div></div></div><script src="https://s3.pstatp.com/cdn/expire-1-M/jquery/3.3.1/jquery.min.js" type="text/javascript"></script></body>
</html>

4.2 webrtc工具类 webrtc-util.js

包括以下方法:

打开本地音视频流

创建PeerConnection对象

创建用于office的SDP对象

创建用于answer的SDP对象

保存SDP对象

保存Candidate信息

收集 candidate 的回调

获得远程视频流的回调

需要注意的是:最后的两个回调,需要在创建PeerConnection对象之后,打开本地音视频流之前执行。

4.2.1 本地变量

其中的 ice对象,根据上一篇测试通过的stun服务器信息填写。

//端点对象
let rtcPeerConnection;//本地视频流
let localMediaStream = null;//ice服务器信息, 用于创建 SDP 对象
let iceServers = {"iceServers": [// {"url": "stun:stun.l.google.com:19302"},{"urls": ["stun:159.75.239.36:3478"]},{"urls": ["turn:159.75.239.36:3478"], "username": "chr", "credential": "123456"},]
};// 本地音视频信息, 用于 打开本地音视频流
const mediaConstraints = {video: {width: 500, height: 300},audio: true //由于没有麦克风,所有如果请求音频,会报错,不过不会影响视频流播放
};// 创建 offer 的信息
const offerOptions = {iceRestart: true,offerToReceiveAudio: true, //由于没有麦克风,所有如果请求音频,会报错,不过不会影响视频流播放
};

4.2.2 打开本地音视频流 

// 1、打开本地音视频流
const openLocalMedia = (callback) => {console.log('打开本地视频流');navigator.mediaDevices.getUserMedia(mediaConstraints).then(stream => {localMediaStream = stream;//将 音视频流 添加到 端点 中for (const track of localMediaStream.getTracks()) {rtcPeerConnection.addTrack(track, localMediaStream);}callback(localMediaStream);})
}

4.2.3 创建 PeerConnection 对象 

// 2、创建 PeerConnection 对象
const createPeerConnection = () => {rtcPeerConnection = new RTCPeerConnection(iceServers);
}

4.2.4 创建用于 offer 的 SDP 对象 

// 3、创建用于 offer 的 SDP 对象
const createOffer = (callback) => {// 调用PeerConnection的 CreateOffer 方法创建一个用于 offer的SDP对象,SDP对象中保存当前音视频的相关参数。rtcPeerConnection.createOffer(offerOptions).then(sdp => {// 保存自己的 SDP 对象rtcPeerConnection.setLocalDescription(sdp).then(() => callback(sdp));}).catch(() => console.log('createOffer 失败'));
}

 4.2.5 创建用于 answer 的 SDP 对象

// 4、创建用于 answer 的 SDP 对象
const createAnswer = (callback) => {// 调用PeerConnection的 CreateAnswer 方法创建一个 answer的SDP对象rtcPeerConnection.createAnswer(offerOptions).then(sdp => {// 保存自己的 SDP 对象rtcPeerConnection.setLocalDescription(sdp).then(() => callback(sdp));}).catch(() => console.log('createAnswer 失败'))
}

4.2.6 保存远程的 SDP 对象 

// 5、保存远程的 SDP 对象
const saveSdp = (answerSdp, callback) => {rtcPeerConnection.setRemoteDescription(new RTCSessionDescription(answerSdp)).then(callback);
}

4.2.7 保存 candidate 信息 

// 6、保存 candidate 信息
const saveIceCandidate = (candidate) => {let iceCandidate = new RTCIceCandidate(candidate);rtcPeerConnection.addIceCandidate(iceCandidate).then(() => console.log('addIceCandidate 成功'));
}

4.2.8 收集 candidate 的回调

// 7、收集 candidate 的回调
const bindOnIceCandidate = (callback) => {// 绑定 收集 candidate 的回调rtcPeerConnection.onicecandidate = (event) => {if (event.candidate) {callback(event.candidate);}};
};

4.2.9 获得 远程视频流 的回调 

// 8、获得 远程视频流 的回调
const bindOnTrack = (callback) => {rtcPeerConnection.ontrack = (event) => callback(event.streams[0]);
};

以上代码都写在 webrtc-util.js 中,是可以复用滴

接下来,就是在html中引入这个js,然后和websocket整合一下,把通知、candidate 信息等等,通过websocket发送给另一端

End

如果你对音视频开发感兴趣,觉得文章对您有帮助,别忘了点赞、收藏哦!或者对本文的一些阐述有自己的看法,有任何问题,欢迎在下方评论区讨论!

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

WebRTC音视频通话(二)简单音视频通话相关推荐

  1. WebRTC 实现P2P音视频通话——实现一对一音视频通话

    WebRTC 实现P2P音视频通话 WebRTC 实现P2P音视频通话--搭建信令服务器 WebRTC 实现P2P音视频通话--搭建stun/trun P2P穿透和转发服务器 WebRTC 实现P2P ...

  2. 一文学会 QQ视频通话、抖音的视频回显实现方案

    QQ视频通话.抖音的视频回显 是如何实现的 先说为什么会有这一篇文章: 2014年联想曾经做过一款 短视频软件,叫"魔力秀".可以说和现在的抖音基本是一样的,但因为"魔力 ...

  3. webrtc 入门第五章 一对一视频通话实现

    webrtc 入门第五章 一对一视频通话实现 一.介绍 ​ 在前面的章节我们学习了如何操作本地的设备摄像头,麦克风等,学会了如何进行本地的流媒体操作如录制,下载,同步等.在第三第四章节学习了webrt ...

  4. 入门启发:音视频的简单理解

    算机技术领域中,『音视频技术』应该说算是较复杂的小门类.较复杂的东西有个简单的入门指引,或者有前辈带路是很重要的. 前阵子,因为项目中急需音视频技术,虽然网上资料看似很丰富,但对初学者来说,很多资料都 ...

  5. 音视频开发二:音视频知识总结

    文章目录 简介 简单理解,音视频原理 音视频理论基础 音频 声音介绍 **为什么要存在数字音频 ?** **什么是数字音频?** 从"模拟信号"到"数字化"的过 ...

  6. JavaWeb-SpringBoot(抖音)_二、服务器间通讯

    JavaWeb-SpringBoot(抖音)_一.抖音项目制作 传送门 JavaWeb-SpringBoot(抖音)_二.服务器间通讯 传送门 JavaWeb-SpringBoot(抖音)_三.抖音项 ...

  7. 不若与众:说说抖音的二创激励计划

    七月份抖音与爱奇艺宣布达成合作,如今,抖音二创激励计划落地.9月5日,抖音公布二创激励计划,相当于为这场燎原之火又添了一大把柴. 此次计划是抖音与爱奇艺双方围绕长视频的二创与推广展开的探索,面向影视综 ...

  8. 音视频开发成长之路—进阶之路3个重要知识点丨WebRTC丨FFmpeg丨SRS流媒体服务器丨C++音视频丨嵌入式音视频

    音视频开发成长之路-进阶之路3个重要知识点 视频讲解如下,点击观看: 音视频开发成长之路-进阶之路3个重要知识点丨WebRTC丨FFmpeg丨SRS流媒体服务器丨C++音视频丨嵌入式音视频 音视频高级 ...

  9. (二)音视频:MediaCodec编码桌面信息 完整Demo 进一步理解H264

    (一)音视频:解码H264文件流程 渲染和拿到解码后源数据YUV 完整Demo] (二)音视频:MediaCodec编码桌面信息 完整Demo 进一步理解H264 (三)音视频:解析H264 SPS ...

  10. WebRTC系列<二> 案例与工具

     阅读关于webRTC的其他文章: WebRTC系列<一> 什么是WebRTC? WebRTC系列<二> 案例与工具 ----------------------------- ...

最新文章

  1. HDU 1877 另一个版本 A+B
  2. 肏蛋的Loadrunner脚本
  3. android activity启动流程_1307页!一线大厂Android面试全套真题解析!
  4. unity 继承会调用start吗_Unity 继承MonoBehaviour脚本 执行顺序 详解
  5. 深度理解java jvm,深度理解JVM
  6. 顺丰薪酬体系大曝光,看完感叹:太走心了,不服不行!
  7. 《南溪的目标检测学习笔记》——基础算子的学习笔记
  8. cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration
  9. 实时即未来!Flink Forward Asia 2021 议程正式上线!
  10. java5、java6、java7、java8的新特性
  11. 自动寻路之 --AStar算法
  12. 数字电子技术基础知识点总结_数字电子技术课程线上授课一周之感想 ——信息与控制学院 电工基础教研室 李姿...
  13. 服务器网站gzip压缩,网站实现Gzip网页压缩的方法与步骤 提高网页浏览速度
  14. GAN领风骚!Github盘点2021最惊艳38篇AI论文
  15. 互联网短信网关接口协议
  16. word自定义多级折叠,自动编码标题【论文框架快速上手】
  17. ei指什么_什么是ei和ei检索是什么意思
  18. 机器人genghis_iRobot的智慧家居:从扫地机器人和擦地机器人的联动开始
  19. header is present on the requested resource. Origin 'null' is therefore not allowed access.
  20. 栈的压入、弹出序列(Java)

热门文章

  1. 更好地使用计算机,詤注表示可以帮助您更好地使用计算机的重要信息.pdf
  2. 二级c语言一本通pdf,《2020年全国计算机等级考试一本通:二级C语言》试题源文件...
  3. 新生必备清单:不想成为虚度青春的“小透明”,手机应该怎样选?
  4. [特别公告]RDIFramework.NET微信公众号迁移通知
  5. 有符号距离场原理及实现源码
  6. 分类按照拼音第一字母排序显示实现
  7. linux服务器如何更改ip,Linux修改IP地址,CentOS设置系统IP地址
  8. Java就业前景分析
  9. 事务四大特性如何保证
  10. Gears of Programmer--工作