mirror of
https://github.com/vector-im/element-call.git
synced 2026-03-16 06:17:10 +00:00
test routing android
This commit is contained in:
@@ -19,6 +19,7 @@ import {
|
||||
} from "rxjs";
|
||||
import { createMediaDeviceObserver } from "@livekit/components-core";
|
||||
import { type Logger, logger as rootLogger } from "matrix-js-sdk/lib/logger";
|
||||
import { type RTCCallIntent } from "matrix-js-sdk/lib/matrixrtc";
|
||||
|
||||
import {
|
||||
audioInput as audioInputSetting,
|
||||
@@ -35,7 +36,6 @@ import { getUrlParams } from "../UrlParams";
|
||||
import { platform } from "../Platform";
|
||||
import { switchWhen } from "../utils/observable";
|
||||
import { type Behavior, constant } from "./Behavior";
|
||||
// import { RTCCallIntent } from "matrix-js-sdk/lib/matrixrtc";
|
||||
|
||||
// This hardcoded id is used in EX ios! It can only be changed in coordination with
|
||||
// the ios swift team.
|
||||
@@ -257,6 +257,8 @@ class AudioInput implements MediaDevice<DeviceLabel, SelectedAudioInputDevice> {
|
||||
public constructor(
|
||||
private readonly usingNames$: Behavior<boolean>,
|
||||
private readonly scope: ObservableScope,
|
||||
private readonly initialCallIntent: RTCCallIntent | undefined = undefined,
|
||||
private platform: "android" | "ios" | "desktop" | undefined = undefined,
|
||||
) {
|
||||
this.available$.subscribe((available) => {
|
||||
this.logger.info("[audio-input] available devices:", available);
|
||||
@@ -368,16 +370,13 @@ class ControlledAudioOutput implements MediaDevice<
|
||||
const availableIds = Array.from(available.entries())
|
||||
.map(([id, label]) => `${id}:${JSON.stringify(label)}`)
|
||||
.join(", ");
|
||||
this.logger.info(
|
||||
"[MediaDevices ControlledAudioOutput] available devices:",
|
||||
availableIds,
|
||||
);
|
||||
this.logger.info("available devices:", availableIds);
|
||||
|
||||
// Create a virtual earpiece device in case a non-earpiece device is
|
||||
// designated for this purpose
|
||||
if (iosDeviceMenu && availableRaw.some((d) => d.forEarpiece)) {
|
||||
this.logger.info(
|
||||
`[MediaDevices ControlledAudioOutput] IOS Add virtual earpiece device with id ${EARPIECE_CONFIG_ID}`,
|
||||
`IOS Add virtual earpiece device with id ${EARPIECE_CONFIG_ID}`,
|
||||
);
|
||||
available.set(EARPIECE_CONFIG_ID, { type: "earpiece" });
|
||||
}
|
||||
@@ -390,9 +389,7 @@ class ControlledAudioOutput implements MediaDevice<
|
||||
private readonly deviceSelection$ = new Subject<string>();
|
||||
|
||||
public select(id: string): void {
|
||||
this.logger.info(
|
||||
`[MediaDevices ControlledAudioOutput] select device: ${id}`,
|
||||
);
|
||||
this.logger.info(`select device: ${id}`);
|
||||
this.deviceSelection$.next(id);
|
||||
}
|
||||
|
||||
@@ -407,18 +404,73 @@ class ControlledAudioOutput implements MediaDevice<
|
||||
],
|
||||
(available, preferredId) => {
|
||||
this.logger.debug(
|
||||
`[MediaDevices ControlledAudioOutput] selecting device. Preferred: ${preferredId}, Available: ${Array.from(available.keys()).join(",")}`,
|
||||
`selecting device: Preferred:${preferredId}: platform ${this.platform}, intent:${this.initialIntent}: Available: ${Array.from(available.keys()).join(",")}`,
|
||||
);
|
||||
const id = preferredId ?? available.keys().next().value;
|
||||
return id === undefined
|
||||
? undefined
|
||||
: { id, virtualEarpiece: id === EARPIECE_CONFIG_ID };
|
||||
let id: string | undefined = preferredId;
|
||||
if (id === undefined) {
|
||||
// No preferred device, so pick a default.
|
||||
// The logic for the default is different based on the call type.
|
||||
// For example for a voice call we want to default to the earpiece if it's available,
|
||||
// but for a video call we want to default to the speaker.
|
||||
// If the user is using a BT headset we want to default to that, as it's likely what they want to use for both video and voice calls.
|
||||
// The audio routing / selection is also different on iOS and Android, so we need to take that into account as well.
|
||||
if (this.platform == "android") {
|
||||
this.logger.debug(
|
||||
`Android routing logic intent: ${this.initialIntent} finding best default...`,
|
||||
);
|
||||
if (this.initialIntent === "audio") {
|
||||
const systemProposed = available.keys().next().value;
|
||||
// If no headset is connected, android will route to the speaker by default,
|
||||
// but for a voice call we want to route to the earpiece instead,
|
||||
// so override the system proposed routing in that case.
|
||||
if (
|
||||
systemProposed &&
|
||||
available.get(systemProposed)?.type === "speaker"
|
||||
) {
|
||||
// search for the headset
|
||||
const headsetEntry = Array.from(available.entries()).find(
|
||||
([_, label]) => label.type === "earpiece",
|
||||
);
|
||||
if (headsetEntry) {
|
||||
this.logger.debug(
|
||||
`Android routing: Switch to earpiece instead of speaker for voice call`,
|
||||
);
|
||||
id = headsetEntry[0];
|
||||
} else {
|
||||
this.logger.debug(
|
||||
`Android routing: no earpiece found, cannot switch, use system proposed routing`,
|
||||
);
|
||||
id = systemProposed;
|
||||
}
|
||||
} else {
|
||||
this.logger.debug(
|
||||
`Android routing: Use system proposed routing`,
|
||||
);
|
||||
id = systemProposed;
|
||||
}
|
||||
} else {
|
||||
// Use the system best proposed best routing.
|
||||
id = available.keys().next().value;
|
||||
}
|
||||
} else {
|
||||
// Pick the first one, based on the assumption that the OS will have already routed to the "best" available output device.
|
||||
id = available.keys().next().value;
|
||||
}
|
||||
}
|
||||
|
||||
if (id !== undefined) {
|
||||
return { id, virtualEarpiece: id === EARPIECE_CONFIG_ID };
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
// id = preferredId ?? available.keys().next().value;
|
||||
// return id === undefined
|
||||
// ? undefined
|
||||
// : { id, virtualEarpiece: id === EARPIECE_CONFIG_ID };
|
||||
},
|
||||
).pipe(
|
||||
tap((selected) => {
|
||||
this.logger.debug(
|
||||
`[MediaDevices ControlledAudioOutput] selected device: ${selected?.id}`,
|
||||
);
|
||||
this.logger.debug(`selected device: ${selected?.id}`);
|
||||
}),
|
||||
),
|
||||
);
|
||||
@@ -426,7 +478,8 @@ class ControlledAudioOutput implements MediaDevice<
|
||||
public constructor(
|
||||
private readonly usingNames$: Behavior<boolean>,
|
||||
private readonly scope: ObservableScope,
|
||||
// private initialIntent: RTCCallIntent | undefined = undefined,
|
||||
private initialIntent: RTCCallIntent | undefined = undefined,
|
||||
private platform: "android" | "ios" | "desktop" | undefined = undefined,
|
||||
) {
|
||||
this.selected$.subscribe((device) => {
|
||||
// Let the hosting application know which output device has been selected.
|
||||
@@ -434,26 +487,17 @@ class ControlledAudioOutput implements MediaDevice<
|
||||
// been selected - for example, Element X iOS listens to this to determine
|
||||
// whether it should enable the proximity sensor.
|
||||
if (device !== undefined) {
|
||||
this.logger.info(
|
||||
"[MediaDevices controlled-output] onAudioDeviceSelect called:",
|
||||
device,
|
||||
);
|
||||
this.logger.info("onAudioDeviceSelect called:", device);
|
||||
window.controls.onAudioDeviceSelect?.(device.id);
|
||||
// Also invoke the deprecated callback for backward compatibility
|
||||
window.controls.onOutputDeviceSelect?.(device.id);
|
||||
}
|
||||
});
|
||||
this.available$.subscribe((available) => {
|
||||
this.logger.info(
|
||||
"[MediaDevices controlled-output] available devices:",
|
||||
available,
|
||||
);
|
||||
this.logger.debug("available devices:", available);
|
||||
});
|
||||
this.availableRaw$.subscribe((availableRaw) => {
|
||||
this.logger.info(
|
||||
"[MediaDevices controlled-output] available raw devices:",
|
||||
availableRaw,
|
||||
);
|
||||
this.logger.debug("available raw devices:", availableRaw);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -520,7 +564,12 @@ export class MediaDevices {
|
||||
AudioOutputDeviceLabel,
|
||||
SelectedAudioOutputDevice
|
||||
> = getUrlParams().controlledAudioDevices
|
||||
? new ControlledAudioOutput(this.usingNames$, this.scope)
|
||||
? new ControlledAudioOutput(
|
||||
this.usingNames$,
|
||||
this.scope,
|
||||
getUrlParams().callIntent,
|
||||
platform,
|
||||
)
|
||||
: new AudioOutput(this.usingNames$, this.scope);
|
||||
|
||||
public readonly videoInput: MediaDevice<DeviceLabel, SelectedDevice> =
|
||||
|
||||
Reference in New Issue
Block a user