bugfix
This commit is contained in:
parent
d78f62604a
commit
0865cc3b6d
403
bricks/rtc.js
403
bricks/rtc.js
@ -355,7 +355,7 @@ bricks.RTCP2PConnect = class {
|
|||||||
const configuration = {
|
const configuration = {
|
||||||
iceServers:this.opts.ice_servers
|
iceServers:this.opts.ice_servers
|
||||||
}
|
}
|
||||||
var pc = RTCPeerConnection(configuration);
|
var pc = new RTCPeerConnection(configuration);
|
||||||
this.localStream.getTracks()
|
this.localStream.getTracks()
|
||||||
.forEach(track => {
|
.forEach(track => {
|
||||||
pc.addTrack(track, this.localStream);
|
pc.addTrack(track, this.localStream);
|
||||||
@ -384,7 +384,7 @@ bricks.RTCP2PConnect = class {
|
|||||||
var mediaOptions = this.opts.media_options;
|
var mediaOptions = this.opts.media_options;
|
||||||
this.local_stream = await navigator.mediaDevices.getUserMedia(mediaOptions);
|
this.local_stream = await navigator.mediaDevices.getUserMedia(mediaOptions);
|
||||||
this.localVideo = new VideoBox();
|
this.localVideo = new VideoBox();
|
||||||
this.localVideo.set_stream(stream);
|
this.localVideo.set_stream(local_stream);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取本地媒体流失败:', error);
|
console.error('获取本地媒体流失败:', error);
|
||||||
}
|
}
|
||||||
@ -392,402 +392,3 @@ bricks.RTCP2PConnect = class {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bricks.RTCClient = class extends bricks.VBox {
|
|
||||||
/*
|
|
||||||
{
|
|
||||||
"signaling_url":
|
|
||||||
"ice_url":
|
|
||||||
"mediaOptions":{
|
|
||||||
"audio":true,
|
|
||||||
"video":true
|
|
||||||
}
|
|
||||||
"auto_call_accept":false
|
|
||||||
"info":{
|
|
||||||
"id":
|
|
||||||
"name":
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
constructor(opts){
|
|
||||||
opts.width = '100%';
|
|
||||||
super(opts);
|
|
||||||
this.build_head();
|
|
||||||
this.videobox = new bricks.ResponsableBox({css:'filler'});
|
|
||||||
this.videobox.set_style('position', 'relative');
|
|
||||||
this.localVideo = new bricks.VideoBox({});
|
|
||||||
this.remoteVideo = new bricks.VideoBox({});
|
|
||||||
this.videobox.add_widget(this.localVideo);
|
|
||||||
this.videobox.add_widget(this.remoteVideo);
|
|
||||||
this.add_widget(this.videobox);
|
|
||||||
this.videosize_switch();
|
|
||||||
this.pc;
|
|
||||||
this.onlineList= [];
|
|
||||||
this.init_websocket();
|
|
||||||
}
|
|
||||||
videosize_switch(){
|
|
||||||
if(! this.local_small){
|
|
||||||
this.local_small = true;
|
|
||||||
this.localVideo.set_css('smallvideo');
|
|
||||||
this.remoteVideo.set_css('bigvideo');
|
|
||||||
} else {
|
|
||||||
this.local_small = false;
|
|
||||||
this.localVideo.set_css('bigvideo');
|
|
||||||
this.remoteVideo.set_css('smallvideo');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
async captureScreen() {
|
|
||||||
let mediaStream = null;
|
|
||||||
try {
|
|
||||||
mediaStream = await navigator.mediaDevices.getDisplayMedia({
|
|
||||||
video: {
|
|
||||||
cursor: "always"
|
|
||||||
},
|
|
||||||
audio: false
|
|
||||||
});
|
|
||||||
return mediaStream;
|
|
||||||
} catch (ex) {
|
|
||||||
console.log("Error occurred", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
init_websocket(){
|
|
||||||
this.socket = new WebSocket(this.signaling_url);
|
|
||||||
this.socket.onmessage = this.signaling_message_handle.bind(this);
|
|
||||||
this.socket.onopen = this.signaling_login.bind(this);
|
|
||||||
this.socket.onclose = function(){
|
|
||||||
console.log('888888 this.socket is closed', this.socket);
|
|
||||||
if (this.parent){
|
|
||||||
this.init_websockset();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.socket.onerror = function(){
|
|
||||||
try {
|
|
||||||
this.socket.close();
|
|
||||||
} catch(e){
|
|
||||||
console.log('error,', e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
build_head(){
|
|
||||||
var box = new bricks.HBox({height:'50px'});
|
|
||||||
this.add_widget(box);
|
|
||||||
this.build_phone(box);
|
|
||||||
this.peer_w = new bricks.Text({text:''});
|
|
||||||
box.add_widget(this.peer_w);
|
|
||||||
}
|
|
||||||
build_phone(box){
|
|
||||||
var opts = {
|
|
||||||
states:[
|
|
||||||
{
|
|
||||||
state:'free',
|
|
||||||
url:bricks_resource('imgs/free_phone.png')
|
|
||||||
},{
|
|
||||||
state:'using',
|
|
||||||
url:bricks_resource('imgs/using_phone.png')
|
|
||||||
}
|
|
||||||
],
|
|
||||||
state:'free',
|
|
||||||
rate:2
|
|
||||||
}
|
|
||||||
this.phone = new bricks.StatedIcon(opts);
|
|
||||||
this.phone.bind('click', this.phone_action.bind(this));
|
|
||||||
box.add_widget(this.phone);
|
|
||||||
}
|
|
||||||
phone_action(){
|
|
||||||
if (this.phone.state == 'free'){
|
|
||||||
this.choose_peer();
|
|
||||||
} else {
|
|
||||||
this.call_close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
choose_peer(){
|
|
||||||
var items = [];
|
|
||||||
if (this.onlineList.length < 1){
|
|
||||||
var w = new bricks.Error({title:'Error',
|
|
||||||
timeout:4,
|
|
||||||
message:'no peer logined'
|
|
||||||
});
|
|
||||||
w.open();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.onlineList.forEach( p => {
|
|
||||||
var m = {
|
|
||||||
name:p.id,
|
|
||||||
label:p.name
|
|
||||||
}
|
|
||||||
items.push(m);
|
|
||||||
});
|
|
||||||
var menu = new bricks.Menu({
|
|
||||||
width:'300px',
|
|
||||||
height:'400px',
|
|
||||||
items:items
|
|
||||||
});
|
|
||||||
menu.set_css('popup');
|
|
||||||
bricks.Body.add_widget(menu);
|
|
||||||
menu.bind('command', this.call_peer.bind(this));
|
|
||||||
this.menu = menu;
|
|
||||||
}
|
|
||||||
async call_peer(event){
|
|
||||||
console.log('event params=', event.params);
|
|
||||||
bricks.Body.remove_widget(this.menu);
|
|
||||||
var peer_info = {
|
|
||||||
id:event.params.name,
|
|
||||||
name:event.params.label
|
|
||||||
}
|
|
||||||
this.call_request(peer_info);
|
|
||||||
}
|
|
||||||
signaling_login(){
|
|
||||||
var d = {
|
|
||||||
type:'login',
|
|
||||||
info:this.info
|
|
||||||
}
|
|
||||||
this.socket_send(JSON.stringify(d));
|
|
||||||
}
|
|
||||||
signaling_logout(){
|
|
||||||
var d= {
|
|
||||||
type:'logout',
|
|
||||||
info:this.info
|
|
||||||
}
|
|
||||||
this.socket_send(JSON.stringify(d));
|
|
||||||
}
|
|
||||||
// 获取本地媒体流
|
|
||||||
async getLocalStream() {
|
|
||||||
try {
|
|
||||||
var mediaOptions = this.mediaOptions || {
|
|
||||||
video: true,
|
|
||||||
audio: true
|
|
||||||
};
|
|
||||||
const stream = await navigator.mediaDevices.getUserMedia(mediaOptions);
|
|
||||||
this.localVideo.set_stream(stream);
|
|
||||||
return stream;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取本地媒体流失败:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async call_request(peer_info){
|
|
||||||
var d = {
|
|
||||||
type:'callRequest',
|
|
||||||
to:peer_info
|
|
||||||
}
|
|
||||||
this.peer_info = peer_info;
|
|
||||||
this.role = 'requester';
|
|
||||||
this.socket_send(JSON.stringify(d));
|
|
||||||
}
|
|
||||||
async send_offer(){
|
|
||||||
var offer = await this.pc.createOffer();
|
|
||||||
await this.pc.setLocalDescription(offer);
|
|
||||||
console.log('########send_offer =', offer, this.pc.localDescription);
|
|
||||||
this.socket_send(JSON.stringify({
|
|
||||||
type:'offer',
|
|
||||||
offer:this.pc.localDescription,
|
|
||||||
to:this.peer_info
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
send_candidate(peer_info, event){
|
|
||||||
if (event.candidate) {
|
|
||||||
var candidate = event.candidate;
|
|
||||||
console.log('send_candidate(), candidate=', candidate);
|
|
||||||
this.socket_send(JSON.stringify({
|
|
||||||
type: 'iceCandidate',
|
|
||||||
to:peer_info,
|
|
||||||
candidate: candidate
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async need_conform_call(peer_info){
|
|
||||||
var w = new bricks.Conform({title:'请确认',
|
|
||||||
message:'来自' + peer_info.name + '的连接请求'});
|
|
||||||
w.bind('conformed', this.call_accepted.bind(this, peer_info));
|
|
||||||
w.bind('cancelled', this.call_rejected.bind(this.peer_info));
|
|
||||||
w.open();
|
|
||||||
}
|
|
||||||
async call_accepted(peer_info){
|
|
||||||
if (this.peer_info){
|
|
||||||
this.call_close();
|
|
||||||
}
|
|
||||||
this.peer_info = peer_info
|
|
||||||
this.role = 'responser'
|
|
||||||
await this.createPeerConnection(this.peer_info);
|
|
||||||
this.socket_send(JSON.stringify({
|
|
||||||
type: 'callAccepted',
|
|
||||||
to: peer_info
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
async call_close(active){
|
|
||||||
this.role = null;
|
|
||||||
this.pc.onicecandidate = null;
|
|
||||||
this.pc.ontrack = null;
|
|
||||||
this.pc.close();
|
|
||||||
if (! active){
|
|
||||||
this.socket_send(JSON.stringify({
|
|
||||||
type:'disconnect',
|
|
||||||
to:this.peer_info
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
this.peer_info = null;
|
|
||||||
if (this.pc.getLocalStreams().length > 0) {
|
|
||||||
for (let stream of this.pc.getLocalStreams()) {
|
|
||||||
if (stream.getTracks) {
|
|
||||||
for (let track of stream.getTracks()) {
|
|
||||||
track.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.localVideo.get_stream().getTracks().forEach(track => track.stop());
|
|
||||||
this.phone.set_state('free');
|
|
||||||
this.peer_w.set_text('');
|
|
||||||
}
|
|
||||||
call_rejected(peer_info){
|
|
||||||
this.socket_send(JSON.stringify({
|
|
||||||
type: 'callRejected',
|
|
||||||
to: peer_info
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
socket_send(s){
|
|
||||||
try {
|
|
||||||
this.socket.send(s);
|
|
||||||
} catch (e){
|
|
||||||
console.log('socket=', this.socket);
|
|
||||||
this.init_websocket();
|
|
||||||
this.socket.send(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async signaling_message_handle(event){
|
|
||||||
var datapkg = JSON.parse(event.data);
|
|
||||||
var d = datapkg.data;
|
|
||||||
console.log('data received from server', 'd=', d);
|
|
||||||
switch (d.type){
|
|
||||||
case 'onlineList':
|
|
||||||
this.onlineList = d.onlineList;
|
|
||||||
console.log('onlineList branch exe');
|
|
||||||
break;
|
|
||||||
case 'callRequest':
|
|
||||||
if (this.auto_call_accept){
|
|
||||||
this.call_accepted(d.msgfrom);
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.need_conform_call(d.msgfrom);
|
|
||||||
console.log('callRequest branch exe');
|
|
||||||
break;
|
|
||||||
case 'offer':
|
|
||||||
var offer = new RTCSessionDescription(d.offer);
|
|
||||||
await this.pc.setRemoteDescription(offer);
|
|
||||||
var answer = await this.pc.createAnswer();
|
|
||||||
await this.pc.setLocalDescription(answer);
|
|
||||||
console.log('###### send_answer=', answer, 'desc=', this.pc.localDescription);
|
|
||||||
this.socket_send(JSON.stringify({
|
|
||||||
type:'answer',
|
|
||||||
answer:this.pc.localDescription,
|
|
||||||
to:this.peer_info
|
|
||||||
}));
|
|
||||||
console.log('offer branch exe');
|
|
||||||
if (this.role == 'responser'){
|
|
||||||
this.send_offer();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'answer':
|
|
||||||
console.log('#### receive_answer: d.msgfrom=', d.msgfrom, 'peer_info=', this.peer_info);
|
|
||||||
if (d.msgfrom.id == this.peer_info.id){
|
|
||||||
var desc = new RTCSessionDescription(d.answer);
|
|
||||||
await this.pc.setRemoteDescription(desc);
|
|
||||||
console.log('answer branch exe');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'iceCandidate':
|
|
||||||
if (d.msgfrom.id == this.peer_info.id){
|
|
||||||
var candidate = new RTCIceCandidate(d.candidate);
|
|
||||||
await this.pc.addIceCandidate(candidate);
|
|
||||||
console.log('iceCandidate branch exe');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'callAccepted':
|
|
||||||
await this.createPeerConnection(this.peer_info);
|
|
||||||
console.log('callAccepted branch exe');
|
|
||||||
break;
|
|
||||||
case 'callRejected':
|
|
||||||
this.peer_info = null;
|
|
||||||
console.log('callRejected branch exe');
|
|
||||||
break;
|
|
||||||
case 'disconnect':
|
|
||||||
this.call_close(true);
|
|
||||||
console.log('disconnect branch exe');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.log(d.type, 'no branch defined');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (this.pc){
|
|
||||||
console.log('signal_status=', this.pc.signalingState,
|
|
||||||
'iceconnectstate=', this.pc.iceConnectionState,
|
|
||||||
'connectstate=', this.pc.connectionState,
|
|
||||||
'cantrickleIceCandidate=', this.pc.canTrickleIceCandidates,
|
|
||||||
'peerConnectionState=', this.pc.peerConnectionState,
|
|
||||||
'icegatheringstate=', this.pc.iceGatheringState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建 PeerConnection
|
|
||||||
async createPeerConnection(to_info) {
|
|
||||||
this.phone.set_state('using');
|
|
||||||
this.peer_w.set_text(to_info.name);
|
|
||||||
await this.getLocalStream();
|
|
||||||
const configuration = {
|
|
||||||
iceServers: this.ice_servers,
|
|
||||||
};
|
|
||||||
console.log('configuration=', configuration);
|
|
||||||
this.pc = new RTCPeerConnection(configuration);
|
|
||||||
// 处理本地流添加到 PeerConnection
|
|
||||||
this.localVideo
|
|
||||||
.get_stream()
|
|
||||||
.getTracks()
|
|
||||||
.forEach(track => {
|
|
||||||
this.pc.addTrack(track, this.localVideo.stream);
|
|
||||||
});
|
|
||||||
if (! this.pc){
|
|
||||||
console.log('pc is null');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 处理 ICE 候选
|
|
||||||
this.pc.onicecandidate = this.send_candidate.bind(this, to_info);
|
|
||||||
this.pc.oniceconnectionstatechange = this.ice_statechange.bind(this);
|
|
||||||
this.pc.onicegatheringstatechange = this.ice_g_statechange.bind(this);
|
|
||||||
this.pc.onconnectionstatechange = this.connection_statechange.bind(this);
|
|
||||||
|
|
||||||
// 处理远程流添加
|
|
||||||
this.pc.ontrack = event => {
|
|
||||||
console.log('=========ontrack() called ......');
|
|
||||||
this.remoteVideo.set_stream(event.streams[0]);
|
|
||||||
};
|
|
||||||
if (this.role == 'requester'){
|
|
||||||
await this.send_offer();
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('createPeerConnection() finished');
|
|
||||||
}
|
|
||||||
async ice_statechange(event){
|
|
||||||
console.log(`oniceconnectionstatechange, pc.iceConnectionState is ${this.pc.iceConnectionState}.`);
|
|
||||||
}
|
|
||||||
async connection_statechange(event){
|
|
||||||
console.log(`onconnectionstatechange, pc.connectionState is ${this.pc.connectionState}.`);
|
|
||||||
if (this.pc.connectionState == 'disconnected'){
|
|
||||||
var w = new bricks.Error({
|
|
||||||
title:'Error',
|
|
||||||
message:'lose connection',
|
|
||||||
timeout:4
|
|
||||||
});
|
|
||||||
w.open();
|
|
||||||
this.call_close();
|
|
||||||
this.dispatch('disconnected', this.peer_info);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.pc.connectionState == 'connected'){
|
|
||||||
this.dispatch('connected', this.peer_info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ice_g_statechange(event){
|
|
||||||
console.log(`onicegatheringstatechange, pc.iceGatheringState is ${this.pc.iceGatheringState}.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bricks.Factory.register('RTCClient', bricks.RTCClient);
|
|
||||||
|
Loading…
Reference in New Issue
Block a user