【问题标题】:Handling errors with gst-rtsp-server Python bindings使用 gst-rtsp-server Python 绑定处理错误
【发布时间】:2020-04-02 05:40:29
【问题描述】:

我有一个简单的 Python 程序使用 gst-rtsp-server 创建一个 RTSP 流。它可以工作,但是没有错误处理。如果管道有错字或连接到视频源时出现问题,我看不到堆栈跟踪或任何日志记录。我会在哪里挂钩代码来处理这样的问题?

我应该提一下,我完全是 GObject 世界的初学者。我怀疑这些库有一种标准的方法来报告错误,但我无法在我读过的文档中找到有关如何完成的任何内容。

如果有帮助,这是我现在拥有的代码:

from threading import Thread
from time import sleep
import signal

import gi
gi.require_version("Gst", "1.0")
gi.require_version("GstRtsp", "1.0")
gi.require_version("GstRtspServer", "1.0")
from gi.repository import GLib, GObject, Gst, GstRtsp, GstRtspServer

PIPELINE = (
    "( videotestsrc ! vp8enc ! rtpvp8pay name=pay0 pt=96 )")


def main():
    GObject.threads_init()
    Gst.init(None)

    server = GstRtspServer.RTSPServer.new()
    server.props.service = "3000"

    server.attach(None)

    loop = GLib.MainLoop.new(None, False)

    def on_sigint(_sig, _frame):
        print("Got a SIGINT, closing...")
        loop.quit()
    signal.signal(signal.SIGINT, on_sigint)

    def run_main_loop():
        loop.run()

    main_loop_thread = Thread(target=run_main_loop)

    main_loop_thread.start()

    media_factory = GstRtspServer.RTSPMediaFactory.new()
    media_factory.set_launch(PIPELINE)
    media_factory.set_shared(True)
    server.get_mount_points().add_factory("/test", media_factory)
    print("Stream ready at rtsp://127.0.0.1:3000/test")


    while loop.is_running():
        sleep(0.1)


if __name__ == "__main__":
    main()

【问题讨论】:

  • 我目前无法访问可以自己测试的环境,但我希望您应该能够访问 gst_parse_launchv () 函数,这应该允许您验证 @ 987654325@ 在你真正去使用它之前。 gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/…
  • 你说得对,我可以调用 Gst.parse_launch 如果管道无效,它会抛出异常。这是朝着正确方向迈出的一步!
  • 似乎期望增加日志详细程度以查看错误,这对开发有所帮助,但不允许我在代码中处理它们。要提高日志级别,请更改 GST_DEBUG 环境变量。此处描述了这些值:gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/…

标签: python gstreamer rtsp pygobject python-gstreamer


【解决方案1】:

因此您可以通过以下方式覆盖Gst.Bin 中的do_handle_message

import gi
gi.require_version("Gst", "1.0")
from gi.repository import Gst, GLib
Gst.init(None)

class SubclassBin(Gst.Bin):
    def do_handle_message(self, message):
        if message.type == Gst.MessageType.ERROR:
            error, message = message.parse_error()
            # TODO: Do something with the error
        # Call the base Gst.Bin do_handle_message
        super().do_handle_message(self, message)

subclass_bin = SubclassBin("mybin")

也就是说,我不确定如何告诉 GstRtspServer.RTSPMediaFactory 使用 SubclassBin 而不是 Gst.Bin,因为据我所知,将任何 Bin 连接到 RTSPMediaFactory 的唯一方法是通过 @ 987654329@ 需要管道字符串而不是预建 bin 的方法。如果有办法在RTSPMediaFactory 中添加一个预建的 bin,这将是一个完整的答案……但不幸的是,这是我所能得到的。

【讨论】:

  • 您可以通过“手动”将元素添加到 SubclassBin 的实例来构建 bin,然后覆盖 RTSPMediaFactory.do_create_element 以返回此 SubclassBin 实例。但是,我还没有找到允许您使用parse_launch 字符串构建管道的解决方案。
  • 对于任何想要继续使用parse_launch 的人,潜在的解决方案可能是this 之一。
【解决方案2】:

编辑:我建议使用 bkanuka 的解决方案(Subclassing Gst.Bin)而不是下面的解决方案,它有很多缺点。

在对 GStreamer 和 RTSP 服务器库进行了一些实验后,错误处理情况变得复杂。

正常方式

在 GStreamer 管道上查看错误的规范方法是将观察程序添加到管道的总线并侦听错误消息。

def watcher(bus, message, *user_data);
    if message.type == Gst.MessageType.ERROR:
        error, message = message.parse_error()
        # TODO: Do something with the error

my_pipeline.get_bus().add_watch(
    GLib.PRIORITY_DEFAULT,
    watcher,
    None)

但是,您不能对提供给 GstRtspServer 的管道执行此操作。这是因为 GstRtspServer 期望能够在管道总线上安装自己的观察程序,并且一次只能将一个观察程序连接到总线。这尤其令人遗憾,因为这会阻止我们监听管道上的任何事件,而不仅仅是错误。

我的解决方法

我们可以将管道分成两部分:一个负责连接到源和解码帧的容易出错的过程,另一个负责对生成的帧进行编码并为 GstRtspServer 加载它们。然后我们可以使用 intervideo 插件在两者之间进行通信。

例如,假设您尝试从 VP8 格式的文件进行流式传输。我们负责读取和解码帧的第一个管道如下所示:

filesrc location="{filepath}" ! decodebin ! intervideosink channel="file-channel"

...我们负责编码和加载帧的第二个管道如下所示:

intervideosrc channel="file-channel" ! videoconvert ! vp8enc deadline=1 ! rtpvp8pay name=pay0 pt=96

这里的关键是只有第二个管道必须由 GstRtspServer 管理,因为它是提供负载数据的管道。第一个由我们管理,我们可以将自己的观察者附加到它 它可以智能地响应错误并执行我们需要的任何其他操作。这当然不是一个完美的解决方案,因为我们无法响应与编码和负载相关的错误,但我们已经获得了接收与读取文件和解码相关的错误的能力。我们现在还能够执行其他与消息相关的任务,例如拦截流结束消息以循环视频文件。

【讨论】:

  • 我在这里的 gst-rtsp-server 存储库上创建了一个问题:gitlab.freedesktop.org/gstreamer/gst-rtsp-server/issues/67
  • 你有继承 GstBin 并覆盖 handle_message() 的例子吗? intervideo 对我不起作用,因为我没有进行任何解码。
  • 不,今天第一次尝试后,我无法得到关于该问题的建议。我在 PyGObject 上打开了一个问题:gitlab.gnome.org/GNOME/pygobject/issues/360。当我到达这个底部时,我会编辑这个答案。
  • 我应该在您打开的 pygobject 错误上留下此评论,但您根本不必致电 new()。只需像使用标准 python 类一样使用该类。
猜你喜欢
  • 2022-02-17
  • 2016-08-14
  • 1970-01-01
  • 2022-08-15
  • 2021-02-21
  • 1970-01-01
  • 2016-08-27
  • 2014-03-09
  • 1970-01-01
相关资源
最近更新 更多