ov-components: Uses device constraints for media tracks

Ensures correct audio and video device selection by applying
`exact` or `ideal` constraints when creating or restarting
media tracks.

This improves device handling and avoids potential issues when
switching between different audio or video devices.
pull/860/head
CSantosM 2026-02-20 16:42:25 +01:00
parent 4617dfd797
commit b2701311fb
1 changed files with 22 additions and 21 deletions

View File

@ -427,28 +427,28 @@ export class OpenViduService {
if (videoDeviceId === true) {
if (this.deviceService.hasVideoDeviceAvailable()) {
const selectedCamera = this.deviceService.getCameraSelected();
options.video = { deviceId: selectedCamera?.device || 'default' };
options.video = { deviceId: this.toDeviceConstraint(selectedCamera?.device) };
} else {
options.video = false;
}
} else if (videoDeviceId === false) {
options.video = false;
} else {
(options.video as VideoCaptureOptions).deviceId = videoDeviceId;
(options.video as VideoCaptureOptions).deviceId = this.toDeviceConstraint(videoDeviceId);
}
// Audio device
if (audioDeviceId === true) {
if (this.deviceService.hasAudioDeviceAvailable()) {
const selectedMic = this.deviceService.getMicrophoneSelected();
(options.audio as AudioCaptureOptions).deviceId = selectedMic?.device || 'default';
(options.audio as AudioCaptureOptions).deviceId = this.toDeviceConstraint(selectedMic?.device);
} else {
options.audio = false;
}
} else if (audioDeviceId === false) {
options.audio = false;
} else {
(options.audio as AudioCaptureOptions).deviceId = audioDeviceId;
(options.audio as AudioCaptureOptions).deviceId = this.toDeviceConstraint(audioDeviceId);
}
let newLocalTracks: LocalTrack[] = [];
@ -518,6 +518,13 @@ export class OpenViduService {
return tracks;
}
private toDeviceConstraint(deviceId?: string): ConstrainDOMString {
if (!deviceId || deviceId === 'default') {
return { ideal: 'default' };
}
return { exact: deviceId };
}
/**
* @internal
* As the Room is not created yet, we need to handle the media tracks with a temporary array of tracks.
@ -588,13 +595,13 @@ export class OpenViduService {
*/
async switchCamera(deviceId: string): Promise<void> {
const existingTrack = this.localTracks.find((t) => t.kind === Track.Kind.Video) as LocalVideoTrack | undefined;
const options: VideoCaptureOptions = { deviceId: this.toDeviceConstraint(deviceId) };
if (existingTrack) {
try {
// restartTrack replaces the underlying MediaStreamTrack in-place.
// LiveKit's setMediaStreamTrack will call processor.restart(newTrack) automatically
// if a background processor is attached, preserving the active effect.
await existingTrack.restartTrack({ deviceId });
await existingTrack.restartTrack(options);
if (!this.deviceService.isCameraEnabled()) {
await existingTrack.mute();
}
@ -608,7 +615,7 @@ export class OpenViduService {
// No existing track (edge case: camera was unavailable/unpublished) → create a fresh one
try {
const newVideoTracks = await createLocalTracks({ video: { deviceId } });
const newVideoTracks = await createLocalTracks({ video: options });
const videoTrack = newVideoTracks.find((t) => t.kind === Track.Kind.Video) as LocalVideoTrack | undefined;
if (videoTrack) {
if (!this.deviceService.isCameraEnabled()) {
@ -679,15 +686,16 @@ export class OpenViduService {
*/
async switchMicrophone(deviceId: string): Promise<void> {
const existingTrack = this.localTracks.find((t) => t.kind === Track.Kind.Audio) as LocalAudioTrack | undefined;
if (existingTrack) {
try {
await existingTrack.restartTrack({
deviceId,
const options: AudioCaptureOptions = {
deviceId: this.toDeviceConstraint(deviceId),
echoCancellation: true,
noiseSuppression: true,
autoGainControl: true
});
};
if (existingTrack) {
try {
await existingTrack.restartTrack(options);
if (!this.deviceService.isMicrophoneEnabled()) {
await existingTrack.mute();
}
@ -701,14 +709,7 @@ export class OpenViduService {
// No existing track (edge case) → create a fresh one
try {
const newAudioTracks = await createLocalTracks({
audio: {
deviceId,
echoCancellation: true,
noiseSuppression: true,
autoGainControl: true
}
});
const newAudioTracks = await createLocalTracks(options as CreateLocalTracksOptions);
const audioTrack = newAudioTracks.find((t) => t.kind === Track.Kind.Audio);
if (audioTrack) {
if (!this.deviceService.isMicrophoneEnabled()) {