| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752 |
- const SkyRTC = function () {
- //创建本地流
- let gThat;
- let PeerConnection = (window.PeerConnection || window.webkitPeerConnection00 || window.webkitRTCPeerConnection ||
- window.mozRTCPeerConnection);
- let getUserMedia = (navigator.getUserMedia ||//旧版API
- navigator.mediaDevices.getUserMedia ||//最新的标准API
- navigator.webkitGetUserMedia || //webkit核心浏览器
- navigator.mozGetUserMedia || //firfox浏览器
- navigator.msGetUserMedia
- );
- let nativeRTCIceCandidate = (window.mozRTCIceCandidate || window.RTCIceCandidate);
- let nativeRTCSessionDescription = (window.mozRTCSessionDescription || window.RTCSessionDescription);
- const iceServer = {
- "iceServers": [
- {
- "url":"stun:118.178.91.140:3478"
- },
- {
- "url": "turn:118.178.91.140:3478",
- "username": "iturn",
- "credential": "Inspu9961234,"
- }
- ]
- };
- let packetSize = 1000;
- /* 事件处理器 */
- function EventEmitter() {
- this.events = {};
- }
- //绑定事件函数
- EventEmitter.prototype.on = function (eventName, callback) {
- this.events[eventName] = this.events[eventName] || [];
- this.events[eventName].push(callback);
- };
- //触发事件函数
- EventEmitter.prototype.emit = function (eventName, _) {
- var events = this.events[eventName],
- args = Array.prototype.slice.call(arguments, 1),
- i, m;
- if (!events) {
- return;
- }
- for (i = 0, m = events.length; i < m; i++) {
- events[i].apply(null, args);
- }
- };
- /**********************************************************/
- /* 流及信道建立部分 */
- /**********************************************************/
- /*******************基础部分*********************/
- function skyrtc() {
- //本地media stream
- this.localMediaStream = null;
- //所在房间
- this.room = "";
- //接收文件时用于暂存接收文件
- this.fileData = {};
- //本地WebSocket连接
- this.socket = null;
- //本地socket的id,由后台服务器创建
- this.me = null;
- //保存所有与本地相连的peer connection, 键为socket id,值为PeerConnection类型
- this.peerConnections = {};
- //保存所有与本地连接的socket的id
- this.connections = [];
- //初始时需要构建链接的数目
- this.numStreams = 0;
- //初始时已经连接的数目
- this.initializedStreams = 0;
- //保存所有的data channel,键为socket id,值通过PeerConnection实例的createChannel创建
- this.dataChannels = {};
- //保存所有发文件的data channel及其发文件状态
- this.fileChannels = {};
- //保存所有接受到的文件
- this.receiveFiles = {};
- }
- //继承自事件处理器,提供绑定事件和触发事件的功能
- skyrtc.prototype = new EventEmitter();
- /*************************服务器连接部分***************************/
- skyrtc.prototype.connect = function (server, room) {
- var socket,
- that = this;
- room = room || "";
- socket = this.socket = new WebSocket(server);
- socket.onopen = function () {
- socket.send(JSON.stringify({
- "eventName": "__join",
- "data": {
- "room": room
- }
- }));
- that.emit("socket_opened", socket);
- };
- socket.onmessage = function (message) {
- var json = JSON.parse(message.data);
- if (json.eventName) {
- that.emit(json.eventName, json.data);
- } else {
- that.emit("socket_receive_message", socket, json);
- }
- };
- socket.onerror = function (error) {
- that.emit("socket_error", error, socket);
- };
- socket.onclose = function (data) {
- that.localMediaStream.close();
- var pcs = that.peerConnections;
- for (i = pcs.length; i--;) {
- that.closePeerConnection(pcs[i]);
- }
- that.peerConnections = [];
- that.dataChannels = {};
- that.fileChannels = {};
- that.connections = [];
- that.fileData = {};
- that.emit('socket_closed', socket);
- };
- this.on('_peers', function (data) {
- //获取所有服务器上的
- that.connections = data.connections;
- that.me = data.you;
- that.emit("get_peers", that.connections);
- that.emit('connected', socket);
- });
- this.on("_ice_candidate", function (data) {
- var candidate = new nativeRTCIceCandidate(data);
- var pc = that.peerConnections[data.socketId];
- if (!pc || !pc.remoteDescription.type) {
- //push candidate onto queue...
- console.log("remote not set!")
- }
- pc.addIceCandidate(candidate);
- that.emit('get_ice_candidate', candidate);
- });
- this.on('_new_peer', function (data) {
- that.connections.push(data.socketId);
- var pc = that.createPeerConnection(data.socketId),
- i, m;
- pc.addStream(that.localMediaStream);
- that.emit('new_peer', data.socketId);
- });
- this.on('_remove_peer', function (data) {
- var sendId;
- that.closePeerConnection(that.peerConnections[data.socketId]);
- delete that.peerConnections[data.socketId];
- delete that.dataChannels[data.socketId];
- for (sendId in that.fileChannels[data.socketId]) {
- that.emit("send_file_error", new Error("Connection has been closed"), data.socketId, sendId, that.fileChannels[
- data.socketId][sendId].file);
- }
- delete that.fileChannels[data.socketId];
- that.emit("remove_peer", data.socketId);
- });
- this.on('_offer', function (data) {
- that.receiveOffer(data.socketId, data.sdp);
- that.emit("get_offer", data);
- });
- this.on('_answer', function (data) {
- that.receiveAnswer(data.socketId, data.sdp);
- that.emit('get_answer', data);
- });
- this.on('send_file_error', function (error, socketId, sendId, file) {
- that.cleanSendFile(sendId, socketId);
- });
- this.on('receive_file_error', function (error, sendId) {
- that.cleanReceiveFile(sendId);
- });
- this.on('ready', function () {
- that.createPeerConnections();
- that.addStreams();
- that.addDataChannels();
- that.sendOffers();
- });
- };
- /*************************流处理部分*******************************/
- //访问用户媒体设备的兼容方法
- function getUserMediaFun(constraints, success, error) {
- if (navigator.mediaDevices.getUserMedia) {
- //最新的标准API
- navigator.mediaDevices.getUserMedia(constraints).then(success).catch(error);
- } else if (navigator.webkitGetUserMedia) {
- //webkit核心浏览器
- navigator.webkitGetUserMedia(constraints, success, error)
- } else if (navigator.mozGetUserMedia) {
- //firfox浏览器
- navigator.mozGetUserMedia(constraints, success, error);
- } else if (navigator.getUserMedia) {
- //旧版API
- navigator.getUserMedia(constraints, success, error);
- } else {
- that.emit("stream_create_error", new Error('WebRTC is not yet supported in this browser.'));
- }
- }
- function createStreamSuccess(stream) {
- if (gThat) {
- gThat.localMediaStream = stream;
- gThat.initializedStreams++;
- gThat.emit("stream_created", stream);
- if (gThat.initializedStreams === gThat.numStreams) {
- gThat.emit("ready");
- }
- }
- }
- function createStreamError(error) {
- if (gThat) {
- gThat.emit("stream_create_error", error);
- }
- }
- skyrtc.prototype.createStream = function (options) {
- var that = this;
- gThat = this;
- options.video = !!options.video;
- options.audio = !!options.audio;
- if (getUserMedia) {
- this.numStreams++;
- // 调用用户媒体设备, 访问摄像头
- getUserMediaFun(options, createStreamSuccess, createStreamError);
- } else {
- that.emit("stream_create_error", new Error('WebRTC is not yet supported in this browser.'));
- }
- };
- //将本地流添加到所有的PeerConnection实例中
- skyrtc.prototype.addStreams = function () {
- var i, m,
- stream,
- connection;
- for (connection in this.peerConnections) {
- this.peerConnections[connection].addStream(this.localMediaStream);
- }
- };
- // 将流绑定到video标签上用于输出
- skyrtc.prototype.attachStream = function (stream, domId) {
- var element = document.getElementById(domId);
- if (navigator.mediaDevices.getUserMedia) {
- element.srcObject = stream;
- } else {
- element.src = webkitURL.createObjectURL(stream);
- }
- element.play();
- };
- /***********************信令交换部分*******************************/
- //向所有PeerConnection发送Offer类型信令
- skyrtc.prototype.sendOffers = function () {
- var i, m,
- pc,
- that = this,
- pcCreateOfferCbGen = function (pc, socketId) {
- return function (session_desc) {
- pc.setLocalDescription(session_desc);
- that.socket.send(JSON.stringify({
- "eventName": "__offer",
- "data": {
- "sdp": session_desc,
- "socketId": socketId
- }
- }));
- };
- },
- pcCreateOfferErrorCb = function (error) {
- console.log(error);
- };
- for (i = 0, m = this.connections.length; i < m; i++) {
- pc = this.peerConnections[this.connections[i]];
- pc.createOffer(pcCreateOfferCbGen(pc, this.connections[i]), pcCreateOfferErrorCb);
- }
- };
- //接收到Offer类型信令后作为回应返回answer类型信令
- skyrtc.prototype.receiveOffer = function (socketId, sdp) {
- var pc = this.peerConnections[socketId];
- this.sendAnswer(socketId, sdp);
- };
- //发送answer类型信令
- skyrtc.prototype.sendAnswer = function (socketId, sdp) {
- var pc = this.peerConnections[socketId];
- var that = this;
- pc.setRemoteDescription(new nativeRTCSessionDescription(sdp));
- pc.createAnswer(function (session_desc) {
- pc.setLocalDescription(session_desc);
- that.socket.send(JSON.stringify({
- "eventName": "__answer",
- "data": {
- "socketId": socketId,
- "sdp": session_desc
- }
- }));
- }, function (error) {
- console.log(error);
- });
- };
- //接收到answer类型信令后将对方的session描述写入PeerConnection中
- skyrtc.prototype.receiveAnswer = function (socketId, sdp) {
- var pc = this.peerConnections[socketId];
- pc.setRemoteDescription(new nativeRTCSessionDescription(sdp));
- };
- /***********************点对点连接部分*****************************/
- //创建与其他用户连接的PeerConnections
- skyrtc.prototype.createPeerConnections = function () {
- var i, m;
- for (i = 0, m = this.connections.length; i < m; i++) {
- this.createPeerConnection(this.connections[i]);
- }
- };
- //创建单个PeerConnection
- skyrtc.prototype.createPeerConnection = function (socketId) {
- var that = this;
- var pc = new PeerConnection(iceServer);
- this.peerConnections[socketId] = pc;
- pc.onicecandidate = function (evt) {
- if (evt.candidate)
- that.socket.send(JSON.stringify({
- "eventName": "__ice_candidate",
- "data": {
- "id": evt.candidate.sdpMid,
- "label": evt.candidate.sdpMLineIndex,
- "sdpMLineIndex": evt.candidate.sdpMLineIndex,
- "candidate": evt.candidate.candidate,
- "socketId": socketId
- }
- }));
- that.emit("pc_get_ice_candidate", evt.candidate, socketId, pc);
- };
- pc.onopen = function () {
- that.emit("pc_opened", socketId, pc);
- };
- pc.onaddstream = function (evt) {
- that.emit('pc_add_stream', evt.stream, socketId, pc);
- };
- pc.ondatachannel = function (evt) {
- that.addDataChannel(socketId, evt.channel);
- that.emit('pc_add_data_channel', evt.channel, socketId, pc);
- };
- return pc;
- };
- //关闭PeerConnection连接
- skyrtc.prototype.closePeerConnection = function (pc) {
- if (!pc) return;
- pc.close();
- };
- /***********************数据通道连接部分*****************************/
- //消息广播
- skyrtc.prototype.broadcast = function (message) {
- var socketId;
- for (socketId in this.dataChannels) {
- this.sendMessage(message, socketId);
- }
- };
- //发送消息方法
- skyrtc.prototype.sendMessage = function (message, socketId) {
- if (this.dataChannels[socketId].readyState.toLowerCase() === 'open') {
- this.dataChannels[socketId].send(JSON.stringify({
- type: "__msg",
- data: message
- }));
- }
- };
- //对所有的PeerConnections创建Data channel
- skyrtc.prototype.addDataChannels = function () {
- var connection;
- for (connection in this.peerConnections) {
- this.createDataChannel(connection);
- }
- };
- //对某一个PeerConnection创建Data channel
- skyrtc.prototype.createDataChannel = function (socketId, label) {
- var pc, key, channel;
- pc = this.peerConnections[socketId];
- if (!socketId) {
- this.emit("data_channel_create_error", socketId, new Error("attempt to create data channel without socket id"));
- }
- if (!(pc instanceof PeerConnection)) {
- this.emit("data_channel_create_error", socketId, new Error("attempt to create data channel without peerConnection"));
- }
- try {
- channel = pc.createDataChannel(label);
- } catch (error) {
- this.emit("data_channel_create_error", socketId, error);
- }
- return this.addDataChannel(socketId, channel);
- };
- //为Data channel绑定相应的事件回调函数
- skyrtc.prototype.addDataChannel = function (socketId, channel) {
- var that = this;
- channel.onopen = function () {
- that.emit('data_channel_opened', channel, socketId);
- };
- channel.onclose = function (event) {
- delete that.dataChannels[socketId];
- that.emit('data_channel_closed', channel, socketId);
- };
- channel.onmessage = function (message) {
- var json;
- json = JSON.parse(message.data);
- if (json.type === '__file') {
- /*that.receiveFileChunk(json);*/
- that.parseFilePacket(json, socketId);
- } else {
- that.emit('data_channel_message', channel, socketId, json.data);
- }
- };
- channel.onerror = function (err) {
- that.emit('data_channel_error', channel, socketId, err);
- };
- this.dataChannels[socketId] = channel;
- return channel;
- };
- /**********************************************************/
- /* */
- /* 文件传输 */
- /* */
- /**********************************************************/
- /************************公有部分************************/
- //解析Data channel上的文件类型包,来确定信令类型
- skyrtc.prototype.parseFilePacket = function (json, socketId) {
- var signal = json.signal,
- that = this;
- if (signal === 'ask') {
- that.receiveFileAsk(json.sendId, json.name, json.size, socketId);
- } else if (signal === 'accept') {
- that.receiveFileAccept(json.sendId, socketId);
- } else if (signal === 'refuse') {
- that.receiveFileRefuse(json.sendId, socketId);
- } else if (signal === 'chunk') {
- that.receiveFileChunk(json.data, json.sendId, socketId, json.last, json.percent);
- } else if (signal === 'close') {
- //TODO
- }
- };
- /***********************发送者部分***********************/
- //通过Dtata channel向房间内所有其他用户广播文件
- skyrtc.prototype.shareFile = function (dom) {
- var socketId,
- that = this;
- for (socketId in that.dataChannels) {
- that.sendFile(dom, socketId);
- }
- };
- //向某一单个用户发送文件
- skyrtc.prototype.sendFile = function (dom, socketId) {
- var that = this,
- file,
- reader,
- fileToSend,
- sendId;
- if (typeof dom === 'string') {
- dom = document.getElementById(dom);
- }
- if (!dom) {
- that.emit("send_file_error", new Error("Can not find dom while sending file"), socketId);
- return;
- }
- if (!dom.files || !dom.files[0]) {
- that.emit("send_file_error", new Error("No file need to be sended"), socketId);
- return;
- }
- file = dom.files[0];
- that.fileChannels[socketId] = that.fileChannels[socketId] || {};
- sendId = that.getRandomString();
- fileToSend = {
- file: file,
- state: "ask"
- };
- that.fileChannels[socketId][sendId] = fileToSend;
- that.sendAsk(socketId, sendId, fileToSend);
- that.emit("send_file", sendId, socketId, file);
- };
- //发送多个文件的碎片
- skyrtc.prototype.sendFileChunks = function () {
- var socketId,
- sendId,
- that = this,
- nextTick = false;
- for (socketId in that.fileChannels) {
- for (sendId in that.fileChannels[socketId]) {
- if (that.fileChannels[socketId][sendId].state === "send") {
- nextTick = true;
- that.sendFileChunk(socketId, sendId);
- }
- }
- }
- if (nextTick) {
- setTimeout(function () {
- that.sendFileChunks();
- }, 10);
- }
- };
- //发送某个文件的碎片
- skyrtc.prototype.sendFileChunk = function (socketId, sendId) {
- var that = this,
- fileToSend = that.fileChannels[socketId][sendId],
- packet = {
- type: "__file",
- signal: "chunk",
- sendId: sendId
- },
- channel;
- fileToSend.sendedPackets++;
- fileToSend.packetsToSend--;
- if (fileToSend.fileData.length > packetSize) {
- packet.last = false;
- packet.data = fileToSend.fileData.slice(0, packetSize);
- packet.percent = fileToSend.sendedPackets / fileToSend.allPackets * 100;
- that.emit("send_file_chunk", sendId, socketId, fileToSend.sendedPackets / fileToSend.allPackets * 100, fileToSend.file);
- } else {
- packet.data = fileToSend.fileData;
- packet.last = true;
- fileToSend.state = "end";
- that.emit("sended_file", sendId, socketId, fileToSend.file);
- that.cleanSendFile(sendId, socketId);
- }
- channel = that.dataChannels[socketId];
- if (!channel) {
- that.emit("send_file_error", new Error("Channel has been destoried"), socketId, sendId, fileToSend.file);
- return;
- }
- channel.send(JSON.stringify(packet));
- fileToSend.fileData = fileToSend.fileData.slice(packet.data.length);
- };
- //发送文件请求后若对方同意接受,开始传输
- skyrtc.prototype.receiveFileAccept = function (sendId, socketId) {
- var that = this,
- fileToSend,
- reader,
- initSending = function (event, text) {
- fileToSend.state = "send";
- fileToSend.fileData = event.target.result;
- fileToSend.sendedPackets = 0;
- fileToSend.packetsToSend = fileToSend.allPackets = parseInt(fileToSend.fileData.length / packetSize, 10);
- that.sendFileChunks();
- };
- fileToSend = that.fileChannels[socketId][sendId];
- reader = new window.FileReader(fileToSend.file);
- reader.readAsDataURL(fileToSend.file);
- reader.onload = initSending;
- that.emit("send_file_accepted", sendId, socketId, that.fileChannels[socketId][sendId].file);
- };
- //发送文件请求后若对方拒绝接受,清除掉本地的文件信息
- skyrtc.prototype.receiveFileRefuse = function (sendId, socketId) {
- var that = this;
- that.fileChannels[socketId][sendId].state = "refused";
- that.emit("send_file_refused", sendId, socketId, that.fileChannels[socketId][sendId].file);
- that.cleanSendFile(sendId, socketId);
- };
- //清除发送文件缓存
- skyrtc.prototype.cleanSendFile = function (sendId, socketId) {
- var that = this;
- delete that.fileChannels[socketId][sendId];
- };
- //发送文件请求
- skyrtc.prototype.sendAsk = function (socketId, sendId, fileToSend) {
- var that = this,
- channel = that.dataChannels[socketId],
- packet;
- if (!channel) {
- that.emit("send_file_error", new Error("Channel has been closed"), socketId, sendId, fileToSend.file);
- }
- packet = {
- name: fileToSend.file.name,
- size: fileToSend.file.size,
- sendId: sendId,
- type: "__file",
- signal: "ask"
- };
- channel.send(JSON.stringify(packet));
- };
- //获得随机字符串来生成文件发送ID
- skyrtc.prototype.getRandomString = function () {
- return (Math.random() * new Date().getTime()).toString(36).toUpperCase().replace(/\./g, '-');
- };
- /***********************接收者部分***********************/
- //接收到文件碎片
- skyrtc.prototype.receiveFileChunk = function (data, sendId, socketId, last, percent) {
- var that = this,
- fileInfo = that.receiveFiles[sendId];
- if (!fileInfo.data) {
- fileInfo.state = "receive";
- fileInfo.data = "";
- }
- fileInfo.data = fileInfo.data || "";
- fileInfo.data += data;
- if (last) {
- fileInfo.state = "end";
- that.getTransferedFile(sendId);
- } else {
- that.emit("receive_file_chunk", sendId, socketId, fileInfo.name, percent);
- }
- };
- //接收到所有文件碎片后将其组合成一个完整的文件并自动下载
- skyrtc.prototype.getTransferedFile = function (sendId) {
- var that = this,
- fileInfo = that.receiveFiles[sendId],
- hyperlink = document.createElement("a"),
- mouseEvent = new MouseEvent('click', {
- view: window,
- bubbles: true,
- cancelable: true
- });
- hyperlink.href = fileInfo.data;
- hyperlink.target = '_blank';
- hyperlink.download = fileInfo.name || dataURL;
- hyperlink.dispatchEvent(mouseEvent);
- (window.URL || window.webkitURL).revokeObjectURL(hyperlink.href);
- that.emit("receive_file", sendId, fileInfo.socketId, fileInfo.name);
- that.cleanReceiveFile(sendId);
- };
- //接收到发送文件请求后记录文件信息
- skyrtc.prototype.receiveFileAsk = function (sendId, fileName, fileSize, socketId) {
- var that = this;
- that.receiveFiles[sendId] = {
- socketId: socketId,
- state: "ask",
- name: fileName,
- size: fileSize
- };
- that.emit("receive_file_ask", sendId, socketId, fileName, fileSize);
- };
- //发送同意接收文件信令
- skyrtc.prototype.sendFileAccept = function (sendId) {
- var that = this,
- fileInfo = that.receiveFiles[sendId],
- channel = that.dataChannels[fileInfo.socketId],
- packet;
- if (!channel) {
- that.emit("receive_file_error", new Error("Channel has been destoried"), sendId, socketId);
- }
- packet = {
- type: "__file",
- signal: "accept",
- sendId: sendId
- };
- channel.send(JSON.stringify(packet));
- };
- //发送拒绝接受文件信令
- skyrtc.prototype.sendFileRefuse = function (sendId) {
- var that = this,
- fileInfo = that.receiveFiles[sendId],
- channel = that.dataChannels[fileInfo.socketId],
- packet;
- if (!channel) {
- that.emit("receive_file_error", new Error("Channel has been destoried"), sendId, socketId);
- }
- packet = {
- type: "__file",
- signal: "refuse",
- sendId: sendId
- };
- channel.send(JSON.stringify(packet));
- that.cleanReceiveFile(sendId);
- };
- //清除接受文件缓存
- skyrtc.prototype.cleanReceiveFile = function (sendId) {
- var that = this;
- delete that.receiveFiles[sendId];
- };
- return new skyrtc();
- };
|