728x90
반응형
저번 화재 관제 시스템을 출력할때는 node-rtsp 라이브러리를
사용했는데 뭔가 안정성이 떨어진다는 느낌을 받았다.
이번에 다른 프로젝트는 조금 다른방법으로 시도 하였고 ws가 아닌 http/https 방식을 사용하고 mp4로 변환 후 사용하는 클라이언트에서 도메인을 연결하고 렌더하는 방식으로 진행하였다.
바로 코드로 설명해보자
클라이언트
'use client';
import { useEffect, useState } from 'react';
const Rtsp = () => {
const [videoRefs, setVideoRefs] = useState<
React.RefObject<HTMLVideoElement>[]
>([]);
const [streamUrls, setStreamUrls] = useState<string[]>([]);
const [streamNames, setStreamNames] = useState<string[]>([]);
useEffect(() => {
fetch('http://localhost:3005/streams')
.then((response) => response.json())
.then((data) => {
setStreamUrls(data);
setVideoRefs(data.map(() => ({ current: null })));
const names = data.map((url: string) => {
const parts = url.split('/');
return parts[parts.length - 1];
});
setStreamNames(names);
});
}, []);
useEffect(() => {
videoRefs.forEach((videoRef, index) => {
const video = videoRef.current;
if (video) {
video.src = `http://localhost:3005/stream${index + 1}`;
video.onloadeddata = () => {
video.play().catch((error) => {
console.error('Error attempting to play', error);
});
};
}
});
}, [videoRefs, streamUrls]);
return (
<div>
<h1>RTSP Streams</h1>
{videoRefs.map((videoRef, index) => (
<div key={index} style={{ margin: '10px' }}>
<h2>{streamNames[index]}</h2>
<video
ref={videoRef}
width='320'
height='240'
autoPlay
muted
loop
></video>
</div>
))}
</div>
);
};
export default Rtsp;
우선 상태 초기화 후 스트림 목록을 가져와서 상태에 저장한다.
각 비디오의 src속성을 받아온 주소로 설정하고 ref배열을 반복하면서 참조를 생성한다.
나중에 fetch나 index부분은 수정해야함 ㅠㅠ
서버 코드
const express = require('express');
const cors = require('cors');
const { spawn } = require('child_process');
const app = express();
const port = 3005;
app.use(cors());
const streams = [
'RTSP주소'
];
app.get('/streams', (req, res) => {
res.json(streams);
});
streams.forEach((streamUrl, index) => {
app.get(`/stream${index + 1}`, (req, res) => {
res.setHeader('Content-Type', 'video/mp4');
const ffmpeg = spawn('ffmpeg', [
'-i', // 입력 파일을 지정하는 옵션
streamUrl, // 입력 파일 (RTSP 스트림 URL)
'-f', // 출력 형식을 지정하는 옵션
'mp4', // 출력 형식 (MP4)
'-vcodec', // 비디오 코덱을 지정하는 옵션
'copy', // 비디오 코덱 (복사)
'-an', // 오디오를 포함하지 않도록 하는 옵션
'-movflags', // MP4 파일의 플래그를 설정하는 옵션
'frag_keyframe+empty_moov', // MP4 파일을 스트리밍 가능하게 설정
'pipe:1', // 출력 파일을 파이프로 설정 (stdout)
]);
ffmpeg.stdout.pipe(res);
ffmpeg.on('close', (code) => {
console.log(`FFmpeg process closed with code ${code}`);
});
req.on('close', () => {
ffmpeg.kill('SIGINT');
});
});
});
app.get('/', (req, res) => {
res.send('RTSP 서버가 실행 중입니다.');
});
app.listen(port, () => {
console.log(`서버가 http://localhost:${port} 에서 실행 중입니다.`);
});
각 스트림 url에 대해 라우트를 설정하고 클라이언트가 요청을 보낼 때 rtsp를 mp4로 변환 한 후 클라이언트로 전송한다.
여기서 파이핑이란 개념이 나왔는데
파이핑이란 한 스트림의 출력을 다른 스트림의 입력으로 연결하는 것을 의미한다고 한다.
ffmprg의 표준 출력을 http응답으로 연결하여 ffmprg가 생성한 비디오 데이터를 실시간으로 클라이언트에게 전송한다.
코드로만 읽어보면 어렵지 않다 아직 수정 부분이 많아 수정해야 하지만
개념에 대해 알아보고자 쓴 글이므로 혹시나 어려움을 겪는 분들에게 도움이 됐으면 한다.
반응형
'WebDev > React-Next' 카테고리의 다른 글
next/dynamic과 Lazy Loading을 알아보자 (0) | 2024.06.13 |
---|---|
Next의 Link 컴포넌트에 대해 알아보자 (0) | 2024.06.12 |
React(리액트)의 useRef에 대해 공부하자! (0) | 2024.06.05 |
React(리액트)의 Props를 공부해보자 (0) | 2024.06.01 |
React(리액트)의 useState를 공부하자! 기초니까! (0) | 2024.05.31 |