Refactor camera handling and video stream management in Assistant and MobileCall pages

- Remove direct camera start/stop calls and replace them with streamlined video stream handling.
- Update DebugVideoPanel to accept a streamOverride for better flexibility in video source management.
- Adjust MobileCallPage to utilize the new videoStream state, enhancing the call experience.
- Introduce videoStream state management in useVoicePreview for improved media handling during voice interactions.
This commit is contained in:
Xin Wang
2026-07-13 07:05:06 +08:00
parent ede5e2334b
commit fd57f34a55
3 changed files with 43 additions and 45 deletions

View File

@@ -2155,11 +2155,8 @@ function DebugDrawer({
const [dynamicVariableValues, setDynamicVariableValues] = useState<
Record<string, string | number | boolean>
>({});
const stopCamera = camera.stop;
const startCamera = camera.start;
const recording =
preview.status === "connecting" || preview.status === "connected";
const shouldKeepCamera = vision && recording;
const dynamicVariableEntries = Object.entries(dynamicVariableDefinitions);
const resolvedDynamicVariables: Record<string, string | number | boolean> = {};
let dynamicVariablesError = "";
@@ -2174,18 +2171,10 @@ function DebugDrawer({
resolvedDynamicVariables[name] = value;
}
useEffect(() => {
if (shouldKeepCamera) {
void startCamera();
} else {
stopCamera();
}
}, [shouldKeepCamera, startCamera, stopCamera]);
const selectCamera = useCallback(
async (deviceId: string) => {
const nextStream = await camera.selectCamera(deviceId);
await preview.replaceVideoStream(nextStream);
await camera.selectCamera(deviceId);
preview.selectCamera(deviceId);
},
[camera, preview],
);
@@ -2666,15 +2655,12 @@ function DebugVoicePanel({
const startConversation = useCallback(async () => {
if (!assistantId || hasUnsavedChanges) return;
if (dynamicVariablesError) return;
const videoStream = vision ? await camera.start() : null;
await connect({
visionEnabled: vision,
videoStream,
dynamicVariables,
});
}, [
assistantId,
camera,
connect,
dynamicVariables,
dynamicVariablesError,
@@ -2695,7 +2681,7 @@ function DebugVoicePanel({
connect={startConversation}
/>
) : (
<DebugVideoPanel camera={camera} />
<DebugVideoPanel camera={camera} streamOverride={preview.videoStream} />
)
) : !SHOW_VOICE_VIZ || showTranscript ? (
showIdleHub ? (
@@ -2955,19 +2941,27 @@ function DebugIdleHub({
);
}
function DebugVideoPanel({ camera }: { camera: CameraPreview }) {
function DebugVideoPanel({
camera,
streamOverride,
}: {
camera: CameraPreview;
streamOverride?: MediaStream | null;
}) {
const { stream, error, starting, active } = camera;
const effectiveStream = streamOverride ?? stream;
const effectiveActive = Boolean(effectiveStream) || active;
const videoRef = useRef<HTMLVideoElement>(null);
useEffect(() => {
if (videoRef.current) videoRef.current.srcObject = stream;
}, [stream]);
if (videoRef.current) videoRef.current.srcObject = effectiveStream;
}, [effectiveStream]);
return (
<div
className={[
"relative flex min-h-0 flex-1 items-center justify-center overflow-hidden",
active ? "bg-black" : "bg-canvas-soft",
effectiveActive ? "bg-black" : "bg-canvas-soft",
].join(" ")}
>
<video
@@ -2977,10 +2971,10 @@ function DebugVideoPanel({ camera }: { camera: CameraPreview }) {
muted
className={[
"h-full w-full -scale-x-100 object-contain",
active ? "" : "hidden",
effectiveActive ? "" : "hidden",
].join(" ")}
/>
{active ? (
{effectiveActive ? (
<Badge
variant="secondary"
className="absolute left-3 top-3 gap-1.5 rounded-full border border-hairline bg-card/80 px-2.5 py-1 text-[11px] font-medium text-muted-foreground shadow-none backdrop-blur"