simple-peer 视频聊天之main.js

‘use strict’;
console.log(‘初始化按钮’);
const callButton = document.getElementById(‘callButton’);
const hangupButton = document.getElementById(‘hangupButton’);
const sendButton = document.getElementById(‘sendButton’);
const cancleButton = document.getElementById(‘cancleButton’);
const progress = document.getElementById(‘progress’);
const progress_bar = document.getElementById(‘progress_bar’);
const pingdom = document.getElementById(‘pingdom’);
const speedDom = document.getElementById(‘speedDom’);
callButton.disabled = false;
hangupButton.disabled = true;
callButton.addEventListener(‘click’, call);
hangupButton.addEventListener(‘click’, hangup);
sendButton.addEventListener(‘click’, sendFile);
cancleButton.addEventListener(‘click’, cancleFile);
console.log(‘初始化变量’);
let startTime;
const localVideo = document.getElementById(‘localVideo’);
const remoteVideo = document.getElementById(‘remoteVideo’);

localVideo.addEventListener(‘loadedmetadata’, function() {
console.log(Local video videoWidth: ${this.videoWidth}px, videoHeight: ${this.videoHeight}px);
});

remoteVideo.addEventListener(‘loadedmetadata’, function() {
console.log(Remote video videoWidth: ${this.videoWidth}px, videoHeight: ${this.videoHeight}px);
});

remoteVideo.addEventListener(‘resize’, () => {
console.log(Remote video size changed to ${remoteVideo.videoWidth}x${remoteVideo.videoHeight});
if (startTime) {
const elapsedTime = window.performance.now() - startTime;
console.log('Setup time: ’ + elapsedTime.toFixed(3) + ‘ms’);
startTime = null;
}
});
function cancleFile(){
if (fileReader && fileReader.readyState === 1) {
console.log(‘Abort read!’);
fileReader.abort();
clearInterval(speedClock);
speed = 0;
speedDom.innerHTML = ‘’;
progress.style.display = ‘none’;
}
}
function sendFile(){
if(!pc){
alert(‘请先建立连接’);
return;
}
const file = fileSource.files[0];
if (file.size === 0) {
alert(‘File is empty, please select a non-empty file’);
return;
}
progress.style.display = ‘block’;
fileReader = new FileReader();
const chunkSize = 16354;
let offset = 0;
fileReader.addEventListener(‘error’, error => console.error(‘Error reading file:’, error));
fileReader.addEventListener(‘abort’, event => console.log(‘File reading aborted:’, event));
fileReader.addEventListener(‘load’, e => {
console.log('FileRead.onload ', e);
console.log(‘Thread ‘, _thread<thread);
// sendChannel.send(e.target.result);
if(_thread<thread){
_thread++;
var data = new Uint8Array(e.target.result.byteLength+1);
var uint8 = new Uint8Array(e.target.result);
data.set([3]);
data.set(uint8,1)
pc.send(data);
speed += e.target.result.byteLength;
offset += e.target.result.byteLength;
let pg = Math.round(offset10000/file.size)/100;
// console.log(‘progress:’+Math.round(offset
10000/file.size)/100);
progress_bar.style.width = pg +’%’;
// sendProgress.value = offset;
if (offset < file.size) {
readSlice(offset);
}else{
var act = new Uint8Array([5]);
var data = new TextEncoder().encode(file.name);
var _data = new Uint8Array(act.byteLength+data.byteLength);
_data.set(act);
_data.set(data,1);
pc.send(_data);
}
}else{
setTimeout(function(){
readSlice(offset);
},200)
}
});
const readSlice = o => {
console.log('readSlice ‘, o);
const slice = file.slice(offset, o + chunkSize);
fileReader.readAsArrayBuffer(slice);
};
readSlice(0);
speedClock = setInterval(function(){
speedDom.innerHTML = speed/1024/1024 + ’ Mb/s’;
speed=0;
},1000)
}
function random(randomFlag, min, max){
var str = “”,
range = min,
arr = [‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’, ‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘g’, ‘h’, ‘i’, ‘j’, ‘k’, ‘l’, ‘m’, ‘n’, ‘o’, ‘p’, ‘q’, ‘r’, ‘s’, ‘t’, ‘u’, ‘v’, ‘w’, ‘x’, ‘y’, ‘z’, ‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’, ‘G’, ‘H’, ‘I’, ‘J’, ‘K’, ‘L’, ‘M’, ‘N’, ‘O’, ‘P’, ‘Q’, ‘R’, ‘S’, ‘T’, ‘U’, ‘V’, ‘W’, ‘X’, ‘Y’, ‘Z’];
// 随机产生
if(randomFlag){
range = Math.round(Math.random() * (max-min)) + min;
}
for(var i=0; i<range; i++){
var pos = Math.round(Math.random() * (arr.length-1));
str += arr[pos];
}
return str;
}
let localStream = false;
let pc;
let signalWS;
let peer_id = ‘rtc’+random(false,16);
let to_peer_id;
let muted = false;
let PingClock;
let speedClock;
let fileReader;
let speed = 0;
let thread = 3;
let _thread = 0;
let blobs = [];
myPeerId.innerHTML = peer_id;

