Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VideoSIPGW updates #2201

Merged
merged 8 commits into from Nov 21, 2017
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions conference.js
Expand Up @@ -25,6 +25,7 @@ import {
conferenceFailed,
conferenceJoined,
conferenceLeft,
conferenceWillJoin,
dataChannelOpened,
EMAIL_COMMAND,
lockStateChanged,
Expand Down Expand Up @@ -1249,6 +1250,7 @@ export default {
= connection.initJitsiConference(
APP.conference.roomName,
this._getConferenceOptions());
APP.store.dispatch(conferenceWillJoin(room));
this._setLocalAudioVideoStreams(localTracks);
this._room = room; // FIXME do not use this

Expand Down
57 changes: 57 additions & 0 deletions doc/sipgw-config.md
@@ -0,0 +1,57 @@
# Configuring sipgw jibri with jitsi-meet

This document describes how you can configure jitsi-meet to use sipgw jibri and enable rooms in 'Add people dialog'
You will need a working deployment of jibri configured to use a regular sip video device, for more info check out the [jibri documentation](https://github.com/jitsi/jibri/blob/master/README.md).

This feature is available for non-guests of the system, so this relies on setting in config.js ``enableUserRolesBasedOnToken: true`` and providing a jwt token when accessing the conference.

* Jicofo configuration:
edit /etc/jitsi/jicofo/sip-communicator.properties (or similar), set the appropriate MUC to look for the Jibri Controllers. This should be the same MUC as is referenced in jibri's config.json file. Restart Jicofo after setting this property.

```
org.jitsi.jicofo.jibri.SIP_BREWERY=TheSipBrewery@conference.yourdomain.com
```

* Jitsi Meet configuration:
- config.js: add
```
enableUserRolesBasedOnToken: true,
peopleSearchQueryTypes: ['conferenceRooms'],
peopleSearchUrl: 'https://api.yourdomain.com/testpath/searchpeople',
```
- interface_config.js:
```
ADD_PEOPLE_APP_NAME: 'Jitsi'
```

The combination of the above settings and providing a jwt token will enable a button under invite option which will show the dialog 'Add people'.

## People search service

When searching in the dialog, a request for results is made to the `peopleSearchUrl` service.

The request is in the following format:
```
https://api.yourdomain.com/testpath/searchpeople?query=testroomname&queryTypes=[%22conferenceRooms%22]&jwt=somejwt
```
The parameters are:
- query - The text entered by the user.
- queryTypes - What type of results we want people, rooms, conferenceRooms. This is the value from config.js `peopleSearchQueryTypes`
- jwt - The token used by the user to access the conference.

The response of the service is a json in the following format:
```
[
{
"id": "address@sip.domain.com",
"name": "Some room name",
"type": "videosipgw"
},
{
"id": "address2@sip.domain.com",
"name": "Some room name2",
"type": "videosipgw"
}
]
```
Type should be `videosipgw`, `name` is the name shown to the user and `id` is the sip address to be called by the sipgw jibri.
18 changes: 16 additions & 2 deletions lang/main.json
Expand Up @@ -420,7 +420,8 @@
"off": "Recording stopped",
"on": "Recording",
"pending": "Recording waiting for a member to join...",
"unavailable": "Oops! The recording service is currently unavailable. We're working on resolving the issue. Please try again later.",
"serviceName": "Recording service",
"unavailable": "Oops! The __serviceName__ is currently unavailable. We're working on resolving the issue. Please try again later.",
"unavailableTitle": "Recording unavailable"
},
"liveStreaming":
Expand All @@ -433,11 +434,24 @@
"off": "Live Streaming stopped",
"on": "Live Streaming",
"pending": "Starting Live Stream...",
"serviceName": "Live Streaming service",
"streamIdRequired": "Please fill in the stream id in order to launch the Live Streaming.",
"streamIdHelp": "Where do I find this?",
"unavailable": "Oops! The Live Streaming service is currently unavailable. We're working on resolving the issue. Please try again later.",
"unavailableTitle": "Live Streaming unavailable"
},
"videoSIPGW":
{
"busy": "We're working on freeing resources. Please try again in a few minutes.",
"busyTitle": "The Room service is currently busy",
"errorInvite": "Conference not established yet. Please try again later.",
"errorInviteTitle": "Error inviting room",
"errorAlreadyInvited": "__displayName__ already invited",
"errorInviteFailedTitle": "Inviting __displayName__ failed",
"errorInviteFailed": "We're working on resolving the issue. Please try again later.",
"pending": "__displayName__ has been invited",
"serviceName": "Room service",
"unavailableTitle": "Room service unavailable"
},
"speakerStats":
{
"hours": "__count__h",
Expand Down
6 changes: 5 additions & 1 deletion modules/UI/recording/Recording.js
Expand Up @@ -51,6 +51,7 @@ export const RECORDING_TRANSLATION_KEYS = {
recordingPendingKey: 'recording.pending',
recordingTitle: 'dialog.recording',
recordingUnavailable: 'recording.unavailable',
recordingUnavailableParams: '$t(recording.serviceName)',
recordingUnavailableTitle: 'recording.unavailableTitle'
};

Expand All @@ -71,7 +72,8 @@ export const STREAMING_TRANSLATION_KEYS = {
recordingOnKey: 'liveStreaming.on',
recordingPendingKey: 'liveStreaming.pending',
recordingTitle: 'dialog.liveStreaming',
recordingUnavailable: 'liveStreaming.unavailable',
recordingUnavailable: 'recording.unavailable',
recordingUnavailableParams: '$t(liveStreaming.serviceName)',
recordingUnavailableTitle: 'liveStreaming.unavailableTitle'
};

Expand Down Expand Up @@ -531,6 +533,8 @@ const Recording = {
default: {
APP.UI.messageHandler.showError({
descriptionKey: this.recordingUnavailable,
descriptionArguments: {
serviceName: this.recordingUnavailableParams },
titleKey: this.recordingUnavailableTitle
});
}
Expand Down
23 changes: 19 additions & 4 deletions react/features/base/conference/actions.js
Expand Up @@ -229,10 +229,25 @@ function _conferenceWillJoin(conference: Object) {
_addLocalTracksToConference(conference, localTracks);
}

dispatch({
type: CONFERENCE_WILL_JOIN,
conference
});
dispatch(conferenceWillJoin(conference));
};
}

/**
* Signals the intention of the application to have the local participant
* join the specified conference.
*
* @param {JitsiConference} conference - The {@code JitsiConference} instance
* the local participant will (try to) join.
* @returns {{
* type: CONFERENCE_WILL_JOIN,
* conference: JitsiConference
* }}
*/
export function conferenceWillJoin(conference: Object) {
return {
type: CONFERENCE_WILL_JOIN,
conference
};
}

Expand Down
1 change: 1 addition & 0 deletions react/features/base/lib-jitsi-meet/index.js
Expand Up @@ -16,6 +16,7 @@ export const JitsiMediaDevicesEvents = JitsiMeetJS.events.mediaDevices;
export const JitsiParticipantConnectionStatus
= JitsiMeetJS.constants.participantConnectionStatus;
export const JitsiRecordingStatus = JitsiMeetJS.constants.recordingStatus;
export const JitsiSIPVideoGWStatus = JitsiMeetJS.constants.sipVideoGW;
export const JitsiTrackErrors = JitsiMeetJS.errors.track;
export const JitsiTrackEvents = JitsiMeetJS.events.track;

Expand Down
23 changes: 16 additions & 7 deletions react/features/invite/components/AddPeopleDialog.web.js
Expand Up @@ -12,7 +12,8 @@ import { Dialog, hideDialog } from '../../base/dialog';
import { translate } from '../../base/i18n';
import { MultiSelectAutocomplete } from '../../base/react';

import { invitePeople, inviteRooms, searchPeople } from '../functions';
import { invitePeople, searchPeople } from '../functions';
import { inviteRooms } from '../../videosipgw';

declare var interfaceConfig: Object;

Expand Down Expand Up @@ -62,6 +63,11 @@ class AddPeopleDialog extends Component<*, *> {
*/
hideDialog: PropTypes.func,

/**
* Used to invite video rooms.
*/
inviteRooms: PropTypes.func,

/**
* Invoked to obtain translated strings.
*/
Expand Down Expand Up @@ -215,16 +221,17 @@ class AddPeopleDialog extends Component<*, *> {
});

this.props._conference
&& inviteRooms(
this.props._conference,
this.state.inviteItems.filter(
i => i.type === 'videosipgw'));
&& this.props.inviteRooms(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a type "room" and the function is called inviteRooms, but you filter for videosipgw. What is the difference between room and videosipgw?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The invitation works with 3 types returned - user, room and videosipgw. User is a regular participant, room is like a chat room with multiple participants like in Stride. And also we videosipgw. So let's say we have chat room (type===room) and conference room (type=== videosipgw).
I agree its a little confusing and I also was wondering about that. If you have an idea how to improve it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can imagine what it would be like with different names, like specifically calling them chatRoom and conferenceRoom/videoRoom/sipGW instead of using the general term room.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I will address that.

this.props._conference,
this.state.inviteItems.filter(
i => i.type === 'videosipgw'));

invitePeople(
this.props._inviteServiceUrl,
this.props._inviteUrl,
this.props._jwt,
this.state.inviteItems.filter(i => i.type === 'user'))
this.state.inviteItems.filter(
i => i.type === 'user' || i.type === 'room'))
.then(
/* onFulfilled */ () => {
this.setState({
Expand Down Expand Up @@ -355,5 +362,7 @@ function _mapStateToProps(state) {
};
}

export default translate(connect(_mapStateToProps, { hideDialog })(
export default translate(connect(_mapStateToProps, {
hideDialog,
inviteRooms })(
AddPeopleDialog));
38 changes: 8 additions & 30 deletions react/features/invite/functions.js
Expand Up @@ -22,19 +22,24 @@ export function getInviteOptionPosition(name: string): number {
* invitation.
* @param {string} inviteUrl - The url to the conference.
* @param {string} jwt - The jwt token to pass to the search service.
* @param {Immutable.List} people - The list of the "user" type items to invite.
* @param {Immutable.List} inviteItems - The list of the "user" or "room"
* type items to invite.
* @returns {Promise} - The promise created by the request.
*/
export function invitePeople( // eslint-disable-line max-params
inviteServiceUrl: string,
inviteUrl: string,
jwt: string,
people: Object) {
inviteItems: Object) {
if (!inviteItems || inviteItems.length === 0) {
return Promise.resolve();
}

return new Promise((resolve, reject) => {
$.post(
`${inviteServiceUrl}?token=${jwt}`,
JSON.stringify({
'invited': people,
'invited': inviteItems,
'url': inviteUrl
}),
resolve,
Expand All @@ -43,33 +48,6 @@ export function invitePeople( // eslint-disable-line max-params
});
}

/**
* Invites room participants to the conference through the SIP Jibri service.
*
* @param {JitsiMeetConference} conference - The conference to which the rooms
* will be invited to.
* @param {Immutable.List} rooms - The list of the "videosipgw" type items to
* invite.
* @returns {void}
*/
export function inviteRooms(
conference: { createVideoSIPGWSession: Function },
rooms: Object) {
for (const room of rooms) {
const { id: sipAddress, name: displayName } = room;

if (sipAddress && displayName) {
const newSession
= conference.createVideoSIPGWSession(sipAddress, displayName);

newSession.start();
} else {
console.error(
`No display name or sip number for ${JSON.stringify(room)}`);
}
}
}

/**
* Indicates if an invite option is enabled in the configuration.
*
Expand Down
23 changes: 23 additions & 0 deletions react/features/videosipgw/actionTypes.js
@@ -0,0 +1,23 @@
/**
* The type of (redux) action which signals that sip GW service change its
* availability status.
*
* {
* type: SIP_GW_AVAILABILITY_CHANGED,
* status: string
* }
*/
export const SIP_GW_AVAILABILITY_CHANGED
= Symbol('SIP_GW_AVAILABILITY_CHANGED');

/**
* The type of the action which signals to invite room participants to the
* conference through the SIP Jibri service.
*
* {
* type: SIP_GW_INVITE_ROOMS,
* conference: JitsiConference,
* rooms: {Immutable.List}
* }
*/
export const SIP_GW_INVITE_ROOMS = Symbol('SIP_GW_INVITE_ROOMS');
22 changes: 22 additions & 0 deletions react/features/videosipgw/actions.js
@@ -0,0 +1,22 @@
/* @flow */

import { SIP_GW_INVITE_ROOMS } from './actionTypes';

/**
* Invites room participants to the conference through the SIP Jibri service.
*
* @param {JitsiMeetConference} conference - The conference to which the rooms
* will be invited to.
* @param {Immutable.List} rooms - The list of the "videosipgw" type items to
* invite.
* @returns {void}
*/
export function inviteRooms(
conference: Object,
rooms: Object) {
return {
type: SIP_GW_INVITE_ROOMS,
conference,
rooms
};
}
4 changes: 4 additions & 0 deletions react/features/videosipgw/index.js
@@ -0,0 +1,4 @@
export * from './actions';

import './middleware';
import './reducer';