HgDeepStream使用指南(v2.1)

更新时间:
复制为 MD 格式

1. 概述

HgDeepStream是基于Gstreamer开源框架,用来构建AI及多媒体应用的插件工具包。目前已支持编解码、图像格式转换等功能,同时对外提供Python适配和接口。后续会提供更多AI及多媒体相关功能的插件。

2. 安装部署

总共分为4个步骤,分别是Gstreamer依赖安装、Gstreamer安装、HgDeepStream插件安装、运行时环境变量设置。

2.1 项目适配及依赖说明

操作系统

主流GPU接口版本

Python版本

Gstreamer版本

Ubuntu 22.04

12.6

3.10

1.20.3

Ubuntu 24.04

12.8

3.12

1.24.2

Ubuntu 24.04

12.9

3.12

1.24.2

2.2 Gstreamer依赖安装

Ubuntu:

apt-get update &&
apt-get install -y autoconf automake libtool autopoint pkg-config \
                 bison nasm meson python3-pip flex libxml2-dev \
                 libglib2.0-dev libjson-glib-dev \
                 libgirepository1.0-dev \
                 python3-gi python-gi-dev

pip install --upgrade pip==26.0.1
pip3 install --upgrade setuptools==69.5.0
PYGOBJECT_WITHOUT_PYCAIRO=1 pip3 install pygobject==3.44.2 --no-build-isolation

2.3 Gstreamer编译安装

Ubuntu用户可选择源码安装或apt安装

2.3.1 源码编译安装(Ubuntu)

Gstreamer 1.20.3版本为例,用户可根据需求修改版本或增删配置。请根据开发环境【2.1. 项目适配及依赖】编译对应的版本。

git clone -b 1.20.3 --depth 1 https://github.com/GStreamer/gstreamer.git

GENERAL_OPTIONS="-Dbase=enabled \
                    -Dgood=enabled \
                    -Dlibav=disabled \
                    -Dges=disabled \
                    -Drtsp_server=enabled \
                    -Dugly=enabled \
                    -Dgtk_doc=disabled \
                    -Dgst-examples=disabled \
                    -Dtests=disabled \
                    -Dexamples=disabled \
                    -Dgpl=enabled \
                    -Dqt5=disabled \
                    -Dlibnice=disabled \
                    -Dtls=disabled \
                    -Dpython=enabled \
                    "
BASE_OPTIONS="-Dgst-plugins-base:gl=disabled \
                    -Dgst-plugins-base:x11=disabled \
                    -Dgst-plugins-base:examples=disabled \
                    -Dgst-plugins-base:tests=disabled \
                    -Dgst-plugins-base:orc=disabled \
                    -Dgst-plugins-base:ogg=disabled \
                    -Dgst-plugins-base:opus=disabled \
                    -Dgst-plugins-base:vorbis=disabled \
                    "

GOOD_OPTIONS="-Dgst-plugins-good:doc=disabled \
                    -Dgst-plugins-good:examples=disabled \
                    -Dgst-plugins-good:tests=disabled \
                    -Dgst-plugins-good:alpha=disabled \
                    -Dgst-plugins-good:goom=disabled \
                    -Dgst-plugins-good:goom2k1=disabled \
                    -Dgst-plugins-good:oss=disabled \
                    -Dgst-plugins-good:oss4=disabled \
                    -Dgst-plugins-good:v4l2=disabled \
                    -Dgst-plugins-good:cairo=disabled \
                    "

BAD_OPTIONS="-Dgst-plugins-bad:doc=disabled  \
                    -Dgst-plugins-bad:examples=disabled \
                    -Dgst-plugins-bad:tests=disabled \
                    -Dgst-plugins-bad:orc=disabled \
                    -Dgst-plugins-bad:openh264=disabled \
                    -Dgst-plugins-bad:openjpeg=disabled \
                    -Dgst-plugins-bad:openmpt=disabled \
                    -Dgst-plugins-bad:gl=disabled \
                    -Dgst-plugins-bad:x11=disabled \
                    -Dgst-plugins-bad:opencv=disabled \
                    -Dgst-plugins-bad:openal=disabled \
                    -Dgst-plugins-bad:openexr=disabled \
                    -Dgst-plugins-bad:wayland=disabled \
                    -Dgst-plugins-bad:vulkan=disabled \
                    -Dgst-plugins-bad:nvcodec=disabled \
                    -Dgst-plugins-bad:videoparsers=enabled \
                    -Dgst-plugins-bad:ivfparse=enabled \
                    -Dgst-plugins-bad:x265=disabled \
                    "

