使用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; }
关键修改说明
- 绑定Caps到AppSrc:通过
g_object_set(appsrc, "caps", caps, NULL)明确告知AppSrc输入帧的格式参数。 - 添加时间戳:为每个Buffer设置PTS/DTS和持续时间,符合GStreamer的流时间模型。
- 替换为GPU编码器:根据平台选择对应的GPU H265编码器(如NVIDIA的
nvh265enc)。 - 严谨的元素链接:使用
gst_element_link_many批量链接,并检查链接结果。 - 完整的EOS处理:循环等待总线消息,直到收到EOS或错误消息,确保MP4封装器完成文件写入。
内容的提问来源于stack exchange,提问作者MeiH
相关产品推荐
相关产品推荐

