feat(iframe-api): Device handling.

This commit is contained in:
Hristo Terezov 2019-03-21 12:33:40 +00:00
parent 659e420005
commit 427f49367b
7 changed files with 434 additions and 144 deletions

View File

@ -65,6 +65,84 @@ var api = new JitsiMeetExternalAPI(domain, options);
### Controlling the embedded Jitsi Meet Conference
You can control the available devices with the following methods of `JitsiMeetExternalAPI` instance:
* **getAvailableDevices** - Retrieve a list of available devices.
```javascript
api.getAvailableDevices().then(function(devices) {
// devices = {
// 'audioInput': [{
// deviceId: "ID"
// groupId: "grpID"
// kind: "audioinput"
// label: "Label"
// },....],
// 'audioOutput': [{
// deviceId: "ID"
// groupId: "grpID"
// kind: "audioOutput"
// label: "Label"
// },....],
// 'videoInput': [{
// deviceId: "ID"
// groupId: "grpID"
// kind: "videoInput"
// label: "Label"
// },....]
// }
...
});
```
* **getCurrentDevices** - Retrieve a list with the devices that are currently sected.
```javascript
api.getCurrentDevices().then(function(devices) {
// devices = {
// 'audioInput': 'deviceID',
// 'audioOutput': 'deviceID',
// 'videoInput': 'deviceID'
// }
...
});
```
* **isDeviceChangeAvailable** - Resolves with true if the device change is available and with false if not.
```javascript
// The accepted deviceType values are - 'output', 'input' or undefined.
api.isDeviceChangeAvailable(deviceType).then(function(isDeviceChangeAvailable) {
...
});
```
* **isDeviceListAvailable** - Resolves with true if the device list is available and with false if not.
```javascript
api.isDeviceListAvailable().then(function(isDeviceListAvailable) {
...
});
```
* **isMultipleAudioInputSupported** - Resolves with true if the device list is available and with false if not.
```javascript
api.isMultipleAudioInputSupported().then(function(isMultipleAudioInputSupported) {
...
});
```
* **setAudioInputDevice** - Sets the audio input device to the one with the id that is passed.
```javascript
api.setAudioInputDevice(deviceId);
```
* **setAudioOutputDevice** - Sets the audio output device to the one with the id that is passed.
```javascript
api.setAudioOutputDevice(deviceId);
```
* **setVideoInputDevice** - Sets the video input device to the one with the id that is passed.
```javascript
api.setVideoInputDevice(deviceId);
```
You can control the embedded Jitsi Meet conference using the `JitsiMeetExternalAPI` object by using `executeCommand`:
```javascript

View File

@ -11,6 +11,9 @@ import { invite } from '../../react/features/invite';
import { getJitsiMeetTransport } from '../transport';
import { API_ID } from './constants';
import {
processRequest
} from '../../react/features/device-selection/functions';
const logger = require('jitsi-meet-logger').getLogger(__filename);
@ -117,6 +120,12 @@ function initCommands() {
return false;
});
transport.on('request', (request, callback) => {
const { dispatch, getState } = APP.store;
if (processRequest(dispatch, getState, request, callback)) {
return true;
}
const { name } = request;
switch (name) {

View File

@ -7,6 +7,16 @@ import {
} from '../../transport';
import electronPopupsConfig from './electronPopupsConfig.json';
import {
getAvailableDevices,
getCurrentDevices,
isDeviceChangeAvailable,
isDeviceListAvailable,
isMultipleAudioInputSupported,
setAudioInputDevice,
setAudioOutputDevice,
setVideoInputDevice
} from './functions';
const logger = require('jitsi-meet-logger').getLogger(__filename);
@ -593,6 +603,24 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
}
}
/**
* Returns Promise that resolves with result an list of available devices.
*
* @returns {Promise}
*/
getAvailableDevices() {
return getAvailableDevices(this._transport);
}
/**
* Returns Promise that resolves with current selected devices.
*
* @returns {Promise}
*/
getCurrentDevices() {
return getCurrentDevices(this._transport);
}
/**
* Check if the audio is available.
*
@ -605,6 +633,38 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
});
}
/**
* Returns Promise that resolves with true if the device change is available
* and with false if not.
*
* @param {string} [deviceType] - Values - 'output', 'input' or undefined.
* Default - 'input'.
* @returns {Promise}
*/
isDeviceChangeAvailable(deviceType) {
return isDeviceChangeAvailable(this._transport, deviceType);
}
/**
* Returns Promise that resolves with true if the device list is available
* and with false if not.
*
* @returns {Promise}
*/
isDeviceListAvailable() {
return isDeviceListAvailable(this._transport);
}
/**
* Returns Promise that resolves with true if the device list is available
* and with false if not.
*
* @returns {Promise}
*/
isMultipleAudioInputSupported() {
return isMultipleAudioInputSupported(this._transport);
}
/**
* Invite people to the call.
*
@ -771,6 +831,36 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
});
}
/**
* Sets the audio input device to the one with the id that is passed.
*
* @param {string} deviceId - The id of the new device.
* @returns {Promise}
*/
setAudioInputDevice(deviceId) {
return setAudioInputDevice(this._transport, deviceId);
}
/**
* Sets the audio output device to the one with the id that is passed.
*
* @param {string} deviceId - The id of the new device.
* @returns {Promise}
*/
setAudioOutputDevice(deviceId) {
return setAudioOutputDevice(this._transport, deviceId);
}
/**
* Sets the video input device to the one with the id that is passed.
*
* @param {string} deviceId - The id of the new device.
* @returns {Promise}
*/
setVideoInputDevice(deviceId) {
return setVideoInputDevice(this._transport, deviceId);
}
/**
* Returns the configuration for electron for the windows that are open
* from Jitsi Meet.

162
modules/API/external/functions.js vendored Normal file
View File

@ -0,0 +1,162 @@
// @flow
import Logger from 'jitsi-meet-logger';
const logger = Logger.getLogger(__filename);
/**
* Returns Promise that resolves with result an list of available devices.
*
* @param {Transport} transport - The @code{Transport} instance responsible for
* the external communication.
* @returns {Promise}
*/
export function getAvailableDevices(transport: Object) {
return transport.sendRequest({
type: 'devices',
name: 'getAvailableDevices'
}).catch(e => {
logger.error(e);
return {};
});
}
/**
* Returns Promise that resolves with current selected devices.
*
* @param {Transport} transport - The @code{Transport} instance responsible for
* the external communication.
* @returns {Promise}
*/
export function getCurrentDevices(transport: Object) {
return transport.sendRequest({
type: 'devices',
name: 'getCurrentDevices'
}).catch(e => {
logger.error(e);
return {};
});
}
/**
* Returns Promise that resolves with true if the device change is available
* and with false if not.
*
* @param {Transport} transport - The @code{Transport} instance responsible for
* the external communication.
* @param {string} [deviceType] - Values - 'output', 'input' or undefined.
* Default - 'input'.
* @returns {Promise}
*/
export function isDeviceChangeAvailable(transport: Object, deviceType: string) {
return transport.sendRequest({
deviceType,
type: 'devices',
name: 'isDeviceChangeAvailable'
}).catch(e => {
logger.error(e);
return false;
});
}
/**
* Returns Promise that resolves with true if the device list is available
* and with false if not.
*
* @param {Transport} transport - The @code{Transport} instance responsible for
* the external communication.
* @returns {Promise}
*/
export function isDeviceListAvailable(transport: Object) {
return transport.sendRequest({
type: 'devices',
name: 'isDeviceListAvailable'
}).catch(e => {
logger.error(e);
return false;
});
}
/**
* Returns Promise that resolves with true if the device list is available
* and with false if not.
*
* @param {Transport} transport - The @code{Transport} instance responsible for
* the external communication.
* @returns {Promise}
*/
export function isMultipleAudioInputSupported(transport: Object) {
return transport.sendRequest({
type: 'devices',
name: 'isMultipleAudioInputSupported'
}).catch(e => {
logger.error(e);
return false;
});
}
/**
* Sets the audio input device to the one with the id that is passed.
*
* @param {Transport} transport - The @code{Transport} instance responsible for
* the external communication.
* @param {string} id - The id of the new device.
* @returns {Promise}
*/
export function setAudioInputDevice(transport: Object, id: string) {
return _setDevice(transport, {
id,
kind: 'audioinput'
});
}
/**
* Sets the audio output device to the one with the id that is passed.
*
* @param {Transport} transport - The @code{Transport} instance responsible for
* the external communication.
* @param {string} id - The id of the new device.
* @returns {Promise}
*/
export function setAudioOutputDevice(transport: Object, id: string) {
return _setDevice(transport, {
id,
kind: 'audiooutput'
});
}
/**
* Sets the currently used device to the one that is passed.
*
* @param {Transport} transport - The @code{Transport} instance responsible for
* the external communication.
* @param {Object} device - The new device to be used.
* @returns {Promise}
*/
function _setDevice(transport: Object, device) {
return transport.sendRequest({
type: 'devices',
name: 'setDevice',
device
});
}
/**
* Sets the video input device to the one with the id that is passed.
*
* @param {Transport} transport - The @code{Transport} instance responsible for
* the external communication.
* @param {string} id - The id of the new device.
* @returns {Promise}
*/
export function setVideoInputDevice(transport: Object, id: string) {
return _setDevice(transport, {
id,
kind: 'videoinput'
});
}

View File

@ -6,17 +6,15 @@ import {
import { createDeviceChangedEvent, sendAnalytics } from '../analytics';
import {
getAudioOutputDeviceId,
setAudioInputDevice,
setAudioOutputDeviceId,
setVideoInputDevice
} from '../base/devices';
import { i18next } from '../base/i18n';
import JitsiMeetJS from '../base/lib-jitsi-meet';
import { updateSettings } from '../base/settings';
import { SET_DEVICE_SELECTION_POPUP_DATA } from './actionTypes';
import { getDeviceSelectionDialogProps } from './functions';
import { getDeviceSelectionDialogProps, processRequest } from './functions';
const logger = require('jitsi-meet-logger').getLogger(__filename);
@ -60,7 +58,7 @@ export function openDeviceSelectionPopup() {
});
transport.on('request',
_processRequest.bind(undefined, dispatch, getState));
processRequest.bind(undefined, dispatch, getState));
transport.on('event', event => {
if (event.type === 'devices-dialog' && event.name === 'close') {
popup.close();
@ -80,75 +78,6 @@ export function openDeviceSelectionPopup() {
};
}
/**
* Processes device requests from external applications.
*
* @param {Dispatch} dispatch - The redux {@code dispatch} function.
* @param {Function} getState - The redux function that gets/retrieves the redux
* state.
* @param {Object} request - The request to be processed.
* @param {Function} responseCallback - The callback that will send the
* response.
* @returns {boolean}
*/
function _processRequest(dispatch, getState, request, responseCallback) { // eslint-disable-line max-len, max-params
if (request.type === 'devices') {
const state = getState();
const settings = state['features/base/settings'];
switch (request.name) {
case 'isDeviceListAvailable':
responseCallback(JitsiMeetJS.mediaDevices.isDeviceListAvailable());
break;
case 'isDeviceChangeAvailable':
responseCallback(
JitsiMeetJS.mediaDevices.isDeviceChangeAvailable(
request.deviceType));
break;
case 'isMultipleAudioInputSupported':
responseCallback(JitsiMeetJS.isMultipleAudioInputSupported());
break;
case 'getCurrentDevices':
responseCallback({
audioInput: settings.micDeviceId,
audioOutput: getAudioOutputDeviceId(),
videoInput: settings.cameraDeviceId
});
break;
case 'getAvailableDevices':
responseCallback(getState()['features/base/devices']);
break;
case 'setDevice': {
const { device } = request;
switch (device.kind) {
case 'audioinput':
dispatch(setAudioInputDevice(device.id));
break;
case 'audiooutput':
setAudioOutputDeviceId(device.id, dispatch);
break;
case 'videoinput':
dispatch(setVideoInputDevice(device.id));
break;
default:
}
responseCallback(true);
break;
}
default:
return false;
}
return true;
}
return false;
}
/**
* Sets information about device selection popup in the store.
*

View File

@ -1,5 +1,10 @@
// @flow
import { getAudioOutputDeviceId } from '../base/devices';
import {
getAudioOutputDeviceId,
setAudioInputDevice,
setAudioOutputDeviceId,
setVideoInputDevice
} from '../base/devices';
import JitsiMeetJS from '../base/lib-jitsi-meet';
import { toState } from '../base/redux';
@ -29,3 +34,72 @@ export function getDeviceSelectionDialogProps(stateful: Object | Function) {
selectedVideoInputId: settings.cameraDeviceId
};
}
/**
* Processes device requests from external applications.
*
* @param {Dispatch} dispatch - The redux {@code dispatch} function.
* @param {Function} getState - The redux function that gets/retrieves the redux
* state.
* @param {Object} request - The request to be processed.
* @param {Function} responseCallback - The callback that will send the
* response.
* @returns {boolean}
*/
export function processRequest(dispatch: Dispatch<*>, getState: Function, request: Object, responseCallback: Function) { // eslint-disable-line max-len, max-params
if (request.type === 'devices') {
const state = getState();
const settings = state['features/base/settings'];
switch (request.name) {
case 'isDeviceListAvailable':
responseCallback(JitsiMeetJS.mediaDevices.isDeviceListAvailable());
break;
case 'isDeviceChangeAvailable':
responseCallback(
JitsiMeetJS.mediaDevices.isDeviceChangeAvailable(
request.deviceType));
break;
case 'isMultipleAudioInputSupported':
responseCallback(JitsiMeetJS.isMultipleAudioInputSupported());
break;
case 'getCurrentDevices':
responseCallback({
audioInput: settings.micDeviceId,
audioOutput: getAudioOutputDeviceId(),
videoInput: settings.cameraDeviceId
});
break;
case 'getAvailableDevices':
responseCallback(getState()['features/base/devices']);
break;
case 'setDevice': {
const { device } = request;
switch (device.kind) {
case 'audioinput':
dispatch(setAudioInputDevice(device.id));
break;
case 'audiooutput':
setAudioOutputDeviceId(device.id, dispatch);
break;
case 'videoinput':
dispatch(setVideoInputDevice(device.id));
break;
default:
}
responseCallback(true);
break;
}
default:
return false;
}
return true;
}
return false;
}

View File

@ -1,7 +1,6 @@
/* global JitsiMeetJS */
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';
@ -10,13 +9,21 @@ import {
PostMessageTransportBackend,
Transport
} from '../../../../../modules/transport';
import {
getAvailableDevices,
getCurrentDevices,
isDeviceChangeAvailable,
isDeviceListAvailable,
isMultipleAudioInputSupported,
setAudioInputDevice,
setAudioOutputDevice,
setVideoInputDevice
} from '../../../../../modules/API/external';
import { parseURLParams } from '../../../base/config';
import { DialogWithTabs } from '../../../base/dialog';
import { DeviceSelection } from '../../../device-selection';
const logger = Logger.getLogger(__filename);
/**
* Implements a class that renders the React components for the device selection
* popup page and handles the communication between the components and Jitsi
@ -102,14 +109,7 @@ export default class DeviceSelectionPopup {
* @returns {Promise}
*/
_getAvailableDevices() {
return this._transport.sendRequest({
type: 'devices',
name: 'getAvailableDevices'
}).catch(e => {
logger.error(e);
return {};
});
return getAvailableDevices(this._transport);
}
/**
@ -118,14 +118,7 @@ export default class DeviceSelectionPopup {
* @returns {Promise}
*/
_getCurrentDevices() {
return this._transport.sendRequest({
type: 'devices',
name: 'getCurrentDevices'
}).catch(e => {
logger.error(e);
return {};
});
return getCurrentDevices(this._transport);
}
/**
@ -170,15 +163,7 @@ export default class DeviceSelectionPopup {
* @returns {Promise}
*/
_isDeviceChangeAvailable(deviceType) {
return this._transport.sendRequest({
deviceType,
type: 'devices',
name: 'isDeviceChangeAvailable'
}).catch(e => {
logger.error(e);
return false;
});
return isDeviceChangeAvailable(this._transport, deviceType);
}
/**
@ -188,14 +173,7 @@ export default class DeviceSelectionPopup {
* @returns {Promise}
*/
_isDeviceListAvailable() {
return this._transport.sendRequest({
type: 'devices',
name: 'isDeviceListAvailable'
}).catch(e => {
logger.error(e);
return false;
});
return isDeviceListAvailable(this._transport);
}
/**
@ -205,14 +183,7 @@ export default class DeviceSelectionPopup {
* @returns {Promise}
*/
_isMultipleAudioInputSupported() {
return this._transport.sendRequest({
type: 'devices',
name: 'isMultipleAudioInputSupported'
}).catch(e => {
logger.error(e);
return false;
});
return isMultipleAudioInputSupported(this._transport);
}
/**
@ -281,10 +252,7 @@ export default class DeviceSelectionPopup {
* @returns {Promise}
*/
_setAudioInputDevice(id) {
return this._setDevice({
id,
kind: 'audioinput'
});
return setAudioInputDevice(this._transport, id);
}
/**
@ -294,24 +262,7 @@ export default class DeviceSelectionPopup {
* @returns {Promise}
*/
_setAudioOutputDevice(id) {
return this._setDevice({
id,
kind: 'audiooutput'
});
}
/**
* Sets the currently used device to the one that is passed.
*
* @param {Object} device - The new device to be used.
* @returns {Promise}
*/
_setDevice(device) {
return this._transport.sendRequest({
type: 'devices',
name: 'setDevice',
device
});
return setAudioOutputDevice(this._transport, id);
}
/**
@ -321,10 +272,7 @@ export default class DeviceSelectionPopup {
* @returns {Promise}
*/
_setVideoInputDevice(id) {
return this._setDevice({
id,
kind: 'videoinput'
});
return setVideoInputDevice(this._transport, id);
}
/**