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

GStreamer命令转C代码遇qtdemux与decodebin链接问题求助

问题:将GStreamer命令转换为C代码时的链接错误

原GStreamer命令:

gst-launch-1.0 filesrc location=/home/pi/Videos/watch.mp4 ! qtdemux name=demux \
demux.audio_0 ! queue ! decodebin ! audioconvert ! audioresample ! autoaudiosink \
demux.video_0 ! queue ! decodebin ! videoconvert ! videoscale ! video/x-raw,width=800,height=480 ! avenc_bmp ! fakesink

尝试转换为C代码时,因同时使用qtdemux和decodebin无法正确完成元素链接,尝试过tee但性能下降。以下是编写的C代码:

#include <stdio.h>
#include <gstreamer-1.0/gst/gst.h>
#include <stdbool.h>

typedef struct {
    GstElement *pipeline;
    GstElement *filesrc;
    GstElement *qtdemux;
    /* Video */
    struct{
        GstElement *queue;
        GstElement *decode;
        GstElement *convert;
        GstElement *scale;
        GstElement *capsfilter;
        GstElement *enc_bmp;
        GstElement *fakesink;
    }video;
    /* Audio */
    struct{
        GstElement *queue;
        GstElement *decode;
        GstElement *convert;
        GstElement *resample;
        GstElement *sink;
    }audio;
} gstreamer_t;

static void pad_added_handler (GstElement *src, GstPad *pad, gstreamer_t* data)
{
    GstCaps *caps;
    GstStructure *pad_sct;
    const gchar *name;

    caps = gst_pad_get_current_caps(pad);
    pad_sct = gst_caps_get_structure( caps, 0 );
    name = gst_structure_get_name(pad_sct);

    printf( "src name = %s\r\n", gst_element_get_name(src) );
    printf( "pad name = %s\r\n", name);
    printf( "Received new pad '%s' from '%s'.\r\n", GST_PAD_NAME (pad), GST_ELEMENT_NAME (src));

    GstPad *sinkpad = NULL;
    if(g_str_has_prefix (name, "video/x-h264")) {
        sinkpad = gst_element_get_static_pad(data->video.queue, "sink");
        if(!gst_pad_link(pad, sinkpad) != GST_PAD_LINK_OK )
            printf("not link !!\n\n"); 
        gst_object_unref (sinkpad);
    }else if(g_str_has_prefix( name, "audio/mpeg")){
        sinkpad = gst_element_get_static_pad ( data->audio.queue, "sink");
        if(gst_pad_link( pad, sinkpad) != GST_PAD_LINK_OK )
            printf("not link !!\n\n"); 
        gst_object_unref (sinkpad);
    }
    else
        printf("Another Pad: %s.\r\n", name);

    gst_caps_unref (caps);
}

