GStreamer Discoverer处理HTTP URL失败的问题排查与解决
GStreamer Discoverer解析HTTP MJPG流失败问题解决
问题描述
使用GStreamer的Discoverer组件提取输入流的Codec信息以配置Pipeline时,部分HTTP流会导致GstDiscovererStreamInfo* stream_info为空,无法获取stream_caps_str,触发默认Pipeline后流无法正常工作或出现严重视觉失真。
失败URL示例:http://user:password@IP/mjpg/3/video.mjpg?resolution=854x480,同格式RTSP URL可正常工作,使用gst-discoverer-1.0命令行工具检测该类HTTP URL也失败。
当前实现代码:
GError* error = nullptr; std::string pipeline_str; std::string stream_caps_str; std::vector<std::string> stream_split_str; if (Utils::String::stringContainsText(source_video, "&")) { stream_split_str = Utils::String::split(source_video, "&", false); if (Utils::String::stringContainsText(stream_split_str[0], "?")) { std::vector<std::string> temp_str_vector = Utils::String::split(stream_split_str[0], "?", false); source_video = temp_str_vector[0] + "?" + stream_split_str[1] + " "; } else source_video = stream_split_str[0]; } //This code obtains information from the incoming stream GstDiscoverer* discoverer = gst_discoverer_new(3 * GST_SECOND, &error); GstDiscovererInfo* discoverer_info = gst_discoverer_discover_uri(discoverer, source_video.c_str(), &error); GstDiscovererStreamInfo* stream_info = gst_discoverer_info_get_stream_info(discoverer_info); if(stream_info != NULL) stream_caps_str = gst_caps_to_string(gst_discoverer_stream_info_get_caps(stream_info)); PRINTF("CAPS INFO: %s\n", stream_caps_str.c_str()); //This part of the code sets the type of the pipeline for the incoming source stream, depending if it's rtsp or http if (Utils::String::stringContainsText(source_video, "rtsp")) pipeline_str = "rtspsrc location=" + source_video + "latency=0"; else if (Utils::String::stringContainsText(source_video, "http")) pipeline_str = "souphttpsrc location=" + source_video; else PRINTF("URL Source type not matching our system!\n"); pipeline_str += " is-live=true ! queue "; //This switch sets the correct codec for encoding and decoding and if it's done in the GPU/CPU //If there's no decoder set or the caps fail, it let's the program choose a codec automatically switch (id_decoder) { case STREAM_GST_CPU: if (Utils::String::stringContainsText(stream_caps_str, "H264")) pipeline_str += "! rtph264depay ! h264parse ! avdec_h264"; else if (Utils::String::stringContainsText(stream_caps_str, "H265")) pipeline_str += "! rtph265depay ! h265parse ! avdec_h265"; else if(Utils::String::stringContainsText(stream_caps_str, "JPEG")) pipeline_str += "! rtpjpegdepay ! jpegparse ! avdec_mjpeg"; else pipeline_str += "! parsebin ! decodebin"; break; case STREAM_GST_GPU: if (Utils::String::stringContainsText(stream_caps_str, "H264")) pipeline_str += "! rtph264depay ! h264parse ! nvh264dec"; else if (Utils::String::stringContainsText(stream_caps_str, "H265")) pipeline_str += "! rtph265depay ! h265parse ! nvh265dec"; else if (Utils::String::stringContainsText(stream_caps_str, "JPEG")) pipeline_str += "! rtpjpegdepay ! jpegparse ! nvjpegdec"; else pipeline_str += " ! multipartdemux ! jpegparse ! nvjpegdec"; pipeline_str += " ! cudaupload ! cudaconvert ! cudascale ! cudadownload"; break; default: pipeline_str += "! parsebin ! decodebin"; logNeuralAndPrint("ERROR: No se puede asignar ninguna pipeline especifica, se devuelve una pipeline generica.\n."); break; } pipeline_str += " ! videoconvert ! video/x-raw, format=BGR "; pipeline_str += "! queue ! appsink name=stream sync=false"; GstElement* temp_pipeline = gst_parse_launch(pipeline_str.c_str(), &error);
失败原因
- MJPG流格式特性:这类HTTP MJPG流采用
multipart/x-mixed-replace格式传输连续JPEG帧,无标准元数据头部描述流信息,GstDiscoverer依赖读取结构化元数据解析Codec和caps,因此无法识别这类流,导致stream_info为空。 - URL处理错误:代码中对URL拆分后添加了末尾空格,使URL无效,进一步导致Discoverer解析失败。
- Discoverer的支持限制:
gst-discoverer-1.0更擅长处理带容器格式(如MP4、MKV)或RTP封装的流,对无容器的MJPG裸帧序列支持不足。
解决方法
1. 修复URL处理逻辑
移除URL末尾的空格,确保传递给Discoverer和source element的URL合法:
// 错误代码 // source_video = temp_str_vector[0] + "?" + stream_split_str[1] + " "; // 修改为 source_video = temp_str_vector[0] + "?" + stream_split_str[1];
2. 跳过Discoverer,直接识别MJPG流
通过URL特征(.mjpg后缀或/mjpg/路径)判断MJPG流,直接标记Codec类型,避免依赖Discoverer:
// 先判断是否为MJPG流 bool is_mjpg_stream = Utils::String::stringContainsText(source_video, ".mjpg") || Utils::String::stringContainsText(source_video, "/mjpg/"); // 调整caps获取逻辑 if (!is_mjpg_stream) { // 保留原有Discoverer逻辑处理其他流 GstDiscoverer* discoverer = gst_discoverer_new(3 * GST_SECOND, &error); GstDiscovererInfo* discoverer_info = gst_discoverer_discover_uri(discoverer, source_video.c_str(), &error); // 增加错误检查 if (discoverer_info) { GstDiscovererStreamInfo* stream_info = gst_discoverer_info_get_stream_info(discoverer_info); if(stream_info != NULL) stream_caps_str = gst_caps_to_string(gst_discoverer_stream_info_get_caps(stream_info)); gst_discoverer_info_unref(discoverer_info); } else if (error) { PRINTF("Discoverer failed: %s\n", error->message); g_error_free(error); error = nullptr; } if (discoverer) gst_discoverer_unref(discoverer); } else { // 直接标记为JPEG类型 stream_caps_str = "image/jpeg"; }
3. 适配MJPG流的Pipeline构建逻辑
MJPG流无需RTP解封装,直接用multipartdemux拆分帧,调整switch分支逻辑:
switch (id_decoder) { case STREAM_GST_CPU: if (is_mjpg_stream) { pipeline_str += "! multipartdemux ! jpegparse ! avdec_mjpeg"; } else if (Utils::String::stringContainsText(stream_caps_str, "H264")) { pipeline_str += "! rtph264depay ! h264parse ! avdec_h264"; } else if (Utils::String::stringContainsText(stream_caps_str, "H265")) { pipeline_str += "! rtph265depay ! h265parse ! avdec_h265"; } else if(Utils::String::stringContainsText(stream_caps_str, "JPEG")) { pipeline_str += "! rtpjpegdepay ! jpegparse ! avdec_mjpeg"; } else { pipeline_str += "! parsebin ! decodebin"; } break; case STREAM_GST_GPU: if (is_mjpg_stream) { pipeline_str += "! multipartdemux ! jpegparse ! nvjpegdec"; pipeline_str += " ! cudaupload ! cudaconvert ! cudascale ! cudadownload"; } else if (Utils::String::stringContainsText(stream_caps_str, "H264")) { pipeline_str += "! rtph264depay ! h264parse ! nvh264dec"; pipeline_str += " ! cudaupload ! cudaconvert ! cudascale ! cudadownload"; } else if (Utils::String::stringContainsText(stream_caps_str, "H265")) { pipeline_str += "! rtph265depay ! h265parse ! nvh265dec"; pipeline_str += " ! cudaupload ! cudaconvert ! cudascale ! cudadownload"; } else if (Utils::String::stringContainsText(stream_caps_str, "JPEG")) { pipeline_str += "! rtpjpegdepay ! jpegparse ! nvjpegdec"; pipeline_str += " ! cudaupload ! cudaconvert ! cudascale ! cudadownload"; } else { pipeline_str += "! parsebin ! decodebin"; logNeuralAndPrint("ERROR: No se puede asignar ninguna pipeline especifica, se devuelve una pipeline generica.\n."); } break; default: pipeline_str += "! parsebin ! decodebin"; logNeuralAndPrint("ERROR: No se puede asignar ninguna pipeline especifica, se devuelve una pipeline generica.\n."); break; }
4. 补充资源释放与错误检查
原代码未释放Discoverer相关资源,会导致内存泄漏,同时增加错误检查可提前发现解析失败情况,避免空指针问题。
内容的提问来源于stack exchange,提问作者JFF
相关产品推荐
相关产品推荐

