refactor(audio): use preloaded audio stream if provided
Avoids a surplus gUM with local echo test et al
This commit is contained in:
parent
1e37924e41
commit
f4ba6dd9a2
@ -297,7 +297,7 @@ export default class FullAudioBridge extends BaseAudioBridge {
|
||||
|
||||
async _startBroker(options) {
|
||||
try {
|
||||
const { isListenOnly, extension } = options;
|
||||
const { isListenOnly, extension, inputStream } = options;
|
||||
this.inEchoTest = !!extension;
|
||||
this.isListenOnly = isListenOnly;
|
||||
|
||||
@ -314,6 +314,7 @@ export default class FullAudioBridge extends BaseAudioBridge {
|
||||
mediaServer: getMediaServerAdapter(),
|
||||
constraints: getAudioConstraints({ deviceId: this.inputDeviceId }),
|
||||
forceRelay: shouldForceRelay(),
|
||||
stream: (inputStream && inputStream.active) ? inputStream : undefined,
|
||||
};
|
||||
|
||||
this.broker = new FullAudioBroker(
|
||||
|
@ -88,6 +88,8 @@ class SIPSession {
|
||||
this._reconnecting = false;
|
||||
this._currentSessionState = null;
|
||||
this._ignoreCallState = false;
|
||||
|
||||
this.mediaStreamFactory = this.mediaStreamFactory.bind(this)
|
||||
}
|
||||
|
||||
get inputStream() {
|
||||
@ -224,6 +226,7 @@ class SIPSession {
|
||||
inputDeviceId,
|
||||
outputDeviceId,
|
||||
validIceCandidates,
|
||||
inputStream,
|
||||
}, managerCallback) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const callExtension = extension ? `${extension}${this.userData.voiceBridge}` : this.userData.voiceBridge;
|
||||
@ -259,6 +262,7 @@ class SIPSession {
|
||||
isListenOnly,
|
||||
inputDeviceId,
|
||||
outputDeviceId,
|
||||
inputStream,
|
||||
}).catch((reason) => {
|
||||
reject(reason);
|
||||
});
|
||||
@ -287,10 +291,14 @@ class SIPSession {
|
||||
isListenOnly,
|
||||
inputDeviceId,
|
||||
outputDeviceId,
|
||||
inputStream,
|
||||
} = options;
|
||||
|
||||
this.inputDeviceId = inputDeviceId;
|
||||
this.outputDeviceId = outputDeviceId;
|
||||
// If a valid MediaStream was provided it means it was preloaded somewhere
|
||||
// else - let's use it so we don't call gUM needlessly
|
||||
if (inputStream && inputStream.active) this.preloadedInputStream = inputStream;
|
||||
|
||||
const {
|
||||
userId,
|
||||
@ -423,6 +431,17 @@ class SIPSession {
|
||||
return this.stopUserAgent();
|
||||
}
|
||||
|
||||
mediaStreamFactory(constraints) {
|
||||
if (this.preloadedInputStream && this.preloadedInputStream.active) {
|
||||
return Promise.resolve(this.preloadedInputStream);
|
||||
}
|
||||
// The rest of this mimicks the default factory behavior.
|
||||
if (!constraints.audio && !constraints.video) {
|
||||
return Promise.resolve(new MediaStream());
|
||||
}
|
||||
return navigator.mediaDevices.getUserMedia(constraints);
|
||||
}
|
||||
|
||||
createUserAgent(iceServers) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.userRequestedHangup === true) reject();
|
||||
@ -465,6 +484,9 @@ class SIPSession {
|
||||
let userAgentConnected = false;
|
||||
const token = `sessionToken=${sessionToken}`;
|
||||
|
||||
// Create session description handler factory
|
||||
const customSDHFactory = SIP.Web.defaultSessionDescriptionHandlerFactory(this.mediaStreamFactory);
|
||||
|
||||
this.userAgent = new SIP.UserAgent({
|
||||
uri: SIP.UserAgent.makeURI(`sip:${encodeURIComponent(callerIdName)}@${hostname}`),
|
||||
transportOptions: {
|
||||
@ -474,6 +496,7 @@ class SIPSession {
|
||||
keepAliveDebounce: WEBSOCKET_KEEP_ALIVE_DEBOUNCE,
|
||||
traceSip: TRACE_SIP,
|
||||
},
|
||||
sessionDescriptionHandlerFactory: customSDHFactory,
|
||||
sessionDescriptionHandlerFactoryOptions: {
|
||||
peerConnectionConfiguration: {
|
||||
iceServers,
|
||||
@ -1280,7 +1303,12 @@ export default class SIPBridge extends BaseAudioBridge {
|
||||
return this.activeSession ? this.activeSession.ignoreCallState : false;
|
||||
}
|
||||
|
||||
joinAudio({ isListenOnly, extension, validIceCandidates }, managerCallback) {
|
||||
joinAudio({
|
||||
isListenOnly,
|
||||
extension,
|
||||
validIceCandidates,
|
||||
inputStream,
|
||||
}, managerCallback) {
|
||||
const hasFallbackDomain = typeof IPV4_FALLBACK_DOMAIN === 'string' && IPV4_FALLBACK_DOMAIN !== '';
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -1319,6 +1347,7 @@ export default class SIPBridge extends BaseAudioBridge {
|
||||
inputDeviceId,
|
||||
outputDeviceId,
|
||||
validIceCandidates,
|
||||
inputStream,
|
||||
}, callback)
|
||||
.then((value) => {
|
||||
this.changeOutputDevice(outputDeviceId, true);
|
||||
@ -1339,6 +1368,7 @@ export default class SIPBridge extends BaseAudioBridge {
|
||||
inputDeviceId,
|
||||
outputDeviceId,
|
||||
validIceCandidates,
|
||||
inputStream,
|
||||
}, callback)
|
||||
.then((value) => {
|
||||
this.changeOutputDevice(outputDeviceId, true);
|
||||
|
@ -135,7 +135,7 @@ class AudioModal extends Component {
|
||||
this.handleRetryGoToEchoTest = this.handleRetryGoToEchoTest.bind(this);
|
||||
this.handleGoToEchoTest = this.handleGoToEchoTest.bind(this);
|
||||
this.handleJoinMicrophone = this.handleJoinMicrophone.bind(this);
|
||||
this.handleJoinSimplifiedEcho = this.handleJoinSimplifiedEcho.bind(this);
|
||||
this.handleJoinLocalEcho = this.handleJoinLocalEcho.bind(this);
|
||||
this.handleJoinListenOnly = this.handleJoinListenOnly.bind(this);
|
||||
this.skipAudioOptions = this.skipAudioOptions.bind(this);
|
||||
|
||||
@ -336,12 +336,14 @@ class AudioModal extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
handleJoinSimplifiedEcho() {
|
||||
handleJoinLocalEcho(inputStream) {
|
||||
const { changeInputStream } = this.props;
|
||||
// Reset the modal to a connecting state - this kind of sucks?
|
||||
// FIXME - prlanzarin Apr 04 2022
|
||||
// prlanzarin Apr 04 2022
|
||||
this.setState({
|
||||
content: null,
|
||||
});
|
||||
if (inputStream) changeInputStream(inputStream);
|
||||
this.handleJoinMicrophone();
|
||||
}
|
||||
|
||||
@ -510,7 +512,7 @@ class AudioModal extends Component {
|
||||
|
||||
const confirmationCallback = !localEchoEnabled
|
||||
? this.handleRetryGoToEchoTest
|
||||
: this.handleJoinSimplifiedEcho;
|
||||
: this.handleJoinLocalEcho;
|
||||
|
||||
const handleGUMFailure = () => {
|
||||
this.setState({
|
||||
|
@ -71,6 +71,7 @@ export default lockContextContainer(withModalMounter(withTracker(({ userLocks })
|
||||
leaveEchoTest,
|
||||
changeInputDevice: (inputDeviceId) => Service
|
||||
.changeInputDevice(inputDeviceId),
|
||||
changeInputStream: (inputStream) => Service.changeInputStream(inputStream),
|
||||
changeOutputDevice: (outputDeviceId, isLive) => Service
|
||||
.changeOutputDevice(outputDeviceId, isLive),
|
||||
joinEchoTest: () => Service.joinEchoTest(),
|
||||
|
@ -71,6 +71,7 @@ class AudioSettings extends React.Component {
|
||||
|
||||
this.handleInputChange = this.handleInputChange.bind(this);
|
||||
this.handleOutputChange = this.handleOutputChange.bind(this);
|
||||
this.handleConfirmationClick = this.handleConfirmationClick.bind(this);
|
||||
|
||||
this.state = {
|
||||
inputDeviceId,
|
||||
@ -202,12 +203,32 @@ class AudioSettings extends React.Component {
|
||||
) : null
|
||||
}
|
||||
|
||||
handleConfirmationClick () {
|
||||
const {
|
||||
withEcho,
|
||||
handleConfirmation,
|
||||
} = this.props;
|
||||
const {
|
||||
stream,
|
||||
} = this.state;
|
||||
|
||||
// The local echo mode is not enabled or there isn't any stream in this:
|
||||
// just run the provided callback
|
||||
if (!withEcho || !stream) return handleConfirmation();
|
||||
|
||||
// Local echo mode was enabled and there is a valid input stream => call
|
||||
// the confirmation callback with the input stream as arg so it can be used
|
||||
// in upstream components. The rationale is not surplus gUM calls.
|
||||
// We're cloning it because the original will be cleaned up on unmount here.
|
||||
const inputStream = stream.clone();
|
||||
return handleConfirmation(inputStream);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
isConnecting,
|
||||
intl,
|
||||
handleBack,
|
||||
handleConfirmation,
|
||||
} = this.props;
|
||||
|
||||
const { inputDeviceId, outputDeviceId } = this.state;
|
||||
@ -269,7 +290,7 @@ class AudioSettings extends React.Component {
|
||||
size="md"
|
||||
color="primary"
|
||||
label={intl.formatMessage(intlMessages.retryLabel)}
|
||||
onClick={handleConfirmation}
|
||||
onClick={this.handleConfirmationClick}
|
||||
/>
|
||||
</Styled.EnterAudio>
|
||||
</Styled.FormWrapper>
|
||||
|
@ -110,6 +110,7 @@ export default {
|
||||
joinEchoTest: () => AudioManager.joinEchoTest(),
|
||||
toggleMuteMicrophone: debounce(toggleMuteMicrophone, 500, { leading: true, trailing: false }),
|
||||
changeInputDevice: inputDeviceId => AudioManager.changeInputDevice(inputDeviceId),
|
||||
changeInputStream: (newInputStream) => AudioManager.inputStream = newInputStream,
|
||||
liveChangeInputDevice: inputDeviceId => AudioManager.liveChangeInputDevice(inputDeviceId),
|
||||
changeOutputDevice: (outputDeviceId, isLive) => {
|
||||
if (AudioManager.outputDeviceId !== outputDeviceId) {
|
||||
|
@ -18,7 +18,7 @@ class FullAudioBroker extends BaseBroker {
|
||||
this.offering = true;
|
||||
|
||||
// Optional parameters are: caleeName, iceServers, offering,
|
||||
// mediaServer, extension, constraints
|
||||
// mediaServer, extension, constraints, stream
|
||||
Object.assign(this, options);
|
||||
}
|
||||
|
||||
@ -62,6 +62,7 @@ class FullAudioBroker extends BaseBroker {
|
||||
joinAudio() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
audioStream: this.stream,
|
||||
mediaConstraints: {
|
||||
audio: this.constraints ? this.constraints : true,
|
||||
video: false,
|
||||
|
Loading…
Reference in New Issue
Block a user