Trudy's picture
Initial commit: Gemini Realtime Console
7f2a14a
raw
history blame
3.8 kB
/**
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useState, useEffect } from "react";
import { UseMediaStreamResult } from "./use-media-stream-mux";
export function useWebcam(): UseMediaStreamResult {
const [stream, setStream] = useState<MediaStream | null>(null);
const [isStreaming, setIsStreaming] = useState(false);
const [availableCameras, setAvailableCameras] = useState<MediaDeviceInfo[]>([]);
const [currentCameraIndex, setCurrentCameraIndex] = useState(-1);
// Get list of available cameras on mount
useEffect(() => {
async function getCameras() {
try {
// First request permission to ensure we can enumerate video devices
await navigator.mediaDevices.getUserMedia({ video: true })
.then(stream => {
// Stop the stream immediately, we just needed permission
stream.getTracks().forEach(track => track.stop());
});
const devices = await navigator.mediaDevices.enumerateDevices();
const videoDevices = devices.filter(device => device.kind === 'videoinput');
setAvailableCameras(videoDevices);
console.log('Available cameras:', videoDevices);
} catch (err) {
console.error('Error getting cameras:', err);
}
}
getCameras();
}, []);
useEffect(() => {
const handleStreamEnded = () => {
setIsStreaming(false);
setStream(null);
};
if (stream) {
stream
.getTracks()
.forEach((track) => track.addEventListener("ended", handleStreamEnded));
return () => {
stream
.getTracks()
.forEach((track) =>
track.removeEventListener("ended", handleStreamEnded),
);
};
}
}, [stream]);
const start = async () => {
// If we're already streaming, cycle to next camera
if (isStreaming) {
const nextIndex = (currentCameraIndex + 1) % (availableCameras.length);
setCurrentCameraIndex(nextIndex);
// Stop current stream
if (stream) {
stream.getTracks().forEach((track) => track.stop());
}
// If we've cycled through all cameras, stop streaming
if (nextIndex === 0) {
setStream(null);
setIsStreaming(false);
return null;
}
const deviceId = availableCameras[nextIndex].deviceId;
const mediaStream = await navigator.mediaDevices.getUserMedia({
video: { deviceId: { exact: deviceId } }
});
setStream(mediaStream);
setIsStreaming(true);
return mediaStream;
} else {
// Start with first camera
setCurrentCameraIndex(0);
const deviceId = availableCameras[0]?.deviceId;
const mediaStream = await navigator.mediaDevices.getUserMedia({
video: deviceId ? { deviceId: { exact: deviceId } } : true
});
setStream(mediaStream);
setIsStreaming(true);
return mediaStream;
}
};
const stop = () => {
if (stream) {
stream.getTracks().forEach((track) => track.stop());
setStream(null);
setIsStreaming(false);
setCurrentCameraIndex(-1);
}
};
const result: UseMediaStreamResult = {
type: "webcam",
start,
stop,
isStreaming,
stream,
};
return result;
}