You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从GStreamer Appsink保存MJPEG图像的最优方案及多管道实现崩溃问题解决

How to Save MJPEG Frames as JPEG Without Re-encoding or Extra Pipelines in GStreamer

Great question! Since you're working with an MJPEG stream, each frame in your GstSample is already a fully formed JPEG image—you don't need to spin up a new GStreamer pipeline or do any encoding/decoding at all. The root cause of your crashes is the messy pipeline creation/teardown logic (especially those unreliable usleep calls and incorrect EOS handling), and we can eliminate all that by writing the raw JPEG data directly to disk.

The Simplest Solution: Write the Buffer Data Directly to File

Here's how to rewrite your saveSampleFromAppsinkJpeg function to skip the pipeline entirely. We'll extract the raw JPEG bytes from the GstBuffer and write them straight to a file:

int saveSampleFromAppsinkJpeg(GstSample *sample) {
    if (!sample) {
        std::cerr << "saveSampleFromAppsinkJpeg: Null sample received" << std::endl;
        return -1;
    }

    GstBuffer *buffer = gst_sample_get_buffer(sample);
    if (!buffer) {
        std::cerr << "saveSampleFromAppsinkJpeg: Null buffer in sample" << std::endl;
        return -1;
    }

    // Get the memory block from the buffer
    GstMemory *memory = gst_buffer_get_memory(buffer, 0);
    if (!memory) {
        std::cerr << "saveSampleFromAppsinkJpeg: Failed to get memory from buffer" << std::endl;
        return -1;
    }

    GstMapInfo info;
    // Map the memory to access raw bytes
    if (!gst_memory_map(memory, &info, GST_MAP_READ)) {
        std::cerr << "saveSampleFromAppsinkJpeg: Failed to map memory" << std::endl;
        gst_memory_unref(memory);
        return -1;
    }

    // Generate a unique filename to avoid overwriting (adjust path as needed)
    static int image_counter = 0;
    char filename[256];
    snprintf(filename, sizeof(filename), "some/path_%d.jpg", image_counter++);

    // Write the raw JPEG data to file
    std::ofstream outfile(filename, std::ios::binary);
    if (!outfile.is_open()) {
        std::cerr << "saveSampleFromAppsinkJpeg: Failed to open file " << filename << std::endl;
        gst_memory_unmap(memory, &info);
        gst_memory_unref(memory);
        return -1;
    }

    outfile.write(reinterpret_cast<const char*>(info.data), info.size);
    outfile.close();

    // Clean up resources
    gst_memory_unmap(memory, &info);
    gst_memory_unref(memory);
    gst_sample_unref(sample);

    std::cout << "Saved image to " << filename << std::endl;
    return 1;
}

Why This Works

MJPEG streams consist of sequential JPEG images wrapped in a lightweight container. Each GstBuffer in your stream holds exactly one complete JPEG file's worth of data—no extra headers or encoding needed. By mapping the buffer's memory and writing those bytes directly, you're essentially copying the JPEG frame as-is to disk, with zero overhead from re-encoding or pipeline management.

What Was Wrong With Your Original Approach

Your original code creates a new GStreamer pipeline every time you save an image, which is inefficient and error-prone:

  • usleep calls are unreliable—you can't guarantee the pipeline has finished processing the sample before sending EOS or tearing it down.
  • Incorrect EOS handling: You're manually posting EOS messages and calling end_of_stream multiple times, which can cause state inconsistencies.
  • Resource leaks: If the pipeline doesn't reach GST_STATE_NULL properly, you leave GStreamer resources hanging, which eventually causes crashes after a few saves.

If You Must Use a Pipeline (For Future Expansion)

If you need to add processing later (like resizing or metadata injection), you should reuse a single pipeline instead of creating a new one each time. Here's a quick outline:

  1. Create the pipeline once at app startup (appsrc → multifilesink, plus any processing elements you need).
  2. When saving an image, update the multifilesink's location property and push the sample to the existing appsrc.
  3. Use GStreamer's bus to wait for EOS or completion messages instead of usleep—this ensures the pipeline finishes writing before you proceed.
  4. Reset the pipeline's state (or just the sink's filename) for the next image.

But for your current use case, direct file writing is by far the best option—it's faster, simpler, and eliminates the crash risk entirely.

内容的提问来源于stack exchange,提问作者kochy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 16:39:10