openvidu-components: Fixed replace tracks in session

pull/743/head
csantosm 2022-06-24 16:12:36 +02:00
parent ea3f16778e
commit eaed1b3692
4 changed files with 87 additions and 150 deletions

View File

@ -1,11 +1,9 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { PublisherSpeakingEvent, StreamManager, StreamPropertyChangedEvent } from 'openvidu-browser';
import { PublisherSpeakingEvent, StreamManager } from 'openvidu-browser';
/** /**
* @internal * @internal
*/ */
@Component({ @Component({
selector: 'ov-audio-wave', selector: 'ov-audio-wave',
templateUrl: './audio-wave.component.html', templateUrl: './audio-wave.component.html',
@ -13,40 +11,49 @@ import { PublisherSpeakingEvent, StreamManager } from 'openvidu-browser';
}) })
export class AudioWaveComponent implements OnInit, OnDestroy { export class AudioWaveComponent implements OnInit, OnDestroy {
isSpeaking: boolean = false; isSpeaking: boolean = false;
// audioVolume: number = 0;
private _streamManager: StreamManager; @Input() streamManager: StreamManager;
@Input()
set streamManager(streamManager: StreamManager) {
this._streamManager = streamManager;
if (this._streamManager) {
this._streamManager.on('publisherStartSpeaking', (event: PublisherSpeakingEvent) => {
this.isSpeaking = true;
});
this._streamManager.on('publisherStopSpeaking', (event: PublisherSpeakingEvent) => {
this.isSpeaking = false;
});
// streamManager.on('streamAudioVolumeChange', (event: any) => {
// // The loudest sounds on your system will be at 0dB
// // and silence in webaudio is -100dB.
// this.audioVolume = 100 + event.value.newValue;
// console.log('Publisher audio volume change from ' + event.value.oldValue + ' to' + event.value.newValue);
// console.log('AUDIO VOLUME', this.audioVolume);
// });
}
}
constructor() {} constructor() {}
ngOnInit(): void {
this.subscribeSpeakingEvents();
this.subscribeToStreamPropertyChanged();
}
ngOnDestroy(): void { ngOnDestroy(): void {
if (this._streamManager) { this.unsubscribeSpeakingEvents();
this._streamManager.off('publisherStartSpeaking'); this.unsubscribePropertyChangedEvents();
this._streamManager.off('publisherStopSpeaking'); }
private subscribeToStreamPropertyChanged() {
if (this.streamManager) {
this.streamManager.on('streamPropertyChanged', (event: StreamPropertyChangedEvent) => {
if (event.reason === 'trackReplaced' && event.changedProperty === 'audioActive') {
// FIXUP: When the audio track is replaced, the startSpeakingEvents is not fired by openvidu-browser
this.unsubscribeSpeakingEvents();
this.subscribeSpeakingEvents();
}
});
} }
} }
ngOnInit(): void {} private subscribeSpeakingEvents() {
if (this.streamManager) {
this.streamManager.on('publisherStartSpeaking', (event: PublisherSpeakingEvent) => (this.isSpeaking = true));
this.streamManager.on('publisherStopSpeaking', (event: PublisherSpeakingEvent) => (this.isSpeaking = false));
}
}
private unsubscribeSpeakingEvents() {
if (this.streamManager) {
this.streamManager.off('publisherStartSpeaking');
this.streamManager.off('publisherStopSpeaking');
}
}
private unsubscribePropertyChangedEvents() {
if (this.streamManager) {
this.streamManager.off('streamPropertyChanged');
}
}
} }

View File

