Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

webrtc config electron #850

Merged
merged 21 commits into from
Jun 1, 2017
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions src/CallMediaHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
Copyright 2017 Michael Telatynski <7t3chguy@gmail.com>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import UserSettingsStore from './UserSettingsStore';
import * as Matrix from 'matrix-js-sdk';
Copy link
Member

Choose a reason for hiding this comment

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

this feels a bit weird - shouldn't this be operating on a MatrixClient rather than importing the whole js-sdk as Matrix?

Copy link
Member Author

@t3chguy t3chguy Apr 29, 2017

Choose a reason for hiding this comment

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

@ara4n it's in the same class as createNewMatrixCall is in, which looking at VectorConferenceHandler is used in the same manner. Just following the patterns I see. The other way to do it would be to pass the devices when initiating/answering a call, would you prefer this?

Copy link
Member

Choose a reason for hiding this comment

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

hm, ok. i guess this is ok

import q from 'q';

export default {
getDevices: function() {
// Only needed for Electron atm, though should work in modern browsers
// once permission has been granted to the webapp
return navigator.mediaDevices.enumerateDevices().then(function(devices) {
const audioIn = {};
const videoIn = {};

if (devices.some((device) => !device.label)) return false;

devices.forEach((device) => {
switch (device.kind) {
case 'audioinput': audioIn[device.deviceId] = device.label; break;
case 'videoinput': videoIn[device.deviceId] = device.label; break;
}
});

// console.log("Loaded WebRTC Devices", mediaDevices);
return {
audioinput: audioIn,
videoinput: videoIn,
};
}, (error) => { console.log('Unable to refresh WebRTC Devices: ', error); });
},

loadDevices: function() {
// this.getDevices().then((devices) => {
const localSettings = UserSettingsStore.getLocalSettings();
// // if deviceId is not found, automatic fallback is in spec
// // recall previously stored inputs if any
Matrix.setMatrixCallAudioInput(localSettings['webrtc_audioinput']);
Matrix.setMatrixCallVideoInput(localSettings['webrtc_videoinput']);
// });
},

setAudioInput: function(deviceId) {
UserSettingsStore.setLocalSetting('webrtc_audioinput', deviceId);
Matrix.setMatrixCallAudioInput(deviceId);
},

setVideoInput: function(deviceId) {
UserSettingsStore.setLocalSetting('webrtc_videoinput', deviceId);
Matrix.setMatrixCallVideoInput(deviceId);
},
};
3 changes: 3 additions & 0 deletions src/components/structures/LoggedInView.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import React from 'react';
import KeyCode from '../../KeyCode';
import Notifier from '../../Notifier';
import PageTypes from '../../PageTypes';
import CallMediaHandler from '../../CallMediaHandler';
import sdk from '../../index';
import dis from '../../dispatcher';

Expand Down Expand Up @@ -71,6 +72,8 @@ export default React.createClass({
// RoomView.getScrollState()
this._scrollStateMap = {};

CallMediaHandler.loadDevices();

document.addEventListener('keydown', this._onKeyDown);
},

Expand Down
107 changes: 106 additions & 1 deletion src/components/structures/UserSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const dis = require("../../dispatcher");
const q = require('q');
const packageJson = require('../../../package.json');
const UserSettingsStore = require('../../UserSettingsStore');
const CallMediaHandler = require('../../CallMediaHandler');
const GeminiScrollbar = require('react-gemini-scrollbar');
const Email = require('../../email');
const AddThreepid = require('../../AddThreepid');
Expand Down Expand Up @@ -119,7 +120,6 @@ const THEMES = [
},
];


