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

求助:基于GStreamer的MP4转AV1+Opus的C++工具无限运行问题

问题分析与修复方案

核心问题分析

你的代码存在多个关键缺陷,导致Pipeline阻塞无限运行:

  • 缺少音频链路与封装器:原代码仅处理视频流,未对音频进行解码→编码(Opus)的处理,且直接用filesink接收编码器输出,没有MKV封装器(matroskamux)来合并音视频流,导致音频流无法处理,Pipeline卡住。
  • 无效的静态链路:decodebin的输出Pad是动态生成的,无法提前静态链接到av1enc;filesink也不能直接接收编码后的裸流,必须通过封装器处理。
  • Pad链接逻辑错误:on_pad_added函数不分视频/音频Pad,直接尝试链接到视频编码器av1enc,音频Pad链接必然失败,导致未处理的流阻塞Pipeline。
  • 错误的Caps设置:"YUV"不是GStreamer支持的具体视频格式(需指定I420/NV12等具体格式),且强制给编码器设置Caps会限制输入,可能与解码后的格式不匹配。

修复后的完整代码

#include <gst/gst.h>

static void on_pad_added(GstElement* decodebin, GstPad* pad, gpointer data) {
    GstCaps* caps = gst_pad_get_current_caps(pad);
    const GstStructure* str = gst_caps_get_structure(caps, 0);
    const gchar* media_type = gst_structure_get_name(str);

    GstElement* pipeline = GST_ELEMENT(data);
    GstElement* av1enc = gst_bin_get_by_name(GST_BIN(pipeline), "av1-encoder");
    GstElement* opusenc = gst_bin_get_by_name(GST_BIN(pipeline), "opus-encoder");
    GstElement* videoconvert = gst_bin_get_by_name(GST_BIN(pipeline), "video-convert");
    GstElement* audioconvert = gst_bin_get_by_name(GST_BIN(pipeline), "audio-convert");

    GstPad* sinkpad = NULL;
    if (g_str_has_prefix(media_type, "video/")) {
        // 链接视频Pad到videoconvert
        sinkpad = gst_element_get_static_pad(videoconvert, "sink");
    } else if (g_str_has_prefix(media_type, "audio/")) {
        // 链接音频Pad到audioconvert
        sinkpad = gst_element_get_static_pad(audioconvert, "sink");
    }

    if (sinkpad != NULL) {
        if (gst_pad_is_linked(sinkpad)) {
            g_print("Pad already linked, skipping.\n");
            goto cleanup;
        }
        GstPadLinkReturn ret = gst_pad_link(pad, sinkpad);
        if (ret != GST_PAD_LINK_OK) {
            g_printerr("Failed to link pad: %d\n", ret);
        } else {
            g_print("Successfully linked %s pad\n", media_type);
        }
        gst_object_unref(sinkpad);
    } else {
        g_printerr("Unsupported media type: %s\n", media_type);
    }

cleanup:
    gst_caps_unref(caps);
    gst_object_unref(av1enc);
    gst_object_unref(opusenc);
    gst_object_unref(videoconvert);
    gst_object_unref(audioconvert);
}

