我正在尝试使用 OpenCV 在 Python 中设置 RTSP 流。我找到了this repository,我以此作为例子开始。我很确定那里的代码不完全是那个人写的,因为我发现了一些具有相同代码的旧源。 但是,我以此为基础来熟悉一切。
这是我尝试运行的完整(略有改动的)Python 脚本:
import gi
import cv2 as cv
import argparse
gi.require_versions({
# 'Gtk': '3.0',
'Gst': '1.0',
'GstRtspServer': '1.0',
'GLib': '2.0'
})
from gi.repository import Gst, GstRtspServer, GLib
class StreamFactory(GstRtspServer.RTSPMediaFactory):
def __init__(self, **properties):
super(StreamFactory, self).__init__(**properties)
self.cap = cv.VideoCapture(0) # 0 is the default camera
self.number_frames = 0
self.fps = opt.fps
self.duration = 1 / self.fps * Gst.SECOND # duration of a frame in nanoseconds
self.launch_string = 'appsrc name=source is-live=true block=true format=GST_FORMAT_TIME ' \
'caps=video/x-raw,format=BGR,width={},height={},framerate={}/1 ' \
'! videoconvert ! video/x-raw,format=I420 ' \
'! x264enc speed-preset=ultrafast tune=zerolatency ' \
'! rtph264pay config-interval=1 name=pay0 pt=96' \
.format(opt.image_width, opt.image_height, self.fps)
def on_need_data(self, src, length):
if self.cap.isOpened():
ret, frame = self.cap.read()
if ret:
frame = cv.resize(frame, (640, 480))
data = frame.tostring()
buf = Gst.Buffer.new_allocate(None, len(data), None)
buf.fill(0, data)
timestamp = self.number_frames * self.duration
buf.pts = int(timestamp)
buf.dts = int(timestamp)
buf.offset = timestamp
self.number_frames += 1
retval = src.emit('push-buffer', buf)
if retval != Gst.FlowReturn.OK:
print(retval)
def do_create_element(self, url):
return Gst.parse_launch(self.launch_string)
def do_configure(self, rtsp_media):
self.number_frames = 0
appsrc = rtsp_media.get_element().get_child_by_name('source')
appsrc.connect('need-data', self.on_need_data)
class GstServer(GstRtspServer.RTSPServer):
def __init__(self, **properties):
super(GstServer, self).__init__(**properties)
self.factory = StreamFactory()
self.factory.set_shared(True)
self.get_mount_points().add_factory(opt.stream_uri, self.factory)
self.attach(None)
parser = argparse.ArgumentParser()
parser.add_argument("--device_id", required=True, help="device id for the video device or video file location")
parser.add_argument("--fps", required=True, help="fps of the camera", type = int)
parser.add_argument("--image_width", required=True, help="video frame width", type = int)
parser.add_argument("--image_height", required=True, help="video frame height", type = int)
parser.add_argument("--port", default=8554, help="port to stream video", type = int)
parser.add_argument("--stream_uri", default = "/video_stream", help="rtsp video stream uri")
opt = parser.parse_args()
# GObject.threads_init()
Gst.init(None)
server = GstServer()
loop = GLib.MainLoop()
loop.run()
运行这个的结果是启动了,但是我一连接上服务器就抛出如下异常:
目前我只能使用M1 Mac,所以我自己无法确认,但根据我发现的一些问题(比如这个),可能与架构相关,虽然我对此并不完全肯定,因为我也看到了this issue,看起来它确实已经过时并且不相关,因为如果我运行
gst-launch-1.0 videotestsrc ! videoconvert ! autovideosink
它工作得很好,不像那个问题中所说的那样。
我通过 HomeBrew 安装了
gstreamer
+ 所有依赖项,但我也验证了我在没有 HomeBrew 的情况下从头开始构建相同的结果。
到目前为止我用自制软件安装的所有库(我将在此处发布完整列表,但请记住并非所有依赖项都与此问题相关):
这也是我的 Python 依赖列表:
✖ pip3 freeze
docutils==0.19
numpy==1.24.2
opencv-python==4.7.0.72
pycairo==1.23.0
PyGObject==3.42.2
six==1.16.0
在python版本上:
3.9.16.final.0
,也尝试了最新的3.11
版本。
OSX 版本:13.0.1 (22A400)
我还使用
appsrc
命令验证了 gst-inspect
插件可用于 gst,它看起来是:
✖ gst-inspect-1.0 appsrc
Factory Details:
Rank none (0)
Long-name AppSrc
Klass Generic/Source
Description Allow the application to feed buffers to a pipeline
Author David Schleef <[email protected]>, Wim Taymans <[email protected]>
Documentation https://gstreamer.freedesktop.org/documentation/app/appsrc.html
Plugin Details:
Name app
Description Elements used to communicate with applications
Filename /opt/homebrew/lib/gstreamer-1.0/libgstapp.dylib
Version 1.22.0
License LGPL
Source module gst-plugins-base
Documentation https://gstreamer.freedesktop.org/documentation/app/
Source release date 2023-01-23
Binary package GStreamer Base Plug-ins source release
Origin URL Unknown package origin
GObject
+----GInitiallyUnowned
+----GstObject
+----GstElement
+----GstBaseSrc
+----GstAppSrc
Implemented Interfaces:
GstURIHandler
Pad Templates:
SRC template: 'src'
Availability: Always
Capabilities:
ANY
Element has no clocking capabilities.
URI handling capabilities:
Element can act as source.
Supported URI protocols:
appsrc
Pads:
SRC: 'src'
Pad Template: 'src'
Element Properties:
block : Block push-buffer when max-bytes are queued
flags: readable, writable
Boolean. Default: false
blocksize : Size in bytes to read per buffer (-1 = default)
flags: readable, writable
Unsigned Integer. Range: 0 - 4294967295 Default: 4096
caps : The allowed caps for the src pad
flags: readable, writable
Caps (NULL)
current-level-buffers: The number of currently queued buffers
flags: readable
Unsigned Integer64. Range: 0 - 18446744073709551615 Default: 0
current-level-bytes : The number of currently queued bytes
flags: readable
Unsigned Integer64. Range: 0 - 18446744073709551615 Default: 0
current-level-time : The amount of currently queued time
flags: readable
Unsigned Integer64. Range: 0 - 18446744073709551615 Default: 0
do-timestamp : Apply current stream time to buffers
flags: readable, writable
Boolean. Default: false
duration : The duration of the data stream in nanoseconds (GST_CLOCK_TIME_NONE if unknown)
flags: readable, writable
Unsigned Integer64. Range: 0 - 18446744073709551615 Default: 18446744073709551615
emit-signals : Emit need-data, enough-data and seek-data signals
flags: readable, writable
Boolean. Default: true
format : The format of the segment events and seek
flags: readable, writable
Enum "GstFormat" Default: 2, "bytes"
(0): undefined - GST_FORMAT_UNDEFINED
(1): default - GST_FORMAT_DEFAULT
(2): bytes - GST_FORMAT_BYTES
(3): time - GST_FORMAT_TIME
(4): buffers - GST_FORMAT_BUFFERS
(5): percent - GST_FORMAT_PERCENT
handle-segment-change: Whether to detect and handle changed time format GstSegment in GstSample. User should set valid GstSegment in GstSample. Must set format property as "time" to enable this property
flags: readable, writable, changeable only in NULL or READY state
Boolean. Default: false
is-live : Whether to act as a live source
flags: readable, writable
Boolean. Default: false
leaky-type : Whether to drop buffers once the internal queue is full
flags: readable, writable, changeable only in NULL or READY state
Enum "GstAppLeakyType" Default: 0, "none"
(0): none - GST_APP_LEAKY_TYPE_NONE
(1): upstream - GST_APP_LEAKY_TYPE_UPSTREAM
(2): downstream - GST_APP_LEAKY_TYPE_DOWNSTREAM
max-buffers : The maximum number of buffers to queue internally (0 = unlimited)
flags: readable, writable
Unsigned Integer64. Range: 0 - 18446744073709551615 Default: 0
max-bytes : The maximum number of bytes to queue internally (0 = unlimited)
flags: readable, writable
Unsigned Integer64. Range: 0 - 18446744073709551615 Default: 200000
max-latency : The maximum latency (-1 = unlimited)
flags: readable, writable
Integer64. Range: -1 - 9223372036854775807 Default: -1
max-time : The maximum amount of time to queue internally (0 = unlimited)
flags: readable, writable
Unsigned Integer64. Range: 0 - 18446744073709551615 Default: 0
min-latency : The minimum latency (-1 = default)
flags: readable, writable
Integer64. Range: -1 - 9223372036854775807 Default: -1
min-percent : Emit need-data when queued bytes drops below this percent of max-bytes
flags: readable, writable
Unsigned Integer. Range: 0 - 100 Default: 0
name : The name of the object
flags: readable, writable
String. Default: "appsrc0"
num-buffers : Number of buffers to output before sending EOS (-1 = unlimited)
flags: readable, writable
Integer. Range: -1 - 2147483647 Default: -1
parent : The parent of the object
flags: readable, writable
Object of type "GstObject"
size : The size of the data stream in bytes (-1 if unknown)
flags: readable, writable
Integer64. Range: -1 - 9223372036854775807 Default: -1
stream-type : the type of the stream
flags: readable, writable
Enum "GstAppStreamType" Default: 0, "stream"
(0): stream - GST_APP_STREAM_TYPE_STREAM
(1): seekable - GST_APP_STREAM_TYPE_SEEKABLE
(2): random-access - GST_APP_STREAM_TYPE_RANDOM_ACCESS
typefind : Run typefind before negotiating (deprecated, non-functional)
flags: readable, writable, deprecated
Boolean. Default: false
Element Signals:
"need-data" : void user_function (GstElement* object,
guint arg0,
gpointer user_data);
"enough-data" : void user_function (GstElement* object,
gpointer user_data);
"seek-data" : gboolean user_function (GstElement* object,
guint64 arg0,
gpointer user_data);
Element Actions:
"push-buffer" : GstFlowReturn user_function (GstElement* object,
GstBuffer* arg0);
"push-buffer-list" : GstFlowReturn user_function (GstElement* object,
GstBufferList* arg0);
"push-sample" : GstFlowReturn user_function (GstElement* object,
GstSample* arg0);
"end-of-stream" : GstFlowReturn user_function (GstElement* object);
我尝试的最后一件事是直接从 CLI 运行命令而不是通过 Python 调用它,看起来它运行时没有崩溃:
❯ gst-launch-1.0 appsrc name=source is-live=true block=true format=GST_FORMAT_TIME caps=video/x-raw,format=BGR,width=1920,height=1080,framerate=30/1 ! videoconvert ! video/x-raw,format=I420 ! x264enc speed-preset=ultrafast tune=zerolatency ! rtph264pay config-interval=1 name=pay0 pt=96
Setting pipeline to PAUSED ...
Pipeline is live and does not need PREROLL ...
Pipeline is PREROLLED ...
Setting pipeline to PLAYING ...
New clock: GstSystemClock
Redistribute latency...
^Chandling interrupt.
Interrupt: Stopping pipeline ...
Execution ended after 0:00:46.577237000
Setting pipeline to NULL ...
Freeing pipeline ...
有没有人解决了这个问题或者在这方面取得了更多进展?我真的不知道还有什么地方可以打开与此相关的问题,因为我从中获得示例脚本的原始回购所有者似乎不知道代码实际发生了什么。在自制软件回购中打开另一个问题似乎也是错误的,因为它看起来不像是自制软件问题(不再),并且当前问题已关闭且无法发表评论。 GStreamer 回购也不允许打开问题。
有机会时,我将尝试验证它是否发生在 intel mac 和/或 x86_64 ubuntu 系统上,但与此同时,欢迎所有指点:)