@eatsjobs/media-mock
Version:
Media-Mock is a JavaScript library that simulates media devices (like webcams) in web applications, allowing developers to test and debug media constraints, device configurations, and stream functionality without needing physical devices. This is particul
265 lines (254 loc) • 8.1 kB
text/typescript
/**
* Creates a mock MediaDeviceInfo object.
*
* @export
* @param {{
* deviceId: string;
* groupId: string;
* kind: MediaDeviceKind;
* label: string;
* mockCapabilities?: EnhancedMediaTrackCapabilities;
* }} param0
* @param {string} param0.deviceId
* @param {string} param0.groupId
* @param {MediaDeviceKind} param0.kind
* @param {string} param0.label
* @param {EnhancedMediaTrackCapabilities} [param0.mockCapabilities={
* width: { min: 1, max: 1280 },
* height: { min: 1, max: 720 },
* }]
* @returns {MockMediaDeviceInfo}
*/
export declare function createMediaDeviceInfo({ deviceId, groupId, kind, label, mockCapabilities, }: {
deviceId: string;
groupId: string;
kind: MediaDeviceKind;
label: string;
mockCapabilities?: EnhancedMediaTrackCapabilities;
}): MockMediaDeviceInfo;
export declare interface DeviceConfig {
videoResolutions: {
width: number;
height: number;
}[];
mediaDeviceInfo: MockMediaDeviceInfo[];
supportedConstraints: Record<keyof MediaTrackSupportedConstraints & "torch", boolean>;
}
declare type DeviceName = "iPhone 12" | "Samsung Galaxy M53" | "Mac Desktop";
/**
* A list of devices to mock. For now we have
* - iPhone 12
* - Samsung Galaxy M53
* - Mac Desktop
*
* @type {Record<DeviceName, DeviceConfig>}
*/
export declare const devices: Record<DeviceName, DeviceConfig>;
export declare interface EnhancedMediaTrackCapabilities extends MediaTrackCapabilities {
whiteBalanceMode?: string[];
focusDistance?: {
min: number;
};
zoom?: {
max: number;
min: number;
};
torch?: boolean;
backgroundBlur?: boolean[];
resizeMode?: string[];
}
export declare const MediaMock: MediaMockClass;
/**
* MediaMock class.
*
* @example
* ```ts
* import { MediaMock, devices } from "@eatsjobs/media-mock";
* // Configure and initialize MediaMock with default settings
* MediaMock.mock(devices["iPhone 12"]); // or devices["Samsung Galaxy M53"] for Android, "Mac Desktop" for desktop mediaDevice emulation
* await MediaMock.setMediaURL("./assets/640x480-sample.png");
*
* // Set up a video element to display the stream
* const videoElement = document.createElement("video");
* document.body.appendChild(videoElement);
*
* videoElement.srcObject = await navigator.mediaDevices.getUserMedia({ video: true });
* videoElement.play();
* ```
* @export
* @class MediaMockClass
*/
export declare class MediaMockClass {
settings: Settings;
private readonly mediaMockImageId;
private readonly mediaMockCanvasId;
private currentImage;
private currentVideo;
private mapUnmockFunction;
private currentStream;
private intervalId;
private rafId;
private debug;
private canvas;
private ctx;
private mockedVideoTracksHandler;
private fps;
private resolution;
private lastDrawTime;
/**
* The Image or the video that will be used as source.
* @public
* @param {string} mediaURL
* @returns {Promise<MediaMockClass>}
*/
setMediaURL(mediaURL: string): Promise<MediaMockClass>;
private startDrawingLoop;
/**
* Start drawing loop for video using RequestAnimationFrame with FPS throttling
*/
private startVideoDrawingLoop;
/**
* Start drawing loop for image using RequestAnimationFrame
* For static images, draws once and then relies on canvas stream
*/
private startImageDrawingLoop;
/**
* Stop the drawing loop (either RAF or setInterval)
*/
private stopDrawingLoop;
/**
* Add a new device and trigger a device change event.
*
* @public
* @param {MockMediaDeviceInfo} newDevice
*/
addMockDevice(newDevice: MockMediaDeviceInfo): typeof MediaMock;
/**
* Remove a device and trigger a device change event.
*
* @public
* @param {string} deviceId
*/
removeMockDevice(deviceId: string): typeof MediaMock;
private triggerDeviceChange;
/**
* Debug mode will append the canvas and loaded image to the body if available.
*
* @public
*/
enableDebugMode(): typeof MediaMock;
/**
* Removes the debug canvas and image from the body.
*
* @public
* @returns {typeof MediaMock}
*/
disableDebugMode(): typeof MediaMock;
setMockedVideoTracksHandler(mockedVideoTracksHandler: (tracks: MediaStreamTrack[]) => MediaStreamTrack[]): typeof MediaMock;
/**
* Replaces the navigator.mediaDevices functions.
*
* @public
* @param {DeviceConfig} device
* @param {MockOptions} [options=createDefaultMockOptions()]
* @returns {typeof MediaMock}
*/
mock(device: DeviceConfig, options?: MockOptions): typeof MediaMock;
/**
* Stops the mock and removes the mock functions.
*
* @public
* @returns {typeof MediaMock}
*/
unmock(): typeof MediaMock;
private stopMockStream;
/**
* Set the scale factor for the image in the canvas.
* Values between 0 and N, where lower values create more margin,
* and higher values fill more of the canvas.
*
* @public
* @param {number} factor - Scale factor between 0 and N
* @returns {typeof MediaMock}
*/
setCanvasScaleFactor(factor: number): typeof MediaMock;
/**
* Set the timeout for media loading (images and videos) in milliseconds.
*
* @public
* @param {number} timeoutMs - Timeout in milliseconds (default: 60000 = 60 seconds)
* @returns {typeof MediaMock}
*/
setMediaTimeout(timeoutMs: number): typeof MediaMock;
private getMockStream;
private getFPSFromConstraints;
/**
* Extract facingMode from constraints (can be a string or ConstrainDOMString)
*/
private getFacingModeFromConstraints;
/**
* Get the appropriate camera device based on facingMode
* Falls back to last videoinput if no matching camera found
*/
private getDeviceForFacingMode;
/**
* Get the appropriate resolution based on device orientation and constraints
* @param constraints Media constraints
* @param deviceConfig Device configuration
* @returns Resolution object with width and height
*/
private getResolution;
/**
* Extract numeric value from constraint (handles number, object with ideal/exact, etc.)
*/
private extractConstraintValue;
/**
* Find exact resolution match considering orientation
*/
private findExactMatch;
/**
* Find best resolution match by aspect ratio and size preference
*/
private findBestFitResolution;
/**
* Get fallback resolution based on orientation
*/
private getFallbackResolution;
}
export declare interface MockMediaDeviceInfo extends MediaDeviceInfo {
getCapabilities: () => EnhancedMediaTrackCapabilities;
}
export declare interface MockOptions {
mediaDevices: {
getUserMedia: boolean;
getSupportedConstraints: boolean;
enumerateDevices: boolean;
};
}
export declare interface Settings {
/**
* The media url to use for the mock. Video or image.
* @type {string}
*/
mediaURL: string;
/**
* The preset device config to emulate
*
* @type {DeviceConfig}
*/
device: DeviceConfig;
constraints: MediaTrackConstraints;
/**
* Scale factor for the image in the canvas (0-1)
* Lower values create more margin, higher values fill more of the canvas
* @type {number}
*/
canvasScaleFactor: number;
/**
* Timeout for media loading (image and video) in milliseconds
* @type {number}
* @default 60000 (60 seconds)
*/
mediaTimeout: number;
}
export { }