Able to change host during a call

This commit is contained in:
Sagi Dayan 2020-04-14 11:06:09 -04:00
parent 977d7a0a01
commit 38ca6b6133
14 changed files with 4210 additions and 34 deletions

View file

@ -47,6 +47,7 @@ class CallSession {
this.onCallEndedCallback = onCallEndedCallback;
this.callId = callModel.id;
this.callModel = callModel;
this.hostId = 2;
this.state = callModel.state;
this.user_1 = {id: callModel.user_1, socket: null, userModel: null};
this.user_2 = {id: callModel.user_2, socket: null, userModel: null};
@ -93,6 +94,7 @@ class CallSession {
socket.on('wrtc:sdp:offer', this.onSdpOffer.bind(this));
socket.on('wrtc:sdp:answer', this.onSdpAnswer.bind(this));
socket.on('wrtc:ice', this.onIceCandidate.bind(this));
socket.on('call:host:changed', this.onHostChanged.bind(this));
socket
.on('book:action:flip-page', this.onActionBookFlip.bind(this))
await this.updateState();
@ -125,15 +127,28 @@ class CallSession {
clearInterval(this.heartbeat);
this.onCallEndedCallback(this.callId);
}
async sendStandby(socket, userIndex) {
console.log(`Call #${this.callId} sendStandby -> ${userIndex}`);
const iceServers = (await IceServer.all()).rows.map(i => i.toJSON());
socket.emit('call:standby', {iceServers, id: userIndex});
socket.emit('call:standby', {
iceServers,
id: userIndex,
child: this.child.toJSON(),
users: await this.callModel.getUsers(),
hostId: this.hostId
});
}
async sendStart(socket, userIndex) {
console.log(`Call #${this.callId} sendStart -> ${userIndex}`);
const iceServers = (await IceServer.all()).rows.map(i => i.toJSON());
socket.emit('call:start', {iceServers, id: userIndex});
socket.emit('call:start', {
iceServers,
id: userIndex,
child: this.child.toJSON(),
users: await this.callModel.getUsers(),
hostId: this.hostId
});
}
async updateState() {
console.log(`Call #${this.callId} state=${this.state}`);
@ -189,6 +204,15 @@ class CallSession {
`[Signal] [book] [action] [flip] [${direction}] ${from} -> ${to}`);
return true;
}
async onHostChanged(payload) {
const {from = payload.id, hostId} = payload;
const to = from === 1 ? 2 : 1;
this.hostId = hostId;
this[`user_${to}`].socket.emit('call:host:changed', {hostId});
console.log(
`[Signal] [host] [changed] [hostId=${hostId}] ${from} -> ${to}`);
return true;
}
}
module.exports = SignalingController

View file

@ -2,8 +2,17 @@
/** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */
const Model = use('Model')
const User = use('App/Models/User');
class Call extends Model {
async getUsers() {
const users = [
(await User.find(this.user_1)).toJSON(),
(await User.find(this.user_2)).toJSON()
];
return users;
}
}
module.exports = Call

View file

@ -39,10 +39,10 @@
"adonis-vue-websocket": "^2.0.2",
"animate.css": "^3.7.2",
"bulma": "^0.8.0",
"flipbook-vue": "^0.8.1",
"fork-awesome": "^1.1.7",
"moment": "^2.24.0",
"regenerator-runtime": "^0.13.5",
"rematrix": "^0.5.0",
"sqlite3": "^4.1.1",
"typescript": "^3.7.5",
"uuid": "^7.0.3",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -9,7 +9,10 @@ export default class CallManager {
private needToAddStream: boolean = true;
private emitter = new EventEmitter();
private pc: RTCPeerConnection;
constructor(private ws: WebSocketService, private callId: number) {
public child;
public guest = { avatar: '' };
public isHost = false;
constructor(private ws: WebSocketService, private callId: number, private userId: number) {
this.inCall = false;
this.peerId = -1;
this.pc = null;
@ -31,7 +34,8 @@ export default class CallManager {
signalingChannel.on('wrtc:sdp:offer', self.onRemoteOffer.bind(self));
signalingChannel.on('wrtc:sdp:answer', self.onRemoteAnswer.bind(self));
signalingChannel.on('wrtc:ice', self.onRemoteIce.bind(self));
signalingChannel.on('book:action:flip-page', self.onActionBookFlip.bind(self))
signalingChannel.on('book:action:flip-page', self.onActionBookFlip.bind(self));
signalingChannel.on('call:host:changed', self.onRemoteHostChanged.bind(self));
signalingChannel.on('error', (e) => {
console.error(e);
resolve(false)
@ -55,11 +59,17 @@ export default class CallManager {
...payload
})
}
async onCallStart(payload: { iceServers: RTCIceServer[], id: number }) {
async onCallStart(payload: { iceServers: RTCIceServer[], id: number, users: any[], child: any, hostId: number }) {
console.log('onCallStart');
console.log(payload);
this.peerId = payload.id;
this.isHost = this.peerId === payload.hostId;
this.pc = new RTCPeerConnection({ iceServers: payload.iceServers });
this.child = payload.child;
payload.users.forEach(u => {
if (u.id !== this.userId) this.guest = u;
});
this.emit(ECallEvents.CALL_HOST_CHANGED, { hostId: payload.hostId });
console.log('Created PeerConnection');
console.log('adding tracks to pc');
this.localStream.getTracks().forEach(t => this.pc.addTrack(t, this.localStream));
@ -73,11 +83,17 @@ export default class CallManager {
return true;
}
async onCallStandby(payload: { iceServers: RTCIceServer[], id: number }) {
async onCallStandby(payload: { iceServers: RTCIceServer[], id: number, users: any[], child: any, hostId: number }) {
console.log('onCallStandby');
console.log(payload);
this.peerId = payload.id;
this.isHost = this.peerId === payload.hostId;
this.pc = new RTCPeerConnection({ iceServers: payload.iceServers });
this.child = payload.child;
payload.users.forEach(u => {
if (u.id !== this.userId) this.guest = u;
});
this.emit(ECallEvents.CALL_HOST_CHANGED, { hostId: payload.hostId });
console.log('Created PeerConnection');
console.log('adding tracks to pc');
this.localStream.getTracks().forEach(t => this.pc.addTrack(t, this.localStream));
@ -156,6 +172,19 @@ export default class CallManager {
this.emit(ECallEvents.ACTION_BOOK_FLIP, payload);
};
changeHost() {
this.isHost = !this.isHost;
const guestPeerId = this.peerId === 1 ? 2 : 1;
const hostId = this.isHost ? this.peerId : guestPeerId;
this.emit(ECallEvents.CALL_HOST_CHANGED, { hostId });
this.send('call:host:changed', { hostId });
}
onRemoteHostChanged(payload) {
this.isHost = this.peerId === payload.hostId;
this.emit(ECallEvents.CALL_HOST_CHANGED, payload);
}
close() {
console.log('Closing...');
if (!this.inCall) return;
@ -174,5 +203,6 @@ export default class CallManager {
export enum ECallEvents {
CLOSE = 'CLOSE',
REMOTE_STREAM = 'REMOTE_STREAM',
ACTION_BOOK_FLIP = 'ACTION_BOOK_FLIP'
ACTION_BOOK_FLIP = 'ACTION_BOOK_FLIP',
CALL_HOST_CHANGED = "CALL_HOST_CHANGED",
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -4,6 +4,18 @@
<Loading />
</div>
<div v-else class>
<div class="floating-host is-flex" style="position:fixed;top:60px;left:100px">
<h1 class="subtitle m-r-md">Host:</h1>
<div class="me">
<figure class="image is-24x24">
<img
:src="isHost? user.avatar : callManager.guest.avatar"
class="is-rounded is-avatar"
@click="changeHost()"
/>
</figure>
</div>
</div>
<div class="video-strip m-b-md is-outlined">
<video
:src-object.prop.camel="localStream"
@ -14,16 +26,32 @@
/>
<video :src-object.prop.camel="remoteStream" autoplay style="max-width:20%" />
</div>
<div class>
<div class="is-flex">
<div class="go-left m-r-sm" style="display: flex; align-items: center;">
<button class="button is-outlined" :disabled="!isHost" @click="onLeftClicked()">
<i class="fa fa-fw fa-arrow-left"></i>
</button>
</div>
<flipbook
class="flipbook"
:pages="createPages(book)"
forwardDirection="left"
:zooms="null"
:enabled="isHost"
@flip-left-end="onFlip('left')"
@flip-right-end="onFlip('right')"
ref="flipbook"
></flipbook>
v-slot="flipbook"
>
<div class="page-progress has-text-centered m-b-none">
<p>Page {{ flipbook.page }} of {{ flipbook.numPages }}</p>
</div>
</flipbook>
<div class="go-right m-l-sm" style="display: flex; align-items: center;">
<button class="button is-outlined" :disabled="!isHost" @click="onRightClicked()">
<i class="fa fa-fw fa-arrow-right"></i>
</button>
</div>
</div>
</div>
</div>
@ -33,7 +61,7 @@
import Loading from "../../shared/components/Loading/Loading.vue";
import WebsocketService from "../scripts/websocket.service";
import CallManager, { ECallEvents } from "../classes/call.manager";
import Flipbook from "flipbook-vue";
import Flipbook from "../components/flipbook/flipbook.cjs.js";
import Services from "../../services/index";
import { mapActions, mapGetters } from "vuex";
export default {
@ -42,19 +70,28 @@ export default {
Flipbook
},
name: "Call",
mounted() {
const self = this;
setTimeout(() => {
self.isMounted = true;
}, 1000);
},
async created() {
this.loading = true;
try {
const callId = Number(this.$route.params.id);
const ws = await WebsocketService.getInstance();
this.callManager = new CallManager(ws, callId);
this.callManager = new CallManager(ws, callId, this.user.id);
this.callManager.on(ECallEvents.CLOSE, this.callEnded);
this.callManager.on(ECallEvents.ACTION_BOOK_FLIP, this.onRemoteFlip);
const success = await this.callManager.connectToCall({
video: true,
audio: true
});
this.callManager.on(
ECallEvents.CALL_HOST_CHANGED,
this.onRemoteHostChanged
);
if (!success) {
this.notify({ message: "Can find this call...", level: "danger" });
this.$router.push({ path: `/` });
@ -80,9 +117,15 @@ export default {
return true;
},
onFlip(direction: "left" | "right") {
if (this.callManager.peerId == 2)
if (this.isHost)
this.callManager.send(`book:action:flip-page`, { direction });
},
onLeftClicked() {
this.$refs.flipbook.flipLeft();
},
onRightClicked() {
this.$refs.flipbook.flipRight();
},
onRemoteFlip(payload) {
switch (payload.direction) {
case "left":
@ -104,10 +147,19 @@ export default {
this.notify({ message: `Call #${callId} Ended` });
this.$router.push({ path: `/` });
},
onRemoteHostChanged(payload) {
console.log("-----------");
console.log(payload);
this.guest = this.callManager.guest;
this.isHost = this.callManager.isHost;
},
changeHost() {
this.callManager.changeHost();
},
...mapActions(["notify"])
},
computed: {
...mapGetters([])
...mapGetters(["user"])
},
data() {
return {
@ -118,7 +170,11 @@ export default {
loading: true,
localStream: null,
remoteStream: null,
callManager: null
callManager: null,
isHost: false,
// currentPage: 0,
// totalPages: 34,
isMounted: false
};
},
beforeCreate: () => {}

View file

@ -3142,13 +3142,6 @@ flagged-respawn@^1.0.0:
resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.1.tgz#e7de6f1279ddd9ca9aac8a5971d618606b3aab41"
integrity sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==
flipbook-vue@^0.8.1:
version "0.8.1"
resolved "https://registry.yarnpkg.com/flipbook-vue/-/flipbook-vue-0.8.1.tgz#1b3d29a5a8d0d6921ff0be1a91e4165b0d9d6d6e"
integrity sha512-HPWrGe6fwIGSG2IV+kN2Bz16pp8DtsgvNrObxaXtr+XNwxgaLWpbKzM8QV5alBcMFJtyo+7d2Ds1zLrCp8h1LQ==
dependencies:
rematrix "^0.4.1"
flush-write-stream@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8"
@ -6000,10 +5993,10 @@ regjsparser@^0.1.4:
dependencies:
jsesc "~0.5.0"
rematrix@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/rematrix/-/rematrix-0.4.1.tgz#496d40b48c79dd3820bc40352b8d7bec44b32887"
integrity sha512-nH4OjL09zw4yhdBClK4B93kRWbHu+std7ZV6ljS2Zsgy2YrPcayL67axKLfMxoFool8mPyVY614m8SFE7sJQLw==
rematrix@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/rematrix/-/rematrix-0.5.0.tgz#9e8cd1abc82a0c1f4114dba333ad9a9c560673fe"
integrity sha512-vlyfr4SoiHeH3gj/AkwgChPmA6Nb4UqFomB9deUcFQugWUC7DijAAAF+MX9K3yuvxm9aURoED5wofRDl9ISi2w==
remove-trailing-separator@^1.0.1:
version "1.1.0"