我有一个用C++编码的管道,如下所示:
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))
当无人驾驶飞机不在空中时,录像效果很好。但是,当它起飞后,经过大约15秒的正确录像,mp4mux元素将失败,因为消息“缓冲区没有PTS”。不幸的是,一些用户一直在报道这一点,但我无法复制它(因为它需要驾驶一架我没有的无人机),这是没有道理的。到目前为止,我猜想在那个特定的时刻,无线视频链路可能会出现一些拥塞,一些视频数据包可能会被推迟几个msecs,这可能会带来一些麻烦。
下面是创建appsrc的(简化的)代码
_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驱动程序填满时,它会像这样被推到管道中:
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的信息:
[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.
我试过的是:
所以我的问题是:
编辑:I在打印每个缓冲区的PTS和DTS时,设法“原位”地复制了它,使用一个附在tee元素接收器垫上的探针,发现问题缓冲区没有,没有DTS,也没有PTS。也许我的h264parse或capsfilter在我的应用程序和我的发球间做了一些讨厌的事情?
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的缓冲区。
时,会遇到许多类似于此"[parser] unable to compute timestamp: VUI not present"
的错误
的原因
所以,现在我试着在这两种选择中做出选择:
发布于 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元素的接收器垫上添加了一个探针,以确保在每个缓冲区中都有正确的时间戳。否则,它们将被迫运行元素的时间如下所示。
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;
}
这样做之后,我就不能再用我的测试转储来重现这个问题了。
https://stackoverflow.com/questions/68534236
复制相似问题