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


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