123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- /* global $, APP, config */
-
- /* eslint-disable no-unused-vars */
- import { AtlasKitThemeProvider } from '@atlaskit/theme';
- import Logger from 'jitsi-meet-logger';
- import React from 'react';
- import ReactDOM from 'react-dom';
- import { I18nextProvider } from 'react-i18next';
- import { Provider } from 'react-redux';
-
- import { i18next } from '../../../react/features/base/i18n';
- import {
- JitsiParticipantConnectionStatus
- } from '../../../react/features/base/lib-jitsi-meet';
- import { getParticipantById } from '../../../react/features/base/participants';
- import { isTestModeEnabled } from '../../../react/features/base/testing';
- import { updateLastTrackVideoMediaEvent } from '../../../react/features/base/tracks';
- import { Thumbnail, isVideoPlayable } from '../../../react/features/filmstrip';
- import { PresenceLabel } from '../../../react/features/presence-status';
- import { stopController, requestRemoteControl } from '../../../react/features/remote-control';
- import { RemoteVideoMenuTriggerButton } from '../../../react/features/remote-video-menu';
- /* eslint-enable no-unused-vars */
- import UIUtils from '../util/UIUtil';
-
- import SmallVideo from './SmallVideo';
-
- const logger = Logger.getLogger(__filename);
-
- /**
- * List of container events that we are going to process, will be added as listener to the
- * container for every event in the list. The latest event will be stored in redux.
- */
- const containerEvents = [
- 'abort', 'canplay', 'canplaythrough', 'emptied', 'ended', 'error', 'loadeddata', 'loadedmetadata', 'loadstart',
- 'pause', 'play', 'playing', 'ratechange', 'stalled', 'suspend', 'waiting'
- ];
-
- /**
- *
- * @param {*} spanId
- */
- function createContainer(spanId) {
- const container = document.createElement('span');
-
- container.id = spanId;
- container.className = 'videocontainer';
-
- const remoteVideosContainer
- = document.getElementById('filmstripRemoteVideosContainer');
- const localVideoContainer
- = document.getElementById('localVideoTileViewContainer');
-
- remoteVideosContainer.insertBefore(container, localVideoContainer);
-
- return container;
- }
-
- /**
- *
- */
- export default class RemoteVideo extends SmallVideo {
- /**
- * Creates new instance of the <tt>RemoteVideo</tt>.
- * @param user {JitsiParticipant} the user for whom remote video instance will
- * be created.
- * @constructor
- */
- constructor(user) {
- super();
-
- this.user = user;
- this.id = user.getId();
- this.videoSpanId = `participant_${this.id}`;
-
- this.addRemoteVideoContainer();
- this.bindHoverHandler();
- this.flipX = false;
- this.isLocal = false;
-
- /**
- * The flag is set to <tt>true</tt> after the 'canplay' event has been
- * triggered on the current video element. It goes back to <tt>false</tt>
- * when the stream is removed. It is used to determine whether the video
- * playback has ever started.
- * @type {boolean}
- */
- this._canPlayEventReceived = false;
-
- this.container.onclick = this._onContainerClick;
- }
-
- /**
- *
- */
- addRemoteVideoContainer() {
- this.container = createContainer(this.videoSpanId);
- this.$container = $(this.container);
- this.renderThumbnail();
- this._setThumbnailSize();
- this.initBrowserSpecificProperties();
-
- return this.container;
- }
-
- /**
- * Renders the thumbnail.
- */
- renderThumbnail(isHovered = false) {
- ReactDOM.render(
- <Provider store = { APP.store }>
- <I18nextProvider i18n = { i18next }>
- <Thumbnail participantID = { this.id } isHovered = { isHovered } />
- </I18nextProvider>
- </Provider>, this.container);
- }
-
- /**
- * Removes the remote stream element corresponding to the given stream and
- * parent container.
- *
- * @param stream the MediaStream
- * @param isVideo <tt>true</tt> if given <tt>stream</tt> is a video one.
- */
- removeRemoteStreamElement(stream) {
- if (!this.container) {
- return false;
- }
-
- const isVideo = stream.isVideoTrack();
- const elementID = `remoteVideo_${stream.getId()}`;
- const select = $(`#${elementID}`);
-
- select.remove();
- if (isVideo) {
- this._canPlayEventReceived = false;
- }
-
- logger.info(`Video removed ${this.id}`, select);
-
- this.updateView();
- }
-
- /**
- * The remote video is considered "playable" once the can play event has been received.
- *
- * @inheritdoc
- * @override
- */
- isVideoPlayable() {
- return isVideoPlayable(APP.store.getState(), this.id) && this._canPlayEventReceived;
- }
-
- /**
- * @inheritDoc
- */
- updateView() {
- this.$container.toggleClass('audio-only', APP.conference.isAudioOnly());
- super.updateView();
- }
-
- /**
- * Removes RemoteVideo from the page.
- */
- remove() {
- ReactDOM.unmountComponentAtNode(this.container);
- super.remove();
- }
-
- /**
- *
- * @param {*} streamElement
- * @param {*} stream
- */
- waitForPlayback(streamElement, stream) {
- $(streamElement).hide();
-
- const webRtcStream = stream.getOriginalStream();
- const isVideo = stream.isVideoTrack();
-
- if (!isVideo || webRtcStream.id === 'mixedmslabel') {
- return;
- }
-
- const listener = () => {
- this._canPlayEventReceived = true;
-
- logger.info(`${this.id} video is now active`, streamElement);
- if (streamElement) {
- $(streamElement).show();
- }
-
- streamElement.removeEventListener('canplay', listener);
-
- // Refresh to show the video
- this.updateView();
- };
-
- streamElement.addEventListener('canplay', listener);
- }
-
- /**
- *
- * @param {*} stream
- */
- addRemoteStreamElement(stream) {
- if (!this.container) {
- logger.debug('Not attaching remote stream due to no container');
-
- return;
- }
-
- const isVideo = stream.isVideoTrack();
-
- if (!stream.getOriginalStream()) {
- logger.debug('Remote video stream has no original stream');
-
- return;
- }
-
- let streamElement = document.createElement('video');
-
- streamElement.autoplay = !config.testing?.noAutoPlayVideo;
- streamElement.id = `remoteVideo_${stream.getId()}`;
- streamElement.mute = true;
- streamElement.playsInline = true;
-
- // Put new stream element always in front
- streamElement = UIUtils.prependChild(this.container, streamElement);
-
- this.waitForPlayback(streamElement, stream);
- stream.attach(streamElement);
-
- if (isVideo && isTestModeEnabled(APP.store.getState())) {
-
- const cb = name => APP.store.dispatch(updateLastTrackVideoMediaEvent(stream, name));
-
- containerEvents.forEach(event => {
- streamElement.addEventListener(event, cb.bind(this, event));
- });
- }
- }
- }
|