mirror of https://github.com/OpenVidu/openvidu.git
openvidu-components: Fixed bug replacing tracks in user-settings
parent
f40093746f
commit
e63ef15d18
|
@ -1,5 +1,5 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Device, OpenVidu } from 'openvidu-browser';
|
import { Device, OpenVidu, OpenViduError, OpenViduErrorName } from 'openvidu-browser';
|
||||||
|
|
||||||
import { CameraType, DeviceType, CustomDevice } from '../../models/device.model';
|
import { CameraType, DeviceType, CustomDevice } from '../../models/device.model';
|
||||||
import { ILogger } from '../../models/logger.model';
|
import { ILogger } from '../../models/logger.model';
|
||||||
|
@ -26,6 +26,7 @@ export class DeviceService {
|
||||||
private _isVideoMuted: boolean;
|
private _isVideoMuted: boolean;
|
||||||
// Initialized with Storage.AUDIO_MUTED info saved on storage
|
// Initialized with Storage.AUDIO_MUTED info saved on storage
|
||||||
private _isAudioMuted: boolean;
|
private _isAudioMuted: boolean;
|
||||||
|
private deviceAccessDeniedError: boolean = false;
|
||||||
|
|
||||||
constructor(private loggerSrv: LoggerService, private platformSrv: PlatformService, private storageSrv: StorageService) {
|
constructor(private loggerSrv: LoggerService, private platformSrv: PlatformService, private storageSrv: StorageService) {
|
||||||
this.log = this.loggerSrv.get('DevicesService');
|
this.log = this.loggerSrv.get('DevicesService');
|
||||||
|
@ -37,13 +38,19 @@ export class DeviceService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async initializeDevices() {
|
async initializeDevices() {
|
||||||
|
|
||||||
|
try {
|
||||||
// Forcing media permissions request.
|
// Forcing media permissions request.
|
||||||
// Sometimes, browser doens't launch the media permissions modal.
|
// Sometimes, browser doens't launch the media permissions modal.
|
||||||
const mediaStream = await this.OV.getUserMedia({ audioSource: undefined, videoSource: undefined });
|
const mediaStream = await this.OV.getUserMedia({ audioSource: undefined, videoSource: undefined });
|
||||||
mediaStream?.getAudioTracks().forEach((track) => track.stop());
|
mediaStream?.getAudioTracks().forEach((track) => track.stop());
|
||||||
mediaStream?.getVideoTracks().forEach((track) => track.stop());
|
mediaStream?.getVideoTracks().forEach((track) => track.stop());
|
||||||
|
} catch (error) {
|
||||||
|
this.deviceAccessDeniedError = (<OpenViduError>error).name === OpenViduErrorName.DEVICE_ACCESS_DENIED;
|
||||||
|
}
|
||||||
|
|
||||||
this.devices = await this.OV.getDevices();
|
this.devices = await this.OV.getDevices();
|
||||||
|
console.log(this.devices);
|
||||||
const customDevices = this.initializeCustomDevices(this.devices);
|
const customDevices = this.initializeCustomDevices(this.devices);
|
||||||
this.cameras = customDevices.cameras;
|
this.cameras = customDevices.cameras;
|
||||||
this.microphones = customDevices.microphones;
|
this.microphones = customDevices.microphones;
|
||||||
|
@ -56,14 +63,14 @@ export class DeviceService {
|
||||||
|
|
||||||
private initializeCustomDevices(defaultVDevices: Device[]) {
|
private initializeCustomDevices(defaultVDevices: Device[]) {
|
||||||
const FIRST_POSITION = 0;
|
const FIRST_POSITION = 0;
|
||||||
const defaultMicrophones = defaultVDevices.filter((device) => device.kind === DeviceType.AUDIO_INPUT);
|
const defaultMicrophones: Device[] = defaultVDevices.filter((device) => device.kind === DeviceType.AUDIO_INPUT);
|
||||||
const defaultCameras = defaultVDevices.filter((device) => device.kind === DeviceType.VIDEO_INPUT);
|
const defaultCameras: Device[] = defaultVDevices.filter((device) => device.kind === DeviceType.VIDEO_INPUT);
|
||||||
const customDevices: { cameras: CustomDevice[]; microphones: CustomDevice[] } = {
|
const customDevices: { cameras: CustomDevice[]; microphones: CustomDevice[] } = {
|
||||||
cameras: [],
|
cameras: [],
|
||||||
microphones: []
|
microphones: []
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.hasAudioDeviceAvailable) {
|
if (defaultMicrophones.length > 0) {
|
||||||
defaultMicrophones.forEach((device: Device) => {
|
defaultMicrophones.forEach((device: Device) => {
|
||||||
customDevices.microphones.push({ label: device.label, device: device.deviceId });
|
customDevices.microphones.push({ label: device.label, device: device.deviceId });
|
||||||
});
|
});
|
||||||
|
@ -73,11 +80,17 @@ export class DeviceService {
|
||||||
if (!!storageMicrophone) {
|
if (!!storageMicrophone) {
|
||||||
this.microphoneSelected = storageMicrophone;
|
this.microphoneSelected = storageMicrophone;
|
||||||
} else if (customDevices.microphones.length > 0) {
|
} else if (customDevices.microphones.length > 0) {
|
||||||
|
if(this.deviceAccessDeniedError && customDevices.microphones.length > 1){
|
||||||
|
// We assume that the default device is already in use
|
||||||
|
// Assign an alternative device with the aim of avoiding the DEVICE_ALREADY_IN_USE error
|
||||||
|
this.microphoneSelected = customDevices.microphones[1];
|
||||||
|
} else {
|
||||||
this.microphoneSelected = customDevices.microphones[0];
|
this.microphoneSelected = customDevices.microphones[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.hasVideoDeviceAvailable) {
|
if (defaultCameras.length > 0) {
|
||||||
defaultCameras.forEach((device: Device, index: number) => {
|
defaultCameras.forEach((device: Device, index: number) => {
|
||||||
const myDevice: CustomDevice = {
|
const myDevice: CustomDevice = {
|
||||||
label: device.label,
|
label: device.label,
|
||||||
|
@ -103,9 +116,15 @@ export class DeviceService {
|
||||||
if (!!storageCamera) {
|
if (!!storageCamera) {
|
||||||
this.cameraSelected = storageCamera;
|
this.cameraSelected = storageCamera;
|
||||||
} else if (customDevices.cameras.length > 0) {
|
} else if (customDevices.cameras.length > 0) {
|
||||||
|
if(this.deviceAccessDeniedError && customDevices.cameras.length > 1){
|
||||||
|
// We assume that the default device is already in use
|
||||||
|
// Assign an alternative device with the aim of avoiding the DEVICE_ALREADY_IN_USE error
|
||||||
|
this.cameraSelected = customDevices.cameras[1];
|
||||||
|
} else {
|
||||||
this.cameraSelected = customDevices.cameras[0];
|
this.cameraSelected = customDevices.cameras[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return customDevices;
|
return customDevices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,14 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Connection, OpenVidu, Publisher, PublisherProperties, Session, SignalOptions } from 'openvidu-browser';
|
import {
|
||||||
|
Connection,
|
||||||
|
OpenVidu,
|
||||||
|
OpenViduError,
|
||||||
|
OpenViduErrorName,
|
||||||
|
Publisher,
|
||||||
|
PublisherProperties,
|
||||||
|
Session,
|
||||||
|
SignalOptions
|
||||||
|
} from 'openvidu-browser';
|
||||||
|
|
||||||
import { LoggerService } from '../logger/logger.service';
|
import { LoggerService } from '../logger/logger.service';
|
||||||
|
|
||||||
|
@ -107,7 +116,6 @@ export class OpenViduService {
|
||||||
* Initialize a publisher checking devices saved on storage or if participant have devices available.
|
* Initialize a publisher checking devices saved on storage or if participant have devices available.
|
||||||
*/
|
*/
|
||||||
async initDefaultPublisher(targetElement: string | HTMLElement): Promise<Publisher> {
|
async initDefaultPublisher(targetElement: string | HTMLElement): Promise<Publisher> {
|
||||||
|
|
||||||
const hasVideoDevices = this.deviceService.hasVideoDeviceAvailable();
|
const hasVideoDevices = this.deviceService.hasVideoDeviceAvailable();
|
||||||
const hasAudioDevices = this.deviceService.hasAudioDeviceAvailable();
|
const hasAudioDevices = this.deviceService.hasAudioDeviceAvailable();
|
||||||
const isVideoActive = !this.deviceService.isVideoMuted();
|
const isVideoActive = !this.deviceService.isVideoMuted();
|
||||||
|
@ -272,29 +280,35 @@ export class OpenViduService {
|
||||||
async replaceTrack(videoType: VideoType, props: PublisherProperties) {
|
async replaceTrack(videoType: VideoType, props: PublisherProperties) {
|
||||||
try {
|
try {
|
||||||
this.log.d(`Replacing ${videoType} track`, props);
|
this.log.d(`Replacing ${videoType} track`, props);
|
||||||
|
|
||||||
if (videoType === VideoType.CAMERA) {
|
if (videoType === VideoType.CAMERA) {
|
||||||
|
let mediaStream: MediaStream;
|
||||||
|
const oldMediaStream = this.participantService.getMyCameraPublisher().stream.getMediaStream();
|
||||||
|
const isFirefoxPlatform = this.platformService.isFirefox();
|
||||||
const isReplacingAudio = !!props.audioSource;
|
const isReplacingAudio = !!props.audioSource;
|
||||||
const isReplacingVideo = !!props.videoSource;
|
const isReplacingVideo = !!props.videoSource;
|
||||||
|
|
||||||
if (this.platformService.isFirefox()) {
|
if (isReplacingVideo) {
|
||||||
|
if (isFirefoxPlatform) {
|
||||||
// Firefox throw an exception trying to get a new MediaStreamTrack if the older one is not stopped
|
// 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
|
// NotReadableError: Concurrent mic process limit. Stopping tracks before call to getUserMedia
|
||||||
if (isReplacingVideo) {
|
oldMediaStream.getVideoTracks()[0].stop();
|
||||||
this.participantService.getMyCameraPublisher().stream.getMediaStream().getVideoTracks()[0].stop();
|
|
||||||
} else if (isReplacingAudio) {
|
|
||||||
this.participantService.getMyCameraPublisher().stream.getMediaStream().getAudioTracks()[0].stop();
|
|
||||||
}
|
}
|
||||||
}
|
mediaStream = await this.createMediaStream(props);
|
||||||
|
|
||||||
const track = await this.OV.getUserMedia(props);
|
|
||||||
if (isReplacingAudio) {
|
|
||||||
// Replace audio track
|
|
||||||
const audioTrack: MediaStreamTrack = track.getAudioTracks()[0];
|
|
||||||
await this.participantService.getMyCameraPublisher().replaceTrack(audioTrack);
|
|
||||||
} else if (isReplacingVideo) {
|
|
||||||
// Replace video track
|
// Replace video track
|
||||||
const videoTrack: MediaStreamTrack = track.getVideoTracks()[0];
|
const videoTrack: MediaStreamTrack = mediaStream.getVideoTracks()[0];
|
||||||
await this.participantService.getMyCameraPublisher().replaceTrack(videoTrack);
|
await this.participantService.getMyCameraPublisher().replaceTrack(videoTrack);
|
||||||
|
|
||||||
|
} 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) {
|
||||||
const newScreenMediaStream = await this.OVScreen.getUserMedia(props);
|
const newScreenMediaStream = await this.OVScreen.getUserMedia(props);
|
||||||
|
@ -307,6 +321,33 @@ export class OpenViduService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async createMediaStream(pp: PublisherProperties): Promise<MediaStream>{
|
||||||
|
let mediaStream: MediaStream;
|
||||||
|
const isFirefoxPlatform = this.platformService.isFirefox();
|
||||||
|
const isReplacingAudio = !!pp.audioSource;
|
||||||
|
const isReplacingVideo = !!pp.videoSource;
|
||||||
|
|
||||||
|
try {
|
||||||
|
mediaStream = await this.OV.getUserMedia(pp);
|
||||||
|
} catch (error) {
|
||||||
|
if ((<OpenViduError>error).name === OpenViduErrorName.DEVICE_ACCESS_DENIED) {
|
||||||
|
if (isFirefoxPlatform) {
|
||||||
|
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
|
||||||
|
if (isReplacingVideo) {
|
||||||
|
pp.videoSource = this.deviceService.getCameraSelected().device;
|
||||||
|
} else if (isReplacingAudio) {
|
||||||
|
pp.audioSource = this.deviceService.getMicrophoneSelected().device;
|
||||||
|
}
|
||||||
|
mediaStream = await this.OV.getUserMedia(pp);
|
||||||
|
// TODO show error alert informing that the new device is not available
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
return mediaStream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
needSendNicknameSignal(): boolean {
|
needSendNicknameSignal(): boolean {
|
||||||
const oldNickname: string = JSON.parse(this.webcamSession.connection.data).clientData;
|
const oldNickname: string = JSON.parse(this.webcamSession.connection.data).clientData;
|
||||||
return oldNickname !== this.participantService.getWebcamNickname();
|
return oldNickname !== this.participantService.getWebcamNickname();
|
||||||
|
|
Loading…
Reference in New Issue