import React, { useEffect, useRef } from "react"; import { useStreamCrafterContext } from "../context/StreamCrafterContext"; import { useStudioTranslate } from "../context/StudioI18nContext"; import { useCompositor } from "../hooks/useCompositor"; import { CompositorControls } from "./CompositorControls"; import type { MediaSource } from "@livepeer-frameworks/streamcrafter-core"; const CameraIcon = ({ size = 18 }: { size?: number }) => ( ); export interface StudioPreviewProps { /** Enable compositor overlay controls */ enableCompositor?: boolean; /** Compositor config */ compositorConfig?: { renderer?: "auto" | "webgpu" | "webgl" | "canvas2d"; width?: number; height?: number; frameRate?: number; }; /** Override sources (falls back to context) */ sources?: MediaSource[]; } export const StudioPreview: React.FC = ({ enableCompositor = true, compositorConfig, sources: propSources, }) => { const ctx = useStreamCrafterContext(); const t = useStudioTranslate(); const videoRef = useRef(null); const sources = propSources ?? ctx.sources; const compositor = useCompositor({ controller: enableCompositor ? ctx.getController() : null, autoEnable: enableCompositor, config: compositorConfig, }); useEffect(() => { if (videoRef.current && ctx.mediaStream) { videoRef.current.srcObject = ctx.mediaStream; videoRef.current.play().catch(() => {}); } else if (videoRef.current) { videoRef.current.srcObject = null; } }, [ctx.mediaStream]); const statusText = ctx.state === "connecting" ? t("connecting") : ctx.state === "reconnecting" ? t("reconnecting") : ""; return ( {!ctx.mediaStream && ( {t("addSourcePrompt")} )} {(ctx.state === "connecting" || ctx.state === "reconnecting") && ( {statusText} )} {ctx.isStreaming && {t("live")}} {enableCompositor && ( compositor.cycleSourceOrder(direction)} /> )} ); };