H265 Streaming and Recording

I am developing a live streaming and recording application on an RK3576 Rockchip board using an IMX415 camera. The system is designed to start and stop recording based on messages received from a client via NanoMQ, while RTSP streaming is initiated upon client request.

However, an issue arises when the device is simultaneously recording and streaming. Specifically, if recording is in progress and a client requests an RTSP stream, the device eventually freezes. I am trying to determine whether the root cause lies in the GStreamer pipeline design or the overall implementation. Could you provide insights into potential bottlenecks or misconfiguration?

#include <gst/gst.h>
#include <gst/rtsp-server/rtsp-server.h>
#include <pthread.h>
#include <signal.h>
#include <nng/nng.h>
#include <nng/protocol/pubsub0/sub.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <gst/app/gstappsink.h>
#include <gst/app/gstappsrc.h>

static GMainLoop *loop;
static GstElement *capture_pipeline, *record_pipeline, *record_src;
static GstElement *stream_src;
static gboolean recording = FALSE;
static gchar *recording_filename = NULL;

// Signal handler for graceful shutdown
static void handle_sigint(int signum) {
    g_print("\nCtrl+C detected. Stopping...\n");
    if (capture_pipeline) {
        gst_element_set_state(capture_pipeline, GST_STATE_NULL);
        gst_object_unref(capture_pipeline);
    }
    if (record_pipeline) {
        gst_element_set_state(record_pipeline, GST_STATE_NULL);
        gst_object_unref(record_pipeline);
    }
    if (stream_src) {
        gst_element_set_state(stream_src, GST_STATE_NULL);
        gst_object_unref(stream_src);
    }
    if (loop) g_main_loop_quit(loop);
}

// Callback for appsink new-sample signal
static GstFlowReturn new_sample_callback(GstElement *appsink, gpointer user_data) {
    GstSample *sample = gst_app_sink_pull_sample(GST_APP_SINK(appsink));
    if (!sample) return GST_FLOW_ERROR;

    if (stream_src) {
        gst_app_src_push_sample(GST_APP_SRC(stream_src), sample);
    }

    if (recording && record_src) {
        gst_app_src_push_sample(GST_APP_SRC(record_src), sample);
    }

    gst_sample_unref(sample);
    return GST_FLOW_OK;
}

// Callback to configure RTSP media factory
static void media_configure(GstRTSPMediaFactory *factory, GstRTSPMedia *media, gpointer user_data) {
    GstElement *element = gst_rtsp_media_get_element(media);
    stream_src = gst_bin_get_by_name(GST_BIN(element), "stream_src");
    g_object_set(stream_src, "format", GST_FORMAT_TIME, NULL);
    gst_object_unref(element);
}

// Function to start recording
static void start_recording() {
    if (!recording) {
        g_print("Starting recording...\n");
        if (recording_filename) g_free(recording_filename);
        recording_filename = g_strdup_printf("output_%ld.mkv", time(NULL));
        g_object_set(gst_bin_get_by_name(GST_BIN(record_pipeline), "filesink"), "location", recording_filename, NULL);
        gst_element_set_state(record_pipeline, GST_STATE_PLAYING);
        recording = TRUE;
    } else {
        g_print("Recording is already running.\n");
    }
}

// Function to stop recording
static void stop_recording() {
    if (recording) {
        g_print("Stopping recording...\n");
        gst_element_set_state(record_pipeline, GST_STATE_NULL);
        recording = FALSE;
        g_print("Recording saved as %s\n", recording_filename);
    } else {
        g_print("Recording is already stopped.\n");
    }
}

// NNG subscriber thread
static void *nng_listener_thread(void *arg) {
    nng_socket sock;
    if (nng_sub_open(&sock) != 0) {
        g_printerr("Failed to open NNG socket\n");
        return NULL;
    }

    if (nng_dial(sock, "ipc:///tmp/pipeline-control", NULL, 0) != 0) {
        g_printerr("Failed to connect to NNG publisher\n");
        nng_close(sock);
        return NULL;
    }

    nng_setopt(sock, NNG_OPT_SUB_SUBSCRIBE, "", 0);

    char msg[256];
    while (1) {
        size_t sz = sizeof(msg);
        if (nng_recv(sock, msg, &sz, 0) == 0) {
            msg[sz] = '\0';
            g_print("Received command: %s\n", msg);

            if (strcmp(msg, "1") == 0) {
                start_recording();
            } else if (strcmp(msg, "2") == 0) {
                stop_recording();
            }
        }
    }

    nng_close(sock);
    return NULL;
}

int main(int argc, char *argv[]) {
    gst_init(&argc, &argv);
    signal(SIGINT, handle_sigint);

    // Capture Pipeline
    capture_pipeline = gst_parse_launch(
        "v4l2src ! videoconvert ! video/x-raw,format=NV12,width=960,height=720 ! appsink name=appsink0 emit-signals=true",
        NULL);
    GstElement *appsink = gst_bin_get_by_name(GST_BIN(capture_pipeline), "appsink0");

    // Recording Pipeline
    record_pipeline = gst_parse_launch(
        "appsrc name=record_src is-live=true format=TIME ! videoconvert ! mpph265enc ! h265parse ! matroskamux ! filesink name=filesink",
        NULL);
    record_src = gst_bin_get_by_name(GST_BIN(record_pipeline), "record_src");

    // RTSP Streaming Pipeline
    GstRTSPServer *server = gst_rtsp_server_new();
    gst_rtsp_server_set_service(server, "8554");
    GstRTSPMountPoints *mounts = gst_rtsp_server_get_mount_points(server);
    GstRTSPMediaFactory *factory = gst_rtsp_media_factory_new();
    gst_rtsp_media_factory_set_launch(factory,
                                      "( appsrc name=stream_src is-live=true format=TIME ! videoconvert ! mpph265enc ! h265parse ! rtph265pay name=pay0 pt=96 )");
    g_signal_connect(factory, "media-configure", G_CALLBACK(media_configure), NULL);
    gst_rtsp_mount_points_add_factory(mounts, "/webcam", factory);
    g_object_unref(mounts);
    gst_rtsp_server_attach(server, NULL);

    // Connect appsink to the callback
    g_signal_connect(appsink, "new-sample", G_CALLBACK(new_sample_callback), NULL);

    // Start Capture Pipeline
    gst_element_set_state(capture_pipeline, GST_STATE_PLAYING);

    g_print("RTSP server running at rtsp://localhost:8554/webcam\n");
    g_print("Recording will start on command\n");

    // Start NNG Listener Thread
    pthread_t nng_thread;
    pthread_create(&nng_thread, NULL, nng_listener_thread, NULL);
    pthread_detach(nng_thread);

    // Start GMainLoop
    loop = g_main_loop_new(NULL, FALSE);
    g_main_loop_run(loop);

    g_print("Cleanup complete.\n");
    return 0;
}

Regards.