6
votes

We're developing a software to stream videos from two different cameras with RTSP using GStreamer. To simplify the acquisition process, we're using OpenCV with Python 3.

The problem is: we wanna push the stream to UDP sink, republish it over our LAN as a RTSP Stream and than read it in another PC. But we can't get this to work.

This is the Python code that gets the camera images and starts the streaming with udpsink. In this case we're accessing our local webcam so that the code may be straightforwardly tested by anyone.

import cv2
import time
from multiprocessing import Process

def send():
    video_writer = cv2.VideoWriter(
        'appsrc ! '
        'videoconvert ! '
        'x264enc tune=zerolatency speed-preset=superfast ! '
        'rtph264pay ! '
        'udpsink host=127.0.0.1 port=5000',
        cv2.CAP_GSTREAMER, 0, 1, (640, 480), True)

    video_getter = cv2.VideoCapture(0)

    while True:

        if video_getter.isOpened():
            ret, frame = video_getter.read()
            video_writer.write(frame)
            # cv2.imshow('send', data_to_stream)
            time.sleep(0.1)   

if __name__ == '__main__':
    s = Process(target=send)
    s.start()
    s.join()
    cv2.destroyAllWindows()

When running this, we only get one warning:

[ WARN:0] global ../modules/videoio/src/cap_gstreamer.cpp (935) open OpenCV | GStreamer warning: Cannot query video position: status=0, value=-1, duration=-1

Then we try to republish the stream over our LAN as RTSP using the examples/test-launch from GStreamer

./test-launch " udpsrc port=5000 ! h264parse ! rtph264pay name=pay0 pt=96"

This gives us no errors but the default message

stream ready at rtsp://127.0.0.1:8554/test

And then VLC fails to open the stream at this address.

                     ~$ vlc -v rtsp://127.0.0.1:8554/test
VLC media player 3.0.11 Vetinari (revision 3.0.11-0-gdc0c5ced72)
[000055f5dacf3b10] main libvlc: Running vlc with the default interface. Use 'cvlc' to use vlc without interface.
Qt: Session management error: None of the authentication protocols specified are supported
[00007f5ea40010f0] live555 demux error: Failed to connect with rtsp://127.0.0.1:8554/test
[00007f5ea4003120] satip stream error: Failed to setup RTSP session

I guess it's something with our pipelines, but I really don't get what it could be. Any help will be appreciated. Thanks in advance.

2

2 Answers

1
votes

you can remove the rtph264pay in VideoWriter, then the python script send h264 data, and test-launch receive h264 data, and do rtp packetize, and then rtsp.

so the VideoWriter should be:

video_writer = cv2.VideoWriter(
    'appsrc ! '
    'videoconvert ! '
    'x264enc tune=zerolatency speed-preset=superfast ! '
    'udpsink host=127.0.0.1 port=5000',
    cv2.CAP_GSTREAMER, 0, 1, (640, 480), True)
0
votes

I've noticed that most questions related to this topic ended up been answered by their own author, like these two

How to Stream PC Webcam with RTSP and Gstreamer on Python

Write OpenCV frames into Gstreamer RTSP server pipeline

It actually shows how specific these questions can be, and that's why I decided to share here what I've been coming up with. It's not the perfect solution right now, but it's "kind of" working.

I'd like to thanks kqmh00 for the support, since it gave me some insights.

First, I stepped back and tried a simpler example using videotestsrc outside Python. After a few tries, these were the pipelines that worked

Sender

gst-launch-1.0 videotestsrc is-live=true ! video/x-raw ! videoconvert ! x264enc tune=zerolatency speed-preset=superfast ! udpsink host=127.0.0.1 port=30000

Intercessor

./test-launch "udpsrc port=30000 ! h264parse ! rtph264pay name=pay0 pt=96"

Receiver

vlc -v rtsp://127.0.0.1:8854/test

Ok. It only prooved that it's possible to send a video by UDP without the rtph264pay, which is received by test-launch that resends it with RTP packaging as RTSP.

Next, I tried Python again. After searching for similar questions I found this one that served as inspiration to the actual pipeline I'm using, which is:

 pipeline = (
        "appsrc format=GST_FORMAT_TIME ! "
        "video/x-raw,format=BGR,width=1280,height=720 ! "
        "videoconvert ! "
        "video/x-raw,format=I420 ! "
        "x264enc tune=zerolatency speed-preset=superfast byte-stream=true threads=2 ! "
        "video/x-h264, stream-format=(string)byte-stream ! "
        "mpegtsmux alignment=7 ! "
        "udpsink host=127.0.0.1 port=30000"
    )

The rest of the Python code is the same, except for the video_writer, that was updated with the correct frame width and height:

   video_writer = cv2.VideoWriter(
        pipeline,
        cv2.CAP_GSTREAMER, 0, 1, (1280, 720), True)

The other pipelines (Intercessor and Receiver) remain the same. With this I'm able to retrieve some pretty monocromatic images (they are entirely gray or green) , but at least I can retrieve something.

VLC throws lots of errors, such as

[h264 @ 0x7ff8e0001da0] Invalid NAL unit 0, skipping.
[h264 @ 0x7ff8e0001da0] top block unavailable for requested intra mode -1
[h264 @ 0x7ff8e0001da0] error while decoding MB 1 0, bytestream 112420

Probably it's some setting missing or some property that must be explicitly set. I'll keep digging, but feel free to add here any possible improvements.