WebRTC音视频通话(二)简单音视频通话
本篇不详细介绍websocket,只针对websocket整合rtc。
一、简单说下webrtc的流程
webrtc是P2P通信,也就是实际交流的只有两个人,而要建立通信,这两个人需要交换一些信息来保证通信安全。而且,webrtc必须通过ssh加密,也就是使用https协议、wss协议。
借用一幅图
1.1 创建端点的解析
以下解析不包括websockt,只针对stun做解析。与上图略有不同
首先,Client A创建端点(Create PeerConnection),并添加音视频流(Add Streams)。接下来通知Client B,让Client B也创建一个端点。
Client B收到通知后,Client B创建端点(Create PeerConnection),并添加音视频流(Add Streams),
接下来,Client B创建一个用于answer的SDP对象(Create Answer),保存并发送给Client A。
Client A收到用于answer的SDP后,保存下来。
然后, Client A创建一个用于offer的SDP对象(Create Office),保存并发送给Client B。
最后,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音视频通话(二)简单音视频通话相关推荐
- WebRTC 实现P2P音视频通话——实现一对一音视频通话
WebRTC 实现P2P音视频通话 WebRTC 实现P2P音视频通话--搭建信令服务器 WebRTC 实现P2P音视频通话--搭建stun/trun P2P穿透和转发服务器 WebRTC 实现P2P ...
- 一文学会 QQ视频通话、抖音的视频回显实现方案
QQ视频通话.抖音的视频回显 是如何实现的 先说为什么会有这一篇文章: 2014年联想曾经做过一款 短视频软件,叫"魔力秀".可以说和现在的抖音基本是一样的,但因为"魔力 ...
- webrtc 入门第五章 一对一视频通话实现
webrtc 入门第五章 一对一视频通话实现 一.介绍 在前面的章节我们学习了如何操作本地的设备摄像头,麦克风等,学会了如何进行本地的流媒体操作如录制,下载,同步等.在第三第四章节学习了webrt ...
- 入门启发:音视频的简单理解
算机技术领域中,『音视频技术』应该说算是较复杂的小门类.较复杂的东西有个简单的入门指引,或者有前辈带路是很重要的. 前阵子,因为项目中急需音视频技术,虽然网上资料看似很丰富,但对初学者来说,很多资料都 ...
- 音视频开发二:音视频知识总结
文章目录 简介 简单理解,音视频原理 音视频理论基础 音频 声音介绍 **为什么要存在数字音频 ?** **什么是数字音频?** 从"模拟信号"到"数字化"的过 ...
- JavaWeb-SpringBoot(抖音)_二、服务器间通讯
JavaWeb-SpringBoot(抖音)_一.抖音项目制作 传送门 JavaWeb-SpringBoot(抖音)_二.服务器间通讯 传送门 JavaWeb-SpringBoot(抖音)_三.抖音项 ...
- 不若与众:说说抖音的二创激励计划
七月份抖音与爱奇艺宣布达成合作,如今,抖音二创激励计划落地.9月5日,抖音公布二创激励计划,相当于为这场燎原之火又添了一大把柴. 此次计划是抖音与爱奇艺双方围绕长视频的二创与推广展开的探索,面向影视综 ...
- 音视频开发成长之路—进阶之路3个重要知识点丨WebRTC丨FFmpeg丨SRS流媒体服务器丨C++音视频丨嵌入式音视频
音视频开发成长之路-进阶之路3个重要知识点 视频讲解如下,点击观看: 音视频开发成长之路-进阶之路3个重要知识点丨WebRTC丨FFmpeg丨SRS流媒体服务器丨C++音视频丨嵌入式音视频 音视频高级 ...
- (二)音视频:MediaCodec编码桌面信息 完整Demo 进一步理解H264
(一)音视频:解码H264文件流程 渲染和拿到解码后源数据YUV 完整Demo] (二)音视频:MediaCodec编码桌面信息 完整Demo 进一步理解H264 (三)音视频:解析H264 SPS ...
- WebRTC系列<二> 案例与工具
阅读关于webRTC的其他文章: WebRTC系列<一> 什么是WebRTC? WebRTC系列<二> 案例与工具 ----------------------------- ...
最新文章
- HDU 1877 另一个版本 A+B
- 肏蛋的Loadrunner脚本
- android activity启动流程_1307页!一线大厂Android面试全套真题解析!
- unity 继承会调用start吗_Unity 继承MonoBehaviour脚本 执行顺序 详解
- 深度理解java jvm,深度理解JVM
- 顺丰薪酬体系大曝光,看完感叹:太走心了,不服不行!
- 《南溪的目标检测学习笔记》——基础算子的学习笔记
- cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration
- 实时即未来!Flink Forward Asia 2021 议程正式上线!
- java5、java6、java7、java8的新特性
- 自动寻路之 --AStar算法
- 数字电子技术基础知识点总结_数字电子技术课程线上授课一周之感想 ——信息与控制学院 电工基础教研室 李姿...
- 服务器网站gzip压缩,网站实现Gzip网页压缩的方法与步骤 提高网页浏览速度
- GAN领风骚!Github盘点2021最惊艳38篇AI论文
- 互联网短信网关接口协议
- word自定义多级折叠,自动编码标题【论文框架快速上手】
- ei指什么_什么是ei和ei检索是什么意思
- 机器人genghis_iRobot的智慧家居:从扫地机器人和擦地机器人的联动开始
- header is present on the requested resource. Origin 'null' is therefore not allowed access.
- 栈的压入、弹出序列(Java)
热门文章
- 更好地使用计算机,詤注表示可以帮助您更好地使用计算机的重要信息.pdf
- 二级c语言一本通pdf,《2020年全国计算机等级考试一本通:二级C语言》试题源文件...
- 新生必备清单:不想成为虚度青春的“小透明”,手机应该怎样选?
- [特别公告]RDIFramework.NET微信公众号迁移通知
- 有符号距离场原理及实现源码
- 分类按照拼音第一字母排序显示实现
- linux服务器如何更改ip,Linux修改IP地址,CentOS设置系统IP地址
- Java就业前景分析
- 事务四大特性如何保证
- Gears of Programmer--工作