์นดํ…Œ๊ณ ๋ฆฌ ์—†์Œ

Python OpenCV์™€ GStreamer ๊ธฐ๋ฐ˜ ๋‹ค์ค‘ RTSP ์นด๋ฉ”๋ผ ์ŠคํŠธ๋ฆฌ๋ฐ ์ง€์—ฐ ๋ฐ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ํ•ด๊ฒฐ ๊ฐ€์ด๋“œ

๊ฒŒ์ž„๊ต์ˆ˜ 2026. 8. 28. 16:01
๋ฐ˜์‘ํ˜•

๐ŸŒ English Abstract

This article provides a comprehensive troubleshooting guide for resolving video latency and memory leak issues in multi-RTSP camera streaming systems using Python, OpenCV, and GStreamer. It examines root causes such as internal frame buffer accumulation and improper pipeline configurations, offering actionable solutions including optimized GStreamer pipeline syntax, non-blocking multi-threaded architecture, and memory management strategies. Designed for computer vision and edge AI engineers, this guide ensures low-latency, high-stability real-time video ingestion in production environments.

์Šค๋งˆํŠธ ํŒฉํ† ๋ฆฌ, ์ง€๋Šฅํ˜• ๊ตํ†ต ์‹œ์Šคํ…œ(ITS), CCTV ๊ธฐ๋ฐ˜ ๊ด€์ œ ์‹œ์Šคํ…œ ๋“ฑ ๋‹ค์–‘ํ•œ ์‚ฐ์—… ๋ถ„์•ผ์—์„œ ๋‹ค์ค‘ RTSP(Real-Time Streaming Protocol) ์นด๋ฉ”๋ผ ์ŠคํŠธ๋ฆผ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ˆ˜์ง‘ํ•˜๊ณ  ๋ถ„์„ํ•˜๋Š” ์‹œ์Šคํ…œ์˜ ์ˆ˜์š”๊ฐ€ ๊ธ‰์ฆํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ๊ฐœ๋ฐœ์ž๋“ค์€ ๋น ๋ฅธ ํ”„๋กœํ† ํƒ€์ดํ•‘์„ ์œ„ํ•ด Python์˜ OpenCV(Open Source Computer Vision Library)์™€ cv2.VideoCapture API๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ 8์ฑ„๋„, 16์ฑ„๋„ ์ด์ƒ์˜ ๋‹ค์ค‘ ์ŠคํŠธ๋ฆผ์„ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•  ๋•Œ ์‹œ๊ฐ„์ด ์ง€๋‚จ์— ๋”ฐ๋ผ ์˜์ƒ ์ง€์—ฐ(Video Latency/Lag)์ด ๊ฐ€์ค‘๋˜๊ฑฐ๋‚˜, ์‹œ์Šคํ…œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜(Memory Leak)๋กœ ์ธํ•ด ํ”„๋กœ์„ธ์Šค๊ฐ€ ๊ฐ•์ œ ์ข…๋ฃŒ๋˜๋Š” ์‹ฌ๊ฐํ•œ ํ˜„์ƒ์„ ๊ฒฝํ—˜ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋ณธ ๊ธ€์—์„œ๋Š” ์ด๋Ÿฌํ•œ ํ˜„์ƒ์ด ๋ฐœ์ƒํ•˜๋Š” ๊ทผ๋ณธ์ ์ธ ๊ธฐ์ˆ ์  ์›์ธ์„ ๋ถ„์„ํ•˜๊ณ , GStreamer ํŒŒ์ดํ”„๋ผ์ธ(GStreamer Pipeline) ์ตœ์ ํ™” ๋ฐ ๋‹ค์ค‘ ์“ฐ๋ ˆ๋“œ ๋น„๋™๊ธฐ ๋ฒ„ํผ๋ง ํŒจํ„ด(Multi-threaded Asynchronous Buffering Pattern)์„ ํ†ตํ•ด ์‹ค์‹œ๊ฐ„ ์ดˆ์ €์ง€์—ฐ(Ultra-Low Latency) ๋ฐ ๋†’์€ ์‹œ์Šคํ…œ ์•ˆ์ •์„ฑ์„ ํ™•๋ณดํ•˜๋Š” ์‹ค๋ฌด ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ๋ฐฉ๋ฒ•์„ ๊ฐ€์ด๋“œํ•ฉ๋‹ˆ๋‹ค.