const audioInputSelect = document.querySelector(‘select#audioSource’);
const audioOutputSelect = document.querySelector(‘select#audioOutput’);
const videoSelect = document.querySelector(‘select#videoSource’);
const selectors = [audioInputSelect, audioOutputSelect, videoSelect];

async function initStream() {
var reconnect = false;
if (localStream) {
localStream.getTracks().forEach(track => {
track.stop();
});
if(pc){
reconnect = true;
pc.destroy();
pc = null;
}
}
// if(fileSource.value){
// var file = fileSource.files[0];
// var type = file.type;
// var canPlay = localVideo.canPlayType(type);
// if (canPlay === ‘’) canPlay = ‘no’
// var isError = canPlay === ‘no’
// var URL = window.URL || window.webkitURL;
// if(!isError){
// var fileURL = URL.createObjectURL(file)
// localVideo.src = fileURL;
// localStream = localVideo.captureStream();
// }
// if(reconnect){
// initP2P(true);
// }
// return;
// }
const audioSource = audioInputSelect.value;
const videoSource = videoSelect.value;
const constraints = {
audio: audioSource==‘no’ ? false : ({deviceId: audioSource ? {exact: audioSource} : undefined,muted:muted}),
video: videoSource==‘no’ ? false : ({deviceId: videoSource ? {exact: videoSource} : undefined})
};
console.log(‘请求获取本地摄像头和麦克风权限’);
if(audioSource==‘no’ && videoSource==‘no’){
localStream = false;
if(reconnect){
initP2P(true);
}
return;
}
try {
navigator.mediaDevices.getUserMedia(constraints).then(gotStream).then(gotDevices).then(function(){
if(reconnect){
initP2P(true);
}
}).catch(handleError);
console.log(‘获取摄像头/麦克风权限成功’);
} catch (e) {
alert(获取摄像头/麦克风权限失败);
console.log(e);
}
}
function gotStream(stream){
localVideo.srcObject = stream;
localStream = stream;
return navigator.mediaDevices.enumerateDevices();
}
function gotDevices(deviceInfos) {
console.log(‘获取设备’);
const values = selectors.map(select => select.value);
selectors.forEach(select => {
while (select.firstChild) {
select.removeChild(select.firstChild);
}
});
selectors.forEach((select) => {
const option = document.createElement(‘option’);
option.text = ‘关闭’;
option.value = ‘no’;
select.appendChild(option);
});

for (let i = 0; i !== deviceInfos.length; ++i) {
const deviceInfo = deviceInfos[i];
const option = document.createElement(‘option’);
option.value = deviceInfo.deviceId;
if (deviceInfo.kind === ‘audioinput’) {
option.text = deviceInfo.label || 麦克风 ${audioInputSelect.length + 1};
audioInputSelect.appendChild(option);
} else if (deviceInfo.kind === ‘audiooutput’) {
option.text = deviceInfo.label || 扬声器 ${audioOutputSelect.length + 1};
audioOutputSelect.appendChild(option);
} else if (deviceInfo.kind === ‘videoinput’) {
option.text = deviceInfo.label || 摄像头 ${videoSelect.length + 1};
videoSelect.appendChild(option);
} else {
console.log('Some other kind of source/device: ', deviceInfo);
}
}

if(audioInputSelect.children.length>=2){
audioInputSelect.children[1].selected = true;
}
if(audioOutputSelect.children.length>=2){
audioOutputSelect.children[1].selected = true;
}
if(videoSelect.children.length>=2){
videoSelect.children[1].selected = true;
}

selectors.forEach((select, selectorIndex) => {
if (Array.prototype.slice.call(select.childNodes).some(n => n.value === values[selectorIndex])) {
select.value = values[selectorIndex];
}
});
}
function changeAudioDestination() {
const audioDestination = audioOutputSelect.value;
attachSinkId(videoElement, audioDestination);
}
function attachSinkId(element, sinkId) {
if (typeof element.sinkId !== ‘undefined’) {
element.setSinkId(sinkId)
.then(() => {
console.log(Success, audio output device attached: ${sinkId});
})
.catch(error => {
let errorMessage = error;
if (error.name === ‘SecurityError’) {
errorMessage = You need to use HTTPS for selecting audio output device: ${error};
}
console.error(errorMessage);
// Jump back to first output device in the list as it’s the default.
audioOutputSelect.selectedIndex = 0;
});
} else {
console.warn(‘Browser does not support output device selection.’);
}
}
function handleError(e){
console.error(e);
}
function initSignal(){
//建立信令服务器连接
signalWS = new WebSocket(‘wss://signal.klink.tech/?id=’+peer_id);

signalWS.onopen = function(evt){
//信令服务器建立连接成功
}
signalWS.onmessage = function(evt){
//信令服务器接受信号
// console.log(evt);
onMessage(JSON.parse(evt.data));
}
signalWS.onclose = function(evt){
//信令服务器关闭
signalWS = undefined;
}
}
function onMessage(data){
switch(data.action){
case ‘signal’:
if(!data.data){
return;
}
if(!pc){
to_peer_id = data.from_peer_id;
initP2P(false);
}
pc.signal(data.data);
break;
}
}
function sendMessage(obj) {
signalWS.send(JSON.stringify(obj));
}
function initP2P(initiator){
console.log(‘创建PeerConection对象’+initiator);
pc = new SimplePeer({
initiator: initiator,
stream: localStream,
config: {
iceServers: [{ urls: ‘turn:yun.zdz.la’,username:“linkec”,credential:“linkec” }]
},
sdpTransform: function (sdp) {
let modifier = ‘AS’;
let bandwidth = 500;
sdp = sdp.replace(/m=video (.)\r\nc=IN (.)\r\n/, ‘m=video $1\r\nc=IN $2\r\nb=’ + modifier + ‘:’ + bandwidth + ‘\r\n’);
return sdp
}
})
pc.on(‘signal’, function (data) {
console.log(‘发送SDP’);
sendMessage({
action: “signal”,
data: data,
peer_id: peer_id,
to_peer_id: to_peer_id
});
})
//本地拨打纯语音,无法接收对面的视频
//对面拨打纯语音,可以正常接收
pc.on(‘stream’, function (stream) {
console.log(‘设置远程流’);
console.log(stream.getTracks());
remoteVideo.srcObject = stream;
})
pc.on(‘connect’,function(){
console.log(‘更改按钮状态’);
callButton.disabled = true;
hangupButton.disabled = false;
PingClock = setInterval(function(){
var ts = new Date().getTime().toString();
var uint8 = new TextEncoder().encode(ts);
var data = new Uint8Array(uint8.length+1);
//设置动作类型
data.set([1]);
//设置时间戳
data.set(uint8,1);
pc.send(data);
},1000)
})
pc.on(‘data’,function(data){
try {
var act = data[0];
if(act>2){
console.log(data);
}
var _data = new Uint8Array(data.buffer,1);
var _data = new Uint8Array(_data);
switch(act){
//ping
case 1:
data[0] = 2;
pc.send(data);
break;
//pong
case 2:
var ts = Uint8ArrayToString(_data);
pingdom.innerHTML = (new Date().getTime()-ts)+ ’ ms’ ;
break;
//接收文件
case 3:
// _data 为文件数据
console.log(_data);
blobs.push(new Blob([_data.buffer]))
var data = new Uint8Array([4]);
pc.send(data);
break;
//接收文件OK
case 4:
// _data 为文件数据
_thread–;
break;
//生成下载文件
case 5:
//合并文件
var fileblob = new Blob(blobs);
var link = document.createElement(‘a’);
link.href = window.URL.createObjectURL(fileblob);
link.download = new TextDecoder().decode(_data);
link.innerHTML = ‘下载’+link.download;
speedDom.appendChild(link);
// link.click();
blobs = [];
// new File()
break;
}
// console.log(data);
// data = JSON.parse(Uint8ArrayToString(data));
// switch(data.act){
// case’ping’:
// var data = {
// ‘act’:‘pong’,
// ‘ts’: data.ts
// };
// pc.send(JSON.stringify(data));
// break;
// case’pong’:
// pingdom.innerHTML = (new Date().getTime()-data.ts)+ ’ ms’ ;
// break;
// }
} catch (error) {
handleError(error);
}
})
pc.on(‘close’,function(){
if(!hangupButton.disabled){
hangup()
}
})
pc.on(‘error’,function(e){
hangup();
})
pc.on(‘track’, function (track, stream) {
console.log(track);
console.log(stream);
})
console.log(‘创建PeerConection对象->ok’);
}
function Uint8ArrayToString(fileData){
var dataString = “”;
for (var i = 0; i < fileData.length; i++) {
dataString += String.fromCharCode(fileData[i]);
}
return dataString
}
async function call() {
console.log(‘更改按钮状态’);
callButton.disabled = true;
hangupButton.disabled = false;
to_peer_id = to_peer_id_input.value;
console.log(‘请求与’+to_peer_id+‘通话’);
initP2P(true);
}
function hangup() {
console.log(‘Ending call’);
hangupButton.disabled = true;
callButton.disabled = false;
clearInterval(PingClock);
pc.destroy();
pc = null;
}
initSignal();
navigator.mediaDevices.enumerateDevices().then(gotDevices).then(initStream).catch(handleError);
videoSelect.onchange = initStream;
audioInputSelect.onchange = initStream;
// fileSource.onchange = initStream;
audioOutputSelect.onchange = changeAudioDestination;

猜你喜欢

转载自blog.csdn.net/weixin_41612889/article/details/89102932