bugfix
This commit is contained in:
parent
8b39dfd3e5
commit
b28d506522
@ -47,6 +47,18 @@ dispatch_event:
|
||||
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){
|
||||
/* fmt like
|
||||
'my name is ${name}, ${age:type}'
|
||||
@ -427,6 +439,7 @@ bricks.App = class extends bricks.Layout {
|
||||
constructor(opts){
|
||||
/*
|
||||
opts = {
|
||||
appname:
|
||||
login_url:
|
||||
"charsize:
|
||||
"language":
|
||||
@ -444,6 +457,7 @@ bricks.App = class extends bricks.Layout {
|
||||
super(opts);
|
||||
bricks.app = this;
|
||||
bricks.Body = this;
|
||||
this.deviceid = bricks.deviceid(opts.appname || 'appname');
|
||||
this.login_url = opts.login_url;
|
||||
this.charsize = this.opts.charsize || 20;
|
||||
this.keyevent_blocked = false;
|
||||
|
278
bricks/rtc.js
278
bricks/rtc.js
@ -1,16 +1,33 @@
|
||||
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 {
|
||||
/*
|
||||
{
|
||||
signaling_url:
|
||||
info:
|
||||
multiple:false
|
||||
connect_opts:
|
||||
}
|
||||
*/
|
||||
constructor(opts){
|
||||
this.signaling_url = opts.signaling_url;
|
||||
this.info = opts.info;
|
||||
this.connect_opts = opts.connect_opts;
|
||||
this.peers = [];
|
||||
this.sessions = {};
|
||||
this.handlers = {};
|
||||
@ -37,12 +54,21 @@ bricks.Signaling = class {
|
||||
async signaling_recvdata(event){
|
||||
var datapkg = JSON.parse(event.data);
|
||||
var data = datapkg.data;
|
||||
var name = data.sessionid
|
||||
var handler = this.handlers.get(name)
|
||||
if (!handler){
|
||||
handler = this.recvdata_handler.bind(this)
|
||||
if (data.session) {
|
||||
var sessionid = data.session.sessionid;
|
||||
var sessiontype = data.session.sessiontype;
|
||||
var handler = this.handlers.get(sessionid)
|
||||
if (!handler){
|
||||
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);
|
||||
} else {
|
||||
await this.recvdata_handler(data);
|
||||
}
|
||||
await handler(data);
|
||||
}
|
||||
add_handler(key, handler){
|
||||
this.handlers[key] = handler;
|
||||
@ -62,23 +88,25 @@ bricks.Signaling = class {
|
||||
}
|
||||
});
|
||||
}
|
||||
if (data.type == 'new_session'){
|
||||
}
|
||||
}
|
||||
new_session(sessiontype, opts){
|
||||
new_session(sessiontype, peer){
|
||||
var k = this.sessionhandlers.get(sessiontype);
|
||||
if (!k){
|
||||
throw 'new_session() exception(' + sessiontype + ')';
|
||||
}
|
||||
var sessionid = bricks.uuid();
|
||||
var session = {
|
||||
sessiontype:sessiontype,
|
||||
sessionid:sessionid
|
||||
}
|
||||
var d = {
|
||||
type:'new_session',
|
||||
session: {
|
||||
sessiontype:sessiontype,
|
||||
sessionid:bricks.uuid()
|
||||
}
|
||||
session: session
|
||||
};
|
||||
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);
|
||||
return h
|
||||
}
|
||||
@ -105,22 +133,222 @@ 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
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
bricks.VideoBox = class extends bricks.JsWidget {
|
||||
create(){
|
||||
this.dom_element = this._create('video');
|
||||
this.set_attribute('autoplay', true);
|
||||
this.set_attribute('muted', true);
|
||||
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));
|
||||
}
|
||||
get_stream(){
|
||||
return this.stream;
|
||||
|
||||
add_handler(type, f):
|
||||
this.signal_handlers[type] = f;
|
||||
}
|
||||
set_stream(stream){
|
||||
this.stream = stream
|
||||
this.dom_element.srcObject = this.stream;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bricks.RTCClient = class extends bricks.VBox {
|
||||
|
Loading…
Reference in New Issue
Block a user