UGLY_OPTIONS="-Dgst-plugins-ugly:orc=disabled \
                    -Dgst-plugins-ugly:doc=disabled \
                    -Dgst-plugins-ugly:x264=disabled \
                    "

RTSP_SERVER_OPTIONS="-Dgst-rtsp-server:doc=disabled \
                        -Dgst-rtsp-server:examples=enabled \
                        -Dgst-rtsp-server:tests=enabled"

PYTHON_OPTIONS="-Dgst-python:python=python3 \
                        -Dgst-python:plugin=enabled"

meson setup  ${GENERAL_OPTIONS} \
            ${BASE_OPTIONS} \
            ${GOOD_OPTIONS} \
            ${BAD_OPTIONS} \
            ${RTSP_SERVER_OPTIONS} \
            ${UGLY_OPTIONS} \
            ${PYTHON_OPTIONS} \
            build \
            gstreamer

ninja -C build install

2.3.2 apt安装(Ubuntu)

适用于Ubuntu 22.04Ubuntu 24.04。

apt-get update && apt-get install -y  libgstreamer1.0-dev \
                    gstreamer1.0-plugins-good \
                    gstreamer1.0-plugins-bad \
                    gstreamer1.0-plugins-ugly  \
                    gstreamer1.0-tools \
                    libgstreamer-plugins-bad1.0-dev \
                    gstreamer1.0-rtsp \
                    gir1.2-gst-rtsp-server-1.0 \
                    python3-gst-1.0

2.4 HgDeepstream pip安装

通过安装T-Head pip源里的pyhgds安装使用。

2.5 运行时环境变量配置

Ubuntu:

#获取python版本号,3.10或3.12
PY_VERSION=$(python3 -c "import sys; print('.'.join(map(str, sys.version_info[:2])))")

#插件搜索路径
export GST_PLUGIN_PATH=/usr/local/lib/x86_64-linux-gnu/:/usr/local/lib/python${PY_VERSION}/site-packages/pyhgds_extlibs/gst-plugins:$GST_PLUGIN_PATH

export LD_LIBRARY_PATH=/usr/local/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH

#python gir搜索路径
export GI_TYPELIB_PATH=/usr/lib/x86_64-linux-gnu/girepository-1.0:/usr/local/lib/x86_64-linux-gnu/girepository-1.0/:/usr/local/lib/girepository-1.0:$GI_TYPELIB_PATH

3. 发布插件说明

3.1 video硬件解码

名称

功能说明

av1_hgdec

av1 8bit/10bit解码

h264_hgdec

h264 8bit/10bit解码

h265_hgdec

h265 8bit/10bit解码

3.2 video硬件编码

名称

功能说明

h264_hgenc

编码8bit/10bit h264视频

h265_hgenc

编码8bit/10bit h265视频

3.3 内存/显存转换

名称

功能说明

hgcudadownload

device显存搬运到host

hgcudaupload

host内存搬运到device

3.4 使用硬件进行图像变换

名称

功能说明

hgvideoconvert

图像像素格式转换,包括NV12、P016_LE19种图像像素格式

图像旋转,包括clockwise、horizontal-flip6种方式

输入输出图像裁剪

图像缩放

4. Python使用示例

4.1 转码+格式转换

输入一个h264 mp4文件,使用硬件解码后将图像格式转换为P010_10LE,最后使用硬件编码为10bit h265 mp4文件。

import sys
import os

import gi
import argparse
gi.require_version('Gst', '1.0')
gi.require_version('GLib', '2.0')
gi.require_version('GObject', '2.0')
from gi.repository import GLib, GObject, Gst

