Skip to content

Commit de7e6cc

Browse files
authored
Add Listing of active streams in UI
If streams API isn’t available don’t render at all.
1 parent 1fb177f commit de7e6cc

3 files changed

Lines changed: 118 additions & 44 deletions

File tree

main.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ func whepLayerHandler(res http.ResponseWriter, req *http.Request) {
160160
}
161161

162162
func statusHandler(res http.ResponseWriter, req *http.Request) {
163+
if os.Getenv("DISABLE_STATUS") != "" {
164+
logHTTPError(res, "Status Service Unavailable", http.StatusServiceUnavailable)
165+
}
166+
163167
res.Header().Add("Content-Type", "application/json")
164168

165169
if err := json.NewEncoder(res).Encode(webrtc.GetStreamStatuses()); err != nil {
@@ -274,10 +278,7 @@ func main() {
274278
mux.HandleFunc("/api/whep", corsHandler(whepHandler))
275279
mux.HandleFunc("/api/sse/", corsHandler(whepServerSentEventsHandler))
276280
mux.HandleFunc("/api/layer/", corsHandler(whepLayerHandler))
277-
278-
if os.Getenv("DISABLE_STATUS") == "" {
279-
mux.HandleFunc("/api/status", corsHandler(statusHandler))
280-
}
281+
mux.HandleFunc("/api/status", corsHandler(statusHandler))
281282

282283
server := &http.Server{
283284
Handler: mux,
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import React, {useEffect, useState} from "react";
2+
import {useNavigate} from "react-router-dom";
3+
4+
function AvailableStreams() {
5+
const apiPath = import.meta.env.VITE_API_PATH;
6+
const navigate = useNavigate();
7+
8+
const [streams, setStreams] = useState(undefined);
9+
useEffect(() => {
10+
updateStreams();
11+
12+
const interval = setInterval(() => {
13+
updateStreams()
14+
}, 5000);
15+
16+
return () => clearInterval(interval);
17+
}, []);
18+
19+
const isActiveSession = (videoStreams) => {
20+
if (videoStreams === undefined || videoStreams.length === 0) {
21+
return false;
22+
}
23+
24+
return videoStreams.filter(stream => (new Date() - new Date(stream.lastKeyFrameSeen)) > 5_000).length === 0;
25+
}
26+
const updateStreams = () => {
27+
fetch(`${apiPath}/status`, {
28+
method: 'GET',
29+
headers: {
30+
'Content-Type': 'application/json'
31+
}
32+
})
33+
.then(result => {
34+
if (result.ok === false) {
35+
throw new Error('Unknown error when calling status');
36+
}
37+
38+
if (result.status === 503) {
39+
throw new Error('Status API disabled');
40+
}
41+
42+
return result.json()
43+
})
44+
.then(result => {
45+
if (result) {
46+
setStreams(result.map((e) => ({
47+
key: e.streamKey,
48+
isActive: isActiveSession(e.videoStreams)
49+
})
50+
));
51+
}
52+
})
53+
.catch(() => {
54+
setStreams(undefined);
55+
});
56+
}
57+
const onWatchStreamClick = (key) => {
58+
if (key !== '') {
59+
navigate(`/${key}`);
60+
}
61+
}
62+
63+
if (streams === undefined) {
64+
return <></>;
65+
}
66+
67+
return (
68+
<div className="flex flex-col">
69+
<h2 className="font-light leading-tight text-4xl mb-2 mt-6">Current Streams</h2>
70+
{streams.length === 0 && <p className='flex justify-center mt-6'>No streams currently available</p>}
71+
{streams.length !== 0 && <p>Click a stream to join it</p>}
72+
73+
<div className="m-2"/>
74+
75+
<div className='flex flex-col'>
76+
{streams.map((e, i) => (
77+
<button
78+
key={i + '_' + e.key}
79+
className={`mt-2 py-2 px-4 ${e.isActive ? 'bg-blue-500' : 'bg-orange-500'} text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-hidden focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75`}
80+
onClick={() => onWatchStreamClick(e.key)}>
81+
{e.key}
82+
</button>
83+
))
84+
}
85+
</div>
86+
</div>
87+
)
88+
}
89+
90+
export default AvailableStreams
Lines changed: 23 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import React from 'react'
2-
import { useNavigate } from 'react-router-dom'
1+
import React, {useState} from 'react'
2+
import {useNavigate} from 'react-router-dom'
3+
import AvailableStreams from "./availableStreams.jsx";
34

4-
function Selection(props) {
5-
const [streamKey, setStreamKey] = React.useState('')
5+
function Selection() {
6+
const [streamKey, setStreamKey] = useState('')
67
const navigate = useNavigate()
78

89
const onStreamKeyChange = e => {
@@ -13,7 +14,6 @@ function Selection(props) {
1314
navigate(`/${streamKey}`)
1415
}
1516
}
16-
1717
const onPublishStreamClick = () => {
1818
if (streamKey !== '') {
1919
navigate(`/publish/${streamKey}`)
@@ -24,55 +24,38 @@ function Selection(props) {
2424
<div className='space-y-4 mx-auto max-w-2xl pt-20 md:pt-24'>
2525
<form className='rounded-md bg-gray-800 shadow-md p-8'>
2626
<h2 className="font-light leading-tight text-4xl mt-0 mb-2">Welcome to Broadcast Box</h2>
27-
<p>Broadcast Box is a tool that allows you to efficiently stream high-quality video in real time, using the latest in video codecs and WebRTC technology.</p>
27+
<p>Broadcast Box is a tool that allows you to efficiently stream high-quality video in real time, using the
28+
latest in video codecs and WebRTC technology.</p>
2829

2930
<div className='my-4'>
3031
<label className='block text-sm font-bold mb-2' htmlFor='streamKey'>
3132
Stream Key
3233
</label>
33-
<input className='appearance-none border w-full py-2 px-3 leading-tight focus:outline-hidden focus:shadow-outline bg-gray-700 border-gray-700 text-white rounded-sm shadow-md placeholder-gray-200' id='streamKey' type='text' placeholder='Stream Key' onChange={onStreamKeyChange} autoFocus />
34+
35+
<input
36+
className='appearance-none border w-full py-2 px-3 leading-tight focus:outline-hidden focus:shadow-outline bg-gray-700 border-gray-700 text-white rounded-sm shadow-md placeholder-gray-200'
37+
id='streamKey' type='text' placeholder='Stream Key' onChange={onStreamKeyChange} autoFocus/>
3438
</div>
35-
<div className='flex'>
36-
<button className='py-2 px-4 bg-blue-500 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-hidden focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75' type='submit' onClick={onWatchStreamClick}>
39+
40+
<div className='flex justify-center'>
41+
<button
42+
className='py-2 px-4 bg-blue-500 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-hidden focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75'
43+
type='submit' onClick={onWatchStreamClick}>
3744
Watch Stream
3845
</button>
3946

40-
<button className='ml-10 py-2 px-4 bg-blue-500 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-hidden focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75' type='button' onClick={onPublishStreamClick}>
47+
<button
48+
className='ml-10 py-2 px-4 bg-blue-500 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-hidden focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75'
49+
type='button' onClick={onPublishStreamClick}>
4150
Publish Stream
4251
</button>
52+
4353
</div>
54+
55+
<AvailableStreams/>
4456
</form>
45-
46-
{/*
47-
<div className="rounded-md bg-gray-800 shadow-md p-8">
48-
<h2 className="font-light leading-tight text-2xl mb-2">Q: What is Broadcast Box?</h2>
49-
50-
51-
<p>A: Broadcast Box is a tool that lets you broadcast video to others in sub-second time. It is designed to be simple to use and easily modifiable, and is built using cutting-edge technology.</p>
52-
53-
54-
<h2 className="font-light leading-tight text-2xl mt-4 mb-2">Q: How does Broadcast Box work?</h2>
55-
56-
<p>A: Broadcast Box uses WebRTC for broadcast and playback, which allows for fast and efficient video streaming. This is in contrast to RTMP and HLS, which can be slower and more complex to use.</p>
57-
58-
59-
<h2 className="font-light leading-tight text-2xl mt-4 mb-2">Q: Can I serve my video with Broadcast Box without a public IP or forwarding ports?</h2>
60-
61-
<p>A: Yes, with Broadcast Box you can share your video without needing a public IP or forwarding ports. This makes it easy to share your video with others, even if you are running Broadcast Box on the same machine as your video source.</p>
62-
63-
64-
<h2 className="font-light leading-tight text-2xl mt-4 mb-2">Q: What are the benefits of using WebRTC with Broadcast Box?</h2>
65-
66-
<p>A: There are several benefits to using WebRTC with Broadcast Box, including access to the latest in video codecs, the ability to upload multiple video streams in the same session, and the ability for your viewers to upload the same video at different quality levels. This can help to provide a high-quality viewing experience while keeping costs low for the server operator.</p>
67-
68-
69-
<h2 className="font-light leading-tight text-2xl mt-4 mb-2">Q: Can I use Broadcast Box to broadcast multiple camera angles or interactive video experiences?</h2>
70-
71-
<p>A: Yes, with WebRTC you can upload multiple video streams in the same session, which means you can broadcast multiple camera angles or interactive video experiences in real time. This can help to provide a more immersive and engaging experience for your viewers.</p>
72-
</div>
73-
*/}
7457
</div>
7558
)
7659
}
7760

78-
export default Selection
61+
export default Selection

0 commit comments

Comments
 (0)