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
相关产品推荐
相关产品推荐

