From 22da5866bf9f717d08f0bcd7b2f4db6f752d3399 Mon Sep 17 00:00:00 2001 From: Max Holland Date: Tue, 11 Nov 2025 12:37:12 +0000 Subject: [PATCH 01/11] Daydream whip connection --- frontend/src/components/SettingsPanel.tsx | 21 ++++ frontend/src/hooks/useStreamState.ts | 1 + frontend/src/hooks/useWebRTC.ts | 18 ++- frontend/src/lib/daydream.ts | 76 +++++++++++++ frontend/src/lib/whip.ts | 104 +++++++++++++++++ frontend/src/pages/StreamPage.tsx | 130 +++++++++++++++++++++- frontend/src/types/index.ts | 1 + 7 files changed, 344 insertions(+), 7 deletions(-) create mode 100644 frontend/src/lib/daydream.ts create mode 100644 frontend/src/lib/whip.ts diff --git a/frontend/src/components/SettingsPanel.tsx b/frontend/src/components/SettingsPanel.tsx index 3e36974f9..73b982c0a 100644 --- a/frontend/src/components/SettingsPanel.tsx +++ b/frontend/src/components/SettingsPanel.tsx @@ -35,6 +35,8 @@ interface SettingsPanelProps { onPipelineIdChange?: (pipelineId: PipelineId) => void; isStreaming?: boolean; isDownloading?: boolean; + cloudMode?: boolean; + onCloudModeChange?: (enabled: boolean) => void; resolution?: { height: number; width: number; @@ -63,6 +65,8 @@ export function SettingsPanel({ onPipelineIdChange, isStreaming = false, isDownloading = false, + cloudMode = false, + onCloudModeChange, resolution, onResolutionChange, seed = 42, @@ -197,6 +201,23 @@ export function SettingsPanel({ Settings +
+

Cloud mode

+
+ Use Daydream (WebRTC) + {})} + variant="outline" + size="sm" + className="h-7" + disabled={isStreaming || isDownloading} + > + {cloudMode ? "ON" : "OFF"} + +
+
+

Pipeline ID

- onInterpolationMethodChange?.(value as "linear" | "slerp") + {!cloudMode && ( + <> + {/* Spatial Blend - only for multiple prompts */} + {managedPrompts.length >= 2 && ( +
+ + Spatial Blend: + + +
+ )} + + onTransitionStepsChange?.(steps)} + temporalInterpolationMethod={temporalInterpolationMethod} + onTemporalInterpolationMethodChange={method => + onTemporalInterpolationMethodChange?.(method) } - disabled={disabled} - > - - - - - Linear - 2}> - Slerp - - - -
+ disabled={disabled || !isStreaming || timelinePrompts.length === 0} + className="space-y-2" + /> + )} - onTransitionStepsChange?.(steps)} - temporalInterpolationMethod={temporalInterpolationMethod} - onTemporalInterpolationMethodChange={method => - onTemporalInterpolationMethodChange?.(method) - } - disabled={disabled || !isStreaming || timelinePrompts.length === 0} - className="space-y-2" - /> - {/* Add/Submit buttons - Bottom row */}
{managedPrompts.length < 4 && ( diff --git a/frontend/src/components/SettingsPanel.tsx b/frontend/src/components/SettingsPanel.tsx index 1db5001e4..0df1c5eb3 100644 --- a/frontend/src/components/SettingsPanel.tsx +++ b/frontend/src/components/SettingsPanel.tsx @@ -161,6 +161,13 @@ export function SettingsPanel({ } else { setWidthError(`Must be at most ${maxValue}`); } + } else if (cloudMode && value % 64 !== 0) { + // In cloud mode, dimension must be a multiple of 64 + if (dimension === "height") { + setHeightError(`Must be a multiple of 64 in cloud mode`); + } else { + setWidthError(`Must be a multiple of 64 in cloud mode`); + } } else { // Clear error if valid if (dimension === "height") { @@ -179,7 +186,18 @@ export function SettingsPanel({ const incrementResolution = (dimension: "height" | "width") => { const maxValue = 2048; - const newValue = Math.min(maxValue, effectiveResolution[dimension] + 1); + const currentValue = effectiveResolution[dimension]; + + let newValue: number; + if (cloudMode && currentValue % 64 !== 0) { + // Snap to next multiple of 64 + newValue = Math.ceil(currentValue / 64) * 64; + } else { + const step = cloudMode ? 64 : 1; + newValue = currentValue + step; + } + + newValue = Math.min(maxValue, newValue); handleResolutionChange(dimension, newValue); }; @@ -190,7 +208,18 @@ export function SettingsPanel({ pipelineId === "krea-realtime-video" ? MIN_DIMENSION : 1; - const newValue = Math.max(minValue, effectiveResolution[dimension] - 1); + const currentValue = effectiveResolution[dimension]; + + let newValue: number; + if (cloudMode && currentValue % 64 !== 0) { + // Snap to previous multiple of 64 + newValue = Math.floor(currentValue / 64) * 64; + } else { + const step = cloudMode ? 64 : 1; + newValue = currentValue - step; + } + + newValue = Math.max(minValue, newValue); handleResolutionChange(dimension, newValue); }; @@ -453,6 +482,7 @@ export function SettingsPanel({ className="text-center border-0 focus-visible:ring-0 focus-visible:ring-offset-0 h-8 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" min={MIN_DIMENSION} max={2048} + step={cloudMode ? 64 : 1} />
@@ -888,6 +902,7 @@ export function StreamPage() { onVideoPlayingCallbackRef.current = null; // Clear after execution } }} + whepClientRef={whepClientRef} /> {/* Timeline area - compact, always visible */} @@ -978,7 +993,7 @@ export function StreamPage() { timelineRef={timelineRef} onLiveStateChange={setIsLive} onLivePromptSubmit={handleLivePromptSubmit} - onDisconnect={stopStream} + onDisconnect={handleDisconnect} onStartStream={handleStartStream} onVideoPlayPauseToggle={handlePlayPauseToggle} onPromptEdit={handleTimelinePromptEdit} @@ -1004,7 +1019,7 @@ export function StreamPage() { className="h-full" pipelineId={settings.pipelineId} onPipelineIdChange={handlePipelineIdChange} - isStreaming={isStreaming} + isStreaming={isStreaming || isStreamingCloud} isDownloading={isDownloading} cloudMode={settings.cloudMode ?? false} onCloudModeChange={enabled => { @@ -1014,10 +1029,7 @@ export function StreamPage() { stopStream(); } if (isStreamingCloud) { - setActiveStream(null); - setIsStreamingCloud(false); - setIsConnectingCloud(false); - setPlaybackUrl(null); + disconnectCloudStream(); } })(); updateSettings({ cloudMode: enabled });