1. ํ”„๋ ˆ์ž„ ๋ฒ„ํผ ๋ˆ„์  ๋ฐ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜์˜ ๊ทผ๋ณธ ์›์ธ ๋ถ„์„

๋‹ค์ค‘ RTSP ํ™˜๊ฒฝ์—์„œ ์ง€์—ฐ๊ณผ ๋ฉ”๋ชจ๋ฆฌ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์›์ธ์€ ์‹œ์Šคํ…œ์˜ ์ƒ์‚ฐ์ž-์†Œ๋น„์ž ๋ถˆ๊ท ํ˜•(Producer-Consumer Mismatch)๊ณผ OpenCV ๋ฐ GStreamer์˜ ๊ธฐ๋ณธ ๋ฒ„ํผ ๊ด€๋ฆฌ ์ •์ฑ…์—์„œ ๋น„๋กฏ๋ฉ๋‹ˆ๋‹ค.

1) FFmpeg / GStreamer ๋‚ด๋ถ€ ๋ฒ„ํผ๋ง ์ •์ฑ… (Internal Buffering Policy)

OpenCV๊ฐ€ RTSP ๋ฐฑ์—”๋“œ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ๋ณธ FFmpeg ๋˜๋Š” GStreamer ์—”์ง„์€ ๋„คํŠธ์›Œํฌ ํŒจํ‚ท ์†์‹ค ์‹œ ์žฌ์ƒ์˜ ๋ถ€๋“œ๋Ÿฌ์›€(Smoothness)์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด ๊ธฐ๋ณธ์ ์œผ๋กœ ๋‚ด๋ถ€ ํ(Internal Queue)์— ํ”„๋ ˆ์ž„์„ ์Œ“์•„๋‘ก๋‹ˆ๋‹ค. ๋””์ฝ”๋”ฉ ํ”„๋กœ์„ธ์Šค๋‚˜ AI ์ถ”๋ก  ๋ชจ๋ธ์˜ ์ฒ˜๋ฆฌ ์†๋„๊ฐ€ ์นด๋ฉ”๋ผ์˜ ๋ถ„๋‹น ํ”„๋ ˆ์ž„ ์ˆ˜(FPS)๋ณด๋‹ค ์•ฝ๊ฐ„์ด๋ผ๋„ ๋А๋ ค์ง€๋ฉด, ๋น„๋””์˜ค ํ”„๋ ˆ์ž„์ด ์ˆ˜์‹  ๋ฒ„ํผ์— ๊ณ„์† ์Šคํƒ(Stack) ํ˜•ํƒœ๋กœ ์Œ“์ด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋กœ ์ธํ•ด ์ˆ˜๋ถ„ ๋‚ด์ง€ ์ˆ˜์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด ํ™”๋ฉด์ด ์ˆ˜ ์ดˆ์—์„œ ์ˆ˜์‹ญ ์ดˆ๊นŒ์ง€ ๋А๋ ค์ง€๋Š” ํ”„๋ ˆ์ž„ ์ง€์—ฐ(Frame Latency) ํ˜„์ƒ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

2) Synchronous Blocking Read (๋™๊ธฐ์‹ ๋ธ”๋กœํ‚น ์ฝ๊ธฐ)

