2016-10-05 14:36:59 +00:00
|
|
|
import { _handleParticipantError } from '../base/conference';
|
2017-01-17 14:32:20 +00:00
|
|
|
import { MEDIA_TYPE, VIDEO_TYPE } from '../base/media';
|
2016-10-05 14:36:59 +00:00
|
|
|
import {
|
|
|
|
getLocalVideoTrack,
|
|
|
|
getTrackByMediaTypeAndParticipant
|
|
|
|
} from '../base/tracks';
|
|
|
|
|
2017-01-11 18:14:00 +00:00
|
|
|
import { SELECT_LARGE_VIDEO_PARTICIPANT } from './actionTypes';
|
2016-10-05 14:36:59 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Signals conference to select a participant.
|
|
|
|
*
|
|
|
|
* @returns {Function}
|
|
|
|
*/
|
|
|
|
export function selectParticipant() {
|
|
|
|
return (dispatch, getState) => {
|
|
|
|
const state = getState();
|
2016-12-12 00:29:13 +00:00
|
|
|
const conference = state['features/base/conference'].conference;
|
2016-10-05 14:36:59 +00:00
|
|
|
|
|
|
|
if (conference) {
|
2017-01-17 14:44:50 +00:00
|
|
|
const largeVideo = state['features/large-video'];
|
2016-10-05 14:36:59 +00:00
|
|
|
const tracks = state['features/base/tracks'];
|
|
|
|
|
2016-11-04 18:28:47 +00:00
|
|
|
const id = largeVideo.participantId;
|
|
|
|
const videoTrack
|
|
|
|
= getTrackByMediaTypeAndParticipant(
|
|
|
|
tracks,
|
|
|
|
MEDIA_TYPE.VIDEO,
|
|
|
|
id);
|
2016-10-05 14:36:59 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
conference.selectParticipant(
|
2016-11-04 18:28:47 +00:00
|
|
|
videoTrack && videoTrack.videoType === VIDEO_TYPE.CAMERA
|
|
|
|
? id
|
|
|
|
: null);
|
2016-10-05 14:36:59 +00:00
|
|
|
} catch (err) {
|
|
|
|
_handleParticipantError(err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Action to select the participant to be displayed in LargeVideo based on a
|
|
|
|
* variety of factors: if there is a dominant or pinned speaker, or if there are
|
2017-01-17 14:32:20 +00:00
|
|
|
* remote tracks, etc.
|
2016-10-05 14:36:59 +00:00
|
|
|
*
|
|
|
|
* @returns {Function}
|
|
|
|
*/
|
|
|
|
export function selectParticipantInLargeVideo() {
|
|
|
|
return (dispatch, getState) => {
|
|
|
|
const state = getState();
|
2016-11-04 18:28:47 +00:00
|
|
|
const participantId = _electParticipantInLargeVideo(state);
|
2017-01-17 14:44:50 +00:00
|
|
|
const largeVideo = state['features/large-video'];
|
2016-10-05 14:36:59 +00:00
|
|
|
|
|
|
|
if (participantId !== largeVideo.participantId) {
|
|
|
|
dispatch({
|
2017-01-11 18:14:00 +00:00
|
|
|
type: SELECT_LARGE_VIDEO_PARTICIPANT,
|
2016-10-05 14:36:59 +00:00
|
|
|
participantId
|
|
|
|
});
|
|
|
|
|
|
|
|
dispatch(selectParticipant());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the most recent existing video track. It can be local or remote
|
|
|
|
* video.
|
|
|
|
*
|
|
|
|
* @param {Track[]} tracks - All current tracks.
|
|
|
|
* @private
|
|
|
|
* @returns {(Track|undefined)}
|
|
|
|
*/
|
|
|
|
function _electLastVisibleVideo(tracks) {
|
|
|
|
// First we try to get most recent remote video track.
|
2016-11-04 18:28:47 +00:00
|
|
|
for (let i = tracks.length - 1; i >= 0; --i) {
|
|
|
|
const track = tracks[i];
|
|
|
|
|
|
|
|
if (!track.local && track.mediaType === MEDIA_TYPE.VIDEO) {
|
|
|
|
return track;
|
2016-10-05 14:36:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// And if no remote video tracks are available, we select the local one.
|
2016-11-04 18:28:47 +00:00
|
|
|
return getLocalVideoTrack(tracks);
|
2016-10-05 14:36:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-11-04 18:28:47 +00:00
|
|
|
* Returns the identifier of the participant who is to be on the stage i.e.
|
|
|
|
* should be displayed in <tt>LargeVideo</tt>.
|
2016-10-05 14:36:59 +00:00
|
|
|
*
|
2016-11-04 18:28:47 +00:00
|
|
|
* @param {Object} state - The Redux state from which the participant to be
|
|
|
|
* displayed in <tt>LargeVideo</tt> is to be elected.
|
2016-10-05 14:36:59 +00:00
|
|
|
* @private
|
|
|
|
* @returns {(string|undefined)}
|
|
|
|
*/
|
2016-11-04 18:28:47 +00:00
|
|
|
function _electParticipantInLargeVideo(state) {
|
|
|
|
// First get the pinned participant. If the local participant is pinned,
|
|
|
|
// he/she will be shown in LargeVideo.
|
|
|
|
const participants = state['features/base/participants'];
|
2016-10-05 14:36:59 +00:00
|
|
|
let participant = participants.find(p => p.pinned);
|
|
|
|
let id = participant ? participant.id : undefined;
|
|
|
|
|
|
|
|
if (!id) {
|
2016-11-04 18:28:47 +00:00
|
|
|
// No participant is pinned so get the dominant speaker. But the local
|
|
|
|
// participant won't be displayed in LargeVideo even if he/she is the
|
|
|
|
// dominant speaker.
|
|
|
|
participant = participants.find(p => p.dominantSpeaker && !p.local);
|
2016-10-05 14:36:59 +00:00
|
|
|
if (participant) {
|
|
|
|
id = participant.id;
|
|
|
|
}
|
|
|
|
|
2016-11-04 18:28:47 +00:00
|
|
|
if (!id) {
|
|
|
|
// There is no dominant speaker so get the participant with the last
|
|
|
|
// visible video track. This may turn out to be the local
|
|
|
|
// participant.
|
|
|
|
const tracks = state['features/base/tracks'];
|
|
|
|
const videoTrack = _electLastVisibleVideo(tracks);
|
2016-10-05 14:36:59 +00:00
|
|
|
|
2016-11-04 18:28:47 +00:00
|
|
|
id = videoTrack && videoTrack.participantId;
|
|
|
|
}
|
2016-10-05 14:36:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return id;
|
|
|
|
}
|