首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >GStreamer mp4mux使用自定义appsrc提供“缓冲区没有PTS”错误。

GStreamer mp4mux使用自定义appsrc提供“缓冲区没有PTS”错误。
EN

Stack Overflow用户
提问于 2021-07-26 17:34:52
回答 1查看 2.1K关注 0票数 3

我有一个用C++编码的管道,如下所示:

代码语言:javascript
运行
复制
appsrc do-timestamp=TRUE is-live=TRUE caps=
“video/x-h264, stream-format=(string)byte-stream, alignment=(string)none, framerate=(fraction)0/1” min-latency=300000000 ! h264parse ! video/x-h264, stream-format=(string)avc, alignment=(string)au ! tee name=t \
t. ! queue ! valve drop=FALSE ! decodebin ! glupload ! glcolorconvert ! qtsink sync=FALSE \
t. ! queue ! valve drop=FALSE ! mp4mux reserved-max-duration=3600000000000 reserved-moov-update-period=10000000000 ! filesink sync=FALSE location=”....../out.mp4”

appsrc将来自无人机USB无线视频接收器的视频注入管道。

一些更多背景:

USB接收硬件给我们512个字节的非时间戳的原始附件-B h.264视频(sync=FALSE))

  • The

  • ,框架应该是60 fps,但在实践中它很少跟上它,并且根据信号强度(因此framerate=(分数)0/1“)而变化,这就是为什么qtsink和文件墨水不同步到管道

  • 硬件引入至少300 ms的延迟,在appsrc

  • appsrc中设置的是自动时间戳我的缓冲区,(do-timestamp=TRUE)

  • I’m使用mp4mux保留-最大持续时间和保留-moov-更新-期间,以防止应用程序崩溃破坏mp4文件

  • ,我使用GStreamer 1.18.4来处理Android

当无人驾驶飞机不在空中时,录像效果很好。但是,当它起飞后,经过大约15秒的正确录像,mp4mux元素将失败,因为消息“缓冲区没有PTS”。不幸的是,一些用户一直在报道这一点,但我无法复制它(因为它需要驾驶一架我没有的无人机),这是没有道理的。到目前为止,我猜想在那个特定的时刻,无线视频链路可能会出现一些拥塞,一些视频数据包可能会被推迟几个msecs,这可能会带来一些麻烦。

下面是创建appsrc的(简化的)代码

代码语言:javascript
运行
复制
   _pAppSrc = gst_element_factory_make("appsrc", "artosyn_source");
    gpointer pAppSrc = static_cast<gpointer>(_pAppSrc);

    // Retain one more ref, so the source is destroyed
    // in a controlled way
    gst_object_ref(_pAppSrc);

    pCaps = gst_caps_from_string("video/x-h264, stream-format=(string)byte-stream, alignment=none, framerate=(fraction)0/1"));
    g_object_set(G_OBJECT(pAppSrc), "caps", pCaps,
                                    "is-live", TRUE,
                                    "min-latency", G_GINT64_CONSTANT(300000000),
                                    "format", GST_FORMAT_TIME,
                                    "do-timestamp", TRUE,
                                    nullptr);

   _pBufferPool = gst_buffer_pool_new();

   pConfig = gst_buffer_pool_get_config (_pBufferPool);

   static const guint kBufferSize  = 512;
   static const guint kPoolSize    = 0x400000;
   static const guint kPoolSizeMax = 0x600000;

    qsizetype nBuffersMin = kPoolSize / kBufferSize;
    qsizetype nBuffersMax = kPoolSizeMax / kBufferSize;

    gst_buffer_pool_config_set_params(pConfig, pCaps, kBufferSize, nBuffersMin, nBuffersMax);

   gst_buffer_pool_set_config(_pBufferPool, pConfig);
   gst_buffer_pool_set_active(_pBufferPool, TRUE);

   gst_caps_unref(GST_CAPS(pCaps));

当一个新的缓冲区被USB驱动程序填满时,它会像这样被推到管道中:

