forked from sagi/seepur
Able to change host during a call
This commit is contained in:
parent
977d7a0a01
commit
38ca6b6133
14 changed files with 4210 additions and 34 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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
1458
resources/scripts/applications/home/components/flipbook/flipbook.js
Normal file
1458
resources/scripts/applications/home/components/flipbook/flipbook.js
Normal file
File diff suppressed because it is too large
Load diff
29
resources/scripts/applications/home/components/flipbook/flipbook.min.js
vendored
Normal file
29
resources/scripts/applications/home/components/flipbook/flipbook.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -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: () => {}
|
||||
|
|
15
yarn.lock
15
yarn.lock
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue