Camera: fall back to plain constraints, explain a silent refusal; show voice failures above the chat
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -140,7 +140,7 @@ class VoiceEngine {
|
|||||||
this.error = '';
|
this.error = '';
|
||||||
try {
|
try {
|
||||||
if (typeof AudioEncoder === 'undefined' || !(await AudioEncoder.isConfigSupported(this.encoderConfig())).supported) {
|
if (typeof AudioEncoder === 'undefined' || !(await AudioEncoder.isConfigSupported(this.encoderConfig())).supported) {
|
||||||
throw new Error('This system cannot encode Opus audio');
|
throw new Error('This browser cannot encode Opus audio (WebCodecs is missing), so you cannot talk or hear others here. Chromium-based browsers and the desktop app can.');
|
||||||
}
|
}
|
||||||
const ctx = new AudioContext({ sampleRate: SAMPLE_RATE, latencyHint: 'interactive' });
|
const ctx = new AudioContext({ sampleRate: SAMPLE_RATE, latencyHint: 'interactive' });
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
|
|||||||
@@ -76,15 +76,27 @@
|
|||||||
async function showCamera(deviceId = camera) {
|
async function showCamera(deviceId = camera) {
|
||||||
error = '';
|
error = '';
|
||||||
dropPreview();
|
dropPreview();
|
||||||
|
const wanted: MediaTrackConstraints = { width: { ideal: 1280 }, height: { ideal: 720 }, frameRate: { ideal: fps() } };
|
||||||
|
if (deviceId) wanted.deviceId = { exact: deviceId };
|
||||||
|
else wanted.facingMode = 'user';
|
||||||
try {
|
try {
|
||||||
preview = await navigator.mediaDevices.getUserMedia({
|
try {
|
||||||
video: { deviceId: deviceId ? { exact: deviceId } : undefined, width: { ideal: 1280 }, height: { ideal: 720 }, frameRate: fps() }
|
preview = await navigator.mediaDevices.getUserMedia({ video: wanted });
|
||||||
});
|
} catch (e) {
|
||||||
|
// A refusal or a missing camera is final; anything else may be a phone camera that
|
||||||
|
// dislikes the wishes above, so ask once more for whatever it has
|
||||||
|
if (['NotAllowedError', 'SecurityError', 'NotFoundError'].includes((e as Error).name)) throw e;
|
||||||
|
preview = await navigator.mediaDevices.getUserMedia({ video: deviceId ? { deviceId: { exact: deviceId } } : true });
|
||||||
|
}
|
||||||
// Names are only given out once a camera was allowed
|
// Names are only given out once a camera was allowed
|
||||||
cameras = (await navigator.mediaDevices.enumerateDevices()).filter(d => d.kind === 'videoinput');
|
cameras = (await navigator.mediaDevices.enumerateDevices()).filter(d => d.kind === 'videoinput');
|
||||||
camera = preview.getVideoTracks()[0]?.getSettings().deviceId ?? deviceId;
|
camera = preview.getVideoTracks()[0]?.getSettings().deviceId ?? deviceId;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = (e as Error).name === 'NotFoundError' ? 'No camera was found.' : `Could not open the camera: ${(e as Error).message}`;
|
const { name, message } = e as Error;
|
||||||
|
error = name === 'NotFoundError' ? 'No camera was found.'
|
||||||
|
: name === 'NotAllowedError' || name === 'SecurityError'
|
||||||
|
? 'The camera was refused. If no question appeared, access is blocked: allow the camera for this site (the icon next to the address), and for the browser app in the phone\'s settings, then try again.'
|
||||||
|
: `Could not open the camera (${name}): ${message}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,6 +253,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if error}<p class="err">{error}</p>{/if}
|
{#if error}<p class="err">{error}</p>{/if}
|
||||||
|
{#if kind === 'camera' && error}<div class="row"><button class="btn" onclick={() => showCamera()}>Try the camera again</button></div>{/if}
|
||||||
|
|
||||||
{#snippet footer()}
|
{#snippet footer()}
|
||||||
<button class="btn" onclick={onclose}>Cancel</button>
|
<button class="btn" onclick={onclose}>Cancel</button>
|
||||||
|
|||||||
@@ -234,6 +234,7 @@
|
|||||||
$effect(() => () => popup?.close());
|
$effect(() => () => popup?.close());
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{#if voice.error && session.status === 'connected'}<div class="bar bad" role="alert"><span>Voice is not working: {voice.error}</span></div>{/if}
|
||||||
{#if share.error}<div class="bar bad" role="alert"><span>{share.error}</span><button class="btn" onclick={() => (share.error = '')}>Dismiss</button></div>{/if}
|
{#if share.error}<div class="bar bad" role="alert"><span>{share.error}</span><button class="btn" onclick={() => (share.error = '')}>Dismiss</button></div>{/if}
|
||||||
|
|
||||||
{#snippet spot()}
|
{#snippet spot()}
|
||||||
|
|||||||
Reference in New Issue
Block a user