RTSP Streaming and video saving

Hello Everyone,

i have a query regarding the streaming and saving the video simultaneously.
My current code starts recording when streaming starts, but I want to make it independent. It should start recording the stream when the “start” command is received over a nano message and stop vice versa. i cant make two separate pipelines for both streaming and recording as it may increase the overhead on the device (two times encoding). any suggestion on how to start/stop recording regardless of the streaming activity.

#include <gst/gst.h>
#include <gst/rtsp-server/rtsp-server.h>
#include <nng/nng.h>
#include <nng/protocol/pair0/pair.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>

#define RTSP_PORT "8554"
#define RTSP_MOUNT_POINT "/stream"
#define RECORDING_FILE "/home/video_p.mkv"
#define NNG_SOCKET "ipc:///tmp/pipeline-control"

static gboolean recording = FALSE;
static GstElement *pipeline = NULL;
static GstElement *filesink = NULL;

static void start_recording() {
    if (recording || !filesink) return;
    g_print("Starting recording...\n");
    g_object_set(filesink, "location", RECORDING_FILE, NULL);
    recording = TRUE;
}

static void stop_recording() {
    if (!recording || !filesink) return;
    g_print("Stopping recording...\n");
    g_object_set(filesink, "location", "/dev/null", NULL);
    recording = FALSE;
}

static void *nng_listener_thread(void *arg) {
    nng_socket sock;
    nng_msg *msg;
    int rv;

    rv = nng_pair0_open(&sock);
    if (rv != 0) {
        fprintf(stderr, "Error opening NNG socket: %s\n", nng_strerror(rv));
        return NULL;
    }

    if ((rv = nng_listen(sock, NNG_SOCKET, NULL, 0)) != 0) {
        fprintf(stderr, "Error binding NNG IPC socket: %s\n", nng_strerror(rv));
        nng_close(sock);
        return NULL;
    }

    g_print("NNG Server Listening on %s\n", NNG_SOCKET);

    while (1) {
        rv = nng_recvmsg(sock, &msg, 0);
        if (rv != 0) {
            fprintf(stderr, "Error receiving NNG message: %s\n", nng_strerror(rv));
            continue;
        }

        char *message = (char *)nng_msg_body(msg);
        if (strcmp(message, "start") == 0) {
            start_recording();
        } else if (strcmp(message, "stop") == 0) {
            stop_recording();
        } else {
            g_print("Unknown command: %s\n", message);
        }

        nng_msg_free(msg);
    }

    nng_close(sock);
    return NULL;
}

static void media_configure(GstRTSPMediaFactory *factory, GstRTSPMedia *media, gpointer user_data) {
    pipeline = gst_rtsp_media_get_element(media);
    filesink = gst_bin_get_by_name(GST_BIN(pipeline), "fsink");
    g_print("Pipeline initialized!\n");
    gst_object_unref(pipeline);
}

static void start_rtsp_server(void) {
    GMainLoop *loop;
    GstRTSPServer *server;
    GstRTSPMountPoints *mounts;
    GstRTSPMediaFactory *factory;

    gst_init(NULL, NULL);
    loop = g_main_loop_new(NULL, FALSE);

    server = gst_rtsp_server_new();
    gst_rtsp_server_set_service(server, RTSP_PORT);

    mounts = gst_rtsp_server_get_mount_points(server);
    factory = gst_rtsp_media_factory_new();

gst_rtsp_media_factory_set_launch(factory,
    "( v4l2src ! video/x-raw,format=NV12,width=960,height=720,framerate=30/1 ! "
    "tee name=t "
    "t. ! queue ! videoconvert ! mpph265enc ! h265parse ! rtph265pay pt=96 name=pay0 config-interval=1 "
    "t. ! queue ! videoconvert ! mpph265enc ! h265parse ! mux. "
    "alsasrc ! queue ! audio/x-raw,rate=44100,channels=2 ! audioconvert ! voaacenc bitrate=320000 ! aacparse ! tee name=audiotee "
    "audiotee. ! queue ! rtpmp4apay pt=97 name=pay1 "
    "audiotee. ! queue ! mux. "
    "matroskamux name=mux ! filesink location=" RECORDING_FILE " sync=false )");

    gst_rtsp_media_factory_set_shared(factory, TRUE);
    g_signal_connect(factory, "media-configure", (GCallback)media_configure, NULL);

    gst_rtsp_mount_points_add_factory(mounts, RTSP_MOUNT_POINT, factory);
    g_object_unref(mounts);

    gst_rtsp_server_attach(server, NULL);

    g_print("RTSP Stream available at rtsp://<RK3576_IP>:%s%s\n", RTSP_PORT, RTSP_MOUNT_POINT);
    g_print("Recording to file: %s\n", RECORDING_FILE);

    g_main_loop_run(loop);
}

int main(int argc, char *argv[]) {
    pthread_t nng_thread;
    if (pthread_create(&nng_thread, NULL, nng_listener_thread, NULL) != 0) {
        fprintf(stderr, "Failed to create NNG listener thread.\n");
        return 1;
    }

    start_rtsp_server();
    pthread_join(nng_thread, NULL);
    return 0;
}

Regards.

You can add a filter element ( or Pass through).
The filter element should have one sink pad and two src pads.
One src pad will be connected to sink and other is connected to filesrc to dump your recording content.

By default when you start the playback, the content will be dropped and won’t be dumped. Make application to trigger a custom event (START_RECORDING). when the filter element receives this it starts dumping the data to file. when the STOP_RECORDING event is sent, it stops dumping…

1 Like

Hi,
Thank you for your response.
i can save and stream the video.
currently, I’m integrating the audio in the recording file but somehow I m still not able to do it.

below is the my pipeline

gst_rtsp_media_factory_set_launch(factory,
    "( "
    "v4l2src ! video/x-raw,format=NV12,width=960,height=720,framerate=30/1 ! "
    "mpph265enc ! h265parse ! tee name=video_tee "
    "video_tee. ! queue ! rtph265pay pt=96 name=pay0 config-interval=1 "
    "video_tee. ! queue ! queue ! rec_sink. "
    
    "alsasrc device=hw:0 ! audio/x-raw,rate=44100,channels=2 ! "
    "audioconvert ! audioresample ! voaacenc bitrate=320000 ! aacparse ! tee name=audio_tee "
    "audio_tee. ! queue ! rtpmp4apay pt=97 name=pay1 "
    "audio_tee. ! queue ! queue ! rec_sink. "

    "splitmuxsink name=rec_sink location=" RECORDING_FILE " muxer=matroskamux max-size-time=30000000000 "
    ")"
);

GST_DEBUG=3 ./avstream_savemkv

(avstream_savemkv:2633): GStreamer-CRITICAL **: 06:06:03.839: gst_object_unref: assertion ‘((GObject *) object)->ref_count > 0’ failed

0:00:01.635575975 2633 0x55a90f6460 WARN mpp gstmpp.c:121:gst_mpp_use_rga: RGA enabled

0:00:01.656796495 2633 0x55a90f6460 ERROR GST_PIPELINE

gst/parse/grammar.y:1088:gst_parse_perform_link: could not link queue5 to rec_sink

0:00:01.656874083 2633 0x55a90f6460 WARN rtspmediafactory rtsp-media-factory.c:1806:default_create_element: recoverable parsing error: could not link queue5 to rec_sink

Error: Failed to find splitmuxsink or valve in pipeline