Skip to content

Commit

Permalink
feat: 실시간 영상 스트리밍 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
sukkyun2 committed Jul 26, 2024
1 parent 2fb898f commit d15f951
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 3 deletions.
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
VITE_HISTORY_API_URL=http://3.34.196.131:8080
VITE_AI_API_URL=http://13.124.103.220:8000
VITE_WS_PUBLISHER=ws://13.124.103.220:8000/ws/publisher
VITE_WS_SUBSCRIBER=ws://13.124.103.220:8000/ws/subscriber
Binary file added src/assets/loading.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions src/components/ConnectionStatus.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const ConnectionStatus = ({isConnected}) => {
return (
<div>
서버 연결 여부 : {isConnected ? '🟢' : '🔴'}
</div>
);
};

export default ConnectionStatus;
14 changes: 12 additions & 2 deletions src/main.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import ReactDOM from 'react-dom/client'
import './index.css'
import {createBrowserRouter, RouterProvider} from "react-router-dom";
import ObjectDetectResult from "./components/ObjectDetectResult.jsx";
import ImageUploader from "./components/ImageUploader.jsx";
import ObjectDetectResult from "./pages/ObjectDetectResult.jsx";
import ImageUploader from "./pages/ImageUploader.jsx";
import Publisher from "./pages/Publisher.jsx";
import Subscriber from "./pages/Subscriber.jsx";

const router = createBrowserRouter([
{
Expand All @@ -13,6 +15,14 @@ const router = createBrowserRouter([
path: "/upload",
element: <ImageUploader/>,
},
{
path: "publisher",
element: <Publisher/>
},
{
path: "subscriber",
element: <Subscriber/>
}
]);

ReactDOM.createRoot(document.getElementById('root')).render(
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ const ObjectDetectResult = () => {
const navigate = useNavigate();
const fetchDetectResults = async () => {
const results = await history.fetchDetectResults()
console.log(results)

setResults(results)
}
const formatDatetime = (datetime) => datetime.split('T').join(' ')
const handleClickViewCCTV = () => {
navigate('/subscriber')
}
const handleClickUpload = () => {
navigate('/upload')
}
Expand All @@ -25,6 +28,7 @@ const ObjectDetectResult = () => {
<div>
<h2>탐지 결과</h2>
<button onClick={handleClickUpload}>이미지 업로드</button>
<button onClick={handleClickViewCCTV}>실시간 화면 보기</button>
<table>
<thead>
<tr>
Expand Down
71 changes: 71 additions & 0 deletions src/pages/Publisher.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React, {useEffect, useRef, useState} from 'react';
import ConnectionStatus from "../components/ConnectionStatus.jsx";

const Publisher = () => {
const websocketRef = useRef(null);
const videoRef = useRef(null);
const [isConnected, setConnected] = useState(false);

useEffect(() => {
const videoElement = videoRef.current;
const socket = new WebSocket(import.meta.env.VITE_WS_PUBLISHER);
websocketRef.current = socket;

socket.binaryType = 'arraybuffer';
socket.onopen = () => {
setConnected(true);
console.log('WebSocket connection opened');
};

socket.onclose = () => {
setConnected(false);
console.log('WebSocket connection closed');
};

socket.onerror = (error) => {
setConnected(false);
console.error('WebSocket error:', error);
};

function captureFrame() {
if (videoElement.readyState !== videoElement.HAVE_ENOUGH_DATA)
return

const offscreenCanvas = new OffscreenCanvas(videoElement.videoWidth, videoElement.videoHeight);
const context = offscreenCanvas.getContext('2d');
context.drawImage(videoElement, 0, 0, offscreenCanvas.width, offscreenCanvas.height);

offscreenCanvas.convertToBlob({ type: 'image/jpeg' }).then(blob => {
if (socket.readyState === WebSocket.OPEN) {
socket.send(blob);
}
});
}

navigator.mediaDevices.getUserMedia({video: true}).then(stream => {
videoElement.srcObject = stream;
const intervalId = setInterval(captureFrame, 100);

return () => {
clearInterval(intervalId);
socket?.close();
};
}).catch(error => {
console.error('Error accessing media devices.', error);
});

return () => {
socket?.close();
};
}, []);

return (
<div>
<h1>Streamer</h1>
<ConnectionStatus isConnected={isConnected}/>
<video ref={videoRef} autoPlay></video>
</div>
);
};

export default Publisher;
55 changes: 55 additions & 0 deletions src/pages/Subscriber.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React, {useEffect, useRef, useState} from 'react';
import ConnectionStatus from "../components/ConnectionStatus.jsx";
import defaultImage from '../assets/loading.png';

const Subscriber = () => {
const imgRef = useRef(null);
const socketRef = useRef(null);
const [isConnected, setConnected] = useState(false);
const handleOnErrorLoadingImage = () => {
imgRef.current.src = defaultImage
}

useEffect(() => {
const imgElement = imgRef.current;
imgElement.src = defaultImage

const socket = new WebSocket(import.meta.env.VITE_WS_SUBSCRIBER);
socket.binaryType = 'arraybuffer';
socketRef.current = socket;

socket.onopen = () => {
setConnected(true);
console.log('WebSocket connection opened');
};

socket.onclose = () => {
setConnected(false);
console.log('WebSocket connection closed');
};

socket.onerror = (error) => {
setConnected(false)
console.error('WebSocket error:', error);
};

socket.onmessage = (event) => {
const blob = new Blob([event.data], {type: 'image/jpeg'});
imgElement.src = URL.createObjectURL(blob);
};

return () => {
socketRef.current?.close();
};
}, []);

return (
<div>
<h1>Viewer</h1>
<ConnectionStatus isConnected={isConnected}/>
<img ref={imgRef} alt="Video Stream" onError={handleOnErrorLoadingImage}/>
</div>
);
};

export default Subscriber;

0 comments on commit d15f951

Please sign in to comment.