mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-10 13:15:25 +03:00
Support opening pip from live view
This commit is contained in:
parent
4b6a3b22ed
commit
eeef39c1c0
@ -22,6 +22,7 @@ type LivePlayerProps = {
|
||||
playAudio?: boolean;
|
||||
micEnabled?: boolean; // only webrtc supports mic
|
||||
iOSCompatFullScreen?: boolean;
|
||||
pip?: boolean;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
@ -35,6 +36,7 @@ export default function LivePlayer({
|
||||
playAudio = false,
|
||||
micEnabled = false,
|
||||
iOSCompatFullScreen = false,
|
||||
pip,
|
||||
onClick,
|
||||
}: LivePlayerProps) {
|
||||
// camera activity
|
||||
@ -105,6 +107,7 @@ export default function LivePlayer({
|
||||
microphoneEnabled={micEnabled}
|
||||
iOSCompatFullScreen={iOSCompatFullScreen}
|
||||
onPlaying={() => setLiveReady(true)}
|
||||
pip={pip}
|
||||
/>
|
||||
);
|
||||
} else if (liveMode == "mse") {
|
||||
@ -116,6 +119,7 @@ export default function LivePlayer({
|
||||
playbackEnabled={cameraActive}
|
||||
audioEnabled={playAudio}
|
||||
onPlaying={() => setLiveReady(true)}
|
||||
pip={pip}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
|
||||
@ -6,6 +6,7 @@ type MSEPlayerProps = {
|
||||
className?: string;
|
||||
playbackEnabled?: boolean;
|
||||
audioEnabled?: boolean;
|
||||
pip?: boolean;
|
||||
onPlaying?: () => void;
|
||||
};
|
||||
|
||||
@ -14,6 +15,7 @@ function MSEPlayer({
|
||||
className,
|
||||
playbackEnabled = true,
|
||||
audioEnabled = false,
|
||||
pip = false,
|
||||
onPlaying,
|
||||
}: MSEPlayerProps) {
|
||||
let connectTS: number = 0;
|
||||
@ -268,6 +270,16 @@ function MSEPlayer({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [playbackEnabled, onDisconnect, onConnect]);
|
||||
|
||||
// control pip
|
||||
|
||||
useEffect(() => {
|
||||
if (!videoRef.current || !pip) {
|
||||
return;
|
||||
}
|
||||
|
||||
videoRef.current.requestPictureInPicture();
|
||||
}, [pip, videoRef]);
|
||||
|
||||
return (
|
||||
<video
|
||||
ref={videoRef}
|
||||
|
||||
@ -8,6 +8,7 @@ type WebRtcPlayerProps = {
|
||||
audioEnabled?: boolean;
|
||||
microphoneEnabled?: boolean;
|
||||
iOSCompatFullScreen?: boolean; // ios doesn't support fullscreen divs so we must support the video element
|
||||
pip?: boolean;
|
||||
onPlaying?: () => void;
|
||||
};
|
||||
|
||||
@ -18,6 +19,7 @@ export default function WebRtcPlayer({
|
||||
audioEnabled = false,
|
||||
microphoneEnabled = false,
|
||||
iOSCompatFullScreen = false,
|
||||
pip = false,
|
||||
onPlaying,
|
||||
}: WebRtcPlayerProps) {
|
||||
// metadata
|
||||
@ -173,8 +175,19 @@ export default function WebRtcPlayer({
|
||||
]);
|
||||
|
||||
// ios compat
|
||||
|
||||
const [iOSCompatControls, setiOSCompatControls] = useState(false);
|
||||
|
||||
// control pip
|
||||
|
||||
useEffect(() => {
|
||||
if (!videoRef.current || !pip) {
|
||||
return;
|
||||
}
|
||||
|
||||
videoRef.current.requestPictureInPicture();
|
||||
}, [pip, videoRef]);
|
||||
|
||||
return (
|
||||
<video
|
||||
ref={videoRef}
|
||||
|
||||
@ -51,7 +51,14 @@ import {
|
||||
import { GiSpeaker, GiSpeakerOff } from "react-icons/gi";
|
||||
import { HiViewfinderCircle } from "react-icons/hi2";
|
||||
import { IoMdArrowBack } from "react-icons/io";
|
||||
import { LuEar, LuEarOff, LuVideo, LuVideoOff } from "react-icons/lu";
|
||||
import {
|
||||
LuEar,
|
||||
LuEarOff,
|
||||
LuPictureInPicture,
|
||||
LuPictureInPicture2,
|
||||
LuVideo,
|
||||
LuVideoOff,
|
||||
} from "react-icons/lu";
|
||||
import {
|
||||
MdNoPhotography,
|
||||
MdPersonOff,
|
||||
@ -113,20 +120,25 @@ export default function LiveCameraView({ camera }: LiveCameraViewProps) {
|
||||
[clickOverlayRef, clickOverlay, sendPtz],
|
||||
);
|
||||
|
||||
// fullscreen state
|
||||
// fullscreen / pip state
|
||||
|
||||
useEffect(() => {
|
||||
if (mainRef.current == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const listener = () => {
|
||||
const fsListener = () => {
|
||||
setFullscreen(document.fullscreenElement != null);
|
||||
};
|
||||
document.addEventListener("fullscreenchange", listener);
|
||||
const pipListener = () => {
|
||||
setPip(document.pictureInPictureElement != null);
|
||||
};
|
||||
document.addEventListener("fullscreenchange", fsListener);
|
||||
document.addEventListener("focusin", pipListener);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("fullscreenchange", listener);
|
||||
document.removeEventListener("fullscreenchange", fsListener);
|
||||
document.removeEventListener("focusin", pipListener);
|
||||
};
|
||||
}, [mainRef]);
|
||||
|
||||
@ -135,6 +147,7 @@ export default function LiveCameraView({ camera }: LiveCameraViewProps) {
|
||||
const [audio, setAudio] = useState(false);
|
||||
const [mic, setMic] = useState(false);
|
||||
const [fullscreen, setFullscreen] = useState(false);
|
||||
const [pip, setPip] = useState(false);
|
||||
|
||||
const growClassName = useMemo(() => {
|
||||
const aspect = camera.detect.width / camera.detect.height;
|
||||
@ -237,6 +250,23 @@ export default function LiveCameraView({ camera }: LiveCameraViewProps) {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{!isIOS && (
|
||||
<CameraFeatureToggle
|
||||
className="p-2 md:p-0"
|
||||
variant={fullscreen ? "overlay" : "primary"}
|
||||
Icon={LuPictureInPicture}
|
||||
isActive={pip}
|
||||
title={pip ? "Close" : "Picture in Picture"}
|
||||
onClick={() => {
|
||||
if (!pip) {
|
||||
setPip(true);
|
||||
} else {
|
||||
document.exitPictureInPicture();
|
||||
setPip(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{window.isSecureContext && (
|
||||
<CameraFeatureToggle
|
||||
className="p-2 md:p-0"
|
||||
@ -293,6 +323,7 @@ export default function LiveCameraView({ camera }: LiveCameraViewProps) {
|
||||
micEnabled={mic}
|
||||
iOSCompatFullScreen={isIOS}
|
||||
preferredLiveMode={preferredLiveMode}
|
||||
pip={pip}
|
||||
/>
|
||||
</div>
|
||||
{camera.onvif.host != "" && (
|
||||
|
||||
Loading…
Reference in New Issue
Block a user