/** Which of our own video streams: the camera's m-line or the screen share's. */ export type VideoRole = "camera" | "screen"; /** What the SFU says viewers want of one of our streams. `unknown` viewers never said, so want it all. */ export interface VideoWanted { width: number; height: number; fps: number; watchers: number; unknown: number; } /** * What the embedder wants of one outgoing video stream: the user's picks. Each is a ceiling, * and the engine may send less when nobody draws it that big. */ export interface VideoSendSettings { maxSize?: { width: number; height: number; } | null; maxFramerate?: number; maxBitrate?: number | null; priority?: RTCPriorityType; degradationPreference?: RTCDegradationPreference; scalabilityMode?: string; /** False keeps the settings as they are, whatever viewers want. For encoders setParameters can't reach. */ followDemand?: boolean; } /** What a viewer reports for one remote video, in device pixels. Zero means nobody can see it. */ export interface VideoDemand { width: number; height: number; fps?: number; } export declare const SCALE_LADDER: number[]; export declare const HOLD_DOWN_MS = 5000; export declare const PAUSE_AFTER_MS = 10000; export declare const RESUME_CHECK_MS = 1000; export declare const RESUME_WATCH_MS = 5000; export declare const MAX_REKICKS = 3; export interface VideoPlanState { /** The demand's rung, held on the way down. The user's ceiling is applied on top of it. */ rung: number; fps: number; rungLowerSince: number | null; fpsLowerSince: number | null; idleSince: number | null; paused: boolean; /** Paused by the budget rather than by nobody watching. */ held: boolean; /** A resume being watched: the next check, when watching ends, the last frame count, re-kicks so far. */ resume: { at: number; until: number; frames: number | null; tries: number; } | null; } export declare const INITIAL_PLAN_STATE: VideoPlanState; export interface VideoPlanInput { role: VideoRole; source: { width: number; height: number; frameRate?: number; } | null; settings: VideoSendSettings; /** Null until this SFU says anything, which an SFU from before GRYT-1432 never does. */ wanted: VideoWanted | null; now: number; /** framesEncoded on this stream's outbound-rtp, when the plan has a resume to check. */ framesEncoded: number | null; /** What the budget allows this second, on top of demand. */ limit?: VideoLimit | null; } /** The budget's say over one stream: at least this scale, at most this frame rate and bitrate. */ export interface VideoLimit { scale: number; fps: number; maxBitrate: number; paused: boolean; } export interface VideoEncodingPlan { active: boolean; scaleResolutionDownBy: number; maxBitrate?: number; maxFramerate?: number; /** The encoder produced nothing since a resume: set active off and on again. */ rekick: boolean; state: VideoPlanState; } /** A camera's bitrate: Chrome's 2.5 Mbps at 1080p30, scaled by pixels^0.75 and (fps/30)^0.7. */ export declare function cameraBitrate(width: number, height: number, fps: number): number; /** A share's bitrate: the picker's table, at the smallest quality that holds this height. */ export declare function screenBitrate(height: number, fps: number): number; /** The largest ladder scale whose output still covers the wanted size, or 1. */ export declare function rungFor(source: { width: number; height: number; }, wanted: { width: number; height: number; }): number; /** * One stream's encoding from its source, the user's settings, what viewers want and the time. * Pure, so a check can run it on a clock of its own; the engine runs it every second. */ export declare function planVideoEncoding(input: VideoPlanInput, previous: VideoPlanState): VideoEncodingPlan; /** The payload of video_wanted, or null for one we can't read. */ export declare function parseVideoWanted(data: unknown): (VideoWanted & { mid: string; }) | null; /** * Sends what this viewer draws, per remote stream, over the SFU socket. Increases go out after * 100 ms of quiet and decreases after 1 s; a new socket gets every stream's last report again. */ export declare function createDemandReporter(getSocket: () => WebSocket | null): { report(streamId: string, demand: VideoDemand | null): void; /** Every stream's last report, for a socket that has just joined. */ resend(): void; };