Skip to content

Instantly share code, notes, and snippets.

@velovix
Last active June 15, 2021 03:28
Show Gist options
  • Save velovix/b6021e72737228944b784978b97d0584 to your computer and use it in GitHub Desktop.
Save velovix/b6021e72737228944b784978b97d0584 to your computer and use it in GitHub Desktop.
Reproduces a segment seeking bug and memory leak in nvcodec
FROM nvidia/cuda:11.2.1-devel-ubuntu18.04 AS gstreamer
# Adds NVCODEC libraries to the environment for use in building NVDEC support
ENV NVIDIA_DRIVER_CAPABILITIES=all
ARG FFMPEG_VERSION=n4.3.1
WORKDIR /ffmpeg
# Get FFmpeg dependencies
RUN apt-get update && \
apt-get -y --no-install-recommends install \
autoconf automake build-essential libtool libxcb1-dev \
libxcb-shm0-dev libxcb-xfixes0-dev texinfo zlib1g-dev yasm git \
ca-certificates \
&& \
rm -rf /var/lib/apt/lists/*
RUN update-ca-certificates && \
git clone --depth=1 --branch=$FFMPEG_VERSION https://github.com/FFmpeg/FFmpeg .
# Build
RUN ./configure \
--enable-shared \
--extra-libs="-lpthread -lm" \
--disable-static
RUN make -j $(($(nproc) + 1))
RUN make install
# Get GStreamer build dependencies
RUN apt-get update && \
apt-get -y --no-install-recommends install \
# Generic build dependencies
pkg-config autopoint libglib2.0-dev gettext wget \
ca-certificates git g++ yasm ninja-build flex bison \
libgirepository1.0-dev gobject-introspection \
# gst-plugins-base dependencies
libogg0 libopus0 libtheora0 libvorbis0a libvorbisenc2 \
# gst-plugins-good dependencies
libv4l-dev libjpeg-dev libmp3lame-dev libmpg123-dev \
libtag1-dev libvpx-dev nettle-dev \
# gst-libav dependencies
libavcodec-dev libavdevice-dev libavfilter-dev libavformat-dev libavresample-dev libavutil-dev \
# gst-plugins-bad dependencies
libsoup2.4-dev \
# Pip, so we can install Meson
python3-pip python3-setuptools \
# For JIT acceleration
liborc-0.4-dev \
&& \
rm -rf /var/lib/apt/lists/*
RUN pip3 install --no-cache-dir meson==0.55.3
ARG GSTREAMER_VERSION=1.19.1
# Build GStreamer core
WORKDIR /gstreamer
RUN update-ca-certificates && \
git clone --depth 1 --branch $GSTREAMER_VERSION https://gitlab.freedesktop.org/gstreamer/gstreamer
WORKDIR /gstreamer/gstreamer
RUN meson builddir
RUN ninja -C builddir install
# Build base GStreamer plugins
WORKDIR /gstreamer
RUN update-ca-certificates && \
git clone --depth 1 --branch $GSTREAMER_VERSION https://gitlab.freedesktop.org/gstreamer/gst-plugins-base
WORKDIR /gstreamer/gst-plugins-base
RUN meson builddir
RUN ninja -C builddir install
# Build good GStreamer plugins
WORKDIR /gstreamer
RUN update-ca-certificates && \
git clone --depth 1 --branch $GSTREAMER_VERSION https://gitlab.freedesktop.org/gstreamer/gst-plugins-good
WORKDIR /gstreamer/gst-plugins-good
RUN meson builddir
RUN ninja -C builddir install
# Build only a few "bad" GStreamer plugins
WORKDIR /gstreamer
RUN update-ca-certificates && \
git clone --depth 1 --branch $GSTREAMER_VERSION https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad
WORKDIR /gstreamer/gst-plugins-bad
RUN meson builddir
RUN meson configure \
# Disable all plugins by default
-Dauto_features=disabled \
# h264parse, required for NVDEC to work
-Dvideoparsers=enabled \
# NVDEC hardware decoding
-Dnvcodec=enabled \
builddir
RUN ninja -C builddir install
# Build GStreamer libav support
WORKDIR /gstreamer
RUN update-ca-certificates && \
git clone --depth 1 --branch $GSTREAMER_VERSION https://gitlab.freedesktop.org/gstreamer/gst-libav
WORKDIR /gstreamer/gst-libav
RUN meson builddir
RUN ninja -C builddir install
WORKDIR /project
RUN apt-get update && \
apt-get -y --no-install-recommends install \
python3-dev python3-wheel libcairo-dev libgirepository1.0-dev \
&& \
rm -rf /var/lib/apt/lists/*
RUN pip3 install pygobject
ENV GI_TYPELIB_PATH="/usr/local/lib/x86_64-linux-gnu/girepository-1.0"
ENV GST_DEBUG=3
COPY reproducer.py .
COPY test_video.mp4 .
CMD [ "python3", "/project/reproducer.py", "/project/test_video.mp4" ]
from threading import Thread
from time import sleep
import sys
import gi
gi.require_version("Gst", "1.0")
gi.require_version("GstApp", "1.0")
from gi.repository import Gst, GLib, GstApp
def main():
if len(sys.argv) < 2:
print("Usage: python3 reproducer.py {video file}")
sys.exit(1)
Gst.init()
main_loop = GLib.MainLoop.new(context=None, is_running=False)
main_loop_thread = Thread(
target=main_loop.run, name="GObjectInitThread", daemon=True
)
main_loop_thread.start()
pipeline = Gst.parse_launch(
f"filesrc location={sys.argv[1]} "
"! qtdemux "
"! h264parse "
"! nvh264dec "
"! fakesink "
)
segment_seeking_on = False
def watch(_bus, message):
nonlocal segment_seeking_on
if message.type == Gst.MessageType.STATE_CHANGED:
_, new_state, _ = message.parse_state_changed()
if (
message.src == pipeline
and new_state == Gst.State.PLAYING
and not segment_seeking_on
):
print("Enabling segment seeking")
_segment_seek_to_start(pipeline, flush=True)
segment_seeking_on = True
elif message.type == Gst.MessageType.SEGMENT_DONE:
print("Seeking back to the start of the video")
_segment_seek_to_start(pipeline, flush=False)
return True
pipeline.get_bus().add_watch(GLib.PRIORITY_DEFAULT, watch)
pipeline.set_state(Gst.State.PLAYING)
print("Requested the PLAYING state")
try:
while True:
sleep(0.1)
except KeyboardInterrupt:
pass
print("Done")
def _segment_seek_to_start(pipeline, flush):
"""Seeks the given pipeline to the start of the video stream. This should
be done as a response to a SEGMENT_DONE event.
"""
flags = Gst.SeekFlags.SEGMENT
if flush:
flags |= Gst.SeekFlags.FLUSH
seek_result = pipeline.seek(
rate=1.0,
format=Gst.Format.TIME,
flags=flags,
start_type=Gst.SeekType.SET,
start=0,
stop_type=Gst.SeekType.NONE,
stop=-1,
)
if not seek_result:
raise RuntimeError(f"Failed to send seek event")
if __name__ == "__main__":
main()
_ = GstApp
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment