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

GStreamer无法获取MP4/RTSP视频帧:代码运行无响应求助

问题与解决方案

问题描述

基于GStreamer的C++代码,意图通过appsink逐帧解析本地MP4视频或RTSP流,代码可正常编译运行,但执行后无响应,无法获取视频帧。视频文件与代码同目录,已安装全部GStreamer插件。

问题分析

代码存在三个核心问题导致无法正常工作:

  • Decodebin Pad链接逻辑错误:在cb_decodebin_pad_added中创建Ghost Pad的方式完全错误,Decodebin输出的Pad应该直接与Videoconvert的Sink Pad链接,且需要先判断Pad的Caps是否为视频类型,避免音频Pad误链接。
  • 消息循环实现错误:主函数中使用gst_bus_timed_pop_filtered阻塞等待消息,同时又注册了gst_bus_add_watch,这会导致消息循环无法正常运转,应该使用GMainLoop来处理事件循环。
  • 无回调确认日志:cb_new_sample中没有任何输出,无法确认回调是否被触发,不利于调试。

修复后的完整代码

#include <iostream>
#include <gst/gst.h>
#include <gst/app/gstappsink.h>

static GstFlowReturn cb_new_sample(GstAppSink *appsink, gpointer user_data) {
    // Get the sample
    GstSample *sample = gst_app_sink_pull_sample(appsink);
    if (!sample) {
        std::cerr << "Failed to pull sample" << std::endl;
        return GST_FLOW_ERROR;
    }

    // Get the buffer
    GstBuffer *buffer = gst_sample_get_buffer(sample);
    if (!buffer) {
        std::cerr << "Failed to get buffer" << std::endl;
        gst_sample_unref(sample);
        return GST_FLOW_ERROR;
    }

    // Get the buffer's data and convert it to the desired format
    GstMapInfo map_info;
    if (!gst_buffer_map(buffer, &map_info, GST_MAP_READ)) {
        std::cerr << "Failed to map buffer" << std::endl;
        gst_sample_unref(sample);
        return GST_FLOW_ERROR;
    }

    // 调试:打印帧数据大小,确认回调被触发
    std::cout << "Received frame, data size: " << map_info.size << std::endl;

    // Perform frame processing
    // ...

    // Release resources
    gst_buffer_unmap(buffer, &map_info);
    gst_sample_unref(sample);

    return GST_FLOW_OK;
}

static void cb_decodebin_pad_added(GstElement *decodebin, GstPad *pad, gpointer user_data) {
    GstElement *videoconvert = static_cast<GstElement*>(user_data);
    GstPad *sink_pad = gst_element_get_static_pad(videoconvert, "sink");

    // 检查Pad的Caps是否为视频类型,避免音频Pad链接
    GstCaps *caps = gst_pad_get_current_caps(pad);
    if (!caps) caps = gst_pad_get_query_caps(pad, NULL);
    gchar *caps_str = gst_caps_to_string(caps);
    if (g_strrstr(caps_str, "video/") == NULL) {
        g_print("Ignoring audio pad: %s\n", caps_str);
        g_free(caps_str);
        gst_caps_unref(caps);
        gst_object_unref(sink_pad);
        return;
    }
    g_free(caps_str);
    gst_caps_unref(caps);

    // 直接链接Pad,无需创建Ghost Pad
    if (gst_pad_is_linked(sink_pad)) {
        gst_object_unref(sink_pad);
        return;
    }

    GstPadLinkReturn ret = gst_pad_link(pad, sink_pad);
    if (ret != GST_PAD_LINK_OK) {
        g_printerr("Failed to link decodebin pad to videoconvert: %d\n", ret);
    } else {
        g_print("Successfully linked decodebin pad to videoconvert\n");
    }

    gst_object_unref(sink_pad);
}

static gboolean cb_message(GstBus *bus, GstMessage *message, gpointer user_data) {
    GMainLoop *loop = static_cast<GMainLoop*>(user_data);
    switch (GST_MESSAGE_TYPE(message)) {
        case GST_MESSAGE_ERROR: {
            GError *error;
            gchar *debug_info;
            gst_message_parse_error(message, &error, &debug_info);
            g_printerr("Error: %s\n", error->message);
            g_printerr("Debug info: %s\n", debug_info ? debug_info : "none");
            g_clear_error(&error);
            g_free(debug_info);
            g_main_loop_quit(loop);
            break;
        }
        case GST_MESSAGE_EOS:
            g_print("End of stream\n");
            g_main_loop_quit(loop);
            break;
        case GST_MESSAGE_WARNING: {
            GError *error;
            gchar *debug_info;
            gst_message_parse_warning(message, &error, &debug_info);
            g_printerr("Warning: %s\n", error->message);
            g_printerr("Debug info: %s\n", debug_info ? debug_info : "none");
            g_clear_error(&error);
            g_free(debug_info);
            break;
        }
        default:
            break;
    }

    return TRUE;
}

int main() {
    // Initialize GStreamer
    gst_init(NULL, NULL);

    // Create the pipeline
    GstElement *pipeline = gst_pipeline_new("video-pipeline");

    // Create the elements (本地MP4用filesrc,RTSP替换为rtspsrc)
    GstElement *src = gst_element_factory_make("filesrc", "source");
    GstElement *decodebin = gst_element_factory_make("decodebin", "decoder");
    GstElement *videoconvert = gst_element_factory_make("videoconvert", "video-converter");
    GstElement *queue = gst_element_factory_make("queue", "video-queue");
    GstElement *appsink = gst_element_factory_make("appsink", "sink");

    if (!pipeline || !src || !decodebin || !videoconvert || !queue || !appsink) {
        std::cerr << "One or more elements could not be created. Exiting." << std::endl;
        return -1;
    }

    // Set the properties
    // 本地MP4设置
    g_object_set(G_OBJECT(src), "location", "1.mp4", NULL);
    // RTSP流替换为以下配置:
    // g_object_set(G_OBJECT(src), "location", "rtsp://your-rtsp-url", NULL);
    // g_object_set(G_OBJECT(src), "protocols", GST_RTSP_LOWER_TRANS_TCP, NULL); // 若UDP丢包可改用TCP

    g_object_set(G_OBJECT(appsink), "emit-signals", TRUE, "sync", FALSE, NULL);
    // 可选:设置appsink的caps,强制输出特定格式(比如RGB)
    // GstCaps *sink_caps = gst_caps_new_simple("video/x-raw",
    //     "format", G_TYPE_STRING, "RGB",
    //     NULL);
    // g_object_set(G_OBJECT(appsink), "caps", sink_caps, NULL);
    // gst_caps_unref(sink_caps);

    // Add the elements to the pipeline
    gst_bin_add_many(GST_BIN(pipeline), src, decodebin, videoconvert, queue, appsink, NULL);

    // Link the elements
    if (!gst_element_link(src, decodebin)) {
        std::cerr << "Failed to link source and decodebin" << std::endl;
        gst_object_unref(pipeline);
        return -1;
    }

    // Connect decodebin pad-added signal
    g_signal_connect(decodebin, "pad-added", G_CALLBACK(cb_decodebin_pad_added), videoconvert);

    // Link videoconvert, queue, and appsink
    if (!gst_element_link_many(videoconvert, queue, appsink, NULL)) {
        std::cerr << "Failed to link videoconvert, queue, and appsink" << std::endl;
        gst_object_unref(pipeline);
        return -1;
    }

    // Set the appsink callback function
    g_signal_connect(appsink, "new-sample", G_CALLBACK(cb_new_sample), NULL);

    // 创建主循环
    GMainLoop *loop = g_main_loop_new(NULL, FALSE);

    // Register a message callback function to handle pipeline messages
    GstBus *bus = gst_element_get_bus(pipeline);
    gst_bus_add_watch(bus, cb_message, loop);
    gst_object_unref(bus);

    // Set the pipeline state to playing
    gst_element_set_state(pipeline, GST_STATE_PLAYING);

    // 运行主循环
    g_main_loop_run(loop);

    // Release resources
    gst_element_set_state(pipeline, GST_STATE_NULL);
    gst_object_unref(pipeline);
    g_main_loop_unref(loop);

    return 0;
}

关键修复点说明

  • Pad链接逻辑修复:在cb_decodebin_pad_added中先判断Pad是否为视频类型,直接将Decodebin的输出Pad与Videoconvert的Sink Pad链接,去掉了错误的Ghost Pad创建逻辑。
  • 消息循环修复:使用GMainLoop替代手动的gst_bus_timed_pop_filtered,确保GStreamer的事件循环正常运转,同时在错误或EOS消息触发时退出主循环。
  • 添加调试日志:在cb_new_sample中打印帧数据大小,确认回调被触发,方便调试。
  • RTSP适配支持:代码中注释了RTSP流的配置方式,只需将filesrc替换为rtspsrc并设置RTSP地址即可。

编译运行说明

编译时需要链接GStreamer相关库,例如:

g++ -o video_parser video_parser.cpp $(pkg-config --cflags --libs gstreamer-1.0 gstreamer-app-1.0)

运行时确保视频文件存在或RTSP地址可访问:

./video_parser

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 09:18:10