CCTV 설치, FastAPI+OpenCV 녹화, 스트리밍 서버 제작과정 (2) - RTSP OpenCV로 받아서 녹화하기

Yany Choi·2023년 3월 25일
0

펜션

목록 보기
2/3

stream.py

여기서는 이전 글에서 말한 CCTV 스트림을 받아서 녹화하는 서비스, stream.py를 작성하는 내용을 설명한다. 주로 OpenCV와 씨름하는데 시간을 사용했고, systemd 커스텀 서비스로 만들어서 프로세스의 관리를 수월하게 하는 것에 집중했다.

RTSP와의 시행착오

첨에 인터넷 조사해서 봤을때 솔직히 말해서 방심했다. 일단 스트리밍 전용 프로토콜도 있고, OpenCV로 해보니까 생각보다 너무 쉬워서 "오 금방 끝나겠네?" 라는 생각이 들었었다. 하지만 나중에 프론트엔드까지 다 만들고 나서야 틀렸다는 것을 알았다...

알고보니까 OpenCV만 이게 연결이 수월하고, HTTP를 거쳐서 재생시키는 것은 완전 다른 차원의 문제였다. 애초에 RTSP 프로토콜을 안받아줄 뿐더러, 이게 스트리밍이라서 영상이 아니라 이미지를 계속 보낸다고 생각해야 하는 것이었다.

ffmpeg의 발견

그러다 ffmpeg라는 오픈소스를 알게 되었고, 이걸 이용하면 존재하는 거의 모든 코덱 간의 변환이 가능하다는 것을 알았다. 그래서 사용하려고 cronjob으로 매시간마다 RTSP 결과물을 h264로 변환했으나...

CCTV를 네트워크에 랜선으로 연결하지 않고 WiFi로 연결해서 그런지 그 품질이 일정하지 않았고, 그만큼 영상이 자주 깨져서 내용 확인이 거의 불가능에 가까웠다. OpenCV를 사용하면 1초당 15개의 프레임을 캡쳐해서 직접 동영상을 새로 만드는 것이라면, ffmpeg의 자세한 원리는 잘 모르지만 품질이 불안정한 상황에서도 OpenCV는 그 결과물이 항상 일정했다. 물론 문제가 있었다면 OpenCV로 만든 영상의 용량이 거의 5배에 달한다는 것이었지만, 당장 CCTV를 활용해야 했기에 추가적인 하드를 구매하고, 코드에 한달이 지난 영상은 삭제하는 기능을 넣기로 정했다.

자세한 내용은 아래에 코드를 설명하면서 말하지만, 이 부분에서 시간이 굉장히 오래걸렸다.

stream.py

우선 RTSP 프로토콜은 rtsp://아이디:비밀번호@URL 이 규격이다. 여기서 포트는 554가 국룰이고, 내가 쓰는 CCTV는 고화질이 stream1, 저화질이 stream2였다. 고화질로 녹화를 하고, 실시간 스트리밍으로 저화질을 보고 싶었기 때문에 여기서는 stream2로 보고 있다.
그 이후는 OpenCV를 써서 프레임을 캡처해서 바로 jpeg로 쏴주고 있다.
그래서 웹브라우저로 이 스트림을 봤을때 영상이 아닌, 사진이 계속 업데이트되는 형식으로 되고 있다.

video.py

여기서는 이제 녹화한 영상들을 재생하는데, 우선 get_video_list_service는 저장한 비디오들을 list로 출력한다.

그리고 range_requests_response에서 본격적으로 지정한 녹화영상을 스트리밍해서 보내주는데, 여기서 스트리밍에 대해 간략하게 설명을 하자면, 가령 파일이 1000바이트일 때, 이를 1~100, 101~200, ..., 901~1000 으로 바이트를 쪼개서 보내는게 스트리밍이다. 이것을 FastAPI에서는 StreamingResponse를 이용해서 send_bytes_range_requests로 파일을 시작바이트, 끝바이트로 쪼개서 계속 보낸다. 여기서 HTTP Status Code는 206이며, 여기서 content 전체 length 또한 알고 있어 언제까지 이 스트림을 이어서 받을지 다 알고 있다.

이후 추가적인 문제들...

근본적인 문제는 내가 이 CCTV의 포트를 외부로 공개하고 싶지 않았고, 사설 네트워크 내에서 통신을 마치고, NGINX로 정적페이지와 FastAPI 포트만 개방하고 싶었다. 그래서 집에 남는 노트북으로 웹서버를 구성했는데, 이게 화근이었다. 원인을 모를 이유로 서버가 1주일마다 먹통이 돼서 재부팅을 하지 않으면 안됐다.

여기서 해결책은 크게 3가지가 있었다.

  • AWS로 서버 구축
    사실 제일 쉬운 방법이다. 서버를 AWS에서 관리해주고 재부팅 또한 쉬워 용이하게 사용할 수 있으나, 인터넷으로 RTSP 스트림 자체를 공개해야 하는 것의 부담 때문에 포기했다.
  • 24/7 잘버티는 서버 컴퓨터 사서 쓰기
    이건 돈이 없어서 패스.
  • 지금 컴퓨터로 어떻게든 버틴다
    사실 선택지가 별로 없었다. 돈없는 학생이기도 하고... 그리고 노트북으로 서버를 구성했더니 보관하기가 편해서 공간 절약도 잘 되었다.
    결국은 오래버티는 서버를 구성해야 했으며, 결국 내가 할 수 있는 가장 좋은 방법으로는 꾸준히 재부팅을 해주는 것이었기에 cronjob을 통해서 매주마다 재부팅을 해주고 있다.

그리고 재부팅을 하면 서비스가 다운되니 이를 자동으로 살려주는 것 또한 필요한데, 이는 system daemon을 활용해서 enable만 해주면 켜짐과 동시에 알아서 데몬들을 살려줘서 크게 문제가 없었다.

cctv-stream.service

[Unit]
Description=CCTV Stream Service

[Service]
WorkingDirectory=/home/yany/cctv-system/api
User=yany
ExecStart=/usr/bin/python3 -m uvicorn --port 8000 main:app
Restart=always

[Install]
WantedBy=multi-user.target

간단하게 요약하면, systemctl을 통해 해당되는 데몬을 돌렸고, 에러가 생겨서 터져도 자동시작하는걸로 구성했다.

cctv-record.service

[Unit]
Description=CCTV Recording Service

[Service]
WorkingDirectory=/home/yany/cctv-system/api
User=yany
ExecStart=/usr/bin/python3 stream.py
Restart=always

[Install]
WantedBy=multi-user.target

CCTV를 녹화하는 stream.py를 실행시켜주는 데몬 서비스이다.

후기

정말 귀찮고 할일이 많았다.
괜히 온프레미스 안쓰고 다 클라우드로 갈아타는게 아니구나... 싶었다.
심지어 난 DB 구축도 안하고 그냥 우분투에 하나 띄우고, 서버에 구축해야 하는 기본적인 유지장치들 또한 없었다.
그래도 이번 기회로 RTSP가 뭔지도 알 수 있었고, systemd에 대한 이해를 조금 할 수 있었다.

그런데 펜션이 이번에 용인반도체클러스터 생기는 곳에 계획지에 들어가버렸다...
2042년까지 짓는다는데 시한부 펜션이니, 적당히 관리하다가 빠지고 싶다.

용인진펜션
https://naver.me/IDBgsd2b

profile
생각하자

0개의 댓글