【发布时间】:2021-05-07 20:33:36
【问题描述】:
我在设置 GStreamer 管道以通过 OpenCV 通过 UDP 转发视频流时遇到问题。我有一台笔记本电脑和一台连接到同一网络的 AGX Xavier。这个想法是将网络摄像头视频馈送转发到 AGX,它将在 GPU(在 Python 中)上进行一些 OpenCV 光流估计,在原始图像上绘制流向量并将其发送回我的笔记本电脑。到目前为止,我可以配置两个管道。举个简单的例子,我制作了两个 bash 脚本和一个 Python 脚本,它们在理想情况下可以作为 OpenCV 的 VideoCapture 和 VideoWriter 对象的传递。
servevideo.bash:
#!/bin/bash
gst-launch-1.0 v4l2src device=[device-fd] \
! video/x-raw, width=800, height=600, framerate=24/1 \
! jpegenc ! rtpjpegpay ! rtpstreampay \
! udpsink host=[destination-ip] port=12345
receivevideo.bash:
#!/bin/bash
gst-launch-1.0 -e udpsrc port=12344 \
! application/x-rtp-stream,encoding-name=JPEG \
! rtpstreamdepay ! rtpjpegdepay ! jpegdec \
! autovideosink
如果我在同一台计算机或网络上的两台不同计算机上运行这两个脚本,它工作正常。当我将 Python 脚本(如下所列)混在一起时,我开始遇到问题。理想情况下,我会在我的笔记本电脑上运行 bash 脚本,同时在我的 Jetson 上运行 Python 脚本时考虑到预期的设置。然后,在绕着 Jetson 绕了一圈后,我希望在我的笔记本电脑上看到网络摄像头视频源。
webcam_passthrough.py:
#!/usr/bin/python3.6
import cv2
video_in = cv2.VideoCapture("udpsrc port=12345 ! application/x-rtp-stream,encoding-name=JPEG ! rtpstreamdepay ! rtpjpegdepay ! jpegdec ! videoconvert ! appsink", cv2.CAP_GSTREAMER)
video_out = cv2.VideoWriter("appsrc ! videoconvert ! jpegenc ! rtpjpegpay ! rtpstreampay ! udpsink host=[destination-ip] port=12344", cv2.CAP_GSTREAMER, 0, 24, (800, 600), True)
while True:
ret, frame = video_in.read()
if not ret: break
video_out.write(frame)
cv2.imshow('Original', frame)
key = cv2.waitKey(1) & 0xff
if key == 27: break
cv2.destroyAllWindows()
video_out.release()
video_in.release()
使用以下 Python 脚本,我可以通过cv2.imshow 可视化从servevideo.bash 脚本设置的管道接收到的帧。所以我认为我的问题与我在 OpenCV 中设置 VideoWriter video_out 的方式有关。当我在创建的这两个管道之间中继网络摄像头视频源时,我已经验证了我的两个 bash 脚本正在工作,并且我已经验证了 cv2.VideoCapture 接收帧。我不是这里的专家,我的 GStreamer 知识几乎不存在,所以在我的最小示例中可能存在一些误解。如果你们中的一些人能指出我在这里遗漏的内容,将不胜感激。
如果有不清楚或遗漏的地方,我也很乐意提供更多信息。
编辑: 因此,我的最小示例的意图似乎没有明确传达。
作为最小示例提供的三个脚本用于将我的网络摄像头视频馈送从我的笔记本电脑中继到 Jetson AGX Xavier,Jetson AGX Xavier 然后将视频馈送回笔记本电脑。 servevideo.bash 在笔记本电脑上创建一个 GStreamer 管道,该管道使用 v4l2 从摄像头抓取帧并将其中继到 UDP 套接字。 webcam_passthrough.py 在 Jetson 上运行,它“连接”到笔记本电脑上运行的管道创建的 UDP 套接字。 Python 脚本提供了一个透传功能,理想情况下将在另一个端口上打开一个新的 UDP 套接字并将帧中继回笔记本电脑。 receivevideo.bash 在笔记本电脑上创建另一个管道,用于接收通过 Jetson 的 Python 脚本传递的帧。笔记本电脑上的第二条管道仅用于可视化目的。理想情况下,这个最小示例显示来自连接到笔记本电脑的摄像头的“原始”视频。
这两个 bash 脚本独立运行,既在笔记本电脑上本地运行,又在另一台计算机上远程运行 receivevideo.bash。
Python 脚本中的cv2.VideoCapture 配置似乎也可以工作,因为我可以可视化通过servevideo.bash 脚本提供的UDP 套接字接收的帧(带有cv2.imshow)。这也适用于本地和远程。让我头疼的部分(我相信)是cv2.VideoWriter的配置;理想情况下,这应该打开一个 UDP 套接字,我可以通过我的 receivevideo.bash 脚本“连接”到它。我已经在本地和远程对此进行了测试,但无济于事。
当我运行 receivevideo.bash 连接到 Python 脚本提供的 UDP 套接字时,我得到以下输出:
Setting pipeline to PAUSED ...
Pipeline is live and does not need PREROLL ...
Setting pipeline to PLAYING ...
New clock: GstSystemClock
这对我来说似乎没有错,我尝试使用 GST_DEBUG=3 运行不同的脚本,这给出了一些警告,但由于 bash 脚本和 cv2 VideoCapture 和 @ 中的管道配置基本相同987654340@我没有为这些警告增加太多价值。例如,我在下面包含了一个这样的警告:
0:00:06.595120595 8962 0x25b8cf0 WARN rtpjpegpay gstrtpjpegpay.c:596:gst_rtp_jpeg_pay_read_sof:<rtpjpegpay0> warning: Invalid component
使用GST_DEBUG=3 连续运行 Python 脚本会打印此警告。以相同的调试级别运行 receivevideo.bash 给出:
Setting pipeline to PAUSED ...
Pipeline is live and does not need PREROLL ...
0:00:00.013911480 9078 0x55be0899de80 FIXME videodecoder gstvideodecoder.c:933:gst_video_decoder_drain_out:<jpegdec0> Sub-class should implement drain()
Setting pipeline to PLAYING ...
New clock: GstSystemClock
我希望我的意图现在更清楚,正如我已经指出的那样,我认为我在 Python 脚本中的 cv2.VideoWriter 有问题,但我不是专家,而且 GStreamer 远非我每天都使用的东西。因此,我可能误解了一些东西。
编辑 2: 所以现在我尝试按照@abysslover 的建议将两个管道分成两个单独的进程。我仍然看到相同的结果,但我仍然不知道为什么会这样。下面列出了我当前的 Python 脚本实现。
webcam_passthrough.py:
#!/usr/bin/python3.6
import signal, cv2
from multiprocessing import Process, Pipe
is_running = True
def signal_handler(sig, frame):
global is_running
print("Program was interrupted - terminating ...")
is_running = False
def produce(pipe):
global is_running
video_in = cv2.VideoCapture("udpsrc port=12345 ! application/x-rtp-stream,encoding-name=JPEG ! rtpstreamdepay ! rtpjpegdepay ! jpegdec ! videoconvert ! appsink", cv2.CAP_GSTREAMER)
while is_running:
ret, frame = video_in.read()
if not ret: break
print("Receiving frame ...")
pipe.send(frame)
video_in.release()
if __name__ == "__main__":
consumer_pipe, producer_pipe = Pipe()
signal.signal(signal.SIGINT, signal_handler)
producer = Process(target=produce, args=(producer_pipe,))
video_out = cv2.VideoWriter("appsrc ! videoconvert ! jpegenc ! rtpjpegpay ! rtpstreampay ! udpsink host=[destination-ip] port=12344", cv2.CAP_GSTREAMER, 0, 24, (800, 600), True)
producer.start()
while is_running:
frame = consumer_pipe.recv()
video_out.write(frame)
print("Sending frame ...")
video_out.release()
producer.join()
我在两个进程之间创建的管道按预期提供了一个新框架。当我尝试使用netcat 监听 UDP 端口 12344 时,我没有收到任何与以前相同的内容。我也很难理解管道的差异化是如何变化的,因为我希望它们已经在不同的环境中运行。不过,关于这个假设,我可能是错的。
【问题讨论】:
标签: python bash opencv gstreamer