代码语言:javascript
运行
复制
bool unref = false;

gst_buffer_unmap(b->pBuffer, &b->mapInfo);
gst_buffer_set_size(b->pBuffer, xfer.pXfer->actual_length);

if(result == LIBUSB_TRANSFER_COMPLETED)
{
    //-- DROP DATA IF NOT IN PLAYING STATE --
    GstState st, pend;
    GstStateChangeReturn scr = gst_element_get_state(GST_ELEMENT(_pAppSrc), &st, &pend, GST_CLOCK_TIME_NONE);
    Q_UNUSED(scr)
    bool drop = (st != GST_STATE_PLAYING);

    if(!drop)
    {
        GstFlowReturn ret = GST_FLOW_OK;

        // Push into pipeline
        ret = gst_app_src_push_buffer(GST_APP_SRC(_pAppSrc), b->pBuffer);

        if(ret != GST_FLOW_OK)
            qCDebug(MMCVideoLog()) << "Can't push buffer to the pipeline (" << ret << ")";
        else
            unref = false;  // Don't unref since gst_app_src_push_buffer() steals one reference and takes ownership
    }
} else if(result == LIBUSB_TRANSFER_CANCELLED)
{
    qCDebug(MMCVideoLog()) << "! Buffer canceled";
} else {
    qCDebug(MMCVideoLog()) << "? Buffer result = " << result;
}    

if(unref)
    gst_buffer_unref(b->pBuffer);

这就是我从一台受影响的机器上得到的Android logcat的信息:

代码语言:javascript
运行
复制
[07-22 18:37:45.753 17414:18734 E/QGroundControl]
VideoReceiverLog: GStreamer error: [element ' "mp4mux0" ']  Could not multiplex stream.

[07-22 18:37:45.753 17414:18734 E/QGroundControl]
VideoReceiverLog: Details:  ../gst/isomp4/gstqtmux.c(5010): gst_qt_mux_add_buffer (): /GstPipeline:receiver/GstBin:sinkbin/GstMP4Mux:mp4mux0:
Buffer has no PTS.

我试过的是:

  • 将GstBaseParser pts_interpolation设置为TRUE,infer_ts设置为TRUE

所以我的问题是:

  • ,你能看到我的代码有什么问题吗?,我遗漏了什么?
  • ,我能依靠matroskamux暂时避免这个问题,直到我找到真正的原因吗?

编辑:I在打印每个缓冲区的PTS和DTS时,设法“原位”地复制了它,使用一个附在tee元素接收器垫上的探针,发现问题缓冲区没有,没有DTS,也没有PTS。也许我的h264parse或capsfilter在我的应用程序和我的发球间做了一些讨厌的事情?

代码语言:javascript
运行
复制
07-28 17:54:49.025  1932  2047 D : PTS:  295659241497 DTS:  295659241497
07-28 17:54:49.026  1932  2047 D : PTS:  295682488791 DTS:  295682488791
07-28 17:54:49.053  1932  2047 D : PTS:  295710463127 DTS:  295710463127
07-28 17:54:49.054  1932  2047 D : PTS:  18446744073709551615  DTS:  18446744073709551615
07-28 17:54:49.054  1932  2047 E : ************** NO PTS
07-28 17:54:49.054  1932  2047 E : ************** NO DTS
07-28 17:54:49.110  1932  2047 D : PTS:  295738607214 DTS:  295738607214
07-28 17:54:49.111  1932  2199 E : GStreamer error: [element ' "mp4mux1" ']  Could not multiplex stream.
07-28 17:54:49.111  1932  2199 E : Details:  ../gst/isomp4/gstqtmux.c(5010): gst_qt_mux_add_buffer (): /GstPipeline:receiver/GstBin:sinkbin/GstMP4Mux:mp4mux1:
07-28 17:54:49.111  1932  2199 E : Buffer has no PTS.

