@pexip/media-control 17.2.0 → 17.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,10 @@
1
+ import type { DisplayMediaOptions } from './types';
2
+ /**
3
+ * A fallback implementation of `monitorTypeSurfaces` limitation when it is not
4
+ * supported by the current browser and the source of the `displaySurface` is
5
+ * available.
6
+ *
7
+ * @param constraints - The constraints used to request display media.
8
+ * @param stream - The MediaStream returned from the requesting display media.
9
+ */
10
+ export declare const limitSharingMonitorInterface: (constraints: DisplayMediaOptions, stream: MediaStream) => MediaStream;
@@ -0,0 +1,31 @@
1
+ import { stopMediaStream } from './devices';
2
+ /**
3
+ * A fallback implementation of `monitorTypeSurfaces` limitation when it is not
4
+ * supported by the current browser and the source of the `displaySurface` is
5
+ * available.
6
+ *
7
+ * @param constraints - The constraints used to request display media.
8
+ * @param stream - The MediaStream returned from the requesting display media.
9
+ */
10
+ export const limitSharingMonitorInterface = (constraints, stream) => {
11
+ // Fallback implementation of `monitorTypeSurfaces`
12
+ if (!constraints.monitorTypeSurfaces ||
13
+ constraints.monitorTypeSurfaces !== 'exclude') {
14
+ return stream;
15
+ }
16
+ for (const track of stream.getTracks()) {
17
+ const { displaySurface } = track.getSettings();
18
+ if (!displaySurface || displaySurface !== 'monitor') {
19
+ continue;
20
+ }
21
+ stopMediaStream(stream);
22
+ throw new TypeError('MonitorSharingNotAllowed', {
23
+ cause: {
24
+ monitorTypeSurfaces: constraints.monitorTypeSurfaces,
25
+ track,
26
+ displaySurface,
27
+ },
28
+ });
29
+ }
30
+ return stream;
31
+ };
@@ -0,0 +1,13 @@
1
+ import type { MediaDeviceInfoLike, MediaDeviceKinds, MediaDeviceRequest } from './types';
2
+ import { MediaDeviceFailure } from './types';
3
+ export interface InputNotFoundErrorParams {
4
+ input: MediaTrackConstraints | boolean | undefined;
5
+ kind: MediaDeviceKinds;
6
+ devices: MediaDeviceInfoLike[];
7
+ }
8
+ export declare const isInputNotFoundError: ({ input, kind, devices }: InputNotFoundErrorParams) => (error: string) => boolean;
9
+ export declare const normalizeGetUserMediaError: (browserError: Error, constraints: MediaStreamConstraints, devices: MediaDeviceInfoLike[]) => MediaDeviceFailure | string;
10
+ export declare const normalizeDeviceError: ({ audio, video, streamingAudioInput, streamingVideoInput, }: {
11
+ streamingAudioInput?: boolean | undefined;
12
+ streamingVideoInput?: boolean | undefined;
13
+ } & MediaDeviceRequest) => false | MediaDeviceFailure;
package/dist/errors.js ADDED
@@ -0,0 +1,105 @@
1
+ import { isMediaTrackConstraints } from './typeGuards';
2
+ import { extractDeviceId } from './constraints';
3
+ import { MediaDeviceFailure } from './types';
4
+ const isDeviceInUseError = (error) => {
5
+ return (error === MediaDeviceFailure.NotReadableError ||
6
+ error === MediaDeviceFailure.TrackStartError);
7
+ };
8
+ const isPermissionDeniedError = (error) => {
9
+ return (error === MediaDeviceFailure.NotAllowedError ||
10
+ error === MediaDeviceFailure.PermissionDeniedError);
11
+ };
12
+ export const isInputNotFoundError = ({ input, kind, devices }) => (error) => {
13
+ if (error === MediaDeviceFailure.NotFoundError) {
14
+ const hasDevices = devices.some(d => d.kind === kind);
15
+ if (typeof input === 'boolean') {
16
+ if (!hasDevices && input) {
17
+ return true;
18
+ }
19
+ return input && !hasDevices;
20
+ }
21
+ if (isMediaTrackConstraints(input)) {
22
+ const [deviceIds, requirement] = extractDeviceId(input);
23
+ if (requirement === 'ideal') {
24
+ return !hasDevices;
25
+ }
26
+ if (requirement === 'exact') {
27
+ return !devices.some(device => deviceIds?.some(id => device.kind === kind && id === device.deviceId));
28
+ }
29
+ }
30
+ }
31
+ return false;
32
+ };
33
+ const normalizeError = (errors, parameters = []) => {
34
+ const error = errors.find(e => e.fn(...parameters));
35
+ if (error) {
36
+ return error.type;
37
+ }
38
+ return false;
39
+ };
40
+ export const normalizeGetUserMediaError = (browserError, constraints, devices) => {
41
+ const isAudioInputNotFoundError = isInputNotFoundError({
42
+ input: constraints.audio,
43
+ kind: 'audioinput',
44
+ devices,
45
+ });
46
+ const isVideoInputNotFoundError = isInputNotFoundError({
47
+ input: constraints.video,
48
+ kind: 'videoinput',
49
+ devices,
50
+ });
51
+ const areBothInputsNotFoundError = (error) => isAudioInputNotFoundError(error) && isVideoInputNotFoundError(error);
52
+ const errorsFn = [
53
+ {
54
+ fn: isDeviceInUseError,
55
+ type: MediaDeviceFailure.NotReadableError, // For now just normalize to current spec
56
+ },
57
+ {
58
+ fn: isPermissionDeniedError,
59
+ type: MediaDeviceFailure.NotAllowedError,
60
+ },
61
+ {
62
+ fn: areBothInputsNotFoundError,
63
+ type: MediaDeviceFailure.AudioAndVideoDeviceNotFoundError,
64
+ },
65
+ {
66
+ fn: isAudioInputNotFoundError,
67
+ type: MediaDeviceFailure.AudioInputDeviceNotFoundError,
68
+ },
69
+ {
70
+ fn: isVideoInputNotFoundError,
71
+ type: MediaDeviceFailure.VideoInputDeviceNotFoundError,
72
+ },
73
+ ];
74
+ const errorMsg = browserError.name === 'Error'
75
+ ? browserError.message
76
+ : browserError.name;
77
+ const error = normalizeError(errorsFn, [errorMsg]);
78
+ return error ? error : errorMsg;
79
+ };
80
+ export const normalizeDeviceError = ({ audio, video, streamingAudioInput, streamingVideoInput, }) => {
81
+ let audioError = false;
82
+ let videoError = false;
83
+ if (audio) {
84
+ audioError = !streamingAudioInput;
85
+ }
86
+ if (video) {
87
+ videoError = !streamingVideoInput;
88
+ }
89
+ const errorsFn = [
90
+ {
91
+ fn: () => audioError && videoError,
92
+ type: MediaDeviceFailure.AudioAndVideoDeviceNotFoundError,
93
+ },
94
+ {
95
+ fn: () => audioError,
96
+ type: MediaDeviceFailure.AudioInputDeviceNotFoundError,
97
+ },
98
+ {
99
+ fn: () => videoError,
100
+ type: MediaDeviceFailure.VideoInputDeviceNotFoundError,
101
+ },
102
+ ];
103
+ const error = normalizeError(errorsFn, []);
104
+ return error ? error : false;
105
+ };
@@ -0,0 +1,119 @@
1
+ import type { MediaDeviceInfoLike } from './types';
2
+ /**
3
+ * MediaStreamTrack Events
4
+ *
5
+ * @remarks
6
+ * See MDN {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack#Events | Events}
7
+ *
8
+ * @beta
9
+ */
10
+ export declare enum MediaEventType {
11
+ /**
12
+ * Sent to the MediaStreamTrack when the value of the muted property is
13
+ * changed to true, indicating that the track is unable to provide data
14
+ * temporarily (such as when the network is experiencing a service
15
+ * malfunction).
16
+ */
17
+ Mute = "mute",
18
+ /**
19
+ * Sent to the track when data becomes available again, ending the muted state.
20
+ */
21
+ Unmute = "unmute",
22
+ /**
23
+ * Sent when playback of the track ends (when the value readyState changes to
24
+ * ended).
25
+ */
26
+ Ended = "ended",
27
+ /**
28
+ * Fired when a media input or output device is attached to or removed from
29
+ * the user's computer.
30
+ *
31
+ * @remarks
32
+ * See MDN {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices#Events | Events}
33
+ */
34
+ DevicesChanged = "devices:changed",
35
+ /**
36
+ * Found new devices from authorized device list
37
+ */
38
+ DevicesFound = "devices:found",
39
+ /**
40
+ * Lost devices from authorized device list
41
+ */
42
+ DevicesLost = "devices:lost",
43
+ /**
44
+ * Lost device from authorized device list
45
+ */
46
+ DeviceLost = "device:lost",
47
+ /**
48
+ * Unauthorized devices
49
+ */
50
+ DevicesUnauthorized = "devices:unauthorized",
51
+ /**
52
+ * No Input Devices, and no further device events will be emitted
53
+ */
54
+ NoInputDevices = "devices:noinput",
55
+ /**
56
+ * Other errors
57
+ */
58
+ Error = "error",
59
+ /**
60
+ * When stream
61
+ */
62
+ Stream = "stream"
63
+ }
64
+ /**
65
+ * Event object by MediaEventType
66
+ *
67
+ * @beta
68
+ */
69
+ export type Events = {
70
+ id: string;
71
+ type: MediaEventType.Mute;
72
+ } | {
73
+ id: string;
74
+ type: MediaEventType.Unmute;
75
+ } | {
76
+ id: string;
77
+ type: MediaEventType.Ended;
78
+ } | {
79
+ devices: MediaDeviceInfoLike[];
80
+ type: MediaEventType.DevicesChanged;
81
+ } | {
82
+ devices: MediaDeviceInfoLike[];
83
+ authorizedDevices: MediaDeviceInfoLike[];
84
+ unauthorizedDevices: MediaDeviceInfoLike[];
85
+ type: MediaEventType.DevicesFound;
86
+ } | {
87
+ devices: MediaDeviceInfoLike[];
88
+ authorizedDevices: MediaDeviceInfoLike[];
89
+ unauthorizedDevices: MediaDeviceInfoLike[];
90
+ type: MediaEventType.DevicesLost;
91
+ } | {
92
+ devices: MediaDeviceInfoLike[];
93
+ authorizedDevices: MediaDeviceInfoLike[];
94
+ type: MediaEventType.DevicesUnauthorized;
95
+ } | {
96
+ devices: MediaDeviceInfoLike[];
97
+ type: MediaEventType.NoInputDevices;
98
+ } | {
99
+ device: MediaDeviceInfoLike;
100
+ type: MediaEventType.DeviceLost;
101
+ } | {
102
+ error: Error;
103
+ type: MediaEventType.Error;
104
+ } | {
105
+ facingMode: boolean;
106
+ stream: MediaStream;
107
+ type: MediaEventType.Stream;
108
+ video: boolean;
109
+ };
110
+ export type Dispatch = (event: Events) => void;
111
+ /**
112
+ * Custom Media Event
113
+ * @beta
114
+ */
115
+ export type MediaEvent = CustomEvent<Events>;
116
+ export declare const eventEmitter: () => {
117
+ dispatch(event: Events): void;
118
+ subscriber(listener: (event: MediaEvent) => void, onUnsubscribe: () => void): () => void;
119
+ };
@@ -0,0 +1,81 @@
1
+ /**
2
+ * MediaStreamTrack Events
3
+ *
4
+ * @remarks
5
+ * See MDN {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack#Events | Events}
6
+ *
7
+ * @beta
8
+ */
9
+ export var MediaEventType;
10
+ (function (MediaEventType) {
11
+ /**
12
+ * Sent to the MediaStreamTrack when the value of the muted property is
13
+ * changed to true, indicating that the track is unable to provide data
14
+ * temporarily (such as when the network is experiencing a service
15
+ * malfunction).
16
+ */
17
+ MediaEventType["Mute"] = "mute";
18
+ /**
19
+ * Sent to the track when data becomes available again, ending the muted state.
20
+ */
21
+ MediaEventType["Unmute"] = "unmute";
22
+ /**
23
+ * Sent when playback of the track ends (when the value readyState changes to
24
+ * ended).
25
+ */
26
+ MediaEventType["Ended"] = "ended";
27
+ /**
28
+ * Fired when a media input or output device is attached to or removed from
29
+ * the user's computer.
30
+ *
31
+ * @remarks
32
+ * See MDN {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices#Events | Events}
33
+ */
34
+ MediaEventType["DevicesChanged"] = "devices:changed";
35
+ /**
36
+ * Found new devices from authorized device list
37
+ */
38
+ MediaEventType["DevicesFound"] = "devices:found";
39
+ /**
40
+ * Lost devices from authorized device list
41
+ */
42
+ MediaEventType["DevicesLost"] = "devices:lost";
43
+ /**
44
+ * Lost device from authorized device list
45
+ */
46
+ MediaEventType["DeviceLost"] = "device:lost";
47
+ /**
48
+ * Unauthorized devices
49
+ */
50
+ MediaEventType["DevicesUnauthorized"] = "devices:unauthorized";
51
+ /**
52
+ * No Input Devices, and no further device events will be emitted
53
+ */
54
+ MediaEventType["NoInputDevices"] = "devices:noinput";
55
+ /**
56
+ * Other errors
57
+ */
58
+ MediaEventType["Error"] = "error";
59
+ /**
60
+ * When stream
61
+ */
62
+ MediaEventType["Stream"] = "stream";
63
+ })(MediaEventType || (MediaEventType = {}));
64
+ export const eventEmitter = () => {
65
+ const element = document.createElement('a');
66
+ const createEvent = (data) => {
67
+ return new CustomEvent('data', { detail: data });
68
+ };
69
+ return {
70
+ dispatch(event) {
71
+ element.dispatchEvent(createEvent(event));
72
+ },
73
+ subscriber(listener, onUnsubscribe) {
74
+ element.addEventListener('data', listener, false);
75
+ return () => {
76
+ element.removeEventListener('data', listener, false);
77
+ onUnsubscribe();
78
+ };
79
+ },
80
+ };
81
+ };
@@ -0,0 +1,11 @@
1
+ import type { MediaDeviceRequest } from './types';
2
+ /**
3
+ * Get MediaStream with provided input constraints
4
+ *
5
+ * @returns MediaStream
6
+ *
7
+ * @beta
8
+ */
9
+ export declare const getMediaStream: (request: MediaDeviceRequest & {
10
+ getDefaultConstraints: () => MediaStreamConstraints;
11
+ }) => Promise<MediaStream>;
@@ -0,0 +1,41 @@
1
+ import { MediaDeviceFailure } from './types';
2
+ import { getDevices } from './devices';
3
+ import { getMediaConstraints, relaxInputConstraint } from './constraints';
4
+ import { normalizeGetUserMediaError } from './errors';
5
+ import { logger } from './logger';
6
+ /**
7
+ * Get MediaStream with provided input constraints
8
+ *
9
+ * @returns MediaStream
10
+ *
11
+ * @beta
12
+ */
13
+ export const getMediaStream = async (request) => {
14
+ if (!request.audio && !request.video) {
15
+ throw new Error(MediaDeviceFailure.MissingConstraintsError);
16
+ }
17
+ const devices = await getDevices();
18
+ const audio = relaxInputConstraint(request.audio, devices);
19
+ const video = relaxInputConstraint(request.video, devices);
20
+ const constraints = getMediaConstraints({
21
+ audio,
22
+ video,
23
+ defaultConstraints: request.getDefaultConstraints(),
24
+ });
25
+ logger.debug({ request, constraints, devices }, 'Constraints used for getting media');
26
+ try {
27
+ // Track device changes before and after the getUserMedia call to keep
28
+ // track of authorized/unauthorized devices
29
+ navigator.mediaDevices.dispatchEvent(new Event('devicechange'));
30
+ const stream = await navigator.mediaDevices.getUserMedia(constraints);
31
+ navigator.mediaDevices.dispatchEvent(new Event('devicechange'));
32
+ return stream;
33
+ }
34
+ catch (error) {
35
+ if (error instanceof Error) {
36
+ const devices = await getDevices();
37
+ throw new Error(normalizeGetUserMediaError(error, constraints, devices));
38
+ }
39
+ throw error;
40
+ }
41
+ };