int main(int argc, char* argv[]) {
    gst_init(&argc, &argv);

    // 创建Pipeline及所有元素
    GstElement* pipeline = gst_pipeline_new("transcode-pipeline");
    GstElement* filesrc = gst_element_factory_make("filesrc", "file-source");
    GstElement* decodebin = gst_element_factory_make("decodebin", "decode-bin");
    // 视频处理链路
    GstElement* videoconvert = gst_element_factory_make("videoconvert", "video-convert");
    GstElement* av1enc = gst_element_factory_make("av1enc", "av1-encoder");
    // 音频处理链路
    GstElement* audioconvert = gst_element_factory_make("audioconvert", "audio-convert");
    GstElement* audioresample = gst_element_factory_make("audioresample", "audio-resample");
    GstElement* opusenc = gst_element_factory_make("opusenc", "opus-encoder");
    // MKV封装器与输出
    GstElement* matroskamux = gst_element_factory_make("matroskamux", "mkv-muxer");
    GstElement* filesink = gst_element_factory_make("filesink", "file-sink");

    if (!pipeline || !filesrc || !decodebin || !videoconvert || !av1enc || !audioconvert || !audioresample || !opusenc || !matroskamux || !filesink) {
        g_printerr("Failed to create one or more elements!\n");
        return -1;
    }

    // 设置输入输出文件路径
    const char* inputFileName = "../input.mp4";
    const char* outputFileName = "output.mkv";
    g_object_set(G_OBJECT(filesrc), "location", inputFileName, NULL);
    g_object_set(G_OBJECT(filesink), "location", outputFileName, NULL);
    // 禁用filesink的同步,避免因编码速度问题阻塞
    g_object_set(G_OBJECT(filesink), "sync", FALSE, NULL);

    // 将元素添加到Pipeline
    gst_bin_add_many(GST_BIN(pipeline), filesrc, decodebin, videoconvert, av1enc, audioconvert, audioresample, opusenc, matroskamux, filesink, NULL);

    // 链接固定链路
    if (!gst_element_link(filesrc, decodebin)) {
        g_printerr("Failed to link filesrc and decodebin!\n");
        return -1;
    }
    if (!gst_element_link_many(videoconvert, av1enc, NULL)) {
        g_printerr("Failed to link video elements!\n");
        return -1;
    }
    if (!gst_element_link_many(audioconvert, audioresample, opusenc, NULL)) {
        g_printerr("Failed to link audio elements!\n");
        return -1;
    }
    if (!gst_element_link(matroskamux, filesink)) {
        g_printerr("Failed to link muxer and filesink!\n");
        return -1;
    }

    // 链接编码器到muxer(动态Pad,需要申请)
    GstPad* av1_src_pad = gst_element_get_static_pad(av1enc, "src");
    GstPad* mux_video_sink_pad = gst_element_request_pad_simple(matroskamux, "video_%u");
    gst_pad_link(av1_src_pad, mux_video_sink_pad);
    gst_object_unref(av1_src_pad);
    gst_object_unref(mux_video_sink_pad);

    GstPad* opus_src_pad = gst_element_get_static_pad(opusenc, "src");
    GstPad* mux_audio_sink_pad = gst_element_request_pad_simple(matroskamux, "audio_%u");
    gst_pad_link(opus_src_pad, mux_audio_sink_pad);
    gst_object_unref(opus_src_pad);
    gst_object_unref(mux_audio_sink_pad);

    // 绑定decodebin的pad-added信号,动态链接解码后的流到对应处理链路
    g_signal_connect(G_OBJECT(decodebin), "pad-added", G_CALLBACK(on_pad_added), pipeline);

    // 启动Pipeline
    gst_element_set_state(pipeline, GST_STATE_PLAYING);

    // 监听Bus消息
    GstBus* bus = gst_element_get_bus(pipeline);
    GstMessage* msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_EOS | GST_MESSAGE_ERROR);

    if (msg != NULL) {
        if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ERROR) {
            GError* error;
            gchar* debug_info;
            gst_message_parse_error(msg, &error, &debug_info);
            g_printerr("Error from %s: %s\n", GST_OBJECT_NAME(msg->src), error->message);
            g_printerr("Debug info: %s\n", debug_info ? debug_info : "none");
            g_clear_error(&error);
            g_free(debug_info);
        } else if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_EOS) {
            g_print("Stream finished successfully.\n");
        }
        gst_message_unref(msg);
    }

    // 资源清理
    gst_element_set_state(pipeline, GST_STATE_NULL);
    gst_object_unref(pipeline);
    gst_deinit();

    return 0;
}

关键修改说明

  1. 完善音视频链路:添加videoconvert(格式转换,确保编码器输入格式兼容)、audioconvert/audioresample(音频格式/采样率转换,适配Opus编码器),以及opusenc音频编码器。
  2. 添加MKV封装器:使用matroskamux合并音视频流,再输出到filesink,这是生成MKV文件的必要步骤。
  3. 动态Pad处理优化:on_pad_added函数根据Pad的媒体类型(视频/音频),分别链接到对应处理链路,避免错误链接。
  4. 修复静态链路:仅链接固定的静态Pad链路,动态生成的Pad通过信号处理链接;编码器到封装器的Pad使用request_pad_simple申请动态Pad。
  5. 移除错误Caps设置:删除了无效的YUV格式设置,让GStreamer自动协商兼容的格式,减少链路冲突。
  6. 添加错误检查:增加元素创建、链路链接的错误检查,方便排查问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 10:27:45