UNPKG

@jsxc/jsxc

Version:

Real-time XMPP chat application with video calls, file transfer and encrypted communication

285 lines (215 loc) 8.36 kB
import Log from '../util/Log'; import VideoWindow from './VideoWindow'; import JingleMediaSession from '../JingleMediaSession'; import Translation from '@util/Translation'; import JingleCallSession from '@src/JingleCallSession'; import * as screenfull from 'screenfull'; import Client from '@src/Client'; const screen = screenfull as screenfull.Screenfull; const VideoDialogTemplate = require('../../template/videoDialog.hbs'); export class VideoDialog { private dom: JQuery; private videoWindows: { [sessionId: string]: VideoWindow } = {}; private localStream: MediaStream; private ready: boolean = true; constructor() { let htmlString = VideoDialogTemplate({}); this.dom = $(htmlString); } public getDom() { return this.dom; } public addSession(session: JingleMediaSession) { session.on('terminated', reason => { this.removeContainer(session.getId()); this.removeSession(session, reason); }); let msg: string; if (session instanceof JingleCallSession) { msg = ':telephone_receiver: ' + Translation.t('Call_started'); } else { msg = ':tv: ' + Translation.t('Stream_started'); } session.getPeer().addSystemMessage(msg); let videoWindow = new VideoWindow(this, session); this.videoWindows[session.getId()] = videoWindow; } public showVideoWindow(localStream?: MediaStream) { this.dom.appendTo('body'); let localVideoElement = this.dom.find('.jsxc-local-video'); let localCameraControl = this.dom.find('.jsxc-video'); let localMicrophoneControl = this.dom.find('.jsxc-microphone'); if (typeof (<any>localVideoElement).draggable === 'function') { (<any>localVideoElement).draggable({ containment: 'parent', }); } if (localStream) { this.localStream = localStream; VideoDialog.attachMediaStream(localVideoElement, localStream); if (localStream.getVideoTracks().length === 0) { Log.debug('No local video device available'); localCameraControl.hide(); localVideoElement.hide(); } else if (localStream.getVideoTracks().filter((track: MediaStreamTrack) => track.enabled).length === 0) { Log.debug('There are no video tracks enabled'); localCameraControl.addClass('jsxc-disabled'); } if (localStream.getAudioTracks().length === 0) { Log.debug('No local audio device available'); localMicrophoneControl.hide(); } else if (localStream.getAudioTracks().filter((track: MediaStreamTrack) => track.enabled).length === 0) { Log.debug('There are no audio tracks enabled'); localMicrophoneControl.addClass('jsxc-disabled'); } } else { Log.debug('No local stream available'); localCameraControl.hide(); localVideoElement.hide(); localMicrophoneControl.hide(); } localMicrophoneControl.click(() => { VideoDialog.changeStreamMediaState(localMicrophoneControl, localStream.getAudioTracks()); }); localCameraControl.click(() => { VideoDialog.changeStreamMediaState(localCameraControl, localStream.getVideoTracks()); }); this.dom.find('.jsxc-hang-up').on('click', () => { Client.getAccountManager() .getAccounts() .forEach(account => { account.getCallManager().terminateAll(); }); this.close(); }); this.dom.find('.jsxc-video-control.jsxc-minmax').click(() => { this.dom.toggleClass('jsxc-minimized'); }); if (screen) { screen.on('change', () => { this.resetPositionOfLocalVideoElement(); }); this.dom.find('.jsxc-fullscreen').click(() => { if (screen.isFullscreen) { screen.exit(); } else { screen.request(this.dom.get(0)); } }); } else { this.dom.find('.jsxc-fullscreen').hide(); } this.dom.find('.jsxc-video-container').click(() => { this.dom.find('.jsxc-controlbar').toggleClass('jsxc-visible'); }); } public minimize() { this.dom.addClass('jsxc-minimized'); } public maximize() { this.dom.removeClass('jsxc-minimized'); } public addContainer(containerElement) { this.dom.find('.jsxc-video-container').addClass('jsxc-device-available'); this.dom.find('.jsxc-video-container').append(containerElement); this.updateNumberOfContainer(); } private resetPositionOfLocalVideoElement() { let localVideoElement = this.dom.find('.jsxc-local-video'); localVideoElement.css({ top: '', left: '', bottom: '', right: '', }); } private removeContainer(sessionId: string) { this.dom.find(`[data-sid="${sessionId}"]`).remove(); this.updateNumberOfContainer(); } private updateNumberOfContainer() { let numberOfContainer = this.dom.find('.jsxc-video-container > .jsxc-video-wrapper').length; this.dom.find('.jsxc-video-container').attr('data-videos', numberOfContainer); } //@REVIEW still used? public setStatus(message, duration = 4000) { let statusElement = this.dom.find('.jsxc-status'); Log.debug('[Webrtc]', message); let messageElement = $('<p>').text(message); messageElement.appendTo(statusElement); statusElement.addClass('jsxc-status--visible'); if (duration === 0) { return; } setTimeout(() => { messageElement.remove(); if (statusElement.children().length === 0) { statusElement.removeClass('jsxc-status--visible'); } }, duration); } public clearStatus() { let statusElement = this.dom.find('.jsxc-status'); statusElement.empty(); statusElement.removeClass('jsxc-status--visible'); } public isReady(): boolean { return this.ready; } private removeSession = (session: JingleMediaSession, reason?) => { let msg = (reason && reason.condition ? ': ' + Translation.t('jingle_reason_' + reason.condition) : '') + '.'; if (session instanceof JingleCallSession) { msg = Translation.t('Call_terminated') + msg; } else { msg = Translation.t('Stream_terminated') + msg; } session.getPeer().addSystemMessage(':checkered_flag: ' + msg); Log.debug('Session ' + session.getId() + ' was removed. Reason: ' + msg); delete this.videoWindows[session.getId()]; if (Object.keys(this.videoWindows).length === 0) { this.close(); } }; public close() { this.ready = false; this.dom.remove(); if (this.localStream) { VideoDialog.stopStream(this.localStream); } } private static changeStreamMediaState = (element: JQuery, tracks: MediaStreamTrack[]) => { element.toggleClass('jsxc-disabled'); let streamEnabled = !element.hasClass('jsxc-disabled'); tracks.forEach((track: MediaStreamTrack) => (track.enabled = streamEnabled)); }; public static attachMediaStream = (element: JQuery | HTMLMediaElement, stream: MediaStream) => { let el = <HTMLMediaElement>(element instanceof jQuery ? (<JQuery>element).get(0) : element); el.srcObject = stream; $(element).show(); }; public static detachMediaStream = (element: JQuery | HTMLMediaElement) => { let el = <HTMLMediaElement>(element instanceof jQuery ? (<JQuery>element).get(0) : element); if (!el) { return; } VideoDialog.stopStream(el.srcObject); el.srcObject = null; $(el).removeClass('jsxc-device-available jsxc-video-available jsxc-audio-available'); }; private static stopStream(stream) { if (!stream) { Log.warn('Could not stop stream. Stream is null.'); return; } if (typeof stream.getTracks === 'function') { let tracks = stream.getTracks(); tracks.forEach(function (track) { track.stop(); }); } else if (typeof stream.stop === 'function') { stream.stop(); } else { Log.warn('Could not stop stream'); } } }