module.exports = React.createClass({
displayName: 'UserSettings',

Expand Down Expand Up @@ -157,6 +157,7 @@ module.exports = React.createClass({
email_add_pending: false,
vectorVersion: undefined,
rejectingInvites: false,
mediaDevices: null,
};
},

Expand All @@ -177,6 +178,8 @@ module.exports = React.createClass({
});
}

this._refreshMediaDevices();

// Bulk rejecting invites:
// /sync won't have had time to return when UserSettings re-renders from state changes, so getRooms()
// will still return rooms with invites. To get around this, add a listener for
Expand Down Expand Up @@ -218,6 +221,20 @@ module.exports = React.createClass({
}
},

_refreshMediaDevices: function() {
q().then(() => {
return CallMediaHandler.getDevices();
}).then((mediaDevices) => {
// console.log("got mediaDevices", mediaDevices, this._unmounted);
if (this._unmounted) return;
this.setState({
mediaDevices,
activeAudioInput: this._localSettings['webrtc_audioinput'] || 'default',
activeVideoInput: this._localSettings['webrtc_videoinput'] || 'default',
Copy link
Member Author

Choose a reason for hiding this comment

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

do these need changing also?

});
});
},

_refreshFromServer: function() {
const self = this;
q.all([
Expand Down Expand Up @@ -787,6 +804,93 @@ module.exports = React.createClass({
</div>;
},

_mapWebRtcDevicesToSpans: function(devices) {
return Object.keys(devices).map(
(deviceId) => <span key={deviceId}>{devices[deviceId]}</span>
);
},

_setAudioInput: function(deviceId) {
this.setState({activeAudioInput: deviceId});
CallMediaHandler.setAudioInput(deviceId);
},

_setVideoInput: function(deviceId) {
this.setState({activeVideoInput: deviceId});
CallMediaHandler.setVideoInput(deviceId);
},

_requestMediaPermissions: function(event) {
const getUserMedia = (
window.navigator.getUserMedia || window.navigator.webkitGetUserMedia || window.navigator.mozGetUserMedia
);
if (getUserMedia) {
return getUserMedia.apply(window.navigator, [
{ video: true, audio: true },
this._refreshMediaDevices,
function() {
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
Modal.createDialog(ErrorDialog, {
title: "No media permissions",
description: "You may need to manually permit Riot to access your microphone/webcam",
});
},
]);
}
},

_renderWebRtcSettings: function() {
if (this.state.mediaDevices === false) {
return <div>
<h3>WebRTC</h3>
Copy link
Member

Choose a reason for hiding this comment

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

let's call it VoIP. needs i18n

<div className="mx_UserSettings_section">
<p className="mx_UserSettings_link" onClick={this._requestMediaPermissions}>
Missing Media Permissions, click here to request.
</p>
</div>
</div>;
} else if (!this.state.mediaDevices) return;

const Dropdown = sdk.getComponent('elements.Dropdown');

let microphoneDropdown = <p>No Microphones detected</p>;
let webcamDropdown = <p>No Webcams detected</p>;

const audioInputs = this.state.mediaDevices.audioinput;
if ('default' in audioInputs) {
Copy link
Member

Choose a reason for hiding this comment

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

needs if (Object.keys(audioInputs).length > 0) {

microphoneDropdown = <div>
<h4>Microphone</h4>
<Dropdown
className="mx_UserSettings_webRtcDevices_dropdown"
value={this.state.activeAudioInput}
onOptionChange={this._setAudioInput}>
{this._mapWebRtcDevicesToSpans(audioInputs)}
</Dropdown>
</div>;
}

const videoInputs = this.state.mediaDevices.videoinput;
if ('default' in videoInputs) {
Copy link
Member

Choose a reason for hiding this comment

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

needs if (Object.keys(videoInputs).length > 0) {

webcamDropdown = <div>
<h4>Cameras</h4>
<Dropdown
className="mx_UserSettings_webRtcDevices_dropdown"
value={this.state.activeVideoInput}
onOptionChange={this._setVideoInput}>
{this._mapWebRtcDevicesToSpans(videoInputs)}
</Dropdown>
</div>;
}

return <div>
<h3>WebRTC</h3>
Copy link
Member

Choose a reason for hiding this comment

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

Let's call it VoIP, and i18n please

<div className="mx_UserSettings_section">
{microphoneDropdown}
{webcamDropdown}
</div>
</div>;
},

_showSpoiler: function(event) {
const target = event.target;
target.innerHTML = target.getAttribute('data-spoiler');
Expand Down Expand Up @@ -983,6 +1087,7 @@ module.exports = React.createClass({

{this._renderUserInterfaceSettings()}
{this._renderLabs()}
{this._renderWebRtcSettings()}
{this._renderDevicesPanel()}
{this._renderCryptoInfo()}
{this._renderBulkOptions()}
Expand Down