WebRTC单机版本(通一个浏览器、同一个主机)
这个仅仅是原理 告你信令服务的作用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>p2p webrtc</title>
<style>
.container {
width: 250px;
margin: 100px auto;
padding: 10px 30px;
border-radius: 4px;
border: 1px solid #ebeef5;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
color: #303133;
}
</style>
</head>
<body>
<div class="container">
<p>流程:</p>
<ul>
<li>打开<a href="/answer" target="_blank">接收方页面</a>;</li>
<li>打开<a href="/offer" target="_blank">发起方页面</a>;</li>
<li>确认双方都已建立连接;</li>
<li>发起方点击 start 按钮。</li>
</ul>
</div>
</body>
<script>
window.localStorage.clear();
</script>
</html>
发起端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
<style>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.container {
width: 100%;
display: flex;
display: -webkit-flex;
justify-content: space-around;
padding-top: 20px;
}
.video-box {
position: relative;
width: 800px;
height: 400px;
}
#remote-video {
width: 100%;
height: 100%;
display: block;
object-fit: cover;
border: 1px solid #eee;
background-color: #F2F6FC;
}
#local-video {
position: absolute;
right: 0;
bottom: 0;
width: 240px;
height: 120px;
object-fit: cover;
border: 1px solid #eee;
background-color: #EBEEF5;
}
.start-button {
position: absolute;
left: 50%;
top: 50%;
width: 100px;
display: none;
line-height: 40px;
outline: none;
color: #fff;
background-color: #409eff;
border: none;
border-radius: 4px;
cursor: pointer;
transform: translate(-50%, -50%);
}
.logger {
width: 40%;
padding: 14px;
line-height: 1.5;
color: #4fbf40;
border-radius: 6px;
background-color: #272727;
}
.logger .error {
color: #DD4A68;
}
</style>
</head>
<body>
<div class="container">
<div class="video-box">
<video id="remote-video"></video>
<video id="local-video" muted></video>
<button class="start-button" onclick="startLive()">start</button>
</div>
<div class="logger"></div>
</div>
<script>
const message = {
el: document.querySelector('.logger'),
log(msg) {
this.el.innerHTML += `<span>${new Date().toLocaleTimeString()}:${msg}</span><br/>`;
},
error(msg) {
this.el.innerHTML += `<span class="error">${new Date().toLocaleTimeString()}:${msg}</span><br/>`;
}
};
const localVideo = document.querySelector('#local-video');
const remoteVideo = document.querySelector('#remote-video');
const button = document.querySelector('.start-button');
localVideo.onloadeddata = () => {
message.log('播放本地视频');
localVideo.play();
}
remoteVideo.onloadeddata = () => {
message.log('播放对方视频');
remoteVideo.play();
}
document.title = '发起方';
message.log('信令通道(localStorage)创建中......');
message.log('信令通道创建成功!');
button.style.display = 'block';
window.addEventListener('storage', e => {
if (e.key === 'answer') {
const {type, sdp, iceCandidate} = JSON.parse(e.newValue)
if (type === 'answer') {
peer.setRemoteDescription(new RTCSessionDescription({type, sdp}));
message.log('offer : \ntype answer setRemoteDescription ');
}
}
if (e.key === 'answer_ice') {
const {type, sdp, iceCandidate} = JSON.parse(e.newValue)
if (type === 'answer_ice') {
peer.addIceCandidate(iceCandidate);
message.log('offer : \ntype answer_ice addIceCandidate ');
}
}
})
const PeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
!PeerConnection && message.error('浏览器不支持WebRTC!');
const peer = new PeerConnection();
peer.ontrack = e => {
if (e && e.streams) {
message.log('收到对方音频/视频流数据...');
remoteVideo.srcObject = e.streams[0];
}
};
peer.onicecandidate = e => {
if (e.candidate) {
message.log('搜集并发送候选人');
window.localStorage.setItem('offer_ice', JSON.stringify({
type: `offer_ice`,
iceCandidate: e.candidate
}));
} else {
message.log('候选人收集完成!');
}
};
async function startLive() {
let stream;
try {
message.log('尝试调取本地摄像头/麦克风');
stream = await navigator.mediaDevices.getUserMedia({video: true, audio: true});
message.log('摄像头/麦克风获取成功!');
localVideo.srcObject = stream;
} catch {
message.error('摄像头/麦克风获取失败!');
return;
}
message.log(`------ WebRTC发起方流程开始 ------`);
message.log('将媒体轨道添加到轨道集');
stream.getTracks().forEach(track => {
peer.addTrack(track, stream);
});
window.localStorage.setItem('offer', '')
window.localStorage.setItem('offer_ice', '');
message.log('创建本地SDP');
const offer = await peer.createOffer();
await peer.setLocalDescription(offer);
message.log(`传输发起方本地SDP`, JSON.stringify(offer));
window.localStorage.setItem('offer', JSON.stringify(offer));
}
</script>
</body>
</html>
接受端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
<style>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.container {
width: 100%;
display: flex;
display: -webkit-flex;
justify-content: space-around;
padding-top: 20px;
}
.video-box {
position: relative;
width: 800px;
height: 400px;
}
#remote-video {
width: 100%;
height: 100%;
display: block;
object-fit: cover;
border: 1px solid #eee;
background-color: #F2F6FC;
}
#local-video {
position: absolute;
right: 0;
bottom: 0;
width: 240px;
height: 120px;
object-fit: cover;
border: 1px solid #eee;
background-color: #EBEEF5;
}
.start-button {
position: absolute;
left: 50%;
top: 50%;
width: 100px;
display: none;
line-height: 40px;
outline: none;
color: #fff;
background-color: #409eff;
border: none;
border-radius: 4px;
cursor: pointer;
transform: translate(-50%, -50%);
}
.logger {
width: 40%;
padding: 14px;
line-height: 1.5;
color: #4fbf40;
border-radius: 6px;
background-color: #272727;
}
.logger .error {
color: #DD4A68;
}
</style>
</head>
<body>
<div class="container">
<div class="video-box">
<video id="remote-video"></video>
<video id="local-video" muted></video>
</div>
<div class="logger"></div>
</div>
<script>
const message = {
el: document.querySelector('.logger'),
log(msg) {
this.el.innerHTML += `<span>${new Date().toLocaleTimeString()}:${msg}</span><br/>`;
},
error(msg) {
this.el.innerHTML += `<span class="error">${new Date().toLocaleTimeString()}:${msg}</span><br/>`;
}
};
const localVideo = document.querySelector('#local-video');
const remoteVideo = document.querySelector('#remote-video');
localVideo.onloadeddata = () => {
message.log('播放本地视频');
localVideo.play();
}
remoteVideo.onloadeddata = () => {
message.log('播放对方视频');
remoteVideo.play();
}
document.title = '接收方';
message.log('信令通道(localStorage)创建中......');
message.log('信令通道创建成功!');
window.addEventListener('storage', e => {
if (e.key === 'offer') {
const {type, sdp, iceCandidate} = JSON.parse(e.newValue)
if (type === 'offer') {
startLive(new RTCSessionDescription({type, sdp}));
message.log('answer : \ntype offer startLive ');
}
}
if (e.key === 'offer_ice') {
const {type, sdp, iceCandidate} = JSON.parse(e.newValue)
if (type === 'offer_ice') {
peer.addIceCandidate(iceCandidate);
message.log('answer : \ntype offer_ice addIceCandidate ');
}
}
})
const PeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
!PeerConnection && message.error('浏览器不支持WebRTC!');
const peer = new PeerConnection();
peer.ontrack = e => {
if (e && e.streams) {
message.log('收到对方音频/视频流数据...');
remoteVideo.srcObject = e.streams[0];
}
};
peer.onicecandidate = e => {
if (e.candidate) {
message.log('搜集并发送候选人');
window.localStorage.setItem('answer_ice', JSON.stringify({
type: `answer_ice`,
iceCandidate: e.candidate
}));
} else {
message.log('候选人收集完成!');
}
};
async function startLive(offerSdp) {
let stream;
try {
message.log('尝试调取本地摄像头/麦克风');
stream = await navigator.mediaDevices.getUserMedia({video: true, audio: true});
message.log('摄像头/麦克风获取成功!');
localVideo.srcObject = stream;
} catch {
message.error('摄像头/麦克风获取失败!');
return;
}
message.log(`------ WebRTC接收方流程开始 ------`);
message.log('将媒体轨道添加到轨道集');
stream.getTracks().forEach(track => {
peer.addTrack(track, stream);
});
message.log('接收到发送方SDP');
window.localStorage.setItem('answer', '');
window.localStorage.setItem('answer_ice', '');
await peer.setRemoteDescription(offerSdp);
message.log('创建接收方(应答)SDP');
const answer = await peer.createAnswer();
message.log(`传输接收方(应答)SDP`);
window.localStorage.setItem('answer', JSON.stringify(answer));
await peer.setLocalDescription(answer);
}
</script>
</body>
</html>
本文来自博客园,作者:vx_guanchaoguo0,转载请注明原文链接:https://www.cnblogs.com/guanchaoguo/p/18060932