I am seeking guidance on optimizing a video processing pipeline to achieve independent control over streaming and recording functionalities. Currently, my implementation initiates recording concurrently with the commencement of video streaming. However, I require a solution that enables recording to be triggered and terminated independently of the streaming state.
Specifically, I aim to initiate video recording upon receiving a “start” command via a nano message and subsequently terminate it upon receiving a “stop” command. A critical constraint is the avoidance of duplicate encoding processes, which precludes the implementation of separate pipelines for streaming and recording due to the potential for increased overhead on the device.
Therefore, I am exploring methods to decouple the recording process from the streaming activity, allowing for independent control without duplicating encoding operations.
Could you please provide suggestions or best practices for achieving this functionality? I am particularly interested in techniques that:
Enable dynamic start/stop control of recording based on external commands.
Minimize computational overhead by avoiding redundant encoding.
Maintain a robust and efficient video processing pipeline.
Any insights or recommendations you can offer would be greatly appreciated.
Thank you for your time and consideration.
#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 ! x265enc ! h265parse ! rtph265pay pt=96 name=pay0 config-interval=1 "
"t. ! queue ! videoconvert ! x265enc ! 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;
}```