mirror of
				https://github.com/blakeblackshear/frigate.git
				synced 2025-10-27 10:52:11 +01:00 
			
		
		
		
	Live player fixes and improvements (#11855)
* Only set stalled error when player is visible * Show activity indicator before live player starts playing * remove comment * keep gradients when still image is showing * fix chips * red dot and outline
This commit is contained in:
		
							parent
							
								
									30b86271ea
								
							
						
					
					
						commit
						18d561da0e
					
				| @ -10,6 +10,7 @@ type JSMpegPlayerProps = { | ||||
|   width: number; | ||||
|   height: number; | ||||
|   containerRef?: React.MutableRefObject<HTMLDivElement | null>; | ||||
|   onPlaying?: () => void; | ||||
| }; | ||||
| 
 | ||||
| export default function JSMpegPlayer({ | ||||
| @ -18,6 +19,7 @@ export default function JSMpegPlayer({ | ||||
|   height, | ||||
|   className, | ||||
|   containerRef, | ||||
|   onPlaying, | ||||
| }: JSMpegPlayerProps) { | ||||
|   const url = `${baseUrl.replace(/^http/, "ws")}live/jsmpeg/${camera}`; | ||||
|   const playerRef = useRef<HTMLDivElement | null>(null); | ||||
| @ -88,7 +90,14 @@ export default function JSMpegPlayer({ | ||||
|       playerRef.current, | ||||
|       url, | ||||
|       { canvas: `#${CSS.escape(uniqueId)}` }, | ||||
|       { protocols: [], audio: false, videoBufferSize: 1024 * 1024 * 4 }, | ||||
|       { | ||||
|         protocols: [], | ||||
|         audio: false, | ||||
|         videoBufferSize: 1024 * 1024 * 4, | ||||
|         onPlay: () => { | ||||
|           onPlaying?.(); | ||||
|         }, | ||||
|       }, | ||||
|     ); | ||||
| 
 | ||||
|     return () => { | ||||
| @ -100,7 +109,7 @@ export default function JSMpegPlayer({ | ||||
|         playerRef.current = null; | ||||
|       } | ||||
|     }; | ||||
|   }, [url, uniqueId]); | ||||
|   }, [url, uniqueId, onPlaying]); | ||||
| 
 | ||||
|   return ( | ||||
|     <div className={className}> | ||||
|  | ||||
| @ -172,6 +172,7 @@ export default function LivePlayer({ | ||||
|           width={cameraConfig.detect.width} | ||||
|           height={cameraConfig.detect.height} | ||||
|           containerRef={containerRef} | ||||
|           onPlaying={() => setLiveReady(true)} | ||||
|         /> | ||||
|       ); | ||||
|     } else { | ||||
| @ -187,7 +188,8 @@ export default function LivePlayer({ | ||||
|       data-camera={cameraConfig.name} | ||||
|       className={cn( | ||||
|         "relative flex w-full cursor-pointer justify-center outline", | ||||
|         activeTracking | ||||
|         activeTracking && | ||||
|           ((showStillWithoutActivity && !liveReady) || liveReady) | ||||
|           ? "outline-3 rounded-lg shadow-severity_alert outline-severity_alert md:rounded-2xl" | ||||
|           : "outline-0 outline-background", | ||||
|         "transition-all duration-500", | ||||
| @ -195,52 +197,60 @@ export default function LivePlayer({ | ||||
|       )} | ||||
|       onClick={onClick} | ||||
|     > | ||||
|       <div className="pointer-events-none absolute inset-x-0 top-0 z-10 h-[30%] w-full rounded-lg bg-gradient-to-b from-black/20 to-transparent md:rounded-2xl"></div> | ||||
|       <div className="pointer-events-none absolute inset-x-0 bottom-0 z-10 h-[10%] w-full rounded-lg bg-gradient-to-t from-black/20 to-transparent md:rounded-2xl"></div> | ||||
|       {player} | ||||
| 
 | ||||
|       {objects.length > 0 && ( | ||||
|         <div className="absolute left-0 top-2 z-40"> | ||||
|           <Tooltip> | ||||
|             <div className="flex"> | ||||
|               <TooltipTrigger asChild> | ||||
|                 <div className="mx-3 pb-1 text-sm text-white"> | ||||
|                   <Chip | ||||
|                     className={`z-0 flex items-start justify-between space-x-1 bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500`} | ||||
|                   > | ||||
|                     {[ | ||||
|                       ...new Set([ | ||||
|                         ...(objects || []).map(({ label }) => label), | ||||
|                       ]), | ||||
|                     ] | ||||
|                       .map((label) => { | ||||
|                         return getIconForLabel(label, "size-3 text-white"); | ||||
|                       }) | ||||
|                       .sort()} | ||||
|                   </Chip> | ||||
|                 </div> | ||||
|               </TooltipTrigger> | ||||
|             </div> | ||||
|             <TooltipContent className="capitalize"> | ||||
|               {[ | ||||
|                 ...new Set([ | ||||
|                   ...(objects || []).map(({ label, sub_label }) => | ||||
|                     label.endsWith("verified") ? sub_label : label, | ||||
|                   ), | ||||
|                 ]), | ||||
|               ] | ||||
|                 .filter( | ||||
|                   (label) => | ||||
|                     label !== undefined && !label.includes("-verified"), | ||||
|                 ) | ||||
|                 .map((label) => capitalizeFirstLetter(label)) | ||||
|                 .sort() | ||||
|                 .join(", ") | ||||
|                 .replaceAll("-verified", "")} | ||||
|             </TooltipContent> | ||||
|           </Tooltip> | ||||
|         </div> | ||||
|       {((showStillWithoutActivity && !liveReady) || liveReady) && ( | ||||
|         <> | ||||
|           <div className="pointer-events-none absolute inset-x-0 top-0 z-10 h-[30%] w-full rounded-lg bg-gradient-to-b from-black/20 to-transparent md:rounded-2xl"></div> | ||||
|           <div className="pointer-events-none absolute inset-x-0 bottom-0 z-10 h-[10%] w-full rounded-lg bg-gradient-to-t from-black/20 to-transparent md:rounded-2xl"></div> | ||||
|         </> | ||||
|       )} | ||||
|       {player} | ||||
|       {!offline && !showStillWithoutActivity && !liveReady && ( | ||||
|         <ActivityIndicator /> | ||||
|       )} | ||||
| 
 | ||||
|       {((showStillWithoutActivity && !liveReady) || liveReady) && | ||||
|         objects.length > 0 && ( | ||||
|           <div className="absolute left-0 top-2 z-40"> | ||||
|             <Tooltip> | ||||
|               <div className="flex"> | ||||
|                 <TooltipTrigger asChild> | ||||
|                   <div className="mx-3 pb-1 text-sm text-white"> | ||||
|                     <Chip | ||||
|                       className={`z-0 flex items-start justify-between space-x-1 bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500`} | ||||
|                     > | ||||
|                       {[ | ||||
|                         ...new Set([ | ||||
|                           ...(objects || []).map(({ label }) => label), | ||||
|                         ]), | ||||
|                       ] | ||||
|                         .map((label) => { | ||||
|                           return getIconForLabel(label, "size-3 text-white"); | ||||
|                         }) | ||||
|                         .sort()} | ||||
|                     </Chip> | ||||
|                   </div> | ||||
|                 </TooltipTrigger> | ||||
|               </div> | ||||
|               <TooltipContent className="capitalize"> | ||||
|                 {[ | ||||
|                   ...new Set([ | ||||
|                     ...(objects || []).map(({ label, sub_label }) => | ||||
|                       label.endsWith("verified") ? sub_label : label, | ||||
|                     ), | ||||
|                   ]), | ||||
|                 ] | ||||
|                   .filter( | ||||
|                     (label) => | ||||
|                       label !== undefined && !label.includes("-verified"), | ||||
|                   ) | ||||
|                   .map((label) => capitalizeFirstLetter(label)) | ||||
|                   .sort() | ||||
|                   .join(", ") | ||||
|                   .replaceAll("-verified", "")} | ||||
|               </TooltipContent> | ||||
|             </Tooltip> | ||||
|           </div> | ||||
|         )} | ||||
| 
 | ||||
|       <div | ||||
|         className={`absolute inset-0 w-full ${ | ||||
| @ -257,9 +267,12 @@ export default function LivePlayer({ | ||||
|       </div> | ||||
| 
 | ||||
|       <div className="absolute right-2 top-2"> | ||||
|         {autoLive && !offline && activeMotion && ( | ||||
|           <MdCircle className="mr-2 size-2 animate-pulse text-danger shadow-danger drop-shadow-md" /> | ||||
|         )} | ||||
|         {autoLive && | ||||
|           !offline && | ||||
|           activeMotion && | ||||
|           ((showStillWithoutActivity && !liveReady) || liveReady) && ( | ||||
|             <MdCircle className="mr-2 size-2 animate-pulse text-danger shadow-danger drop-shadow-md" /> | ||||
|           )} | ||||
|         {offline && ( | ||||
|           <Chip | ||||
|             className={`z-0 flex items-start justify-between space-x-1 bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500 text-xs capitalize`} | ||||
|  | ||||
| @ -328,7 +328,9 @@ function MSEPlayer({ | ||||
| 
 | ||||
|           setBufferTimeout( | ||||
|             setTimeout(() => { | ||||
|               onError("stalled"); | ||||
|               if (document.visibilityState === "visible") { | ||||
|                 onError("stalled"); | ||||
|               } | ||||
|             }, 3000), | ||||
|           ); | ||||
|         } | ||||
|  | ||||
| @ -238,7 +238,9 @@ export default function WebRtcPlayer({ | ||||
| 
 | ||||
|               setBufferTimeout( | ||||
|                 setTimeout(() => { | ||||
|                   onError("stalled"); | ||||
|                   if (document.visibilityState === "visible") { | ||||
|                     onError("stalled"); | ||||
|                   } | ||||
|                 }, 3000), | ||||
|               ); | ||||
|             } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user