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.