cv2.VideoCapture.read() ๋ฉ”์˜๋“œ๋Š” ๋™๊ธฐ์‹์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ๋„คํŠธ์›Œํฌ ์ŠคํŒŒ์ดํฌ๋‚˜ ํŒจํ‚ท ์žฌ์ „์†ก์ด ์ผ์–ด๋‚  ๊ฒฝ์šฐ ๋ฉ”์ธ ์“ฐ๋ ˆ๋“œ๊ฐ€ ํ•ด๋‹น ๋ธ”๋ก์—์„œ ๋Œ€๊ธฐ ์ƒํƒœ(Blocking)์— ๋น ์ง€๋ฉฐ, ์ด๋Š” ์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ์˜ ๋ณ‘๋ชฉ์œผ๋กœ ์ด์–ด์ง‘๋‹ˆ๋‹ค. ๋‹ค์ค‘ ์ฑ„๋„ ํ™˜๊ฒฝ์—์„œ๋Š” ๋‹จ ํ•˜๋‚˜์˜ ์นด๋ฉ”๋ผ์˜ ํ”„๋ ˆ์ž„ ๋“œ๋ž์ด ์ „์ฒด ๋ฃจํ”„์˜ ์Šค๋ฃจํ’‹(Throughput)์„ ์ €ํ•˜์‹œํ‚ต๋‹ˆ๋‹ค.

3) C/C++ ๋ž˜ํผ(Wrapper) ๊ณ„์ธต์˜ ์ž์› ํ•ด์ œ ๋ฏธํก ๋ฐ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜

Python-OpenCV ๋ฐ PyGObject ๊ณ„์ธต์€ C/C++ ๋ฐ”์ธ๋”ฉ์œผ๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ๋„คํŠธ์›Œํฌ ๋Š๊น€ ํ˜„์ƒ์ด ๋ฐœ์ƒํ•˜์—ฌ ์นด๋ฉ”๋ผ ์žฌ์—ฐ๊ฒฐ(Reconnection)์„ ์‹œ๋„ํ•  ๋•Œ, ๊ธฐ์กด VideoCapture ๊ฐ์ฒด๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํ•ด์ œ(Release)ํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ C-level ๋ฉ”๋ชจ๋ฆฌ ํฌ์ธํ„ฐ๊ฐ€ GC(Garbage Collector)์— ์˜ํ•ด ์ฆ‰์‹œ ์ˆ˜๊ฑฐ๋˜์ง€ ์•Š์œผ๋ฉด, Unmanaged Heap ์˜์—ญ์˜ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์ง€์†์ ์œผ๋กœ ๋ˆ„์ˆ˜๋˜๋Š” ํ˜„์ƒ์ด ์ผ์–ด๋‚ฉ๋‹ˆ๋‹ค.

2. ์ดˆ์ €์ง€์—ฐ์„ ์œ„ํ•œ GStreamer ํŒŒ์ดํ”„๋ผ์ธ ์ตœ์ ํ™” ์„ค๊ณ„

OpenCV ๋‹จ๋… ์‚ฌ์šฉ ์‹œ ๋ฐœ์ƒํ•˜๋Š” ๋ฒ„ํผ ๋ฌธ์ œ๋Š” GStreamer ๋นŒ๋“œ ์˜ต์…˜๊ณผ ๋งž์ถคํ˜• ํŒŒ์ดํ”„๋ผ์ธ ๋ฌธ์ž์—ด ์„ค์ •์„ ํ†ตํ•ด ์™„์ „ํžˆ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. GStreamer์˜ appsink ์š”์†Œ์˜ ์†์„ฑ์„ ์กฐ์ •ํ•˜์—ฌ ํ•ญ์ƒ ์ตœ์‹  ํ”„๋ ˆ์ž„(Latest Frame)๋งŒ ์œ ์ง€ํ•˜๊ณ  ์ด์ „ ํ”„๋ ˆ์ž„์€ ์ฆ‰์‹œ ํ๊ธฐ(Drop)ํ•˜๋Š” ๊ฒƒ์ด ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.

์ตœ์ ํ™”๋œ GStreamer ํŒŒ์ดํ”„๋ผ์ธ ๋ฌธ์ž์—ด ์˜ˆ์‹œ (NVIDIA GPU / CPU ๋ฒ”์šฉ)

๋‹ค์Œ์€ ๋„คํŠธ์›Œํฌ ๋ฒ„ํผ๋ฅผ ์ตœ์†Œํ™”ํ•˜๊ณ  ์ตœ์‹  ํ”„๋ ˆ์ž„๋งŒ ๋“œ๋žํ•˜์—ฌ ์ˆ˜์ง‘ํ•˜๋Š” GStreamer ํŒŒ์ดํ”„๋ผ์ธ์ž…๋‹ˆ๋‹ค.


