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 { Device, OpenVidu } from 'openvidu-browser';
|
||||
import { Device, OpenVidu, OpenViduError, OpenViduErrorName } from 'openvidu-browser';
|
||||
|
||||
import { CameraType, DeviceType, CustomDevice } from '../../models/device.model';
|
||||
import { ILogger } from '../../models/logger.model';
|
||||
|
@ -26,6 +26,7 @@ export class DeviceService {
|
|||
private _isVideoMuted: boolean;
|
||||
// Initialized with Storage.AUDIO_MUTED info saved on storage
|
||||
private _isAudioMuted: boolean;
|
||||
private deviceAccessDeniedError: boolean = false;
|
||||
|
||||
constructor(private loggerSrv: LoggerService, private platformSrv: PlatformService, private storageSrv: StorageService) {
|
||||
this.log = this.loggerSrv.get('DevicesService');
|
||||
|
@ -37,13 +38,19 @@ export class DeviceService {
|
|||
}
|
||||
|
||||
async initializeDevices() {
|
||||
// Forcing media permissions request.
|
||||
// Sometimes, browser doens't launch the media permissions modal.
|
||||
const mediaStream = await this.OV.getUserMedia({ audioSource: undefined, videoSource: undefined });
|
||||
mediaStream?.getAudioTracks().forEach((track) => track.stop());
|
||||
mediaStream?.getVideoTracks().forEach((track) => track.stop());
|
||||
|
||||
try {
|
||||
// Forcing media permissions request.
|
||||
// Sometimes, browser doens't launch the media permissions modal.
|
||||
const mediaStream = await this.OV.getUserMedia({ audioSource: undefined, videoSource: undefined });
|
||||
mediaStream?.getAudioTracks().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();
|
||||
console.log(this.devices);
|
||||
const customDevices = this.initializeCustomDevices(this.devices);
|
||||
this.cameras = customDevices.cameras;
|
||||
this.microphones = customDevices.microphones;
|
||||
|
@ -56,14 +63,14 @@ export class DeviceService {
|
|||
|
||||
private initializeCustomDevices(defaultVDevices: Device[]) {
|
||||
const FIRST_POSITION = 0;
|
||||
const defaultMicrophones = defaultVDevices.filter((device) => device.kind === DeviceType.AUDIO_INPUT);
|
||||
const defaultCameras = defaultVDevices.filter((device) => device.kind === DeviceType.VIDEO_INPUT);
|
||||
const defaultMicrophones: Device[] = defaultVDevices.filter((device) => device.kind === DeviceType.AUDIO_INPUT);
|
||||
const defaultCameras: Device[] = defaultVDevices.filter((device) => device.kind === DeviceType.VIDEO_INPUT);
|
||||
const customDevices: { cameras: CustomDevice[]; microphones: CustomDevice[] } = {
|
||||
cameras: [],
|
||||
microphones: []
|
||||
};
|
||||
|
||||
if (this.hasAudioDeviceAvailable) {
|
||||
if (defaultMicrophones.length > 0) {
|
||||
defaultMicrophones.forEach((device: Device) => {
|
||||
customDevices.microphones.push({ label: device.label, device: device.deviceId });
|
||||
});
|
||||
|
@ -73,11 +80,17 @@ export class DeviceService {
|
|||
if (!!storageMicrophone) {
|
||||
this.microphoneSelected = storageMicrophone;
|
||||
} else if (customDevices.microphones.length > 0) {
|
||||
this.microphoneSelected = customDevices.microphones[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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.hasVideoDeviceAvailable) {
|
||||
if (defaultCameras.length > 0) {
|
||||
defaultCameras.forEach((device: Device, index: number) => {
|
||||
const myDevice: CustomDevice = {
|
||||
label: device.label,
|
||||
|
@ -103,7 +116,13 @@ export class DeviceService {
|
|||
if (!!storageCamera) {
|
||||
this.cameraSelected = storageCamera;
|
||||
} else if (customDevices.cameras.length > 0) {
|
||||
this.cameraSelected = customDevices.cameras[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];
|
||||
}
|
||||
}
|
||||
}
|
||||
return customDevices;
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
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';
|
||||
|
||||
|
@ -107,7 +116,6 @@ export class OpenViduService {
|
|||
* Initialize a publisher checking devices saved on storage or if participant have devices available.
|
||||
*/
|
||||
async initDefaultPublisher(targetElement: string | HTMLElement): Promise<Publisher> {
|
||||
|
||||
const hasVideoDevices = this.deviceService.hasVideoDeviceAvailable();
|
||||
const hasAudioDevices = this.deviceService.hasAudioDeviceAvailable();
|
||||
const isVideoActive = !this.deviceService.isVideoMuted();
|
||||
|
@ -272,29 +280,35 @@ export class OpenViduService {
|
|||
async replaceTrack(videoType: VideoType, props: PublisherProperties) {
|
||||
try {
|
||||
this.log.d(`Replacing ${videoType} track`, props);
|
||||
|
||||
if (videoType === VideoType.CAMERA) {
|
||||
let mediaStream: MediaStream;
|
||||
const oldMediaStream = this.participantService.getMyCameraPublisher().stream.getMediaStream();
|
||||
const isFirefoxPlatform = this.platformService.isFirefox();
|
||||
const isReplacingAudio = !!props.audioSource;
|
||||
const isReplacingVideo = !!props.videoSource;
|
||||
|
||||
if (this.platformService.isFirefox()) {
|
||||
// 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
|
||||
if (isReplacingVideo) {
|
||||
this.participantService.getMyCameraPublisher().stream.getMediaStream().getVideoTracks()[0].stop();
|
||||
} else if (isReplacingAudio) {
|
||||
this.participantService.getMyCameraPublisher().stream.getMediaStream().getAudioTracks()[0].stop();
|
||||
if (isReplacingVideo) {
|
||||
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.getVideoTracks()[0].stop();
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
mediaStream = await this.createMediaStream(props);
|
||||
// Replace video track
|
||||
const videoTrack: MediaStreamTrack = track.getVideoTracks()[0];
|
||||
const videoTrack: MediaStreamTrack = mediaStream.getVideoTracks()[0];
|
||||
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) {
|
||||
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 {
|
||||
const oldNickname: string = JSON.parse(this.webcamSession.connection.data).clientData;
|
||||
return oldNickname !== this.participantService.getWebcamNickname();
|
||||
|
|
Loading…
Reference in New Issue