fix(api-devices): Initial device function calls

This commit is contained in:
Hristo Terezov 2019-03-25 11:33:41 +00:00
parent 427f49367b
commit ed1d3d3df5
8 changed files with 115 additions and 33 deletions

View File

@ -29,6 +29,7 @@ Its constructor gets a number of options:
* **jwt**: (optional) [JWT](https://jwt.io/) token.
* **onload**: (optional) handler for the iframe onload event.
* **invitees**: (optional) Array of objects containing information about new participants that will be invited in the call.
* **devices**: (optional) A map containing information about the initial devices that will be used in the call.
Example:
@ -43,6 +44,21 @@ var options = {
var api = new JitsiMeetExternalAPI(domain, options);
```
You can set the initial media devices for the call:
```javascript
var domain = "meet.jit.si";
var options = {
...
devices: {
'audioInput': '<device_id>',
'audioOutput': '<device_id>',
'videoInput': '<device_id>'
}
}
var api = new JitsiMeetExternalAPI(domain, options);
```
You can overwrite options set in [config.js] and [interface_config.js].
For example, to enable the filmstrip-only interface mode, you can use:

View File

@ -221,6 +221,8 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
* for iframe onload event.
* @param {Array<Object>} [options.invitees] - Array of objects containing
* information about new participants that will be invited in the call.
* @param {Array<Object>} [options.devices] - Array of objects containing
* information about the initial devices that will be used in the call.
*/
constructor(domain, ...args) {
super();
@ -234,7 +236,8 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
noSSL = false,
jwt = undefined,
onload = undefined,
invitees
invitees,
devices
} = parseArguments(args);
this._parentNode = parentNode;
@ -243,7 +246,8 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
interfaceConfigOverwrite,
jwt,
noSSL,
roomName
roomName,
devices
});
this._createIFrame(height, width, onload);
this._transport = new Transport({

View File

@ -7,6 +7,8 @@ declare var config: Object;
const logger = require('jitsi-meet-logger').getLogger(__filename);
import { configureInitialDevices } from '../devices';
export {
connectionEstablished,
connectionFailed,
@ -25,12 +27,13 @@ export function connect() {
// XXX For web based version we use conference initialization logic
// from the old app (at the moment of writing).
return APP.conference.init({
roomName: room
}).catch(error => {
APP.API.notifyConferenceLeft(APP.conference.roomName);
logger.error(error);
});
return dispatch(configureInitialDevices()).then(
() => APP.conference.init({
roomName: room
}).catch(error => {
APP.API.notifyConferenceLeft(APP.conference.roomName);
logger.error(error);
}));
};
}

View File

@ -1,10 +1,12 @@
import JitsiMeetJS from '../lib-jitsi-meet';
import { updateSettings } from '../settings';
import {
SET_AUDIO_INPUT_DEVICE,
SET_VIDEO_INPUT_DEVICE,
UPDATE_DEVICE_LIST
} from './actionTypes';
import { getDevicesFromURL } from './functions';
/**
* Queries for connected A/V input and output devices and updates the redux
@ -77,3 +79,22 @@ export function updateDeviceList(devices) {
devices
};
}
/**
* Configures the initial A/V devices before the conference has started.
*
* @returns {Function}
*/
export function configureInitialDevices() {
return (dispatch, getState) => new Promise(resolve => {
const devices = getDevicesFromURL(getState());
if (devices) {
dispatch(updateSettings({
...devices
}));
resolve();
}
});
}

View File

@ -1,5 +1,6 @@
// @flow
import { parseURLParams } from '../config';
import JitsiMeetJS from '../lib-jitsi-meet';
import { updateSettings } from '../settings';
@ -30,3 +31,47 @@ export function setAudioOutputDeviceId(
audioOutputDeviceId: newId
})));
}
/**
* Converts an array of media devices into an object organized by device kind.
*
* @param {Array<MediaDeviceInfo>} devices - Available media devices.
* @private
* @returns {Object} An object with the media devices split by type. The keys
* are device type and the values are arrays with devices matching the device
* type.
*/
export function groupDevicesByKind(devices: Object[]): Object {
return {
audioInput: devices.filter(device => device.kind === 'audioinput'),
audioOutput: devices.filter(device => device.kind === 'audiooutput'),
videoInput: devices.filter(device => device.kind === 'videoinput')
};
}
/**
* Returns the devices set in the URL.
*
* @param {Object} state - The redux state.
* @returns {Object|undefined}
*/
export function getDevicesFromURL(state: Object) {
const urlParams
= parseURLParams(state['features/base/connection'].locationURL);
const audioOutputDeviceId = urlParams['devices.audioOutput'];
const cameraDeviceId = urlParams['devices.videoInput'];
const micDeviceId = urlParams['devices.audioInput'];
if (!audioOutputDeviceId && !cameraDeviceId && !micDeviceId) {
return undefined;
}
const devices = {};
audioOutputDeviceId && (devices.audioOutputDeviceId = audioOutputDeviceId);
cameraDeviceId && (devices.cameraDeviceId = cameraDeviceId);
micDeviceId && (devices.micDeviceId = micDeviceId);
return devices;
}

View File

@ -3,6 +3,7 @@ import {
SET_VIDEO_INPUT_DEVICE,
UPDATE_DEVICE_LIST
} from './actionTypes';
import { groupDevicesByKind } from './functions';
import { ReducerRegistry } from '../redux';
@ -27,7 +28,7 @@ ReducerRegistry.register(
(state = DEFAULT_STATE, action) => {
switch (action.type) {
case UPDATE_DEVICE_LIST: {
const deviceList = _groupDevicesByKind(action.devices);
const deviceList = groupDevicesByKind(action.devices);
return {
...deviceList
@ -44,19 +45,3 @@ ReducerRegistry.register(
}
});
/**
* Converts an array of media devices into an object organized by device kind.
*
* @param {Array<MediaDeviceInfo>} devices - Available media devices.
* @private
* @returns {Object} An object with the media devices split by type. The keys
* are device type and the values are arrays with devices matching the device
* type.
*/
function _groupDevicesByKind(devices) {
return {
audioInput: devices.filter(device => device.kind === 'audioinput'),
audioOutput: devices.filter(device => device.kind === 'audiooutput'),
videoInput: devices.filter(device => device.kind === 'videoinput')
};
}

View File

@ -469,16 +469,16 @@ export function urlObjectToString(o: Object): ?string {
let { hash } = url;
for (const configName of [ 'config', 'interfaceConfig' ]) {
for (const urlPrefix of [ 'config', 'interfaceConfig', 'devices' ]) {
const urlParamsArray
= _objectToURLParamsArray(
o[`${configName}Overwrite`]
|| o[configName]
|| o[`${configName}Override`]);
o[`${urlPrefix}Overwrite`]
|| o[urlPrefix]
|| o[`${urlPrefix}Override`]);
if (urlParamsArray.length) {
let urlParamsString
= `${configName}.${urlParamsArray.join(`&${configName}.`)}`;
= `${urlPrefix}.${urlParamsArray.join(`&${urlPrefix}.`)}`;
if (hash.length) {
urlParamsString = `&${urlParamsString}`;

View File

@ -1,6 +1,8 @@
// @flow
import {
getAudioOutputDeviceId,
getAvailableDevices,
groupDevicesByKind,
setAudioInputDevice,
setAudioOutputDeviceId,
setVideoInputDevice
@ -46,7 +48,11 @@ export function getDeviceSelectionDialogProps(stateful: Object | Function) {
* response.
* @returns {boolean}
*/
export function processRequest(dispatch: Dispatch<*>, getState: Function, request: Object, responseCallback: Function) { // eslint-disable-line max-len, max-params
export function processRequest( // eslint-disable-line max-params
dispatch: Dispatch<*>,
getState: Function,
request: Object,
responseCallback: Function) {
if (request.type === 'devices') {
const state = getState();
const settings = state['features/base/settings'];
@ -71,7 +77,9 @@ export function processRequest(dispatch: Dispatch<*>, getState: Function, reques
});
break;
case 'getAvailableDevices':
responseCallback(getState()['features/base/devices']);
dispatch(getAvailableDevices()).then(
devices => responseCallback(groupDevicesByKind(devices)));
break;
case 'setDevice': {
const { device } = request;
@ -87,7 +95,7 @@ export function processRequest(dispatch: Dispatch<*>, getState: Function, reques
dispatch(setVideoInputDevice(device.id));
break;
default:
responseCallback(false);
}
responseCallback(true);