rtspsrc location=rtsp://admin:password@192.168.1.100:554/stream1 latency=0 drop-on-latency=true ! \
rtph264depay ! h264parse ! \
nvv4l2decoder ! nvvidconv ! video/x-raw, format=BGRx ! \
videoconvert ! video/x-raw, format=BGR ! \
appsink drop=true max-buffers=1 sync=false

์ฃผ์š” ํŒŒ์ดํ”„๋ผ์ธ ์†์„ฑ ์ƒ์„ธ ๋ถ„์„:

  • rtspsrc latency=0: RTSP ์ˆ˜์‹  ์ธก์˜ ๋„คํŠธ์›Œํฌ ๋ฒ„ํผ๋ง ์‹œ๊ฐ„์„ 0ms๋กœ ์„ค์ •ํ•˜์—ฌ ์ง€์—ฐ์„ ๊ทน๋‹จ์ ์œผ๋กœ ์ค„์ž…๋‹ˆ๋‹ค.
  • drop-on-latency=true: ์ง€์—ฐ ์‹œ๊ฐ„์ด ์ง€์ •๋œ ์œ ์˜ˆ ๋ฒ”์œ„๋ฅผ ์ดˆ๊ณผํ•  ๊ฒฝ์šฐ ๊ณผ๊ฐํ•˜๊ฒŒ ์ธ์ปค๋ฐ ํŒจํ‚ท์„ ๋“œ๋žํ•ฉ๋‹ˆ๋‹ค.
  • nvv4l2decoder / nvvidconv: (NVIDIA Jetson ๋˜๋Š” CUDA ํ™˜๊ฒฝ ์ ์šฉ ์‹œ) ํ•˜๋“œ์›จ์–ด ๊ฐ€์†(Hardware Acceleration) ๋””์ฝ”๋”๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ CPU ์ ์œ ์œจ์„ ๋Œ€ํญ ๋‚ฎ์ถฅ๋‹ˆ๋‹ค. (CPU ์ „์šฉ ์‹œ์Šคํ…œ์˜ ๊ฒฝ์šฐ avdec_h264 ์‚ฌ์šฉ)
  • appsink drop=true max-buffers=1: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ์ „๋‹ฌ๋˜๋Š” ํ”„๋ ˆ์ž„ ํ์˜ ํฌ๊ธฐ๋ฅผ ์ •ํ™•ํžˆ 1๊ฐœ๋กœ ์ œํ•œํ•˜๋ฉฐ, ์ƒˆ๋กœ์šด ํ”„๋ ˆ์ž„์ด ์ˆ˜์‹ ๋˜๋ฉด ์†Œ๋น„๋˜์ง€ ์•Š์€ ์ด์ „ ํ”„๋ ˆ์ž„์€ ์ฆ‰์‹œ ๋ฒ„๋ฆฝ๋‹ˆ๋‹ค.
  • sync=false: ํƒ€์ž„์Šคํƒฌํ”„ ๋™๊ธฐํ™”๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•˜์—ฌ ๋””์ฝ”๋”ฉ๋˜๋Š” ์ฆ‰์‹œ ํ”„๋ ˆ์ž„์„ ์†Œ๋น„์ž์—๊ฒŒ ๋ฐฉ์ถœํ•ฉ๋‹ˆ๋‹ค.


3. ๋น„๋™๊ธฐ ํ”„๋ ˆ์ž„ ์ˆ˜์ง‘๊ธฐ(Non-blocking Frame Grabber) ์Šค๋ ˆ๋“œ ํŒจํ„ด

