Загрузка данных
#include <gst/gst.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <pthread.h>
// Camera actions definition
typedef enum {
CMD_NONE = 0,
CMD_START_CAM,
CMD_STOP_CAM
} CamAction;
// Command structure to pass data between main and worker thread
typedef struct {
int camera_id;
CamAction action;
bool has_new_command;
} ThreadCommand;
// Global Application Context
typedef struct {
GstElement *pipeline;
GstElement *valve_cam0;
GstElement *valve_cam1;
// Thread synchronization primitives
pthread_mutex_t mutex;
pthread_cond_t cond;
ThreadCommand shared_cmd;
bool keep_running;
} AppContext;
// Function to safely update the valve element properties inside GStreamer graph
void set_camera_state(AppContext *ctx, int camera_id, bool start) {
GstElement *target_valve = NULL;
if (camera_id == 0) {
target_valve = ctx->valve_cam0;
} else if (camera_id == 1) {
target_valve = ctx->valve_cam1;
} else {
fprintf(stderr, "[Camera Worker] Error: Invalid camera ID: %d\n", camera_id);
return;
}
if (target_valve) {
// drop=TRUE blocks video buffers; drop=FALSE passes buffers to compositor
g_object_set(target_valve, "drop", start ? FALSE : TRUE, NULL);
printf("[Camera Worker] Camera %d status changed to: %s\n", camera_id, start ? "STARTED" : "STOPPED");
}
}
// Separate worker thread handling camera pipeline manipulations
void* camera_worker_thread(void *arg) {
AppContext *ctx = (AppContext *)arg;
printf("[Camera Worker] Thread started, entering standby loop...\n");
while (true) {
int target_id = -1;
CamAction action = CMD_NONE;
// Lock mutex to safely check shared command state
pthread_mutex_lock(&ctx->mutex);
// Sleep efficiently until main thread signals a new RPMsg command arrived
while (!ctx->shared_cmd.has_new_command && ctx->keep_running) {
pthread_cond_wait(&ctx->cond, &ctx->mutex);
}
// Exit thread loop gracefully if termination signal received
if (!ctx->keep_running) {
pthread_mutex_unlock(&ctx->mutex);
break;
}
// Copy command details locally to minimize mutex locked time
target_id = ctx->shared_cmd.camera_id;
action = ctx->shared_cmd.action;
// Reset command trigger flag
ctx->shared_cmd.has_new_command = false;
pthread_mutex_unlock(&ctx->mutex);
// Execute GStreamer pipeline modification safely outside the lock
if (action == CMD_START_CAM) {
set_camera_state(ctx, target_id, true);
} else if (action == CMD_STOP_CAM) {
set_camera_state(ctx, target_id, false);
}
}
printf("[Camera Worker] Thread terminating...\n");
return NULL;
}
// Helper function to push commands from your RPMsg handler to the worker thread
void push_camera_command(AppContext *ctx, int camera_id, CamAction action) {
pthread_mutex_lock(&ctx->mutex);
ctx->shared_cmd.camera_id = camera_id;
ctx->shared_cmd.action = action;
ctx->shared_cmd.has_new_command = true;
// Wake up the sleeping camera worker thread
pthread_cond_signal(&ctx->cond);
pthread_mutex_unlock(&ctx->mutex);
}
// Simulated RPMsg polling loop (Replace this logic with your real RPMsg file descriptor read/ioctl)
void rpmsg_polling_loop_mock(AppContext *ctx) {
printf("[Main/RPMsg] Starting RPMsg listener simulation...\n");
// Simulate Cortex-M7 sending a STOP command for Camera 0 after 4 seconds
sleep(4);
printf("[Main/RPMsg] Received RPMsg packet: Stop Camera 0\n");
push_camera_command(ctx, 0, CMD_STOP_CAM);
// Simulate Cortex-M7 sending a STOP command for Camera 1 after 4 more seconds
sleep(4);
printf("[Main/RPMsg] Received RPMsg packet: Stop Camera 1\n");
push_camera_command(ctx, 1, CMD_STOP_CAM);
// Simulate Cortex-M7 sending a START command for Camera 0 again
sleep(4);
printf("[Main/RPMsg] Received RPMsg packet: Start Camera 0\n");
push_camera_command(ctx, 0, CMD_START_CAM);
sleep(4);
}
int main(int argc, char *argv[]) {
// Force environment variable setup required by Yocto imx95 BSP
setenv("LIBCAMERA_PIPELINES_MATCH_LIST", "nxp/neo", 1);
// Initializing framework
gst_init(&argc, &argv);
AppContext ctx;
memset(&ctx, 0, sizeof(AppContext));
// Set thread control variables
ctx.keep_running = true;
pthread_mutex_init(&ctx.mutex, NULL);
pthread_cond_init(&ctx.cond, NULL);
// Constructing exact layout using valve modules for flow interception
const gchar *pipeline_str =
"imxcompositor_g2d name=mix "
"sink_0::xpos=0 sink_0::ypos=0 sink_0::width=960 sink_0::height=1080 "
"sink_1::xpos=960 sink_1::ypos=0 sink_1::width=960 sink_1::height=1080 ! "
"video/x-raw,width=1920,height=1080 ! queue ! waylandsink "
"libcamerasrc camera-name=\"/base/soc/bus@42000000/i2c@42540000/max96724@27/i2c-mux/i2c@0/mx95mbcam@40\" ! "
"video/x-raw,format=YUY2,width=1920,height=1280 ! queue ! "
"valve name=valve0 drop=false ! mix.sink_0 "
"libcamerasrc camera-name=\"/base/soc/bus@42000000/i2c@42540000/max96724@27/i2c-mux/i2c@1/mx95mbcam@40\" ! "
"video/x-raw,format=YUY2,width=1920,height=1280 ! queue ! "
"valve name=valve1 drop=false ! mix.sink_1";
GError *error = NULL;
ctx.pipeline = gst_parse_launch(pipeline_str, &error);
if (!ctx.pipeline) {
fprintf(stderr, "[Main] Pipeline initialization failed: %s\n", error->message);
g_error_free(error);
return -1;
}
// Retrieve valve component hooks by pipeline assignment labels
ctx.valve_cam0 = gst_bin_get_by_name(GST_BIN(ctx.pipeline), "valve0");
ctx.valve_cam1 = gst_bin_get_by_name(GST_BIN(ctx.pipeline), "valve1");
// Spin up cameras and display output actively
printf("[Main] Spinning up multimedia pipelines...\n");
gst_element_set_state(ctx.pipeline, GST_STATE_PLAYING);
// Launch asynchronous worker handling the state changes
pthread_t worker_thread_id;
if (pthread_create(&worker_thread_id, NULL, camera_worker_thread, &ctx) != 0) {
fprintf(stderr, "[Main] Error: Could not generate background worker thread.\n");
return -1;
}
// --- MAIN LOOP INTEGRATION PLACEHOLDER ---
// This is where your actual main loop runs polling the RPMsg device node.
// Integrate 'push_camera_command' inside your RPMsg parser branch.
rpmsg_polling_loop_mock(&ctx);
// --- END OF LOOP ---
// Clean up and notify background thread to drop loop
printf("[Main] Initiating application teardown sequence...\n");
pthread_mutex_lock(&ctx->mutex);
ctx.keep_running = false;
pthread_cond_signal(&ctx->cond); // Wake up to let it notice keep_running is false
pthread_mutex_unlock(&ctx->mutex);
// Await safe cleanup of thread context
pthread_join(worker_thread_id, NULL);
// Shut down pipeline processing channels
gst_element_set_state(ctx.pipeline, GST_STATE_NULL);
// Destroy memory instances
if (ctx.valve_cam0) gst_object_unref(ctx.valve_cam0);
if (ctx.valve_cam1) gst_object_unref(ctx.valve_cam1);
if (ctx.pipeline) gst_object_unref(ctx.pipeline);
pthread_mutex_destroy(&ctx->mutex);
pthread_cond_destroy(&ctx->cond);
printf("[Main] Application exited successfully.\n");
return 0;
}