-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[TMP-Argus]stream switch via dropdown or monitors
- Loading branch information
1 parent
830012c
commit 3ed1917
Showing
3 changed files
with
246 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
import React, { useState, useEffect } from 'react'; | ||
import { useSelector } from 'react-redux'; | ||
import { JSMpeg } from '../SampleView/jsmpeg.min.js'; | ||
|
||
import styles from './StreamSwitch.module.css'; | ||
|
||
export function StreamSwitch(props) { | ||
const { handleSourceSwitch, numberOfShownMonitors = 3 } = props; | ||
const cameras = useSelector( | ||
(state) => state.beamline.hardwareObjects.argus?.attributes?.camera_streams, | ||
); | ||
const [players, setPlayers] = useState({}); | ||
const [streammode, setStreammode] = useState('monitors'); | ||
const [currentMonitor, setCurrentMonitor] = useState(0); | ||
|
||
useEffect(() => { | ||
if (cameras) { | ||
setPlayers((prevPlayers) => { | ||
if (streammode === 'monitors') { | ||
let new_players = {}; | ||
// remove all JSMpeg players from streams that have been removed | ||
if (prevPlayers) { | ||
new_players = Object.fromEntries( | ||
Object.entries(prevPlayers).filter(([key]) => { | ||
if (!Object.keys(cameras).includes(key)) { | ||
prevPlayers[key].destroy(); | ||
return false; | ||
} | ||
return true; | ||
}), | ||
); | ||
} | ||
|
||
// add players for new streams | ||
Object.keys(cameras).forEach((key) => { | ||
if (prevPlayers && !prevPlayers[key]) { | ||
const curr_canv = document.querySelector(`#${key}-canv`); | ||
const url = `ws://localhost:7000/ws/${key}`; | ||
new_players[key] = new JSMpeg.Player(url, { | ||
canvas: curr_canv, | ||
decodeFirstFrame: true, | ||
preserveDrawingBuffer: false, | ||
protocols: [], | ||
}); | ||
new_players[key].stop(); | ||
curr_canv.src = url; | ||
} | ||
}); | ||
|
||
return new_players; | ||
} | ||
return {}; | ||
}); | ||
|
||
setCurrentMonitor((prevMonitor) => { | ||
return Math.max(prevMonitor, Object.entries(cameras).length - 3); | ||
}); | ||
} | ||
}, [cameras, streammode]); | ||
|
||
useEffect(() => { | ||
if (!cameras) { | ||
return; | ||
} | ||
|
||
const keys = Object.keys(cameras); | ||
|
||
// hide all canvases | ||
keys.forEach((key) => { | ||
const curr_button = document.querySelector(`#${key}-button`); | ||
if (curr_button) { | ||
curr_button.style.display = 'none'; | ||
} | ||
}); | ||
|
||
// show necessary canvases | ||
for ( | ||
let pos = currentMonitor; | ||
pos <= Math.min(keys.length - 1, currentMonitor + 2); | ||
pos++ | ||
) { | ||
const curr_button = document.querySelector(`#${keys[pos]}-button`); | ||
if (curr_button) { | ||
curr_button.style.display = 'inline'; | ||
} | ||
} | ||
}, [currentMonitor, cameras, streammode]); | ||
|
||
let result = null; | ||
// in dropdown mode, we only create a dropdown where each option is one stream, | ||
// upon changing the value, we call the handleSourceSwitch function from props | ||
if (streammode === 'dropdown') { | ||
result = ( | ||
<select | ||
title="Camera Streams" | ||
id="streams-dropdown" | ||
onChange={(event) => { | ||
handleSourceSwitch(event.target.value); | ||
}} | ||
> | ||
<option value="">-- Streams --</option> | ||
{Object.entries(cameras).map((stream) => { | ||
const key = stream.slice(0, 1); | ||
const url = `ws://localhost:7000/ws/${stream.slice(0, 1)}`; | ||
return ( | ||
<option key={key} value={url}> | ||
{' '} | ||
{key}{' '} | ||
</option> | ||
); | ||
})} | ||
</select> | ||
); | ||
} // in monitors mode, one small monitor is created per stream. | ||
// clicking changes the source, while hovering plays/stops the monitor stream | ||
else if (streammode === 'monitors') { | ||
result = ( | ||
<div style={{ verticalAlign: 'center' }}> | ||
{cameras && Object.entries(cameras).length > 3 ? ( | ||
<button | ||
type="button" | ||
style={{ border: 'none', background: 'none', width: '4%' }} | ||
onClick={() => { | ||
setCurrentMonitor((prevCurrent) => Math.max(0, prevCurrent - 1)); | ||
}} | ||
> | ||
<div | ||
className={styles.triangle} | ||
style={{ transform: 'rotate(-90deg)' }} | ||
/> | ||
</button> | ||
) : null} | ||
{cameras | ||
? Object.entries(cameras).map((stream) => { | ||
const key = stream.slice(0, -1)[0]; | ||
const url = `ws://localhost:7000/ws/${stream.slice(0, -1)}`; | ||
return ( | ||
<button | ||
key={`${key}-button`} | ||
id={`${key}-button`} | ||
onClick={() => handleSourceSwitch(url)} | ||
onMouseEnter={() => { | ||
if (players && players[key]) { | ||
players[key].play(); | ||
} | ||
}} | ||
onMouseLeave={() => { | ||
if (players && players[key]) { | ||
players[key].stop(); | ||
} | ||
}} | ||
type="button" | ||
style={{ | ||
width: '30%', | ||
}} | ||
> | ||
<canvas | ||
id={`${key}-canv`} | ||
aria-label={`Canvas for ${key}`} | ||
style={{ | ||
width: '100%', | ||
height: '180px', | ||
overflow: 'hidden', | ||
}} | ||
/> | ||
{key} | ||
</button> | ||
); | ||
}) | ||
: null} | ||
{cameras && Object.entries(cameras).length > 3 ? ( | ||
<button | ||
type="button" | ||
style={{ border: 'none', background: 'none', width: '4%' }} | ||
onClick={() => | ||
setCurrentMonitor((prevCurrent) => | ||
Math.max( | ||
0, | ||
Math.min( | ||
prevCurrent + 1, | ||
Object.entries(cameras).length - numberOfShownMonitors, | ||
), | ||
), | ||
) | ||
} | ||
> | ||
<div | ||
className={styles.triangle} | ||
style={{ transform: 'rotate(90deg)' }} | ||
/> | ||
</button> | ||
) : null} | ||
</div> | ||
); | ||
} | ||
|
||
return cameras ? ( | ||
<div style={{ textAlign: 'center' }}> | ||
{result} | ||
<button | ||
type="button" | ||
onClick={() => { | ||
setStreammode(streammode === 'monitors' ? 'dropdown' : 'monitors'); | ||
}} | ||
style={{ display: 'block', margin: '10px auto' }} | ||
> | ||
Switch Stream Mode | ||
</button> | ||
</div> | ||
) : null; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
.triangle { | ||
display: inline-block; | ||
width: 10px; | ||
height: 10px; | ||
border-left: 10px solid transparent; | ||
border-right: 10px solid transparent; | ||
border-bottom: 20px solid black; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters