You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

求助:GStreamer获取RTSP流帧存入OpenCV Mat时Appsink取样本报错

解决GStreamer Appsink获取RTSP帧并转换为OpenCV Mat的问题

嘿,我来帮你搞定从GStreamer的appsink里拿帧转成OpenCV Mat的事儿!你说的获取样本出错,大概率是没正确处理GstSampleGstBuffer的流程,或者appsink的配置没跟上。下面我一步步给你讲正确的做法:

关键注意点

  • 首先得给appsink设置匹配的Caps,确保输出的帧格式是OpenCV能直接处理的(比如BGR,因为OpenCV默认用BGR通道顺序)
  • 获取样本后要正确处理buffer的内存映射,用完记得释放资源,不然会内存泄漏或者崩溃

完整示例代码

#include <gst/gst.h>
#include <opencv2/opencv.hpp>
#include <gst/app/gstappsink.h> // 注意正确的头文件,旧版本可能是gstappsink.h

static GstFlowReturn new_sample_callback(GstAppSink *sink, gpointer user_data) {
    cv::Mat *frame = static_cast<cv::Mat*>(user_data);
    GstSample *sample = gst_app_sink_pull_sample(sink);
    if (!sample) {
        g_printerr("Failed to pull sample from appsink\n");
        return GST_FLOW_ERROR;
    }

    // 1. 从sample中获取buffer
    GstBuffer *buffer = gst_sample_get_buffer(sample);
    if (!buffer) {
        g_printerr("Failed to get buffer from sample\n");
        gst_sample_unref(sample);
        return GST_FLOW_ERROR;
    }

    // 2. 获取buffer的caps,提取宽高和格式信息
    GstCaps *caps = gst_sample_get_caps(sample);
    GstStructure *structure = gst_caps_get_structure(caps, 0);
    gint width, height;
    if (!gst_structure_get_int(structure, "width", &width) || 
        !gst_structure_get_int(structure, "height", &height)) {
        g_printerr("Failed to get frame dimensions\n");
        gst_sample_unref(sample);
        return GST_FLOW_ERROR;
    }

    // 3. 映射buffer内存到可访问的区域
    GstMapInfo map_info;
    if (!gst_buffer_map(buffer, &map_info, GST_MAP_READ)) {
        g_printerr("Failed to map buffer\n");
        gst_sample_unref(sample);
        return GST_FLOW_ERROR;
    }

    // 4. 将映射的数据转换为OpenCV Mat
    // 这里假设我们配置的caps是BGR格式,比如video/x-raw, format=BGR, width=..., height=...
    *frame = cv::Mat(cv::Size(width, height), CV_8UC3, map_info.data, cv::Mat::AUTO_STEP);
    // 如果是RGB格式,需要转成BGR:cv::cvtColor(rgb_mat, bgr_mat, cv::COLOR_RGB2BGR);

    // 5. 解除内存映射,释放sample资源
    gst_buffer_unmap(buffer, &map_info);
    gst_sample_unref(sample);

    // 这里可以添加帧处理逻辑,比如显示帧
    cv::imshow("RTSP Stream", *frame);
    if (cv::waitKey(1) == 'q') {
        // 可以在这里设置退出标志
    }

    return GST_FLOW_OK;
}

int main(int argc, char *argv[]) {
    gst_init(&argc, &argv);
    cv::namedWindow("RTSP Stream", cv::WINDOW_AUTOSIZE);
    cv::Mat frame;

    // 构建GStreamer管道:RTSP源 -> 解码 -> 格式转换 -> appsink
    // 替换成你的RTSP地址
    gchar *pipeline_str = g_strdup_printf(
        "rtspsrc location=rtsp://your_rtsp_url_here ! rtph264depay ! h264parse ! avdec_h264 ! "
        "videoconvert ! video/x-raw, format=BGR ! appsink name=sink"
    );

    GstElement *pipeline = gst_parse_launch(pipeline_str, NULL);
    if (!pipeline) {
        g_printerr("Failed to create pipeline\n");
        return -1;
    }
    g_free(pipeline_str);

    // 获取appsink元素,设置回调和属性
    GstElement *appsink = gst_bin_get_by_name(GST_BIN(pipeline), "sink");
    gst_app_sink_set_emit_signals(GST_APP_SINK(appsink), TRUE);
    gst_app_sink_set_drop(GST_APP_SINK(appsink), FALSE);
    gst_app_sink_set_max_buffers(GST_APP_SINK(appsink), 1);
    g_signal_connect(appsink, "new-sample", G_CALLBACK(new_sample_callback), &frame);

    // 启动管道
    gst_element_set_state(pipeline, GST_STATE_PLAYING);

    // 主循环,直到用户按下q
    while (true) {
        if (cv::waitKey(1) == 'q') {
            break;
        }
        // 处理GStreamer的消息,避免阻塞
        GstBus *bus = gst_element_get_bus(pipeline);
        GstMessage *msg = gst_bus_timed_pop(bus, GST_CLOCK_TIME_NONE);
        if (msg) {
            gst_message_unref(msg);
        }
        gst_object_unref(bus);
    }

    // 清理资源
    gst_element_set_state(pipeline, GST_STATE_NULL);
    gst_object_unref(pipeline);
    cv::destroyAllWindows();

    return 0;
}

核心步骤解释(就是你标粗的获取样本环节)

  1. 获取GstSample:用gst_app_sink_pull_sample从appsink拉取样本,一定要检查返回值是否为NULL,避免空指针崩溃
  2. 提取GstBuffer:样本里的实际帧数据存在buffer中,用gst_sample_get_buffer获取
  3. 获取帧信息:从caps里提取宽高和格式,确保和OpenCV Mat的参数匹配
  4. 映射内存:用gst_buffer_map把buffer的内存映射到用户空间,这样才能直接读取数据
  5. 转换为Mat:根据宽高、通道数(BGR是3通道)创建Mat,直接用映射的内存地址作为数据指针(注意格式要对应)
  6. 释放资源:一定要解除内存映射,并且调用gst_sample_unref释放样本,否则会内存泄漏

编译命令

记得编译时链接GStreamer和OpenCV的库,比如:

g++ -o rtsp_opencv rtsp_opencv.cpp `pkg-config --cflags --libs gstreamer-1.0 gstreamer-app-1.0 opencv4`

如果你的系统是GStreamer 0.10版本,需要调整头文件和pkg-config的参数,但推荐用1.0版本。

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

火山引擎 最新活动