使用GStreamer Appsrc/Appsink解码H.264转JPEG协商失败求助
问题:GStreamer Appsrc构建Pipeline时出现not-negotiated错误
需要解码单帧H.264文件(由NV12编码而来),目标是解码回NV12格式后转换为JPEG。命令行gst-launch-1.0能正常实现,但用appsrc/appsink通过代码构建Pipeline时触发not-negotiated错误。
正常运行的命令行Pipeline
$ gst-launch-1.0 filesrc location=input_h264.h264 ! video/x-h264, width=1920, height=1080, encoding-name=H264 ! h264parse! avdec_h264 ! videoconvert ! video/x-raw, format=NV12 ! jpegenc ! image/jpeg ! filesink location=output.jpg
错误信息
Error: Internal data stream error. Error received from element mysource: Internal data stream error. Debugging information: gstbasesrc.c(3072): gst_base_src_loop (): /GstPipeline:mypipeline/GstAppSrc:mysource: streaming stopped, reason not-negotiated (-4)
问题代码片段
pipeline = gst_pipeline_new("mypipeline"); appsrc = gst_element_factory_make("appsrc", "mysource"); jpegenc = gst_element_factory_make("jpegenc", "myenc"); parser = gst_element_factory_make("h264parse", "parser"); decoder = gst_element_factory_make("avdec_h264", "decode"); rawfilter = gst_element_factory_make("capsfilter", "rawfilter"); h264filter = gst_element_factory_make("capsfilter", "h264filter"); videoconvert = gst_element_factory_make("videoconvert", "myvideoconvert"); appsink = gst_element_factory_make("appsink", "mysink"); // 省略元素创建检查逻辑... caps = gst_caps_new_simple("video/x-h264", "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, "encoding-name", G_TYPE_STRING, "H264", "framerate", GST_TYPE_FRACTION, 1, 1, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, "stream-format", G_TYPE_STRING, "avc", "parsed", G_TYPE_BOOLEAN, TRUE, "level", G_TYPE_STRING, "4", NULL); // 设置appsrc参数 const gchar *customStreamId = "stream-1"; g_object_set(G_OBJECT(appsrc), "stream-id", customStreamId, NULL); g_object_set(G_OBJECT(appsrc), "stream-type", GST_APP_STREAM_TYPE_STREAM, NULL); g_object_set(G_OBJECT(rawfilter), "caps", caps, NULL); g_object_set(G_OBJECT(h264filter), "caps", caps, NULL); gst_caps_unref(caps); // 设置blocksize char szTemp[64]; sprintf(szTemp, "%d", (int)fileSize); g_object_set(G_OBJECT (appsrc), "blocksize", szTemp, NULL); // 设置JPEG质量 g_object_set(G_OBJECT (jpegenc), "quality", quality, NULL); // 创建主循环与总线监听 loop = g_main_loop_new(NULL, FALSE); bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline)); bus_watch_id = gst_bus_add_watch(bus, bus_call, loop); gst_object_unref(bus); // 添加并链接元素 gst_bin_add_many(GST_BIN(pipeline), appsrc, parser, rawfilter, decoder, h264filter,videoconvert,jpegenc, appsink, NULL); if (gst_element_link_many(appsrc, rawfilter, parser, decoder,videoconvert,jpegenc, appsink, NULL) != TRUE) { g_error("Failed to link elements"); return NULL; }
调试日志(级别4)
0:00:00.099873945 274680 0x7f6b24075580 WARN h264parse gsth264parse.c:2963:gst_h264_parse_set_caps:<parser> H.264 AVC caps, but no codec_data 0:00:00.099908490 274680 0x7f6b24075580 WARN h264parse gsth264parse.c:2989:gst_h264_parse_set_caps:<parser> refused caps video/x-h264, width=(int)1920, height=(int)1080, encoding-name=(string)H264, framerate=(fraction)1/1, pixel-aspect-ratio=(fraction)1/1, stream-format=(string)avc, parsed=(boolean)true, level=(string)4 Read for data push Buffer Size: 122460 0:00:00.108377141 274680 0x7f6b24075580 INFO GST_EVENT gstevent.c:900:gst_event_new_segment: creating segment event time segment start=0:00:00.000000000, offset=0:00:00.000000000, stop=99:99:99.999999999, rate=1.000000, applied_rate=1.000000, flags=0x00, time=0:00:00.000000000, base=0:00:00.000000000, position 0:00:00.000000000, duration 99:99:99.999999999 0:00:00.108472425 274680 0x7f6b24075580 WARN h264parse gsth264parse.c:2963:gst_h264_parse_set_caps:<parser> H.264 AVC caps, but no codec_data 0:00:00.108518647 274680 0x7f6b24075580 WARN h264parse gsth264parse.c:2989:gst_h264_parse_set_caps:<parser> refused caps video/x-h264, width=(int)1920, height=(int)1080, encoding-name=(string)H264, framerate=(fraction)1/1, pixel-aspect-ratio=(fraction)1/1, stream-format=(string)avc, parsed=(boolean)true, level=(string)4 0:00:00.108548569 274680 0x7f6b24075580 INFO basesrc gstbasesrc.c:2962:gst_base_src_loop:<mysource> marking pending DISCONT 0:00:00.108589666 274680 0x7f6b24075580 WARN h264parse gsth264parse.c:2963:gst_h264_parse_set_caps:<parser> H.264 AVC caps, but no codec_data 0:00:00.108623674 274680 0x7f6b24075580 WARN h264parse gsth264parse.c:2989:gst_h264_parse_set_caps:<parser> refused caps video/x-h264, width=(int)1920, height=(int)1080, encoding-name=(string)H264, framerate=(fraction)1/1, pixel-aspect-ratio=(fraction)1/1, stream-format=(string)avc, parsed=(boolean)true, level=(string)4 0:00:00.108726002 274680 0x7f6b24075580 WARN basesrc gstbasesrc.c:3072:gst_base_src_loop:<mysource> error: Internal data stream error. 0:00:00.108749012 274680 0x7f6b24075580 WARN basesrc gstbasesrc.c:3072:gst_base_src_loop:<mysource> error: streaming stopped, reason not-negotiated (-4) 0:00:00.108812807 274680 0x7f6b24075580 INFO GST_ERROR_SYSTEM gstelement.c:2153:gst_element_message_full_with_details:<mysource> posting message: Internal data stream error. 0:00:00.108890560 274680 0x7f6b24075580 INFO GST_ERROR_SYSTEM gstelement.c:2180:gst_element_message_full_with_details:<mysource> posted error message: Internal data stream error. Error: Internal data stream error.
问题根源与修复方案
核心问题分析
从日志警告可以定位到几个关键错误:
- Caps配置冲突:手动设置
parsed=true和stream-format=avc,但appsrc输出的是原始H.264字节流,h264parse的作用就是解析原始流,这两个参数会导致解析器拒绝Caps。 - 元素链接顺序错误:冗余添加了
h264filter且未使用,同时将rawfilter(CapsFilter)放在appsrc和h264parse之间,位置不合理。 - 参数类型错误:
appsrc的blocksize是整数类型,代码中传入字符串导致设置无效。 - 缺少NV12格式约束:命令行中明确指定了解码后转NV12,但代码中未添加对应CapsFilter。
修复步骤
1. 修正H.264 Caps配置
移除parsed=true和stream-format=avc,让h264parse自动处理原始字节流:
GstCaps *h264_caps = gst_caps_new_simple("video/x-h264", "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, "encoding-name", G_TYPE_STRING, "H264", "framerate", GST_TYPE_FRACTION, 1, 1, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, "level", G_TYPE_STRING, "4", NULL);
2. 调整元素结构与链接顺序
- 移除冗余的
h264filter - 添加约束NV12格式的CapsFilter
- 直接给appsrc设置Caps,无需额外的CapsFilter在appsrc和解析器之间
- 修正元素链接顺序,对齐命令行逻辑
// 添加NV12格式约束的CapsFilter GstElement *nv12_filter = gst_element_factory_make("capsfilter", "nv12filter"); GstCaps *nv12_caps = gst_caps_new_simple("video/x-raw", "format", G_TYPE_STRING, "NV12", NULL); g_object_set(G_OBJECT(nv12_filter), "caps", nv12_caps, NULL); gst_caps_unref(nv12_caps); // 给appsrc直接设置H.264 Caps g_object_set(G_OBJECT(appsrc), "caps", h264_caps, NULL); gst_caps_unref(h264_caps); // 修正元素添加与链接 gst_bin_add_many(GST_BIN(pipeline), appsrc, parser, decoder, videoconvert, nv12_filter, jpegenc, appsink, NULL); if (!gst_element_link_many(appsrc, parser, decoder, videoconvert, nv12_filter, jpegenc, appsink, NULL)) { g_error("Failed to link elements"); return NULL; }
3. 修正Blocksize参数设置
直接传入整数类型:
g_object_set(G_OBJECT(appsrc), "blocksize", (guint)fileSize, NULL);
4. 调整Appsrc流类型
单帧文件适合使用随机访问类型:
g_object_set(G_OBJECT(appsrc), "stream-type", GST_APP_STREAM_TYPE_RANDOM_ACCESS, NULL);
5. 确保数据推送流程正确
推送数据到appsrc时,需要按顺序发送流事件、段事件、数据Buffer和EOS事件:
// 发送流开始事件 GstEvent *start_event = gst_event_new_stream_start("stream-1"); gst_element_send_event(appsrc, start_event); // 发送段事件 GstSegment segment; gst_segment_init(&segment, GST_FORMAT_TIME); gst_segment_set_start(&segment, GST_FORMAT_TIME, 0); gst_segment_set_stop(&segment, GST_FORMAT_TIME, GST_CLOCK_TIME_NONE); GstEvent *seg_event = gst_event_new_segment(&segment); gst_element_send_event(appsrc, seg_event); // 推送H.264数据Buffer GstBuffer *buffer = gst_buffer_new_allocate(NULL, fileSize, NULL); gst_buffer_fill(buffer, 0, file_data, fileSize); gboolean ret; g_signal_emit_by_name(appsrc, "push-buffer", buffer, &ret); gst_buffer_unref(buffer); // 推送EOS事件 g_signal_emit_by_name(appsrc, "end-of-stream", &ret);
内容的提问来源于stack exchange,提问作者Abdul Rehman
相关产品推荐
相关产品推荐

