vidgear: [Bug]: Memory Leakage with YouTube video

Hi! These days I came across this issue, where it appears that the memory usage keeps increasing as I stream a video. In this example, it increases even more, but in a microserver setting, like launching this script in a REST-API server, there is a steady but small increase over time.

In this scenario, I run the script with docker;

# main.py

from memory_profiler import profile
from vidgear.gears import VideoGear
from typing import Callable
import logging
import time
import cv2


fp = open("/workspace/memory_profiler.log","w+")


class VideoStream:

    def __init__(
        self,
        stream_source: str,
        stream_resolution: str = "sd",
        stream_framerate: int = 4,
        stream_max_frames: int = float("inf"),
        stream_backend: int = cv2.CAP_GSTREAMER,
        func: Callable = None,
        reconnect_frequency: int = 3600,
        *args, **kwargs
    ):

        # stream params
        self.stream_source = stream_source
        self.stream_resolution = stream_resolution
        self.stream_framerate = stream_framerate
        self.stream_max_frames = stream_max_frames \
            if stream_max_frames not in [-1, None] else float("inf")
        self.stream_backend = stream_backend
        self.reconnect_frequency = reconnect_frequency

        self.stream = self.init_stream()
        self.func = func

        # utils
        self.n_frame = 0
        self.last_reconnection = time.time()
        # For YouTube video, its better to reduced the amount of requests
        self.stream_sleep = 0.05 if self.is_stream_mode(self.stream_source) else 0

    def get_stream_info(self):
        info = {
            "height": int(self.stream.stream.get(cv2.CAP_PROP_FRAME_HEIGHT)),
            "width": int(self.stream.stream.get(cv2.CAP_PROP_FRAME_WIDTH)),
            "total": int(self.stream.stream.get(cv2.CAP_PROP_FRAME_COUNT)),
            "fps": self.stream.stream.get(cv2.CAP_PROP_FPS),
        }
        return info

    @staticmethod
    def is_stream_mode(source):
        return False if str(source).isdigit() else True

    def init_stream(self):

        stream_mode = self.is_stream_mode(self.stream_source)

        if stream_mode:
            options = {
                "STREAM_RESOLUTION": "best",
                "STREAM_PARAMS": {"nocheckcertificate": True},
            }

        else:
            options = {"CAP_PROP_FPS": 30}

        stream = VideoGear(
            source=self.stream_source,
            backend=self.stream_backend,
            stream_mode=stream_mode,
            logging=False,
            **options
        )
        logging.info(f"Stream with source '{self.stream_source}' initialized.")
        return stream

    def start_stream(self):
        self.stream.start()
        logging.info(f"Stream with source '{self.stream_source}' started.")

    def stop_stream(self):
        self.stream.stop()
        logging.info(f"Stream with source '{self.stream_source}' stopped.")

    def stop(self):
        self.stop_stream()

    def fetch_frame(self):
        return self.stream.read()

    def generator(self):
        self.start_stream()

        total_frames = 0
        while True:
            frame = self.fetch_frame()

            if frame is None or self.n_frame >= self.stream_max_frames:
                break

            if total_frames % (30 // self.stream_framerate) == 0:
                inputs = {
                    "id": self.n_frame,
                    "image": cv2.imencode(".jpg", frame)[1].tobytes(),
                    "source": str(self.stream_source),
                    "timestamp": time.time()
                }
                yield self.func(**inputs) if self.func else inputs
                self.n_frame += 1

            if self.stream_sleep:
                time.sleep(self.stream_sleep)
            total_frames += 1

        self.stop_stream()

    def __iter__(self):
        yield from self.generator()


@profile(stream=fp)
def main():

    stream = {
        "name": "live1",
        "stream_source": "http://youtube.com/watch?v=y7QiNgui5Tg",
        "stream_framerate": 4,
        "stream_resolution": "best",
        "stream_max_frames": 100
    }

    videostreamer = VideoStream(**stream)

    for frame in videostreamer:
        print(frame["id"], end='\r')


if __name__ == "__main__":
    main()
# Dockerfile

FROM python:3.8-slim

# utils -------------------------------------------
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV OPENCV_VERSION="4.5.1"
ENV TZ Europe/Amsterdam
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# init virtualenv ---------------------------------
ENV VIRTUAL_ENV=/opt/venv
RUN python3 -m venv $VIRTUAL_ENV
ENV PATH="$VIRTUAL_ENV/bin:$PATH"

# install OpenCV ----------------------------------
WORKDIR /opt/build
RUN apt-get -qq update \
    && apt-get -qq install -y --no-install-recommends \
        build-essential \
        cmake \
        git \
        wget \
        unzip \
        yasm \
        pkg-config \
        libswscale-dev \
        libtbb2 \
        libtbb-dev \
        libjpeg-dev \
        libpng-dev \
        libtiff-dev \
        libopenjp2-7-dev \
        libavformat-dev \
        libpq-dev \
        libgstreamer1.0-0 \
        ffmpeg \
        gstreamer1.0-plugins-base \
        gstreamer1.0-plugins-good \
        gstreamer1.0-plugins-bad \
        gstreamer1.0-plugins-ugly \
        gstreamer1.0-libav \
        gstreamer1.0-tools \
        libgstreamer1.0-dev \
        libgstreamer-plugins-base1.0-dev \
        protobuf-compiler \
        libgtk2.0-dev \
        ocl-icd-opencl-dev \
    && pip install numpy \
    && wget -q https://github.com/opencv/opencv/archive/${OPENCV_VERSION}.zip -O opencv.zip \
    && unzip -qq opencv.zip -d /opt \
    && rm -rf opencv.zip \
    && cmake \
        -D BUILD_TIFF=ON \
        -D BUILD_opencv_python2=OFF \
        -D BUILD_opencv_java=OFF \
        -D CMAKE_BUILD_TYPE=RELEASE \
        -D WITH_CUDA=OFF \
        -D WITH_OPENGL=ON \
        -D WITH_OPENCL=ON \
        -D WITH_TBB=ON \
        -D WITH_EIGEN=ON \
        -D WITH_V4L=ON \
        -D BUILD_TESTS=OFF \
        -D BUILD_PERF_TESTS=OFF \
        -D CMAKE_BUILD_TYPE=RELEASE \
        -D CMAKE_INSTALL_PREFIX=$(python -c "import sys; print(sys.prefix)") \
        -D PYTHON3_EXECUTABLE=$(which python3) \
        -D PYTHON3_INCLUDE_DIR=$(python -c "from distutils.sysconfig import get_python_inc; print(get_python_inc())") \
        -D PYTHON3_PACKAGES_PATH=$(python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())") \
        -D WITH_GSTREAMER=ON \
        -D WITH_FFMPEG=ON \
        /opt/opencv-${OPENCV_VERSION} \
    && make -j$(nproc) \
    && make install \
    && rm -rf /opt/build/* \
    && rm -rf /opt/opencv-${OPENCV_VERSION} \
    && rm -rf /var/lib/apt/lists/* \
    && apt-get -qq autoremove \
    && apt-get -qq clean

# install pip packages ----------------------------
RUN pip3 install --upgrade pip \
    && pip3 install --no-cache-dir \
        vidgear[core]>=0.2.4

RUN pip3 install memory_profiler
# run.sh
docker build . -t venv:latest
docker run \
    -v $PWD:/workspace \
    venv:latest python3 /workspace/main.py

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 24 (14 by maintainers)

Most upvoted comments

@abhiTronix I think thats a good solution, I’ll take a look at it! Thank you!

@ioangatop Good news is that I’m working on new library that will eliminate use of threading and will produce high performance frames with FFmpeg. Follow issue #148