So I suppose you don’t already have a RTSP server running. You may try the following example. Note that this requires that you have installed gst rtsp server package (such as sudo apt install libgstrtspserver-1.0-dev
with Ubuntu), and that your opencv build has gstreamer support (the python code below checks that):
C++ version
#include <iostream>
#include <thread>
#include <gst/gst.h>
#include <gst/rtsp-server/rtsp-server.h>
#include <opencv2/opencv.hpp>
#include <opencv2/videoio.hpp>
int main() {
/********************************************************
* First create a RTSP server that would read RTP/H264
* from localhost UDP port 5004 and stream as RTSP to
* rtsp://127.0.0.1:8554/test
********************************************************/
gst_init(NULL, NULL);
GMainLoop *serverloop = g_main_loop_new(NULL, FALSE);
GstRTSPServer *server = gst_rtsp_server_new();
GstRTSPMountPoints *mounts = gst_rtsp_server_get_mount_points(server);
GstRTSPMediaFactory *factory = gst_rtsp_media_factory_new();
gst_rtsp_media_factory_set_launch(factory, "( udpsrc port=5004 ! application/x-rtp,encoding-name=H264 ! rtph264depay ! h264parse ! rtph264pay name=pay0 )");
gst_rtsp_mount_points_add_factory(mounts, "/test", factory);
gst_rtsp_server_attach(server, NULL);
std::thread serverloopthread(g_main_loop_run, serverloop);
std::cout << "stream ready at rtsp://127.0.0.1:8554/test" << std::endl;
/********************************************************
* Now RTSP server is running in its own thread, let's
* create an opencv application reading from the
* camera,encoding into H264 and sending as RTP/H264
* to localhost UDP/5004
********************************************************/
//cv::VideoCapture camera("videotestsrc is-live=1 ! video/x-raw,format=BGR,width=640,height=480,framerate=30/1 ! queue ! appsink drop=1", cv::CAP_GSTREAMER);
cv::VideoCapture camera(0);
if (!camera.isOpened()) {
std::cerr << "Failed to open camera. Exiting" << std::endl;
g_main_loop_quit(serverloop);
serverloopthread.join();
return -1;
}
float fps = camera.get(cv::CAP_PROP_FPS);
int w = camera.get(cv::CAP_PROP_FRAME_WIDTH);
int h = camera.get(cv::CAP_PROP_FRAME_HEIGHT);
if (w%4) {
std::cerr << "Width is not a multiple of 4. Unsupported" << std::endl;
g_main_loop_quit(serverloop);
serverloopthread.join();
return(-1);
}
if (h%2) {
std::cerr << "Height is not a multiple of 2. Unsupported" << std::endl;
g_main_loop_quit(serverloop);
serverloopthread.join();
return(-1);
}
cv::VideoWriter rtph264_writer("appsrc ! queue ! videoconvert ! video/x-raw,format=I420 ! x264enc key-int-max=30 insert-vui=1 tune=zerolatency ! h264parse ! rtph264pay ! udpsink host=127.0.0.1 port=5004", cv::CAP_GSTREAMER, 0, fps, cv::Size(w, h));
if (!rtph264_writer.isOpened()) {
std::cerr << "Failed to open writer. Exiting" << std::endl;
g_main_loop_quit(serverloop);
serverloopthread.join();
return -1;
}
while (true) {
cv::Mat frame;
camera >> frame;
if (frame.empty()) {
break;
}
rtph264_writer.write(frame);
}
rtph264_writer.release();
camera.release();
return 0;
}
Built with:
gcc -Wall -o test test.cpp $(pkg-config --cflags --libs gstreamer-1.0) $(pkg-config --cflags --libs gstreamer-rtsp-server-1.0) $(pkg-config --cflags --libs opencv4) -lstdc++
python version:
#######################################################
# First create a RTSP server that would read RTP/H264
# from localhost UDP port 5004 and stream as RTSP to
# rtsp://127.0.0.1:8554/test
#######################################################
import gi
gi.require_version('Gst','1.0')
gi.require_version('GstVideo','1.0')
gi.require_version('GstRtspServer','1.0')
from gi.repository import GLib, Gst, GstVideo, GstRtspServer
from threading import Thread
Gst.init(None)
serverloop = GLib.MainLoop()
server = GstRtspServer.RTSPServer()
mounts = server.get_mount_points()
factory = GstRtspServer.RTSPMediaFactory()
factory.set_launch('( udpsrc port=5004 ! application/x-rtp,encoding-name=H264 ! rtph264depay ! h264parse ! rtph264pay name=pay0 )')
mounts.add_factory("/test", factory)
server.attach(None)
server_loop_thread = Thread(target=serverloop.run)
print("stream ready at rtsp://127.0.0.1:8554/test")
server_loop_thread.start()
######################################################
# Now RTSP server is running in its own thread, let's
# create an opencv application reading from the
# camera,encoding into H264 and sending as RTP/H264
# to localhost UDP/5004
######################################################
import cv2
import re
print('GStreamer support: %s' % re.search(r'GStreamer\:\s+(.*)', cv2.getBuildInformation()).group(1))
print('FFMPEG support: %s' % re.search(r'FFMPEG\:\s+(.*)', cv2.getBuildInformation()).group(1))
print('V4L/V4L2 support: %s' % re.search(r'v4l/v4l2\:\s+(.*)', cv2.getBuildInformation()).group(1))
# Initialize the camera
#camera = cv2.VideoCapture('videotestsrc is-live=1 ! video/x-raw,format=BGR,width=640,height=480,framerate=30/1 ! queue ! appsink drop=1', cv2.CAP_GSTREAMER)
camera = cv2.VideoCapture(0)
if not camera.isOpened():
print('Failed to open camera. Exiting');
serverloop.quit()
quit()
fps = float(camera.get(cv2.CAP_PROP_FPS))
w = int(camera.get(cv2.CAP_PROP_FRAME_WIDTH))
h = int(camera.get(cv2.CAP_PROP_FRAME_HEIGHT))
size = (w, h)
print(f"Camera opened: framing {(w)}x{(h)} @{fps} fps")
if w%4:
print("Width is not a multiple of 4. Unsupported")
serverloop.quit()
quit()
if h%2:
print("Height is not a multiple of 2. Unsupported")
serverloop.quit()
quit()
# Create a VideoWriter to localhost port 5004 as RTP/H264
rtph264_writer = cv2.VideoWriter('appsrc ! queue ! videoconvert ! video/x-raw,format=I420 ! x264enc key-int-max=30 insert-vui=1 tune=zerolatency ! queue ! h264parse ! rtph264pay ! udpsink host=127.0.0.1 port=5004', cv2.CAP_GSTREAMER, 0, fps, (w,h))
if not rtph264_writer.isOpened():
print('Failed to open writer. Exiting');
serverloop.quit()
quit()
print('Writer opened, streaming RTP/H264 to localhost:5004')
# Forever loop
while True:
# Read a frame from the camera
ret, frame = camera.read()
if not ret:
break
# Write the frame to the RTP stream
rtph264_writer.write(frame)
# Clean up
rtph264_writer.release()
camera.release()
Don’t worry about the opencv warning about duration and position.
When your app is running, from another terminal you would test with:
gst-play-1.0 rtsp://127.0.0.1:8554/test
# Or for better latency:
gst-launch-1.0 playbin uri=rtsp://127.0.0.1:8554/test uridecodebin0::source::latency=0