GStreamer ํŒŒ์ดํ”„๋ผ์ธ ์ตœ์ ํ™” ์™ธ์—๋„ Python์˜ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ(Multi-threading)์„ ํ™œ์šฉํ•œ ์ƒ์‚ฐ์ž-์†Œ๋น„์ž ๋ถ„๋ฆฌ ํŒจํ„ด์„ ์ ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ๋Š” ํ”„๋ ˆ์ž„์„ ๋ฌดํ•œํžˆ ์ฝ์–ด ๋ฉ”๋ชจ๋ฆฌ์˜ ๋‹จ์ผ ์Šฌ๋กฏ์— ๋ฎ์–ด์“ฐ๊ณ , ๋ฉ”์ธ ์Šค๋ ˆ๋“œ(AI ์ถ”๋ก /๋ถ„์„)๋Š” ํ•„์š”ํ•  ๋•Œ๋งŒ ์ตœ์‹  ํ”„๋ ˆ์ž„์„ ๊ฐ€์ ธ๊ฐ€๋Š” ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.

๊ณ ์„ฑ๋Šฅ RTSP ํ”„๋ ˆ์ž„ ์บก์ฒ˜ ํด๋ž˜์Šค ๊ตฌํ˜„ ์˜ˆ์ œ


import threading
import cv2
import time
import gc

class RTSPFrameGrabber:
    def __init__(self, rtsp_url, pipeline_builder=None):
        self.rtsp_url = rtsp_url
        self.pipeline_builder = pipeline_builder
        self.capture = None
        self.frame = None
        self.grabbed = False
        self.read_lock = threading.Lock()
        self.running = False
        self.thread = None

    def _build_gstreamer_pipeline(self):
        # GStreamer ํŒŒ์ดํ”„๋ผ์ธ ๋™์  ์ƒ์„ฑ
        return (
            f"rtspsrc location={self.rtsp_url} latency=0 drop-on-latency=true ! "
            f"rtph264depay ! h264parse ! avdec_h264 ! "
            f"videoconvert ! video/x-raw, format=BGR ! "
            f"appsink drop=true max-buffers=1 sync=false"
        )

    def start(self):
        if self.running:
            return
        
        pipeline = self._build_gstreamer_pipeline() if not self.pipeline_builder else self.pipeline_builder(self.rtsp_url)
        self.capture = cv2.VideoCapture(pipeline, cv2.CAP_GSTREAMER)
        
        if not self.capture.isOpened():
            raise RuntimeError(f"Failed to open RTSP stream: {self.rtsp_url}")
            
        self.running = True
        self.thread = threading.Thread(target=self._update, daemon=True)
        self.thread.start()

    def _update(self):
        while self.running:
            grabbed, frame = self.capture.read()
            if not grabbed:
                time.sleep(0.01)
                continue
            
            with self.read_lock:
                self.frame = frame
                self.grabbed = grabbed

    def read(self):
        with self.read_lock:
            if not self.grabbed:
                return False, None
            # ํ”„๋ ˆ์ž„ ๋ณต์‚ฌ๋ฅผ ์ง„ํ–‰ํ•˜์—ฌ Thread-safe ๋ณด์žฅ
            return True, self.frame.copy()

    def stop(self):
        self.running = False
        if self.thread is not None:
            self.thread.join(timeout=2.0)
            
        if self.capture is not None:
            self.capture.release()
            self.capture = None
            
        # Explicit C-level memory garbage collection
        gc.collect()

# ์‚ฌ์šฉ ์˜ˆ์‹œ
if __name__ == "__main__":
    url = "rtsp://admin:password@192.168.1.100:554/stream1"
    grabber = RTSPFrameGrabber(url)
    grabber.start()

    try:
        while True:
            ret, frame = grabber.read()
            if ret:
                # ๋ฉ”์ธ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋ฐ AI ์ถ”๋ก  ์ˆ˜ํ–‰
                pass
            time.sleep(0.033) # ์•ฝ 30 FPS ์‚ฌ์ดํด
    finally:
        grabber.stop()

4. ์‹ค๋ฌด ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…: ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐ ๋„คํŠธ์›Œํฌ ์—ฃ์ง€ ์ผ€์ด์Šค ๋Œ€์‘

