[DIY 스마트팜] 파이썬 Flask + MJPEG + Cloudflare Tunnel을 이용한 간이 실시간 CCTV 구축
개요
USB 웹캠이 연결된 PC에서 영상을 캡처하여 Flask 서버를 통해 MJPEG 스트리밍으로 제공하고, Cloudflare Tunnel을 이용하여 외부에서 휴대폰으로 실시간 확인할 수 있는 간이 CCTV 시스템을 구축한다.
특징:
무료
포트포워딩 불필요
공인 IP 불필요
공유기 설정 불필요
별도 앱 설치 불필요
웹 브라우저만 있으면 접속 가능
거의 실시간 영상 확인 가능
시스템 구성
USB Webcam
↓
OpenCV
↓
Flask MJPEG Server
↓
localhost:8000
↓
Cloudflare Tunnel
↓
https://xxxxx.trycloudflare.com
↓
휴대폰 브라우저
프로젝트 폴더 구조
camera_server/
└── camera_server.py
필요 패키지 설치
pip install flask
pip install opencv-python
확인:
python -c "import cv2; print(cv2.__version__)"
CCTV 서버 코드
from flask import Flask, Response
import cv2
from datetime import datetime
app = Flask(__name__)
cap = cv2.VideoCapture(0)
if not cap.isOpened():
raise RuntimeError("카메라 열기 실패")
# Logitech C525 권장 설정
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
def generate_frames():
while True:
success, frame = cap.read()
if not success:
continue
timestamp = datetime.now().strftime(
"%Y-%m-%d %H:%M:%S"
)
cv2.putText(
frame,
timestamp,
(20, 40),
cv2.FONT_HERSHEY_SIMPLEX,
1,
(0, 255, 0),
2,
cv2.LINE_AA
)
ret, buffer = cv2.imencode(
'.jpg',
frame,
[cv2.IMWRITE_JPEG_QUALITY, 80]
)
if not ret:
continue
frame_bytes = buffer.tobytes()
yield (
b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n'
+ frame_bytes +
b'\r\n'
)
@app.route('/')
def index():
return """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport"
content="width=device-width,initial-scale=1">
<style>
html,body{
margin:0;
width:100%;
height:100%;
background:black;
}
img{
width:100vw;
height:100vh;
object-fit:contain;
}
</style>
</head>
<body>
<img src="/video">
</body>
</html>
"""
@app.route('/video')
def video():
return Response(
generate_frames(),
mimetype='multipart/x-mixed-replace; boundary=frame'
)
if __name__ == '__main__':
app.run(
host='0.0.0.0',
port=8000,
threaded=True
)
서버 실행
python camera_server.py
출력 예:
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:8000
* Running on http://192.168.0.63:8000
로컬 확인
브라우저 접속:
http://localhost:8000
실시간 영상이 보이면 성공.
Cloudflare Tunnel 설치
다운로드:
Windows:
cloudflared-windows-amd64.exe
Cloudflare Tunnel 실행
cloudflared-windows-amd64.exe tunnel --url http://localhost:8000
출력 예:
Your quick Tunnel has been created!
https://partly-flexibility-raid-experiences.trycloudflare.com
외부 접속
휴대폰 브라우저:
https://partly-flexibility-raid-experiences.trycloudflare.com
접속.
현재 PC 웹캠 영상이 실시간으로 표시된다.
실행 순서
창 1
python camera_server.py
창 2
cloudflared-windows-amd64.exe tunnel --url http://localhost:8000
장점
이미지 파일 저장 불필요
SSD/HDD 수명 영향 거의 없음
브라우저에서 바로 확인 가능
Firebase 불필요
데이터베이스 불필요
앱 개발 불필요
무료
확장 아이디어
움직임 감지(Motion Detection)
사람 감지(YOLO)
식물 성장 타임랩스
온도/습도 표시
PLC 상태 표시
다중 카메라 지원
QR 코드 접속
홈 화면 바로가기(PWA)
MJPEG가 실시간 영상처럼 보이는 원리
많은 사람들이 처음 보면
"이게 영상 파일도 아닌데 왜 실시간으로 보이지?"
라는 의문을 갖는다.
실제로는 영상 스트리밍이 아니라 JPEG 이미지를 매우 빠르게 연속 전송하는 방식이다.
일반적인 웹 페이지
보통 브라우저는:
GET /image.jpg
↓
서버 응답
↓
연결 종료
과정을 수행한다.
즉 한 번 요청하고 끝난다.
MJPEG 방식
MJPEG는:
브라우저
↓
GET /video
서버
↓
연결 유지
상태를 계속 유지한다.
연결을 끊지 않는다.
서버는 같은 연결 안에서:
JPEG #1
JPEG #2
JPEG #3
JPEG #4
...
를 계속 흘려보낸다.
실제로는 다음과 같은 형태다.
--frame
Content-Type: image/jpeg
[첫 번째 JPEG]
--frame
Content-Type: image/jpeg
[두 번째 JPEG]
--frame
Content-Type: image/jpeg
[세 번째 JPEG]
브라우저는:
JPEG 수신
↓
화면 표시
JPEG 수신
↓
화면 교체
JPEG 수신
↓
화면 교체
를 반복한다.
결과적으로:
정지 이미지
+
정지 이미지
+
정지 이미지
+
정지 이미지
가 매우 빠르게 바뀌어
실시간 영상
처럼 보인다.
현재 프로젝트에서의 데이터 흐름
Logitech C525
↓
OpenCV
cap.read()
↓
현재 프레임
↓
JPEG 압축
cv2.imencode()
↓
Flask Response
↓
MJPEG Stream
↓
Cloudflare Tunnel
↓
휴대폰 브라우저
왜 최신 사진 방식보다 부드러운가?
이전 방식:
latest.jpg 저장
↓
브라우저 새로고침
↓
latest.jpg 재다운로드
1초마다 반복
실질적으로:
1 FPS
MJPEG 방식:
캡처
↓
즉시 전송
↓
즉시 표시
반복
보통:
10 ~ 30 FPS
가능
실제 CCTV와의 관계
많은 저가형 IP 카메라와 초기 네트워크 CCTV도 MJPEG 방식을 사용했다.
장점:
구현 단순
브라우저 호환성 좋음
디코더 필요 없음
단점:
대역폭 사용량 큼
하지만 개인용 스마트팜 CCTV나 간이 모니터링 용도로는 지금도 매우 실용적인 방식이다.
댓글
댓글 쓰기