python_version = f"{sys.version_info.major}.{sys.version_info.minor}"
gst_env_value = f"/usr/local/lib/python{python_version}/site-packages/pyhgds_extlibs/gst-plugins"

if 'GST_PLUGIN_PATH' in os.environ:
    os.environ['GST_PLUGIN_PATH'] = gst_env_value + ':'+ os.environ['GST_PLUGIN_PATH']
else:
    os.environ['GST_PLUGIN_PATH'] = gst_env_value

def bus_call(bus, message, loop):
    t = message.type
    if t == Gst.MessageType.EOS:
        sys.stdout.write("End-of-stream\n")
        loop.quit()
    elif t == Gst.MessageType.ERROR:
        err, debug = message.parse_error()
        sys.stderr.write("Error: %s: %s\n" % (err, debug))
        loop.quit()
    return True

def transcode_main(args):
    # Parse command line arguments
    parser = argparse.ArgumentParser(description='Transcode MP4 file from H.264 to H.265')
    parser.add_argument('-i', '--input', required=True, help='Input MP4 file path')
    parser.add_argument('-o', '--output', required=True, help='Output MP4 file path')
    parsed_args = parser.parse_args(args[1:])

     # Check if input file exists
    if not os.path.exists(parsed_args.input):
        print(f"Error: Input file '{parsed_args.input}' does not exist.")
        return -1

    # Initialize GStreamer
    GObject.threads_init()
    Gst.init(None)

# Create the pipeline
    pipeline = Gst.Pipeline.new('video-transcoder')

    # Create elements
    filesrc = Gst.ElementFactory.make('filesrc', 'file-source')
    qtdemux = Gst.ElementFactory.make('qtdemux', 'qtdemux')
    h264parse = Gst.ElementFactory.make('h264parse', 'h264-parser')
    h264_hgdec = Gst.ElementFactory.make('h264_hgdec', 'h264-decoder')
    hgvideoconvert = Gst.ElementFactory.make('hgvideoconvert', 'video-converter')
    filter1 = Gst.ElementFactory.make("capsfilter", "filter1")
    h265_hgenc = Gst.ElementFactory.make('h265_hgenc', 'h265-encoder')
    h265_parse = Gst.ElementFactory.make('h265parse', 'h265-parse')

    mux = Gst.ElementFactory.make('mp4mux', 'mp4-mux')
    filesink = Gst.ElementFactory.make('filesink', 'file-sink')

    # Check if all elements were created successfully
    if not all([pipeline, filesrc, qtdemux, h264parse, h264_hgdec, hgvideoconvert, h265_hgenc, mux, h265_parse, filesink]):
        sys.stderr.write("Failed to create one or more elements\n")
        return -1

 # Add elements to the pipeline
    pipeline.add(filesrc)
    pipeline.add(qtdemux)
    pipeline.add(h264parse)
    pipeline.add(h264_hgdec)
    pipeline.add(hgvideoconvert)
    pipeline.add(filter1)
    pipeline.add(h265_hgenc)
    pipeline.add(mux)
    pipeline.add(h265_parse)
    pipeline.add(filesink)

    # Set properties
    filesrc.set_property('location', parsed_args.input)
    caps1 = Gst.Caps.from_string('video/x-raw(memory:NVMM), format=P010_10LE')
    filter1.set_property("caps", caps1)

    def on_pad_added(demuxer, pad):
        print("on_pad_added")
        if pad.get_property("template").name_template == "video_%u":
            pad.link(h264parse.get_static_pad("sink"))
        elif pad.get_property("template").name_template == "audio_%u":
            # ignore audio stream
            pass
    qtdemux.connect("pad-added", on_pad_added)

    # Set the output location
    filesink.set_property('location', parsed_args.output)

    filesrc.link(qtdemux)
    h264parse.link(h264_hgdec)
    h264_hgdec.link(hgvideoconvert)
    hgvideoconvert.link(filter1)
    filter1.link(h265_hgenc)
    h265_hgenc.link(h265_parse)
    h265_parse.link(mux)
    mux.link(filesink)

     # Create a GLib loop to handle events
    loop = GLib.MainLoop()

    # Add a message handler to the bus
    bus = pipeline.get_bus()
    bus.add_signal_watch()
    bus.connect("message", bus_call, loop)

     # Start playing the pipeline
    ret = pipeline.set_state(Gst.State.PLAYING)
    if ret == Gst.StateChangeReturn.FAILURE:
        sys.stderr.write("Unable to set the pipeline to the playing state.\n")
        return -1

 # Wait until error or EOS
    try:
        loop.run()
    except KeyboardInterrupt:
        pass
 # Clean up
    pipeline.set_state(Gst.State.NULL)

    # Print output file location
    print(f"Transcoding completed. Output file saved to: {os.path.abspath(parsed_args.output)}")

