Skip to content

Commit 8b3697b

Browse files
committed
feat: useMediaDevices, useMediaDeviceSelect
1 parent 9ec6852 commit 8b3697b

File tree

5 files changed

+136
-4
lines changed

5 files changed

+136
-4
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "solid-livekit-components",
3-
"version": "2.0.4-1",
3+
"version": "2.9.15-1",
44
"description": "Solid components for building applications with LiveKit",
55
"license": "MIT",
66
"author": "insertish",

src/components/participant/VideoTrack.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// @livekit/[email protected]
2+
// Apache-2.0
3+
14
import { RemoteTrackPublication } from 'livekit-client'
25
import { useMediaTrackBySourceOrName } from '../../signals/useMediaTrackBySourceOrName'
36
import type { ParticipantClickEvent, TrackReference } from '@livekit/components-core'

src/signals/index.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@
1111
// TODO: useLiveKitRoom
1212
// TODO: useLocalParticipant
1313
// TODO: useLocalParticipantPermissions
14-
// TODO: useMediaDevices
15-
// TODO: useMediaDeviceSelect
16-
// TODO: useMediaTrackBySourceOrName
1714
// TODO: usePagination
1815
// TODO: usePaginationTile
1916
// TODO: usePaginationTracks
@@ -50,5 +47,11 @@ export { useIsMuted } from './useIsMuted'
5047

5148
export { useIsSpeaking } from './useIsSpeaking'
5249

50+
export { useMediaDevices } from './useMediaDevices';
51+
52+
export { useMediaDeviceSelect } from './useMediaDeviceSelect';
53+
54+
export { useMediaTrackBySourceOrName } from './useMediaTrackBySourceOrName';
55+
5356
export type { UseTracksHookReturnType, UseTracksOptions } from './useTracks'
5457
export { useTracks } from './useTracks'
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// @livekit/[email protected]
2+
// Apache-2.0
3+
4+
import { createMediaDeviceObserver, setupDeviceSelector } from '@livekit/components-core'
5+
import { LocalAudioTrack, LocalVideoTrack, Room } from 'livekit-client'
6+
import { createEffect, createMemo, createSignal, on, onCleanup } from 'solid-js'
7+
import { useMaybeRoomContext } from 'src/context'
8+
import { useObservableState } from './internal'
9+
10+
/** @public */
11+
export interface UseMediaDeviceSelectProps {
12+
kind: MediaDeviceKind
13+
room?: Room
14+
track?: LocalAudioTrack | LocalVideoTrack
15+
/**
16+
* this will call getUserMedia if the permissions are not yet given to enumerate the devices with device labels.
17+
* in some browsers multiple calls to getUserMedia result in multiple permission prompts.
18+
* It's generally advised only flip this to true, once a (preview) track has been acquired successfully with the
19+
* appropriate permissions.
20+
*
21+
* @see {@link MediaDeviceMenu}
22+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/enumerateDevices | MDN enumerateDevices}
23+
*/
24+
requestPermissions?: boolean
25+
/**
26+
* this callback gets called if an error is thrown when failing to select a device and also if a user
27+
* denied permissions, eventhough the `requestPermissions` option is set to `true`.
28+
* Most commonly this will emit a MediaDeviceError
29+
*/
30+
onError?: (e: Error) => void
31+
}
32+
33+
/**
34+
* The `useMediaDeviceSelect` hook is used to implement the `MediaDeviceSelect` component and
35+
* returns o.a. the list of devices of a given kind (audioinput or videoinput), the currently active device
36+
* and a function to set the the active device.
37+
*
38+
* @example
39+
* ```tsx
40+
* const { devices, activeDeviceId, setActiveMediaDevice } = useMediaDeviceSelect({kind: 'audioinput'});
41+
* ```
42+
* @public
43+
*/
44+
export function useMediaDeviceSelect({
45+
kind,
46+
room,
47+
track,
48+
requestPermissions,
49+
onError,
50+
}: UseMediaDeviceSelectProps) {
51+
const roomContext = useMaybeRoomContext()
52+
const roomFallback = createMemo(() => room ?? roomContext?.() ?? new Room())
53+
54+
// List of all devices.
55+
const deviceObserver = createMemo(() =>
56+
createMediaDeviceObserver(kind, onError, requestPermissions),
57+
)
58+
59+
const devices = useObservableState(deviceObserver(), [] as MediaDeviceInfo[])
60+
61+
// Active device management.
62+
const [currentDeviceId, setCurrentDeviceId] = createSignal(
63+
roomFallback()?.getActiveDevice(kind) ?? 'default',
64+
)
65+
66+
const deviceSelector = createMemo(() => setupDeviceSelector(kind, roomFallback()))
67+
68+
createEffect(
69+
on(
70+
() => deviceSelector(),
71+
({ activeDeviceObservable }) => {
72+
const listener = activeDeviceObservable.subscribe(deviceId => {
73+
if (!deviceId) {
74+
return
75+
}
76+
console.info('setCurrentDeviceId', deviceId)
77+
setCurrentDeviceId(deviceId)
78+
})
79+
80+
onCleanup(() => {
81+
listener?.unsubscribe()
82+
})
83+
},
84+
),
85+
)
86+
87+
return {
88+
devices,
89+
className: () => deviceSelector().className,
90+
activeDeviceId: currentDeviceId,
91+
setActiveMediaDevice: (
92+
...args: Parameters<ReturnType<typeof deviceSelector>['setActiveMediaDevice']>
93+
) => deviceSelector().setActiveMediaDevice(...args),
94+
}
95+
}

src/signals/useMediaDevices.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// @livekit/[email protected]
2+
// Apache-2.0
3+
4+
import { createMediaDeviceObserver } from "@livekit/components-core";
5+
import { createMemo } from "solid-js";
6+
import { useObservableState } from "./internal";
7+
8+
/**
9+
* The `useMediaDevices` hook returns the list of media devices of a given kind.
10+
*
11+
* @example
12+
* ```tsx
13+
* const videoDevices = useMediaDevices({ kind: 'videoinput' });
14+
* const audioDevices = useMediaDevices({ kind: 'audioinput' });
15+
* ```
16+
* @public
17+
*/
18+
export function useMediaDevices({
19+
kind,
20+
onError,
21+
}: {
22+
kind: MediaDeviceKind;
23+
onError?: (e: Error) => void;
24+
}) {
25+
const deviceObserver = createMemo(
26+
() => createMediaDeviceObserver(kind, onError),
27+
);
28+
29+
const devices = useObservableState(deviceObserver(), [] as MediaDeviceInfo[]);
30+
return devices;
31+
}

0 commit comments

Comments
 (0)