์‹œ์Šคํ…œ์„ 24์‹œ๊ฐ„ 365์ผ ๋ฌด์ธ์œผ๋กœ ์šด์˜ํ•  ๋•Œ๋Š” ์ •์ „, ๋„คํŠธ์›Œํฌ ์ˆœ๊ฐ„ ๋‹จ์„ , ์นด๋ฉ”๋ผ ์žฌ๋ถ€ํŒ… ๋“ฑ ๋‹ค์–‘ํ•œ ์—ฃ์ง€ ์ผ€์ด์Šค(Edge Cases)๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ง€์—ฐ ๋ฐฉ์ง€ ์™ธ์— ์ง€์†์ ์ธ ๋ฉ”๋ชจ๋ฆฌ ์ƒ์Šน์„ ๋ง‰๊ธฐ ์œ„ํ•ด ๋ฐ˜๋“œ์‹œ ๋‹ค์Œ ์‚ฌํ•ญ์„ ์ ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

1) ์ž๋™ ์žฌ์—ฐ๊ฒฐ(Auto Reconnection) ์‹œ ์ž์› ํ•ด์ œ ์ˆœ์„œ ์ค€์ˆ˜

๋„คํŠธ์›Œํฌ๊ฐ€ ๋Š์–ด์กŒ์„ ๋•Œ ๋‹จ์ˆœํžˆ ์ƒˆ cv2.VideoCapture ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋ฉด ์ด์ „ C++ ๊ฐ์ฒด๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ์— ๊ทธ๋Œ€๋กœ ๋‚จ์Šต๋‹ˆ๋‹ค. ์žฌ์—ฐ๊ฒฐ ์‹œ์—๋Š” ๋ฐ˜๋“œ์‹œ ๊ธฐ์กด ์ธ์Šคํ„ด์Šค์˜ release()๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , del capture ํ›„ gc.collect()๋ฅผ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ์— ๋ช…์‹œ์ ์œผ๋กœ ์š”์ฒญํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

2) Python Memory Profiling ๊ธฐ๋ฒ•

ํŒŒ์ดํ”„๋ผ์ธ ์šด์˜ ์ค‘ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ์˜์‹ฌ๋˜๋Š” ๊ฒฝ์šฐ Python ๋‚ด์žฅ ๋ชจ๋“ˆ์ธ tracemalloc ๋ชจ๋“ˆ์„ ๋„์ž…ํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น ์Šค๋ƒ…์ƒท์˜ ์ฐจ์ด์ (Diff)์„ ์ธก์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.


import tracemalloc

tracemalloc.start()
snapshot1 = tracemalloc.take_snapshot()

# ๋‹ค์ค‘ ์ŠคํŠธ๋ฆผ ์ˆ˜์ง‘ ์ˆ˜ํ–‰ ํ›„
snapshot2 = tracemalloc.take_snapshot()
top_stats = snapshot2.compare_to(snapshot1, 'lineno')

print("[ Memory Leak Trace Top 5 ]")
for stat in top_stats[:5]:
    print(stat)

3) TCP vs UDP ์ „์†ก ํ”„๋กœํ† ์ฝœ์˜ ์ฑ„ํƒ ๊ธฐ์ค€

GStreamer rtspsrc ํŒจํ‚ท ์ „์†ก ์‹œ protocols=tcp ํ˜น์€ protocols=udp ์„ค์ •์„ ๋ช…์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • UDP (๊ธฐ๋ณธ๊ฐ’): ํŒจํ‚ท ์†์‹ค ์‹œ ํ•ด๋‹น ํ”„๋ ˆ์ž„์„ ๋ฒ„๋ฆฌ๊ณ  ๋‹ค์Œ ํ”„๋ ˆ์ž„์œผ๋กœ ๋„˜์–ด๊ฐ€๋ฏ€๋กœ ์ง€์—ฐ ์‹œ๊ฐ„์— ๋งค์šฐ ์œ ๋ฆฌํ•˜๋‚˜, ๋„คํŠธ์›Œํฌ ํ’ˆ์งˆ์ด ๋‚˜์˜๋ฉด ํ™”๋ฉด ๊นจ์ง(Artifacts) ํ˜„์ƒ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
  • TCP: ํŒจํ‚ท ์žฌ์ „์†ก์œผ๋กœ ํ™”๋ฉด ๊นจ์ง์€ ์—†์œผ๋‚˜ ํŒจํ‚ท ์—ฐ์‡„ ๋Œ€๊ธฐ๋กœ ์ธํ•ด ์ˆœ๊ฐ„์ ์œผ๋กœ ์ง€์—ฐ์ด ์œ ๋ฐœ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•ˆ์ •์ ์ธ ๋ง์—์„œ๋Š” UDP๋ฅผ, ๋น„๋””์˜ค ๋น„ํŠธ๋ ˆ์ดํŠธ ์†์‹ค์„ ๋ฐฉ์ง€ํ•ด์•ผ ํ•˜๋Š” ํ™˜๊ฒฝ์—์„œ๋Š” TCP + appsink max-buffers=1์˜ ์กฐํ•ฉ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.


