Stream frame opencv via rtsp using gstreamer c++

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