Skip to content

Commit

Permalink
Merge pull request #758 from roflcoopter/feature/gstreamer-fix
Browse files Browse the repository at this point in the history
fix gstreamer hanging and support vaapi
  • Loading branch information
roflcoopter authored May 21, 2024
2 parents 217f1c4 + 6b40112 commit 8a85f89
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 16 deletions.
1 change: 1 addition & 0 deletions requirements_test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ pyupgrade==3.15.2
# types
types-requests
types-python-slugify
pygobject-stubs
6 changes: 6 additions & 0 deletions rootfs/etc/cont-init.d/40-set-env-vars
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ fi
export XDG_RUNTIME_DIR=/home/abc
printf "/home/abc" > /var/run/environment/XDG_RUNTIME_DIR

# Set XDG_CURRENT_DESKTOP to avoid GStreamer hanging
# Context: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3487
export XDG_CURRENT_DESKTOP=ubuntu:GNOME
printf "ubuntu:GNOME" > /var/run/environment/XDG_CURRENT_DESKTOP


# Set HOME to /home/abc
export HOME=/home/abc
printf "/home/abc" > /var/run/environment/HOME
Expand Down
14 changes: 11 additions & 3 deletions viseron/components/gstreamer/const.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""GStreamer constants."""
# pyright: reportMissingModuleSource=false
from __future__ import annotations

import logging
Expand All @@ -17,10 +18,10 @@
# pylint: disable=useless-suppression
# pylint: disable=wrong-import-position,wrong-import-order,no-name-in-module
gi.require_version("Gst", "1.0")
from gi.repository import Gst # pyright: ignore[reportMissingImports] # noqa: E402
from gi.repository import Gst # noqa: E402

# pylint: enable=wrong-import-position,wrong-import-order,no-name-in-module
# pylint: enable=useless-suppression
# enable: disable=useless-suppression
# enable: disable=wrong-import-position,wrong-import-order,no-name-in-module


class StreamFormat(TypedDict):
Expand Down Expand Up @@ -71,6 +72,13 @@ class StreamFormat(TypedDict):
"mjpeg": "jpegdec",
}

VAAPI_DECODER_ELEMENT_MAP = {
"h264": "vaapih264dec",
"h265": "vaapih265dec",
"hevc": "vaapih265dec",
"mjpeg": "vaapimjpegdec",
}

PIXEL_FORMAT = "NV12"

CONFIG_LOGLEVEL_TO_GSTREAMER = {
Expand Down
13 changes: 11 additions & 2 deletions viseron/components/gstreamer/pipeline.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
"""GStreamer pipelines."""
from __future__ import annotations

import os
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING

from viseron.components.ffmpeg.const import CAMERA_SEGMENT_DURATION
from viseron.const import ENV_VAAPI_SUPPORTED

from .const import (
CONFIG_AUDIO_CODEC,
Expand All @@ -21,6 +23,7 @@
PARSE_ELEMENT_MAP,
PIXEL_FORMAT,
STREAM_FORMAT_MAP,
VAAPI_DECODER_ELEMENT_MAP,
)

if TYPE_CHECKING:
Expand Down Expand Up @@ -62,7 +65,6 @@ def input_pipeline(self):
+ [f"location={self._stream.mainstream.url}"]
+ [
"name=input_stream",
"do-timestamp=true",
]
+ STREAM_FORMAT_MAP[self._config[CONFIG_STREAM_FORMAT]]["options"]
+ (
Expand Down Expand Up @@ -132,7 +134,14 @@ def decoder_element(self):
Returns decoder element from override map if it exists.
Otherwise we assume the decoder element shares name with the codec.
"""
decoder_element = DECODER_ELEMENT_MAP.get(self._stream.mainstream.codec, None)
if os.getenv(ENV_VAAPI_SUPPORTED) == "true":
decoder_element = VAAPI_DECODER_ELEMENT_MAP.get(
self._stream.mainstream.codec, None
)
else:
decoder_element = DECODER_ELEMENT_MAP.get(
self._stream.mainstream.codec, None
)

if decoder_element:
return [
Expand Down
22 changes: 11 additions & 11 deletions viseron/components/gstreamer/stream.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Class to interact with a GStreamer stream."""
# pyright: reportMissingModuleSource=false
from __future__ import annotations

import datetime
Expand Down Expand Up @@ -42,15 +43,10 @@
# pylint: disable=wrong-import-position,wrong-import-order,no-name-in-module
gi.require_version("Gst", "1.0")
gi.require_version("GstApp", "1.0")
from gi.repository import ( # pyright: ignore[reportMissingImports] # noqa: E402
GLib,
Gst,
GstApp,
)
from gi.repository import GLib, Gst, GstApp # type: ignore[attr-defined] # noqa: E402

_ = GstApp
# pylint: enable=wrong-import-position,wrong-import-order,no-name-in-module
# pylint: enable=useless-suppression
# pylint: enable=wrong-import-position,wrong-import-order,no-name-in-module


class Stream(FFmpegStream):
Expand Down Expand Up @@ -151,7 +147,11 @@ def on_new_sample(self, app_sink: GstApp.AppSink) -> Gst.FlowReturn:
self._logger.debug("Did not get sample from appsink")
return Gst.FlowReturn.ERROR

buffer: Gst.Buffer = sample.get_buffer()
buffer = sample.get_buffer()
if not buffer:
self._logger.debug("Could not get buffer from sample")
return Gst.FlowReturn.ERROR

success, map_info = buffer.map(Gst.MapFlags.READ)
if not success:
self._logger.debug("Could not map buffer data")
Expand Down Expand Up @@ -206,14 +206,14 @@ def run_gstreamer(self, process_frames_proc_exit: EventClass) -> None:
Gst.debug_add_log_function(self.on_gst_log_message, None)

gst_pipeline = Gst.parse_launch(" ".join(self._pipeline.build_pipeline()))
appsink = gst_pipeline.get_by_name(
appsink = gst_pipeline.get_by_name( # type: ignore[attr-defined]
"sink",
)
gst_pipeline.set_state(Gst.State.PLAYING)
appsink.connect("new-sample", self.on_new_sample)
mux = gst_pipeline.get_by_name("mux")
mux = gst_pipeline.get_by_name("mux") # type: ignore[attr-defined]
mux.connect("format-location", self.on_format_location, None)

gst_pipeline.set_state(Gst.State.PLAYING)
while not process_frames_proc_exit.is_set():
time.sleep(1)

Expand Down

0 comments on commit 8a85f89

Please sign in to comment.