Загрузка данных
#include <gst/gst.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
// Explicit definitions for your 3 specific pipelines
typedef enum {
CMD_NONE = 0,
CMD_START_CAM0, // Pipeline 1: Camera 0 direct fullscreen via imxvideoconvert
CMD_START_CAM1, // Pipeline 2: Camera 1 direct fullscreen via imxvideoconvert
CMD_START_BOTH, // Pipeline 3: Combined cameras via imxcompositor Mix
CMD_STOP_ALL // State to clean up and stop all video feeds
} CamCommandType;
// Application context structure
typedef struct {
GstElement *current_pipeline; // Pointer to the currently running pipeline
// Thread synchronization primitives
pthread_mutex_t mutex;
pthread_cond_t cond;
CamCommandType pending_command;
bool has_new_command;
bool keep_running;
} AppContext;
// Pipelines definitions strictly matching your provided shell commands
const gchar *PIPELINE_CAM0_STR =
"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 ! imxvideoconvert_g2d ! queue ! waylandsink";
const gchar *PIPELINE_CAM1_STR =
"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 ! imxvideoconvert_g2d ! queue ! waylandsink";
const gchar *PIPELINE_MIX_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 ! 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 ! mix.sink_1";
// Helper function to stop and destroy any actively running pipeline
void stop_current_pipeline(AppContext *ctx) {
if (ctx->current_pipeline) {
printf("[Camera Worker] Stopping active pipeline...\n");
// Instruct GStreamer to block the threads and stop hardware access
gst_element_set_state(ctx->current_pipeline, GST_STATE_NULL);
// Unreference and free the memory allocations of the old pipeline
gst_object_unref(ctx->current_pipeline);
ctx->current_pipeline = NULL;
}
}
// Logic function to launch a brand new independent pipeline topology
void switch_pipeline(AppContext *ctx, CamCommandType command) {
const gchar *target_string = NULL;
// Stop whatever is currently running on the screen first
stop_current_pipeline(ctx);
// Select the exact configuration string based on incoming command token
switch (command) {
case CMD_START_CAM0:
target_string = PIPELINE_CAM0_STR;
printf("[Camera Worker] Launching Pipeline: Camera 0 Direct\n");
break;
case CMD_START_CAM1:
target_string = PIPELINE_CAM1_STR;
printf("[Camera Worker] Launching Pipeline: Camera 1 Direct\n");
break;
case CMD_START_BOTH:
target_string = PIPELINE_MIX_STR;
printf("[Camera Worker] Launching Pipeline: Mix (Both Cameras)\n");
break;
case CMD_STOP_ALL:
printf("[Camera Worker] All cameras stopped. Screen cleared.\n");
return;
default:
return;
}
GError *error = NULL;
// Build the dynamic GStreamer graph layout from the selected string text
ctx->current_pipeline = gst_parse_launch(target_string, &error);
if (!ctx->current_pipeline) {
fprintf(stderr, "[Camera Worker] Failed to build pipeline: %s\n", error->message);
g_error_free(error);
return;
}
// Set the state to PLAYING to re-initialize drivers and map onto Wayland sink
gst_element_set_state(ctx->current_pipeline, GST_STATE_PLAYING);
}
// Background worker thread driven by the synchronization conditional signals
void* camera_worker_thread(void *arg) {
AppContext *ctx = (AppContext *)arg;
printf("[Camera Worker] Thread initialized. Waiting for commands...\n");
while (true) {
CamCommandType current_action = CMD_NONE;
pthread_mutex_lock(&ctx->mutex);
// Block execution efficiently until main loop catches an RPMsg message
while (!ctx->has_new_command && ctx->keep_running) {
pthread_cond_wait(&ctx->cond, &ctx->mutex);
}
if (!ctx->keep_running) {
pthread_mutex_unlock(&ctx->mutex);
break;
}
// Copy data out of the shared memory region
current_action = ctx->pending_command;
ctx->has_new_command = false;
pthread_mutex_unlock(&ctx->mutex);
// Perform the hard reset and launch of the required pipeline
switch_pipeline(ctx, current_action);
}
// Ensure resources are cleared if thread is destroyed unexpectedly
stop_current_pipeline(ctx);
printf("[Camera Worker] Thread safely terminated.\n");
return NULL;
}
// Thread-safe function designed to be executed directly within your RPMsg listener block
void push_camera_command(AppContext *ctx, CamCommandType command) {
pthread_mutex_lock(&ctx->mutex);
ctx->pending_command = command;
ctx->has_new_command = true;
pthread_cond_signal(&ctx->cond); // Signal worker to wake up immediately
pthread_mutex_unlock(&ctx->mutex);
}
int main(int argc, char *argv[]) {
// 1. Core initialization of GStreamer libraries (suppressing local args check)
gst_init(NULL, NULL);
// 2. Set the critical Yocto BSP layer target driver mapping routing rule
setenv("LIBCAMERA_PIPELINES_MATCH_LIST", "nxp/neo", 1);
AppContext ctx;
memset(&ctx, 0, sizeof(AppContext));
ctx.keep_running = true;
pthread_mutex_init(&ctx.mutex, NULL);
pthread_cond_init(&ctx.cond, NULL);
// 3. Spawn background camera processing runtime channel
pthread_t thread_id;
if (pthread_create(&thread_id, NULL, camera_worker_thread, &ctx) != 0) {
fprintf(stderr, "[Main] Fatal: Could not create processing thread.\n");
return -1;
}
// ------------------------------------------------------------------------
// RPMsg LOOP INTEGRATION PLACEHOLDER
// This blocks mirrors reading packets from /dev/rpmsgX within your project.
// ------------------------------------------------------------------------
sleep(2);
printf("[Main/RPMsg] Simulated packet: Start Camera 0 Fullscreen\n");
push_camera_command(&ctx, CMD_START_CAM0);
sleep(6);
printf("[Main/RPMsg] Simulated packet: Switch to Camera 1 Fullscreen\n");
push_camera_command(&ctx, CMD_START_CAM1);
sleep(6);
printf("[Main/RPMsg] Simulated packet: Switch to Both Cameras (Mix View)\n");
push_camera_command(&ctx, CMD_START_BOTH);
sleep(6);
printf("[Main/RPMsg] Simulated packet: Stop All Feeds\n");
push_camera_command(&ctx, CMD_STOP_ALL);
sleep(3);
// ------------------------------------------------------------------------
// 4. Graceful application termination execution sequence
pthread_mutex_lock(&ctx.mutex);
ctx.keep_running = false;
pthread_cond_signal(&ctx.cond);
pthread_mutex_unlock(&ctx.mutex);
pthread_join(thread_id, NULL);
pthread_mutex_destroy(&ctx.mutex);
pthread_cond_destroy(&ctx.cond);
printf("[Main] Application closed.\n");
return 0;
}