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

使用GStreamer将自定义帧编码为GPU H265视频遇空输出求助

GStreamer AppSrc推帧无输出问题排查与修复

问题背景

开发C++应用时,需要将unsigned char*格式的图像帧通过GStreamer的GPU加速H265编码器编码为视频,采用AppSrc替代摄像头输入,但输出视频为空,gst_app_src_push_buffer未生效。

代码中的核心错误

  • AppSrc未绑定输入Caps:创建了Caps但未关联到AppSrc,下游元素无法识别输入帧的格式、分辨率和帧率,导致帧被丢弃。
  • 缺少帧时间戳:GStreamer依赖时间戳管理流,无时间戳的帧会被下游元素忽略。
  • 元素链接不严谨:直接调用gst_element_link可能因Caps不匹配导致链接失败,需确保上下游Caps兼容。
  • 未使用GPU编码器:原代码用x264enc(CPU编码器),未满足GPU加速需求,应替换为对应平台的GPU H265编码器。
  • 未等待编码与封装完成:发送EOS后未等待MP4封装器完成文件写入,导致文件未正确闭合。

修正后的代码

#include <gst/gst.h>
#include <cstring>

int main(int argc, char* argv[]) {
    GstElement *pipeline, *appsrc, *videoconvert, *h265enc, *mp4mux, *filesink;
    GstCaps *caps;
    GstBuffer *buf;
    GstMapInfo map;
    GstClockTime timestamp = 0;
    const guint fps = 30;
    const guint frame_duration = GST_SECOND / fps;

    gst_init(&argc, &argv);

    // 创建管道与元素
    pipeline = gst_pipeline_new("mypipeline");
    appsrc = gst_element_factory_make("appsrc", "mysource");
    videoconvert = gst_element_factory_make("videoconvert", "myconvert");
    // 替换为GPU编码器:NVIDIA用nvh265enc,Intel/AMD用v4l2h265enc或vaapih265enc
    h265enc = gst_element_factory_make("nvh265enc", "myencoder");
    mp4mux = gst_element_factory_make("mp4mux", "mymux");
    filesink = gst_element_factory_make("filesink", "myfileoutput");

    if (!pipeline || !appsrc || !videoconvert || !h265enc || !mp4mux || !filesink) {
        g_printerr("Not all elements could be created.\n");
        return -1;
    }

    // 设置文件输出路径
    g_object_set(filesink, "location", "output.mp4", NULL);
    // 设置AppSrc为实时模式,匹配帧率
    g_object_set(appsrc, "is-live", TRUE, "format", GST_FORMAT_TIME, NULL);

    // 定义输入帧的Caps并绑定到AppSrc
    caps = gst_caps_from_string("video/x-raw, format=(string)BGR, width=(int)800, height=(int)600, framerate=(fraction)30/1");
    g_object_set(appsrc, "caps", caps, NULL);
    gst_caps_unref(caps);

    // 添加元素到管道并链接
    gst_bin_add_many(GST_BIN(pipeline), appsrc, videoconvert, h265enc, mp4mux, filesink, NULL);
    if (!gst_element_link_many(appsrc, videoconvert, h265enc, mp4mux, filesink, NULL)) {
        g_printerr("Elements could not be linked.\n");
        gst_object_unref(pipeline);
        return -1;
    }

    // 启动管道
    gst_element_set_state(pipeline, GST_STATE_PLAYING);

    // 推送10帧测试数据
    for (int i = 0; i < 10; i++) {
        buf = gst_buffer_new_and_alloc(800 * 600 * 3);
        gst_buffer_map(buf, &map, GST_MAP_WRITE);
        memset(map.data, i * 25, map.size); // 生成渐变的测试帧
        gst_buffer_unmap(buf, &map);

        // 设置帧的时间戳和持续时间
        GST_BUFFER_PTS(buf) = timestamp;
        GST_BUFFER_DTS(buf) = timestamp;
        GST_BUFFER_DURATION(buf) = frame_duration;
        timestamp += frame_duration;

        // 推送缓冲帧
        GstFlowReturn ret = gst_app_src_push_buffer(GST_APP_SRC(appsrc), buf);
        if (ret != GST_FLOW_OK) {
            g_printerr("Failed to push buffer: %s\n", gst_flow_return_get_name(ret));
            gst_buffer_unref(buf);
            break;
        }
    }

    // 发送流结束信号
    gst_app_src_end_of_stream(GST_APP_SRC(appsrc));

    // 等待管道处理完成所有帧并关闭文件
    GstBus *bus = gst_element_get_bus(pipeline);
    GstMessage *msg;
    gboolean done = FALSE;
    while (!done) {
        msg = gst_bus_timed_pop(bus, GST_CLOCK_TIME_NONE);
        if (!msg) break;

        switch (GST_MESSAGE_TYPE(msg)) {
            case GST_MESSAGE_EOS:
                g_print("End of stream reached.\n");
                done = TRUE;
                break;
            case GST_MESSAGE_ERROR: {
                GError *err;
                gchar *debug_info;
                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);
                done = TRUE;
                break;
            }
            default:
                break;
        }
        gst_message_unref(msg);
    }

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

    return 0;
}

关键修改说明

  1. 绑定Caps到AppSrc:通过g_object_set(appsrc, "caps", caps, NULL)明确告知AppSrc输入帧的格式参数。
  2. 添加时间戳:为每个Buffer设置PTS/DTS和持续时间,符合GStreamer的流时间模型。
  3. 替换为GPU编码器:根据平台选择对应的GPU H265编码器(如NVIDIA的nvh265enc)。
  4. 严谨的元素链接:使用gst_element_link_many批量链接,并检查链接结果。
  5. 完整的EOS处理:循环等待总线消息,直到收到EOS或错误消息,确保MP4封装器完成文件写入。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 04:36:04