@ -7,6 +7,7 @@ import { CustomDevice } from '../../../models/device.model';
import { ParticipantAbstractModel } from '../../../models/participant.model'; import { ParticipantAbstractModel } from '../../../models/participant.model';
import { ParticipantService } from '../../../services/participant/participant.service'; import { ParticipantService } from '../../../services/participant/participant.service';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { VideoType } from '../../../models/video-type.model';
/** /**
* @internal * @internal
@ -59,14 +60,8 @@ export class AudioDevicesComponent implements OnInit, OnDestroy {
async onMicrophoneSelected(event: any) { async onMicrophoneSelected(event: any) {
const audioSource = event?.value; const audioSource = event?.value;
if (this.deviceSrv.needUpdateAudioTrack(audioSource)) { if (this.deviceSrv.needUpdateAudioTrack(audioSource)) {
//TODO: Uncomment this when replaceTrack issue is fixed const pp: PublisherProperties = { audioSource, videoSource: false };
// const pp: PublisherProperties = { audioSource, videoSource: false }; await this.openviduService.replaceTrack(VideoType.CAMERA, pp);
// await this.openviduService.replaceTrack(VideoType.CAMERA, pp);
// TODO: Remove this when replaceTrack issue is fixed
const mirror = this.deviceSrv.cameraNeedsMirror(this.deviceSrv.getCameraSelected().device);
const pp: PublisherProperties = { videoSource: this.deviceSrv.getCameraSelected().device, audioSource, mirror };
await this.openviduService.republishTrack(pp);
this.deviceSrv.setMicSelected(audioSource); this.deviceSrv.setMicSelected(audioSource);
this.microphoneSelected = this.deviceSrv.getMicrophoneSelected(); this.microphoneSelected = this.deviceSrv.getMicrophoneSelected();
} }

View File

@ -4,6 +4,7 @@ import { Subscription } from 'rxjs';
import { CustomDevice } from '../../../models/device.model'; import { CustomDevice } from '../../../models/device.model';
import { PanelType } from '../../../models/panel.model'; import { PanelType } from '../../../models/panel.model';
import { ParticipantAbstractModel } from '../../../models/participant.model'; import { ParticipantAbstractModel } from '../../../models/participant.model';
import { VideoType } from '../../../models/video-type.model';
import { DeviceService } from '../../../services/device/device.service'; import { DeviceService } from '../../../services/device/device.service';
import { OpenViduService } from '../../../services/openvidu/openvidu.service'; import { OpenViduService } from '../../../services/openvidu/openvidu.service';
import { PanelService } from '../../../services/panel/panel.service'; import { PanelService } from '../../../services/panel/panel.service';
@ -72,18 +73,14 @@ export class VideoDevicesComponent implements OnInit, OnDestroy {
// Is New deviceId different from the old one? // Is New deviceId different from the old one?
if (this.deviceSrv.needUpdateVideoTrack(videoSource)) { if (this.deviceSrv.needUpdateVideoTrack(videoSource)) {
const mirror = this.deviceSrv.cameraNeedsMirror(videoSource); const mirror = this.deviceSrv.cameraNeedsMirror(videoSource);
//TODO: Uncomment this when replaceTrack issue is fixed
// const pp: PublisherProperties = { videoSource, audioSource: false, mirror };
// await this.openviduService.replaceTrack(VideoType.CAMERA, pp);
// TODO: Remove this when replaceTrack issue is fixed
const pp: PublisherProperties = { videoSource, audioSource: this.deviceSrv.getMicrophoneSelected().device, mirror };
// Reapply Virtual Background to new Publisher if necessary // Reapply Virtual Background to new Publisher if necessary
const backgroundSelected = this.backgroundService.backgroundSelected.getValue(); const backgroundSelected = this.backgroundService.backgroundSelected.getValue();
if (this.backgroundService.isBackgroundApplied()) { if (this.backgroundService.isBackgroundApplied()) {
await this.backgroundService.removeBackground(); await this.backgroundService.removeBackground();
} }
await this.openviduService.republishTrack(pp); const pp: PublisherProperties = { videoSource, audioSource: false, mirror };
await this.openviduService.replaceTrack(VideoType.CAMERA, pp);
if (this.backgroundService.isBackgroundApplied()) { if (this.backgroundService.isBackgroundApplied()) {
const bgSelected = this.backgroundService.backgrounds.find((b) => b.id === backgroundSelected); const bgSelected = this.backgroundService.backgrounds.find((b) => b.id === backgroundSelected);
if (bgSelected) { if (bgSelected) {

View File

@ -1,5 +1,5 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Connection, OpenVidu, Publisher, PublisherProperties, Session, SignalOptions, Stream } from 'openvidu-browser'; import { Connection, OpenVidu, OpenViduError, OpenViduErrorName, Publisher, PublisherProperties, Session, SignalOptions, Stream } from 'openvidu-browser';
import { LoggerService } from '../logger/logger.service'; import { LoggerService } from '../logger/logger.service';
@ -323,7 +323,6 @@ export class OpenViduService {
const willWebcamBePresent = this.participantService.isMyCameraActive() && this.participantService.isMyVideoActive(); const willWebcamBePresent = this.participantService.isMyCameraActive() && this.participantService.isMyVideoActive();
const hasAudio = willWebcamBePresent ? false : hasAudioDevicesAvailable && this.participantService.isMyAudioActive(); const hasAudio = willWebcamBePresent ? false : hasAudioDevicesAvailable && this.participantService.isMyAudioActive();
console.warn('will be audio active', hasAudio);
const properties: PublisherProperties = { const properties: PublisherProperties = {
videoSource: ScreenType.SCREEN, videoSource: ScreenType.SCREEN,
audioSource: hasAudioDevicesAvailable ? this.deviceService.getMicrophoneSelected().device : null, audioSource: hasAudioDevicesAvailable ? this.deviceService.getMicrophoneSelected().device : null,
@ -388,45 +387,6 @@ export class OpenViduService {
} }
} }
/**
* TODO: Remove this method when replaceTrack issue is fixed
* https://github.com/OpenVidu/openvidu/pull/700
* @internal
*/
republishTrack(properties: PublisherProperties): Promise<void> {
const { videoSource, audioSource, mirror } = properties;
return new Promise(async (resolve, reject) => {
if (!!videoSource) {
this.log.d('Replacing video track ' + videoSource);
this.videoSource = videoSource;
}
if (!!audioSource) {
this.log.d('Replacing audio track ' + audioSource);
this.audioSource = audioSource;
}
this.destroyPublisher(this.participantService.getMyCameraPublisher());
const properties: PublisherProperties = {
videoSource: this.videoSource,
audioSource: this.audioSource,
publishVideo: this.participantService.isMyVideoActive(),
publishAudio: this.participantService.isMyAudioActive(),
mirror
};
const publisher = await this.initPublisher(undefined, properties);
this.participantService.setMyCameraPublisher(publisher);
publisher.once('streamPlaying', () => {
this.participantService.setMyCameraPublisher(publisher);
resolve();
});
publisher.once('accessDenied', () => {
reject();
});
});
}
/** /**
* @internal * @internal
*/ */
@ -437,12 +397,6 @@ export class OpenViduService {
to: connections && connections.length > 0 ? connections : undefined to: connections && connections.length > 0 ? connections : undefined
}; };
this.webcamSession.signal(signalOptions); this.webcamSession.signal(signalOptions);
// TODO: Check if it is necessary
// if (type === Signal.NICKNAME_CHANGED && !!this.getScreenSession().connection) {
// signalOptions.data = JSON.stringify({ clientData: this.participantService.getScreenNickname() });
// this.getScreenSession()?.signal(signalOptions);
// }
} }
/** /**
@ -453,40 +407,24 @@ export class OpenViduService {
this.log.d(`Replacing ${videoType} track`, props); this.log.d(`Replacing ${videoType} track`, props);
if (videoType === VideoType.CAMERA) { if (videoType === VideoType.CAMERA) {
//TODO: Uncomment this section when replaceTrack issue is fixed let mediaStream: MediaStream;
// https://github.com/OpenVidu/openvidu/pull/700 const isReplacingAudio = !!props.audioSource;
throw 'Replace track feature has a bug. We are trying to fix it'; const isReplacingVideo = !!props.videoSource;
// let mediaStream: MediaStream;
// const oldMediaStream = this.participantService.getMyCameraPublisher().stream.getMediaStream();
// const isFirefoxPlatform = this.platformService.isFirefox();
// const isReplacingAudio = !!props.audioSource;
// const isReplacingVideo = !!props.videoSource;
// if (isReplacingVideo) { if (isReplacingVideo) {
// if (isFirefoxPlatform) { mediaStream = await this.createMediaStream(props);
// // Firefox throw an exception trying to get a new MediaStreamTrack if the older one is not stopped // Replace video track
// // NotReadableError: Concurrent mic process limit. Stopping tracks before call to getUserMedia const videoTrack: MediaStreamTrack = mediaStream.getVideoTracks()[0];
// oldMediaStream.getVideoTracks()[0].stop(); await this.participantService.getMyCameraPublisher().replaceTrack(videoTrack);
// } } else if (isReplacingAudio) {
// mediaStream = await this.createMediaStream(props); mediaStream = await this.createMediaStream(props);
// // Replace video track // Replace audio track
// const videoTrack: MediaStreamTrack = mediaStream.getVideoTracks()[0]; const audioTrack: MediaStreamTrack = mediaStream.getAudioTracks()[0];
// await this.participantService.getMyCameraPublisher().replaceTrack(videoTrack); await this.participantService.getMyCameraPublisher().replaceTrack(audioTrack);
// } else if (isReplacingAudio) { }
// if (isFirefoxPlatform) {
// // Firefox throw an exception trying to get a new MediaStreamTrack if the older one is not stopped
// // NotReadableError: Concurrent mic process limit. Stopping tracks before call to getUserMedia
// oldMediaStream.getAudioTracks()[0].stop();
// }
// mediaStream = await this.createMediaStream(props);
// // Replace audio track
// const audioTrack: MediaStreamTrack = mediaStream.getAudioTracks()[0];
// await this.participantService.getMyCameraPublisher().replaceTrack(audioTrack);
// }
} else if (videoType === VideoType.SCREEN) { } else if (videoType === VideoType.SCREEN) {
let newScreenMediaStream;
try { try {
newScreenMediaStream = await this.OVScreen.getUserMedia(props); let newScreenMediaStream = await this.OVScreen.getUserMedia(props);
this.participantService.getMyScreenPublisher().stream.getMediaStream().getVideoTracks()[0].stop(); this.participantService.getMyScreenPublisher().stream.getMediaStream().getVideoTracks()[0].stop();
await this.participantService.getMyScreenPublisher().replaceTrack(newScreenMediaStream.getVideoTracks()[0]); await this.participantService.getMyScreenPublisher().replaceTrack(newScreenMediaStream.getVideoTracks()[0]);
} catch (error) { } catch (error) {
@ -513,32 +451,32 @@ export class OpenViduService {
} }
//TODO: Uncomment this section when replaceTrack issue is fixed //TODO: Uncomment this section when replaceTrack issue is fixed
// private async createMediaStream(pp: PublisherProperties): Promise<MediaStream> { private async createMediaStream(pp: PublisherProperties): Promise<MediaStream> {
// let mediaStream: MediaStream; let mediaStream: MediaStream;
// const isFirefoxPlatform = this.platformService.isFirefox(); const isFirefoxPlatform = this.platformService.isFirefox();
// const isReplacingAudio = !!pp.audioSource; const isReplacingAudio = !!pp.audioSource;
// const isReplacingVideo = !!pp.videoSource; const isReplacingVideo = !!pp.videoSource;
// try { try {
// mediaStream = await this.OV.getUserMedia(pp); mediaStream = await this.OV.getUserMedia(pp);
// } catch (error) { } catch (error) {
// if ((<OpenViduError>error).name === OpenViduErrorName.DEVICE_ACCESS_DENIED) { if ((<OpenViduError>error).name === OpenViduErrorName.DEVICE_ACCESS_DENIED) {
// if (isFirefoxPlatform) { if (isFirefoxPlatform) {
// this.log.w('The device requested is not available. Restoring the older one'); this.log.w('The device requested is not available. Restoring the older one');
// // The track requested is not available so we are getting the old tracks ids for recovering the track // The track requested is not available so we are getting the old tracks ids for recovering the track
// if (isReplacingVideo) { if (isReplacingVideo) {
// pp.videoSource = this.deviceService.getCameraSelected().device; pp.videoSource = this.deviceService.getCameraSelected().device;
// } else if (isReplacingAudio) { } else if (isReplacingAudio) {
// pp.audioSource = this.deviceService.getMicrophoneSelected().device; pp.audioSource = this.deviceService.getMicrophoneSelected().device;
// } }
// mediaStream = await this.OV.getUserMedia(pp); mediaStream = await this.OV.getUserMedia(pp);
// // TODO show error alert informing that the new device is not available // TODO show error alert informing that the new device is not available
// } }
// } }
// } finally { } finally {
// return mediaStream; return mediaStream;
// } }
// } }
/** /**
* @internal * @internal