int main(int argc, char *argv[]) {
    gstreamer_t gstreamer;
    GstBus *bus;
    GstMessage *msg;
    GstStateChangeReturn ret;
    gboolean terminate = FALSE;

    /* Initialize GStreamer */
    gst_init (&argc, &argv);

    /* Create the elements */
    gstreamer.filesrc = gst_element_factory_make ("filesrc", "filesrc");
    g_object_set (gstreamer.filesrc, "location", "../../Videos/watch.mp4", NULL);
    gstreamer.qtdemux = gst_element_factory_make ("qtdemux", "qtdemux");

    /* Video */
    gstreamer.video.queue = gst_element_factory_make("queue", "video");
    gstreamer.video.decode = gst_element_factory_make ("decodebin", "decodebin");
    gstreamer.video.convert = gst_element_factory_make ("videoconvert", "videoconvert");
    gstreamer.video.scale = gst_element_factory_make ("videoscale", "videoscale");
    gstreamer.video.capsfilter = gst_element_factory_make ("capsfilter", "capsfilter");
    GstCaps *Caps = gst_caps_from_string("video/x-raw,width=800,height=480");
    g_object_set(G_OBJECT(gstreamer.video.capsfilter), "caps", Caps, NULL);
    gst_caps_unref(Caps);
    gstreamer.video.enc_bmp = gst_element_factory_make ("avenc_bmp", "avenc_bmp");

    gstreamer.video.fakesink = gst_element_factory_make ("fakesink", "fakesink");
    g_object_set (gstreamer.video.fakesink, "sync", true, NULL);

    /* Audio */
    gstreamer.audio.queue = gst_element_factory_make("queue", "queue_audio");
    gstreamer.audio.decode = gst_element_factory_make("decodebin", "decodebin");
    gstreamer.audio.convert = gst_element_factory_make("audioconvert", "audioconvert");
    gstreamer.audio.resample = gst_element_factory_make("audioresample", "audioresample");
    gstreamer.audio.sink = gst_element_factory_make("autoaudiosink", "autoaudiosink");

    /* Create the empty pipeline */
    gstreamer.pipeline = gst_pipeline_new ("gstreamer-pipeline");

    if (!gstreamer.pipeline || !gstreamer.filesrc || !gstreamer.qtdemux || 
        !gstreamer.video.queue || !gstreamer.video.decode || !gstreamer.video.convert || !gstreamer.video.scale || !gstreamer.video.capsfilter || !gstreamer.video.enc_bmp || !gstreamer.video.fakesink||
        !gstreamer.audio.queue || !gstreamer.audio.decode || !gstreamer.audio.convert || !gstreamer.audio.resample ||!gstreamer.audio.sink) 
    {
        g_printerr ("Not all elements could be created.\n");
        return -1;
    }

    /* Build the pipeline. Note that we are NOT linking the source at this
    * point. We will do it later. */
    gst_bin_add_many (GST_BIN (gstreamer.pipeline), gstreamer.filesrc, gstreamer.qtdemux,
                        gstreamer.video.queue, gstreamer.video.decode, gstreamer.video.convert, gstreamer.video.scale, gstreamer.video.capsfilter, gstreamer.video.enc_bmp, gstreamer.video.fakesink, 
                        gstreamer.audio.queue, gstreamer.audio.decode, gstreamer.audio.convert, gstreamer.audio.resample, gstreamer.audio.sink, NULL);
    
    if (!gst_element_link (gstreamer.filesrc, gstreamer.qtdemux)) {
        g_printerr ("Elements filesrc and qtdemux could not be linked.\n");
        gst_object_unref (gstreamer.pipeline);
        return -1;
    }

    if (!gst_element_link_many (gstreamer.video.convert, gstreamer.video.scale, gstreamer.video.capsfilter, gstreamer.video.enc_bmp, gstreamer.video.fakesink, NULL)) {
        g_printerr ("Video elements could not be linked.\n");
        gst_object_unref (gstreamer.pipeline);
        return -1;
    }

    if (!gst_element_link_many (gstreamer.audio.convert, gstreamer.audio.resample, gstreamer.audio.sink, NULL)) {
        g_printerr ("Audio elements could not be linked.\n");
        gst_object_unref (gstreamer.pipeline);
        return -1;
    }

    g_signal_connect (gstreamer.qtdemux, "pad-added", G_CALLBACK (pad_added_handler), &gstreamer);

    /* Start playing */
    ret = gst_element_set_state (gstreamer.pipeline, GST_STATE_PLAYING);
    if (ret == GST_STATE_CHANGE_FAILURE) {
        g_printerr ("Unable to set the pipeline to the playing state.\n");
        gst_object_unref (gstreamer.pipeline);
        return -1;
    }

    /* Listen to the bus */
    bus = gst_element_get_bus (gstreamer.pipeline);
    do {
        msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
            GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS);

        /* Parse message */
        if (msg != NULL) {
        GError *err;
        gchar *debug_info;

        switch (GST_MESSAGE_TYPE (msg)) {
            case GST_MESSAGE_ERROR:
                gst_message_parse_error (msg, &err, &debug_info);
                g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);
                g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");
                g_clear_error (&err);
                g_free (debug_info);
                terminate = TRUE;
            break;
            
            case GST_MESSAGE_EOS:
                g_print ("\nEnd-Of-Stream reached.\n");
                terminate = TRUE;
            break;
            
            case GST_MESSAGE_STATE_CHANGED:
                /* We are only interested in state-changed messages from the pipeline */
                if (GST_MESSAGE_SRC (msg) == GST_OBJECT (gstreamer.pipeline)) {
                    GstState old_state, new_state, pending_state;
                    gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);
                    g_print ("Pipeline state changed from %s to %s:\n",
                        gst_element_state_get_name (old_state), gst_element_state_get_name (new_state));
                }
            break;
        break;
            default:
            /* We should not reach here */
            g_printerr ("Unexpected message received.\n");
            break;
        }
        gst_message_unref (msg);
        }
    } while (!terminate);

    /* Free resources */
    gst_object_unref (bus);
    gst_element_set_state (gstreamer.pipeline, GST_STATE_NULL);
    gst_object_unref (gstreamer.pipeline);

    return 0;
}

