bugfix
This commit is contained in:
parent
8b39dfd3e5
commit
b28d506522
@ -47,6 +47,18 @@ dispatch_event:
|
|||||||
params:
|
params:
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
bricks.uuid = window.crypto.randomUUID;
|
||||||
|
|
||||||
|
bricks.deviceid = function(appname){
|
||||||
|
var deviceid = appname + 'deviceid';
|
||||||
|
var id = localStorage.getItem(deviceid);
|
||||||
|
if (!id){
|
||||||
|
id = bricks.uuid();
|
||||||
|
localStorage.setItem(deviceid, id);
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
bricks.str2data = function(s, d){
|
bricks.str2data = function(s, d){
|
||||||
/* fmt like
|
/* fmt like
|
||||||
'my name is ${name}, ${age:type}'
|
'my name is ${name}, ${age:type}'
|
||||||
@ -427,6 +439,7 @@ bricks.App = class extends bricks.Layout {
|
|||||||
constructor(opts){
|
constructor(opts){
|
||||||
/*
|
/*
|
||||||
opts = {
|
opts = {
|
||||||
|
appname:
|
||||||
login_url:
|
login_url:
|
||||||
"charsize:
|
"charsize:
|
||||||
"language":
|
"language":
|
||||||
@ -444,6 +457,7 @@ bricks.App = class extends bricks.Layout {
|
|||||||
super(opts);
|
super(opts);
|
||||||
bricks.app = this;
|
bricks.app = this;
|
||||||
bricks.Body = this;
|
bricks.Body = this;
|
||||||
|
this.deviceid = bricks.deviceid(opts.appname || 'appname');
|
||||||
this.login_url = opts.login_url;
|
this.login_url = opts.login_url;
|
||||||
this.charsize = this.opts.charsize || 20;
|
this.charsize = this.opts.charsize || 20;
|
||||||
this.keyevent_blocked = false;
|
this.keyevent_blocked = false;
|
||||||
|
272
bricks/rtc.js
272
bricks/rtc.js
@ -1,16 +1,33 @@
|
|||||||
var bricks = window.bricks || {}
|
var bricks = window.bricks || {}
|
||||||
|
|
||||||
|
bricks.VideoBox = class extends bricks.JsWidget {
|
||||||
|
create(){
|
||||||
|
this.dom_element = this._create('video');
|
||||||
|
this.set_attribute('autoplay', true);
|
||||||
|
this.set_attribute('muted', true);
|
||||||
|
}
|
||||||
|
get_stream(){
|
||||||
|
return this.stream;
|
||||||
|
}
|
||||||
|
set_stream(stream){
|
||||||
|
this.stream = stream
|
||||||
|
this.dom_element.srcObject = this.stream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bricks.Signaling = class {
|
bricks.Signaling = class {
|
||||||
/*
|
/*
|
||||||
{
|
{
|
||||||
signaling_url:
|
signaling_url:
|
||||||
info:
|
info:
|
||||||
multiple:false
|
multiple:false
|
||||||
|
connect_opts:
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
constructor(opts){
|
constructor(opts){
|
||||||
this.signaling_url = opts.signaling_url;
|
this.signaling_url = opts.signaling_url;
|
||||||
this.info = opts.info;
|
this.info = opts.info;
|
||||||
|
this.connect_opts = opts.connect_opts;
|
||||||
this.peers = [];
|
this.peers = [];
|
||||||
this.sessions = {};
|
this.sessions = {};
|
||||||
this.handlers = {};
|
this.handlers = {};
|
||||||
@ -37,12 +54,21 @@ bricks.Signaling = class {
|
|||||||
async signaling_recvdata(event){
|
async signaling_recvdata(event){
|
||||||
var datapkg = JSON.parse(event.data);
|
var datapkg = JSON.parse(event.data);
|
||||||
var data = datapkg.data;
|
var data = datapkg.data;
|
||||||
var name = data.sessionid
|
if (data.session) {
|
||||||
var handler = this.handlers.get(name)
|
var sessionid = data.session.sessionid;
|
||||||
|
var sessiontype = data.session.sessiontype;
|
||||||
|
var handler = this.handlers.get(sessionid)
|
||||||
if (!handler){
|
if (!handler){
|
||||||
handler = this.recvdata_handler.bind(this)
|
var k = this.sessionhandlers.get(sessiontype);
|
||||||
|
if (!k){
|
||||||
|
throw 'recvdata_handler() exception(' + sessiontype + ')';
|
||||||
|
}
|
||||||
|
var handler = new k(this, data.session, this.connect_opts);
|
||||||
}
|
}
|
||||||
await handler(data);
|
await handler(data);
|
||||||
|
} else {
|
||||||
|
await this.recvdata_handler(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
add_handler(key, handler){
|
add_handler(key, handler){
|
||||||
this.handlers[key] = handler;
|
this.handlers[key] = handler;
|
||||||
@ -62,23 +88,25 @@ bricks.Signaling = class {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (data.type == 'new_session'){
|
|
||||||
}
|
}
|
||||||
}
|
new_session(sessiontype, peer){
|
||||||
new_session(sessiontype, opts){
|
|
||||||
var k = this.sessionhandlers.get(sessiontype);
|
var k = this.sessionhandlers.get(sessiontype);
|
||||||
if (!k){
|
if (!k){
|
||||||
throw 'new_session() exception(' + sessiontype + ')';
|
throw 'new_session() exception(' + sessiontype + ')';
|
||||||
}
|
}
|
||||||
|
var sessionid = bricks.uuid();
|
||||||
|
var session = {
|
||||||
|
sessiontype:sessiontype,
|
||||||
|
sessionid:sessionid
|
||||||
|
}
|
||||||
var d = {
|
var d = {
|
||||||
type:'new_session',
|
type:'new_session',
|
||||||
session: {
|
session: session
|
||||||
sessiontype:sessiontype,
|
|
||||||
sessionid:bricks.uuid()
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
this.send_data(d);
|
this.send_data(d);
|
||||||
var h = new k(this, sessiontype, opts);
|
opts = this.connect_options.copy();
|
||||||
|
opts.peer_info = peer;
|
||||||
|
var h = new k(this, session, this.connect_opts);
|
||||||
this.add_handler(sessionid, h.recvdata_handler);
|
this.add_handler(sessionid, h.recvdata_handler);
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
@ -105,21 +133,221 @@ bricks.Signaling = class {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bricks.RTCSession = class extends bricks.VBox {
|
bricks.RTCP2PConnect = class {
|
||||||
|
/*
|
||||||
|
opts:{
|
||||||
|
ice_servers:
|
||||||
|
peer_info:
|
||||||
|
auto_callaccept: true or false
|
||||||
|
media_options: { video:trur or false, audio:true or false }
|
||||||
|
data_connect: true or false
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
constructor(signaling, session, opts){
|
||||||
|
this.signaling = signaling;
|
||||||
|
this.session = session;
|
||||||
|
this.requester = false;
|
||||||
|
this.opts = opts;
|
||||||
|
this.peers = {};
|
||||||
|
this.signal_handlers = {};
|
||||||
|
this.localStream = null;
|
||||||
|
this.localVideo = null;
|
||||||
|
this.add_handler('sessioncreated', this.h_sessioncreated.bind(this));
|
||||||
|
this.add_handler('callrequest', this.h_callrequest.bind(this));
|
||||||
|
this.add_handler('callaccepted', this.h_callaccepted.bind(this));
|
||||||
|
this.add_handler('offer', this.h_offer.bind(this));
|
||||||
|
this.add_handler('answer', this.h_answer.bind(this));
|
||||||
|
this.add_handler('icecandidate', this.h_icecandidate.bind(this));
|
||||||
|
this.add_handler('sessionquit', this.h_sessionquit.bind(this));
|
||||||
}
|
}
|
||||||
bricks.VideoBox = class extends bricks.JsWidget {
|
|
||||||
create(){
|
add_handler(type, f):
|
||||||
this.dom_element = this._create('video');
|
this.signal_handlers[type] = f;
|
||||||
this.set_attribute('autoplay', true);
|
}
|
||||||
this.set_attribute('muted', true);
|
async p2pconnect(peer){
|
||||||
|
await this.getLocalStream();
|
||||||
|
await this.createPeerConnection(peer);
|
||||||
|
}
|
||||||
|
async h_sessioncreated(data){
|
||||||
|
if (this.opts.peer_info){
|
||||||
|
var d = {
|
||||||
|
type:'callrequest',
|
||||||
|
msgto:this.opts.peer_info
|
||||||
|
}
|
||||||
|
this.signaling_send(d);
|
||||||
|
this.requester = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async h_callrequest(data){
|
||||||
|
if (this.opts.auto_callaccept){
|
||||||
|
var d = {
|
||||||
|
type:'callaccepted',
|
||||||
|
msgto:data.msgfrom
|
||||||
|
};
|
||||||
|
this.signaling_send(d);
|
||||||
|
await this.p2pconnect(data.msgfrom);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.dispatch('callrequest', data.msgfrom);
|
||||||
|
}
|
||||||
|
async h_callaccepted(data){
|
||||||
|
await this.p2pconnect(data.msgfrom);
|
||||||
|
await this.send_offer(data.msgfrom);
|
||||||
|
}
|
||||||
|
async h_offer(data){
|
||||||
|
var pc = this.peers[data.msgfrom.id].pc;
|
||||||
|
var offer = new RTCSessionDescription(data.offer);
|
||||||
|
await pc.setRemoteDescription(offer);
|
||||||
|
var answer = await pc.createAnswer();
|
||||||
|
await pc.setLocalDescription(answer);
|
||||||
|
this.signal_send({
|
||||||
|
type:'answer',
|
||||||
|
answer:pc.localDescription,
|
||||||
|
msgto:data.msgfrom
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async h_answer(data){
|
||||||
|
var desc = new RTCSessionDescription(data.answer);
|
||||||
|
var pc = this.peers[data.msgfrom.id].pc;
|
||||||
|
await pc.setRemoteDescription(desc);
|
||||||
|
}
|
||||||
|
async h_icecandidate(data){
|
||||||
|
var candidate = new RTCIceCandidate(data.candidate);
|
||||||
|
var pc = this.peers[data.msgfrom.id].pc;
|
||||||
|
await pc.addIceCandidate(candidate);
|
||||||
|
}
|
||||||
|
async h_sessionquit(data){
|
||||||
|
var pc = self.peers[data.msgfrom.id].pc;
|
||||||
|
pc.close();
|
||||||
|
}
|
||||||
|
async send_offer(peer){
|
||||||
|
pc = this.peers[peer.id].pc;
|
||||||
|
var offer = await pc.createOffer();
|
||||||
|
await pc.setLocalDescription(offer);
|
||||||
|
var d = {
|
||||||
|
type:'offer',
|
||||||
|
offer:pc.localDescription,
|
||||||
|
msgto:peer
|
||||||
|
};
|
||||||
|
this.signal_send(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
send_candidate(peer, event){
|
||||||
|
if (event.candidate) {
|
||||||
|
var candidate = event.candidate;
|
||||||
|
this.signaling_send({
|
||||||
|
type: 'icecandidate',
|
||||||
|
msgto:peer,
|
||||||
|
candidate: candidate
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signaling_send(d){
|
||||||
|
d.session = this.session;
|
||||||
|
this.signaling.send_data(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
async recvdata_handler(data){
|
||||||
|
var f = this.get_handler(data.type);
|
||||||
|
if (f) {
|
||||||
|
await f(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async ice_statechange(peer, event){
|
||||||
|
var pc = this.peers[peer.id].pc;
|
||||||
|
console.log(`oniceconnectionstatechange, pc.iceConnectionState is ${pc.iceConnectionState}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async connection_statechange(peer, event){
|
||||||
|
var pc = this.peers[peer.id].pc;
|
||||||
|
if (pc.connectionState == 'disconnected'){
|
||||||
|
var w = new bricks.Error({
|
||||||
|
title:'Error',
|
||||||
|
message:'lose connection',
|
||||||
|
timeout:4
|
||||||
|
});
|
||||||
|
w.open();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (pc.connectionState == 'connected'){
|
||||||
|
if (this.opts.data_connect){
|
||||||
|
var dc = pc.createDataChannel();
|
||||||
|
if (this.requester and this.opts.data_connect){
|
||||||
|
await this.createDataChannel(peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(`${peer.id} connect losed. ${pc.connectionState}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async createDataChannel(peer){
|
||||||
|
var pc = this.peers[peer.id].pc;
|
||||||
|
dc = pc.createDataChannel('data');
|
||||||
|
this.peers[peer.id].dc = dc;
|
||||||
|
dc.onmessage = this.datachannel_message.bind(this, peer);
|
||||||
|
dc.onopen = this.datachannel_open(this, peer);
|
||||||
|
dc.onclose = this.datachannl_close(this, peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
async def datachannel_message(peer, msg){
|
||||||
|
var dc = this.peers[peer.id].dc;
|
||||||
|
if (this.on_dc_messaage){
|
||||||
|
await this.on_dc_message(dc, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async def datachannel_open(peer){
|
||||||
|
var dc = this.peers[peer.id].dc
|
||||||
|
if (this.on_dc_open){
|
||||||
|
await this.on_dc_open(dc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async def datachannel_close(peer){
|
||||||
|
var dc = this.peers[peer.id].dc
|
||||||
|
if (this.on_dc_close){
|
||||||
|
await this.on_dc_close(dc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async createPeerConnection(peer){
|
||||||
|
const configuration = {
|
||||||
|
iceServers:this.opts.ice_servers
|
||||||
|
}
|
||||||
|
var pc = RTCPeerConnection(configuration);
|
||||||
|
this.localStream.getTracks()
|
||||||
|
.forEach(track => {
|
||||||
|
pc.addTrack(track, this.localStream);
|
||||||
|
});
|
||||||
|
this.peers[peer.id] = peer;
|
||||||
|
this.peers[peer.id][pc] = pc;
|
||||||
|
var remoteVideo = new VideoBox();
|
||||||
|
this.peers[peer.id][video] = remoteVideo;
|
||||||
|
pc.onicecandidate = this.send_candidate.bind(this, peer);
|
||||||
|
pc.oniceconnectionstatechange = this.ice_statechange.bind(this, peer);
|
||||||
|
pc.onconnectionstatechange = this.connection_statechange.bind(this, peer);
|
||||||
|
pc.ondatachanel = event => {
|
||||||
|
var dc = event.channel;
|
||||||
|
this.peers[peer.id].dc = dc;
|
||||||
|
dc.onmessage = this.data_message_recv.bind(this, peer);
|
||||||
|
dc.onopen = this.datachannel_opened(this, peer);
|
||||||
|
dc.onclose = this.datachannl_closed(this, peer);
|
||||||
|
}
|
||||||
|
pc.ontrack = event => {
|
||||||
|
remoteVideo.set_stream(event.streams[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async getLocalStream() {
|
||||||
|
if (this.opts.media_options){
|
||||||
|
try {
|
||||||
|
var mediaOptions = this.opts.media_options;
|
||||||
|
this.local_stream = await navigator.mediaDevices.getUserMedia(mediaOptions);
|
||||||
|
this.localVideo = new VideoBox();
|
||||||
|
this.localVideo.set_stream(stream);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取本地媒体流失败:', error);
|
||||||
}
|
}
|
||||||
get_stream(){
|
|
||||||
return this.stream;
|
|
||||||
}
|
}
|
||||||
set_stream(stream){
|
|
||||||
this.stream = stream
|
|
||||||
this.dom_element.srcObject = this.stream;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user