if __name__ == '__main__':
    sys.exit(transcode_main(sys.argv))

使用命令python3 transcode_mp4.py -i input.mp4 -o output.mp4

5. gst-launch-1.0使用示例

在执行【2.5 运行环境变量配置】后,也可以使用gst-launch-1.0命令加载插件。

插件参数可使用gst-inspect-1.0查看,例如:gst-inspect-1.0 hgvideoconvert

5.1. 转码+格式转换

h264 mp4文件解码,解码后的图像进行裁切并顺时针旋转90°,并转为P010_10LE数据格式,最后编码为10bit h265 mp4文件。

gst-launch-1.0 filesrc location=input.mp4 ! qtdemux ! h264parse ! h264_hgdec gpu-id=0 ! queue ! hgvideoconvert src-crop=100:100:1280:720 dest-crop=100:100:640:360 flip-method=clockwise  ! 'video/x-raw(memory:NVMM), format=P010_10LE, width=1920, height=1080' ! queue ! h265_hgenc gpu-id=0 ! h265parse !  mp4mux ! filesink location=output.mp4

6. Q&A

Q1:安装pygobject 3.44.2报错

检查pipsetuptools的版本。

alios python3.10建议使用setuptools 59.8.0,其余建议使用setuptools 69.5.0。

Q2:报错 ModuleNotFoundError: No module named 'gi'

请确保已正确安装pygobject:

PYGOBJECT_WITHOUT_PYCAIRO=1 pip3 install pygobject==3.44.2 --no-build-isolation

如果还有报错,请检查环境变量LD_LIBRARY_PATHPYTHONPATH是否正确设置。

Q3:报错 No such element or plugin 'hgvideoconvert', 或者其它hg plugin找不到

请检查环境变量GST_PLUGIN_PATH是否正确设置。

如果插件在环境变量的路径中,可以使用gst-inspect-1.0 -b检查是否在黑名单中。如果在黑名单中,可根据以下步骤排查解决错误:

  • 1、GST_DEBUG=2 gst-inspect-1.0 xxx.so 查看具体报错。

  • 2、如果是依赖库没有加载,查看其路径是否在环境变量LD_LIBRARY_PATH中。

  • 3、清理插件缓存 rm ~/.cache/gstreamer-1.0/registry.x86_64.bin

Q4:Gstreamer编译时可以使用本地缓存的第三方安装包吗?

Gstreamer在编译时,如果发现依赖项不存在或者版本不满足的情况下,会从网络下载第三方安装包。在无法直接从网络下载的情况下,可以将本地的安装包放在缓存目录gstreamer/subprojects/packagecache/中,编译时会直接使用。

7. 附录

7.1 链接

GStreamer官方文档

7.2 x11图像显示配置

本地有X server的情况下,可以使用GStreamer官方插件ximagesink显示图像。

操作步骤如下:

1、在执行【2.3 Gstreamer编译】时,打开x11配置-Dgst-plugins-base:x11=enabled

2、配置环境变量DISPLAY。

3、将图像格式转为BGRx,并且将显存数据搬运到host后,传送给ximagesink插件。

使用命令举例:

gst-launch-1.0 filesrc location=input.mp4 ! qtdemux ! h264parse ! h264_hgdec gpu-id=0 ! hgvideoconvert flip-method=clockwise ! 'video/x-raw(memory:NVMM), format=BGRx, width=1920, height=1080' ! hgcudadownload ! ximagesink

7.3 rtsp推拉流