运行代码后得到如下错误输出:

(stream:5337): GStreamer-WARNING **: 02:28:38.279: Name 'decodebin' is not unique in bin 'gstreamer-pipeline', not adding
Pipeline state changed from NULL to READY:
src name = qtdemux
pad name = video/x-h264
Received new pad 'video_0' from 'qtdemux'.
not link !!

src name = qtdemux
pad name = audio/mpeg
Received new pad 'audio_0' from 'qtdemux'.
Error received from element qtdemux: Internal data stream error.
Debugging information: ../gst/isomp4/qtdemux.c(6545): gst_qtdemux_loop (): /GstPipeline:gstreamer-pipeline/GstQTDemux:qtdemux:
streaming stopped, reason not-linked (-1)

无法正确链接音视频部分,请求帮助。


解决方法

你的代码存在四个核心问题,逐一修复即可解决:

1. 修复decodebin重名问题

GStreamer的bin中元素名称必须唯一,你创建两个decodebin时都用了"decodebin"作为名称,导致第二个decodebin无法被正确添加到管道。修改创建代码,给两个decodebin分配唯一名称:

// 视频decodebin
gstreamer.video.decode = gst_element_factory_make ("decodebin", "video-decodebin");
// 音频decodebin
gstreamer.audio.decode = gst_element_factory_make("decodebin", "audio-decodebin");

2. 修复queue到decodebin的链接

你只链接了qtdemux到queue,但没有链接queue到对应的decodebin,这会导致数据流中断。在添加元素到管道后,补充这部分链接:

// 链接视频queue到视频decodebin
if (!gst_element_link(gstreamer.video.queue, gstreamer.video.decode)) {
    g_printerr ("Video queue and decodebin could not be linked.\n");
    gst_object_unref (gstreamer.pipeline);
    return -1;
}

// 链接音频queue到音频decodebin
if (!gst_element_link(gstreamer.audio.queue, gstreamer.audio.decode)) {
    g_printerr ("Audio queue and decodebin could not be linked.\n");
    gst_object_unref (gstreamer.pipeline);
    return -1;
}

3. 处理decodebin的动态pad链接

decodebin也是动态pad元素,它会在解码出原始音视频流后动态创建src pad,你需要为两个decodebin分别添加pad-added回调,将它们的src pad链接到后续的convert元素:

新增decodebin的pad处理函数

static void video_decode_pad_added_handler(GstElement *src, GstPad *pad, gstreamer_t* data) {
    GstPad *sinkpad = gst_element_get_static_pad(data->video.convert, "sink");
    if (gst_pad_link(pad, sinkpad) != GST_PAD_LINK_OK) {
        g_printerr ("Video decodebin to convert link failed.\n");
    }
    gst_object_unref(sinkpad);
}

static void audio_decode_pad_added_handler(GstElement *src, GstPad *pad, gstreamer_t* data) {
    GstPad *sinkpad = gst_element_get_static_pad(data->audio.convert, "sink");
    if (gst_pad_link(pad, sinkpad) != GST_PAD_LINK_OK) {
        g_printerr ("Audio decodebin to convert link failed.\n");
    }
    gst_object_unref(sinkpad);
}

在main函数中注册回调

在创建decodebin之后,添加信号连接:

// 注册视频decodebin的pad-added回调
g_signal_connect(gstreamer.video.decode, "pad-added", G_CALLBACK(video_decode_pad_added_handler), &gstreamer);
// 注册音频decodebin的pad-added回调
g_signal_connect(gstreamer.audio.decode, "pad-added", G_CALLBACK(audio_decode_pad_added_handler), &gstreamer);

4. 修复qtdemux链接的判断逻辑错误

你的pad_added_handler中判断链接结果的逻辑有问题:if(!gst_pad_link(pad, sinkpad) != GST_PAD_LINK_OK )多了一个!,导致判断逻辑反转。修改为正确的判断:

if(gst_pad_link(pad, sinkpad) != GST_PAD_LINK_OK )
    printf("not link !!\n\n"); 

将上述修改整合后,代码即可正常运行,完整实现原gst-launch命令的功能。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 00:30:42