Загрузка данных


#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;
}