GStreamer提供rtsp工具,方便开发者搭建rtsp server。需要在执行【2.3 Gstreamer编译】时打开编译选项-Drtsp_server=enabled

7.3.1 rtsp server示例

#!/usr/bin/python3
"""
The server will stream test video at rtsp://127.0.0.1:8554/test
"""

import sys
import gi
import os

gi.require_version('Gst', '1.0')
gi.require_version('GLib', '2.0')
gi.require_version('GObject', '2.0')

try:
    gi.require_version('GstRtspServer', '1.0')
except ValueError:
    sys.stderr.write("GstRtspServer 1.0 not found. Install gstreamer1.0-rtsp-server.\n")
    sys.exit(1)

from gi.repository import GLib, GObject, Gst, GstRtspServer

python_version = f"{sys.version_info.major}.{sys.version_info.minor}"
gst_env_value = f"/usr/local/lib/python{python_version}/site-packages/pyhgds_extlibs/gst-plugins"

if 'GST_PLUGIN_PATH' in os.environ:
    os.environ['GST_PLUGIN_PATH'] = gst_env_value + ':'+ os.environ['GST_PLUGIN_PATH']
else:
    os.environ['GST_PLUGIN_PATH'] = gst_env_value

def main():
    Gst.init(None)

    loop = GLib.MainLoop()
    server = GstRtspServer.RTSPServer.new()
    mounts = server.get_mount_points()
    factory = GstRtspServer.RTSPMediaFactory.new()
    factory.set_launch(
        "( videotestsrc is-live=1 ! video/x-raw, format=NV12, width=1280, height=720 ! "
        "hgcudaupload ! h264_hgenc ! rtph264pay name=pay0 pt=96 )"
    )

    factory.set_shared(True)

    mounts.add_factory("/test", factory)
    mounts = None

    server.attach(None)

    print("stream ready at rtsp://127.0.0.1:8554/test")

    try:
        loop.run()
    except KeyboardInterrupt:
        print("Interrupted by user")

    loop = None
    server = None

    return 0

if __name__ == '__main__':
    sys.exit(main())

7.3.2 rtsp拉流示例

gst-launch-1.0 rtspsrc location=rtsp://127.0.0.1:8554/test ! queue ! rtph264depay ! h264parse ! h264_hgdec ! hgvideoconvert ! ximagesink

7.4 glib升级

# e.g. 2.78.3
git clone -b 2.78.3 --depth 1 https://github.com/GNOME/glib.git
cd glib
meson build --prefix=/usr
ninja -C build install

7.5 FFmpeg相关插件使用说明

Gstreamer提供FFmpeg相关插件,需要在执行【2.3 Gstreamer编译】时打开编译选项-Dlibav=enabled。同时在编译时需要配置环境变量export PKG_CONFIG_PATH=/xxx/ffmpeg/lib/pkgconfig:$PKG_CONFIG_PATH

参考【Video FFmpeg使用指南】,在使用FFmpeg版本6.1.2 编译 Gstreamer1.20.3时需要如下patch:

diff --git a/subprojects/gst-libav/ext/libav/gstavviddec.c b/subprojects/gst-libav/ext/libav/gstavviddec.c
index b957d22..cc38398
--- a/subprojects/gst-libav/ext/libav/gstavviddec.c
+++ b/subprojects/gst-libav/ext/libav/gstavviddec.c
@@ -567,7 +567,7 @@ gst_ffmpegviddec_set_format (GstVideoDecoder * decoder,
   if (ffmpegdec->max_threads == 0) {
     /* When thread type is FF_THREAD_FRAME, extra latency is introduced equal
      * to one frame per thread. We thus need to calculate the thread count ourselves */
-    if ((!(oclass->in_plugin->capabilities & AV_CODEC_CAP_AUTO_THREADS)) ||
+    if ((!(oclass->in_plugin->capabilities & AV_CODEC_CAP_OTHER_THREADS)) ||
         (ffmpegdec->context->thread_type & FF_THREAD_FRAME))
       ffmpegdec->context->thread_count =
           MIN (gst_ffmpeg_auto_max_threads (), 16);