编辑2:在深入挖掘之后,我发现了更多的线索:我编写了一些代码,将所有通过USB传输的视频数据包连同时间戳一起转储到二进制文件中,并由播放器播放它。然后我去了现场,让顾客驾驶无人机,直到窃听器被触发。这样,我就可以随意地复制错误。

通过使用两个探针,一个连接到我的'appsrc‘元素的src垫上,另一个连接到我的’to‘元素的’接收器‘垫上,我打印了每个通过它们的数据包的PTS和DTS。

以下是我的调查结果:

TL;DR:在某个(随机)点上,即使h264parse被赋予时间戳缓冲区,它还是输出一个没有PTS和DTS的缓冲区。

  • 是生成通过USB的流的硬件h264编码器,它插入SPS NALS而没有VUI,当将h264parse调试级别设置为6

时,会遇到许多类似于此"[parser] unable to compute timestamp: VUI not present"的错误

  • -- "VUI不存在“错误非常一致,并且经常出现。大多数时候都没人注意到,所以我不能百分之百肯定这就是

的原因

  • 由于由appsrc推送的h264缓冲区没有对齐,而h264parse输出的是au对齐缓冲区,因此appsrc中的512个字节缓冲区的数量与h264parse中的缓冲区数之间没有直接关系。因此,我认为可以相当安全地说,appsrc生成的时间戳与h264parse之外的时间戳之间也没有直接关系。h264parse必须重新计算它们。--

  • My h264流非常简单:SPS->PPS->I>(多个P-帧)。没有B帧,每个帧都包含在一个大的胖片中(只有1NAL)。

所以,现在我试着在这两种选择中做出选择:

  1. 手动计算和插入假SPS与VUI每次VUI无SPS被检测到。流应该有可变的帧,因为无线信号强度(通常编码为60 The )和分辨率和图片属性总是一样的。
  2. 自己解析流,以便为流水线提供时间戳的自动对齐缓冲区。
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-08-23 18:54:55

经过多次脑震荡后,我终于找出了原因。有点模糊..。

无人机中的无线视频发射机能够根据无线电链路的可用带宽动态地改变视频比特率。或者换个说法:当无人机离得太远或者干扰很大时,视频质量就会下降。

当这种情况发生时,视频帧(仅包含在一个NAL中的一个片段中)开始变得小得多。由于我正在读取没有特定对齐方式的h264流的512字节块,并将它们作为GstBuffers转发给GStreamer,如果一个帧所需数据的大小小于512个字节,缓冲区可能包含多个帧。在本例中,h264parse认为这是N个具有相同时间戳的不同缓冲区。默认行为似乎是忽略上游PTS和DTS,并试图通过从SPS读取VUI来根据帧的持续时间来计算时间戳,而SPS中不存在该时间戳。因此,离开h264parse的源盘的缓冲区将没有PTS和DTS,从而引起mp4mux的抱怨。

正如我前面提到的,我的流非常简单,所以我编写了一个简单的解析器来检测每个NAL的开头。通过这种方式,我可以“解压”来自USB硬件的流,并确保插入到管道中的每个缓冲区只包含一个NAL (因此,多达一个帧),并且独立地加盖了时间戳。

为了保持冗余,我在tee元素的接收器垫上添加了一个探针,以确保在每个缓冲区中都有正确的时间戳。否则,它们将被迫运行元素的时间如下所示。

代码语言:javascript
运行
复制
if (!GST_BUFFER_PTS_IS_VALID(buffer) || !GST_BUFFER_DTS_IS_VALID(buffer))
{

    GstElement* elm = gst_pad_get_parent_element(pad);
    qCDebug(VideoReceiverLog) << "Invalid timestamp out of source. Replacing with element running time.";
    GstClockTime ts = gst_element_get_current_running_time(elm);
    GST_BUFFER_PTS(buffer) = ts;
    GST_BUFFER_DTS(buffer) = ts;
}

这样做之后,我就不能再用我的测试转储来重现这个问题了。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68534236

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档