5. ๊ฒฐ๋ก  ๋ฐ ์š”์•ฝ

Python๊ณผ OpenCV๋ฅผ ํ™œ์šฉํ•œ ๋‹ค์ค‘ RTSP ์ŠคํŠธ๋ฆฌ๋ฐ ์ˆ˜์ง‘ ์‹œ ํ”„๋ ˆ์ž„ ์ง€์—ฐ ๋ฐ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋Š” ๋‹จ์ˆœํ•œ ์ฝ”๋“œ ์˜ค๋ฅ˜๊ฐ€ ์•„๋‹Œ, ๋ฏธ๋””์–ด ์—”์ง„์˜ ๊ธฐ๋ณธ ๋ฒ„ํผ ๋ฉ”์ปค๋‹ˆ์ฆ˜๊ณผ ๋น„๋™๊ธฐ ์ž์› ๊ด€๋ฆฌ์˜ ๋ฏธ๋น„์—์„œ ์˜ค๋Š” ๊ตฌ์กฐ์ ์ธ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.

๋ณธ ๊ฐ€์ด๋“œ์—์„œ ์ œ์‹œํ•œ ํ•ด๊ฒฐ์ฑ…์„ ์ข…ํ•ฉํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  1. OpenCV์˜ ๊ธฐ๋ณธ ๋ฐฑ์—”๋“œ ๋Œ€์‹  GStreamer ํŒŒ์ดํ”„๋ผ์ธ์„ ๋ช…์‹œ์ ์œผ๋กœ ๊ตฌ์„ฑํ•˜์—ฌ appsink max-buffers=1 drop=true ์˜ต์…˜์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  2. ์ˆ˜์‹  ์Šค๋ ˆ๋“œ์™€ ๋ถ„์„ ์Šค๋ ˆ๋“œ๋ฅผ ์™„์ „ ๋ถ„๋ฆฌํ•˜๋Š” ๋น„๋™๊ธฐ ์Šค๋ ˆ๋“œ ์›Œ์ปค ํŒจํ„ด์„ ๋„์ž…ํ•˜์—ฌ ์ง€์—ฐ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  3. ์žฌ์—ฐ๊ฒฐ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ์‹œ C-Level ์ž์›์˜ ๋ช…์‹œ์  ํ•ด์ œ(release(), gc.collect())๋ฅผ ํ†ตํ•ด 24/7 ์•ˆ์ •์„ฑ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ธฐ๋ฒ•๋“ค์„ ์‹ค๋ฌด ํ˜„์žฅ ํŒŒ์ดํ”„๋ผ์ธ์— ์ ์šฉํ•˜๋ฉด 10ms ๋ฏธ๋งŒ์˜ ์ดˆ์ €์ง€์—ฐ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•จ๊ณผ ๋™์‹œ์—, ์ˆ˜์ผ ์ด์ƒ ์žฅ์‹œ๊ฐ„ ์šด์˜์—๋„ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์ผ์ •ํ•˜๊ฒŒ ์œ ์ง€๋˜๋Š” ๋†’์€ ์‹ ๋ขฐ์„ฑ์˜ ์‹œ๊ฐ ์ง€๋Šฅ ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜•