168 lines
No EOL
4.8 KiB
TypeScript
168 lines
No EOL
4.8 KiB
TypeScript
import {ClientIcon} from "svg-sprites/client-icons";
|
|
import {VideoBroadcastType} from "tc-shared/connection/VideoConnection";
|
|
import {LogCategory, logWarn} from "tc-shared/log";
|
|
|
|
export const kLocalVideoId = "__local__video__";
|
|
export const kLocalBroadcastChannels: VideoBroadcastType[] = ["screen", "camera"];
|
|
|
|
export type ChannelVideoInfo = { clientName: string, clientUniqueId: string, clientId: number, statusIcon: ClientIcon };
|
|
export type ChannelVideoStreamState = "available" | "streaming" | "ignored" | "none";
|
|
|
|
export type VideoStatistics = {
|
|
type: "sender",
|
|
mode: "camara" | "screen",
|
|
|
|
dimensions: { width: number, height: number },
|
|
frameRate: number,
|
|
|
|
codec: { name: string, payloadType: number }
|
|
|
|
maxBandwidth: number,
|
|
bandwidth: number,
|
|
|
|
qualityLimitation: "cpu" | "bandwidth",
|
|
|
|
source: {
|
|
frameRate: number,
|
|
dimensions: { width: number, height: number },
|
|
}
|
|
} | {
|
|
type: "receiver",
|
|
mode: "camara" | "screen",
|
|
|
|
dimensions: { width: number, height: number },
|
|
frameRate: number,
|
|
|
|
codec: { name: string, payloadType: number }
|
|
};
|
|
|
|
export type VideoStreamState = {
|
|
state: "disconnected"
|
|
} | {
|
|
state: "available"
|
|
} | {
|
|
state: "connecting"
|
|
} | {
|
|
/* like join failed or whatever */
|
|
state: "failed",
|
|
reason?: string
|
|
} | {
|
|
state: "connected",
|
|
stream: MediaStream
|
|
};
|
|
|
|
export type VideoSubscribeInfo = {
|
|
totalSubscriptions: number,
|
|
subscribedStreams: {[T in VideoBroadcastType]: number},
|
|
|
|
subscribeLimits: {[T in VideoBroadcastType]?: number},
|
|
maxSubscriptions: number | undefined
|
|
};
|
|
|
|
/**
|
|
* "muted": The video has been muted locally
|
|
* "unset": The video will be normally played
|
|
* "empty": No video available
|
|
*/
|
|
export type LocalVideoState = "muted" | "unset" | "empty";
|
|
|
|
export interface ChannelVideoEvents {
|
|
action_toggle_expended: { expended: boolean },
|
|
action_video_scroll: { direction: "left" | "right" },
|
|
action_set_spotlight: { videoId: string | undefined, expend: boolean },
|
|
action_focus_spotlight: {},
|
|
action_set_fullscreen: { videoId: string | undefined },
|
|
action_set_pip: { videoId: string | undefined, broadcastType: VideoBroadcastType },
|
|
action_toggle_mute: { videoId: string, broadcastType: VideoBroadcastType | undefined, muted: boolean },
|
|
action_dismiss: { videoId: string, broadcastType: VideoBroadcastType },
|
|
|
|
query_expended: {},
|
|
query_videos: {},
|
|
query_video: { videoId: string },
|
|
query_video_info: { videoId: string },
|
|
query_video_statistics: { videoId: string, broadcastType: VideoBroadcastType },
|
|
query_spotlight: {},
|
|
query_video_stream: { videoId: string, broadcastType: VideoBroadcastType },
|
|
query_subscribe_info: {}
|
|
|
|
notify_expended: { expended: boolean },
|
|
notify_videos: {
|
|
videoIds: string[]
|
|
},
|
|
notify_video: {
|
|
videoId: string,
|
|
|
|
cameraStream: ChannelVideoStreamState,
|
|
screenStream: ChannelVideoStreamState,
|
|
},
|
|
notify_video_info: {
|
|
videoId: string,
|
|
info: ChannelVideoInfo
|
|
},
|
|
notify_video_info_status: {
|
|
videoId: string,
|
|
statusIcon: ClientIcon
|
|
},
|
|
notify_video_arrows: {
|
|
left: boolean,
|
|
right: boolean
|
|
},
|
|
notify_spotlight: {
|
|
videoId: string | undefined
|
|
},
|
|
notify_video_statistics: {
|
|
videoId: string | undefined,
|
|
broadcastType: VideoBroadcastType,
|
|
statistics: VideoStatistics
|
|
},
|
|
notify_video_stream: {
|
|
videoId: string,
|
|
broadcastType: VideoBroadcastType,
|
|
state: VideoStreamState
|
|
},
|
|
notify_subscribe_info: {
|
|
info: VideoSubscribeInfo
|
|
}
|
|
}
|
|
|
|
export function makeVideoAutoplay(video: HTMLVideoElement) : () => void {
|
|
let replayTimeout;
|
|
|
|
video.autoplay = true;
|
|
|
|
const executePlay = () => {
|
|
if(replayTimeout) {
|
|
return;
|
|
}
|
|
|
|
video.play().then(undefined).catch(() => {
|
|
logWarn(LogCategory.VIDEO, tr("Failed to start video replay. Retrying in 500ms intervals."));
|
|
replayTimeout = setInterval(() => {
|
|
video.play().then(() => {
|
|
clearInterval(replayTimeout);
|
|
replayTimeout = undefined;
|
|
}).catch(() => {});
|
|
});
|
|
});
|
|
};
|
|
|
|
const listenerPause = () => {
|
|
logWarn(LogCategory.VIDEO, tr("Video replay paused. Executing play again."));
|
|
executePlay();
|
|
};
|
|
|
|
const listenerEnded = () => {
|
|
logWarn(LogCategory.VIDEO, tr("Video replay ended. Executing play again."));
|
|
executePlay();
|
|
};
|
|
|
|
video.addEventListener("pause", listenerPause);
|
|
video.addEventListener("ended", listenerEnded);
|
|
executePlay();
|
|
|
|
return () => {
|
|
clearTimeout(replayTimeout);
|
|
video.removeEventListener("pause", listenerPause);
|
|
video.removeEventListener("ended", listenerEnded);
|
|
};
|
|
} |