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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 12:58:08