169 g_object_set(buffer,
"latency",
latencyMs,
"do-lost", TRUE,
"do-retransmission",
175 g_object_set(buffer,
"rtx-delay", 25,
"rtx-max-retries", 1,
nullptr);
180struct DynamicLinkContext
188void linkPad(
GstElement* element, GstPad* pad, gpointer data)
190 const auto* ctx =
static_cast<const DynamicLinkContext*
>(data);
194 if (GST_PAD_DIRECTION(pad) != GST_PAD_SRC) {
198 bool isVideo =
false;
200 GstCaps* caps = gst_pad_get_current_caps(pad);
202 caps = gst_pad_query_caps(pad,
nullptr);
205 const guint n = gst_caps_get_size(caps);
206 for (guint i = 0; i < n; ++i) {
207 const GstStructure* st = gst_caps_get_structure(caps, i);
208 const gchar* sname = gst_structure_get_name(st);
212 if (g_str_has_prefix(sname,
"video/")) {
214 }
else if (g_str_equal(sname,
"application/x-rtp")) {
220 gst_clear_caps(&caps);
228 if (GstPad* parserSink = gst_element_get_static_pad(ctx->binParser,
"sink")) {
229 const gboolean alreadyLinked = gst_pad_is_linked(parserSink);
230 gst_object_unref(parserSink);
240 const auto removeDynamicBuffer = [&dynamicBuffer] {
241 if (!dynamicBuffer) {
245 GstObject* parent = gst_object_get_parent(GST_OBJECT(dynamicBuffer));
247 gst_bin_remove(GST_BIN(parent), dynamicBuffer);
248 gst_object_unref(parent);
250 dynamicBuffer =
nullptr;
253 if (isRtp && ctx->allowJitterBuffer && (ctx->config.jitterBuffer !=
JitterBuffer::None)) {
254 GstElement* bin = GST_ELEMENT(gst_object_get_parent(GST_OBJECT(ctx->binParser)));
256 GstElement* buffer = gst_element_factory_make(
"rtpjitterbuffer",
nullptr);
257 if (buffer && gst_bin_add(GST_BIN(bin), buffer)) {
258 configureJitterBuffer(buffer, ctx->config, ctx->latencyMs);
259 if (gst_element_sync_state_with_parent(buffer) && gst_element_link(buffer, ctx->binParser)) {
261 dynamicBuffer = buffer;
263 qCWarning(GstSourceFactoryLog)
264 <<
"dynamic rtpjitterbuffer link/sync failed; linking pad straight to parser";
265 gst_bin_remove(GST_BIN(bin), buffer);
269 qCWarning(GstSourceFactoryLog) <<
"dynamic rtpjitterbuffer create/add failed; skipping jitter buffer";
271 gst_object_unref(buffer);
274 gst_object_unref(bin);
278 gchar* name = gst_pad_get_name(pad);
280 qCWarning(GstSourceFactoryLog) <<
"gst_pad_get_name() failed";
281 removeDynamicBuffer();
285 if (!gst_element_link_pads(element, name, downstream,
"sink")) {
286 qCWarning(GstSourceFactoryLog) <<
"gst_element_link_pads() failed for" << name;
287 removeDynamicBuffer();
290 g_clear_pointer(&name, g_free);
303 GstElement* source = gst_element_factory_make(
"rtspsrc",
"source");
305 qCCritical(GstSourceFactoryLog) <<
"gst_element_factory_make('rtspsrc') failed";
309 QUrl cleanUrl(sourceUrl);
310 cleanUrl.setUserInfo(QString());
311 const QByteArray cleanLocation = cleanUrl.toEncoded();
315 constexpr GstRTSPLowerTrans kRtspProtocols =
316 static_cast<GstRTSPLowerTrans
>(GST_RTSP_LOWER_TRANS_UDP | GST_RTSP_LOWER_TRANS_TCP);
320 const gboolean doRetransmission =
323 g_object_set(source,
"location", cleanLocation.constData(),
"latency", rtspLatencyMs,
"do-rtcp", TRUE,
324 "do-retransmission", doRetransmission,
"tcp-timeout", kRtspTcpTimeoutUs,
"udp-reconnect", TRUE,
325 "drop-on-latency", dropOnLatency,
"retry", kRtspRetry,
"protocols", kRtspProtocols,
nullptr);
327 const QString rtspUser = sourceUrl.userName(QUrl::FullyDecoded);
328 const QString rtspPassword = sourceUrl.password(QUrl::FullyDecoded);
329 if (!rtspUser.isEmpty()) {
330 g_object_set(source,
"user-id", rtspUser.toUtf8().constData(),
"user-pw",
331 rtspPassword.toUtf8().constData(),
nullptr);
336GstElement* buildTcpSource(
const QUrl& sourceUrl)
338 const int port = sourceUrl.port();
339 if (!validPort(port)) {
340 qCWarning(GstSourceFactoryLog) <<
"Invalid TCP port" <<
port <<
"in"
344 const QString host = sourceUrl.host();
345 if (host.isEmpty()) {
346 qCWarning(GstSourceFactoryLog) <<
"Missing host in TCP URI"
351 GstElement* source = gst_element_factory_make(
"tcpclientsrc",
"source");
353 qCCritical(GstSourceFactoryLog) <<
"gst_element_factory_make('tcpclientsrc') failed";
357 g_object_set(source,
"host", host.toUtf8().constData(),
"port",
static_cast<int>(port),
nullptr);
361GstElement* buildUdpSource(
const QUrl& sourceUrl,
bool isUdpH264,
bool isUdpH265)
363 const int port = sourceUrl.port();
364 if (!validPort(port)) {
365 qCWarning(GstSourceFactoryLog) <<
"Invalid UDP port" <<
port <<
"in"
370 GstElement* source = gst_element_factory_make(
"udpsrc",
"source");
372 qCCritical(GstSourceFactoryLog) <<
"gst_element_factory_make('udpsrc') failed";
377 QUrl udpUrl(sourceUrl);
378 udpUrl.setScheme(QStringLiteral(
"udp"));
379 udpUrl.setUserInfo(QString());
380 const QByteArray udpUri = udpUrl.toEncoded();
384 int udpBufferSize = kUdpBufferSizeBytes;
387 static const int s_rmemMax = [] {
388 QFile rmemMaxFile(QStringLiteral(
"/proc/sys/net/core/rmem_max"));
389 if (rmemMaxFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
391 const int sysMax = rmemMaxFile.readAll().trimmed().toInt(&ok);
392 if (ok && (sysMax > 0)) {
398 if ((s_rmemMax > 0) && (s_rmemMax < udpBufferSize)) {
399 qCDebug(GstSourceFactoryLog) <<
"Clamping UDP buffer-size from" << udpBufferSize <<
"to net.core.rmem_max"
401 udpBufferSize = s_rmemMax;
404 g_object_set(source,
"uri", udpUri.constData(),
"buffer-size", udpBufferSize,
nullptr);
406 GstCaps* caps =
nullptr;
408 caps = gst_caps_from_string(
409 "application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)H264");
410 }
else if (isUdpH265) {
411 caps = gst_caps_from_string(
412 "application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)H265");
415 if ((isUdpH264 || isUdpH265) && !caps) {
416 qCCritical(GstSourceFactoryLog) <<
"gst_caps_from_string() failed";
417 gst_object_unref(source);
422 g_object_set(source,
"caps", caps,
nullptr);
423 gst_clear_caps(&caps);
431 guint
latencyMs,
bool isMpegTs,
bool isRtsp)
437 (void) gst_element_foreach_src_pad(upstream, padProbe, &probeRes);
442 GstElement* buffer = gst_element_factory_make(
"rtpjitterbuffer",
nullptr);
444 qCCritical(GstSourceFactoryLog) <<
"gst_element_factory_make('rtpjitterbuffer') failed";
450 if (!gst_bin_add(GST_BIN(bin), buffer)) {
451 qCCritical(GstSourceFactoryLog) <<
"gst_bin_add(rtpjitterbuffer) failed";
452 gst_object_unref(buffer);
456 if (!gst_element_link_many(upstream, buffer,
binParser,
nullptr)) {
457 qCCritical(GstSourceFactoryLog) <<
"gst_element_link_many(source, rtpjitterbuffer, parser) failed";
461 if (!gst_element_link(upstream,
binParser)) {
462 qCCritical(GstSourceFactoryLog) <<
"gst_element_link(source, parser) failed";
470 g_object_set_data_full(G_OBJECT(
binParser),
"qgc-dynamic-link-ctx", ctx,
471 [](gpointer p) {
delete static_cast<DynamicLinkContext*
>(p); });
472 (void) g_signal_connect(upstream,
"pad-added", G_CALLBACK(linkPad), ctx);
477 (void) g_signal_connect(
binParser,
"pad-added", G_CALLBACK(wrapWithGhostPad),
nullptr);
485 if (!gst_element_link_many(source, depay, parser,
nullptr)) {
486 qCCritical(GstSourceFactoryLog) <<
"gst_element_link_many(source, depay, parser) failed";
492 GstElement* buffer = gst_element_factory_make(
"rtpjitterbuffer",
nullptr);
494 qCCritical(GstSourceFactoryLog) <<
"gst_element_factory_make('rtpjitterbuffer') failed";
500 if (!gst_bin_add(GST_BIN(bin), buffer)) {
501 qCCritical(GstSourceFactoryLog) <<
"gst_bin_add(rtpjitterbuffer) failed";
502 gst_object_unref(buffer);
506 if (!gst_element_link_many(source, buffer, depay, parser,
nullptr)) {
507 qCCritical(GstSourceFactoryLog) <<
"gst_element_link_many(source, rtpjitterbuffer, depay, parser) failed";
519 qCCritical(GstSourceFactoryLog) <<
"Failed because URI is not specified";
525 const QUrl sourceUrl(uri);
526 const QString scheme = sourceUrl.scheme().toLower();
528 const bool isRtsp = scheme.startsWith(QLatin1String(
"rtsp"));
529 const bool isUdpH264 = (scheme == QLatin1String(
"udp"));
530 const bool isUdpH265 = (scheme == QLatin1String(
"udp265"));
531 const bool isUdpMPEGTS = (scheme == QLatin1String(
"mpegts"));
532 const bool isTcpMPEGTS = (scheme == QLatin1String(
"tcp"));
534 if (!isRtsp && !isUdpH264 && !isUdpH265 && !isUdpMPEGTS && !isTcpMPEGTS) {
535 qCWarning(GstSourceFactoryLog) <<
"Unsupported URI scheme:" << scheme <<
"in"
552 }
else if (isTcpMPEGTS) {
553 source = buildTcpSource(sourceUrl);
555 source = buildUdpSource(sourceUrl, isUdpH264, isUdpH265);
561 bin = gst_bin_new(
"sourcebin");
563 qCCritical(GstSourceFactoryLog) <<
"gst_bin_new('sourcebin') failed";
567 parser = gst_element_factory_make(isUdpH265 ?
"h265parse" :
"parsebin",
"parser");
569 qCCritical(GstSourceFactoryLog) <<
"gst_element_factory_make("
570 << (isUdpH265 ?
"'h265parse'" :
"'parsebin'") <<
") failed";
578 rtpDepay = gst_element_factory_make(
"rtph265depay",
nullptr);
580 qCCritical(GstSourceFactoryLog) <<
"gst_element_factory_make('rtph265depay') failed";
583 configureH26xParser(parser);
587 (void) g_signal_connect(parser,
"deep-element-added", G_CALLBACK(configureAutopluggedParser),
nullptr);
592#if defined(QGC_GST_ENABLE_LEGACY_PARSEBIN_CAPS_FILTER)
594 (void) g_signal_connect(parser,
"autoplug-query", G_CALLBACK(filterParserCaps),
nullptr);
600 if (!gst_bin_add(GST_BIN(bin), source)) {
601 qCCritical(GstSourceFactoryLog) <<
"gst_bin_add(source) failed";
607 if (rtpDepay && !gst_bin_add(GST_BIN(bin), rtpDepay)) {
608 qCCritical(GstSourceFactoryLog) <<
"gst_bin_add(rtph265depay) failed";
614 if (!gst_bin_add(GST_BIN(bin), parser)) {
615 qCCritical(GstSourceFactoryLog) <<
"gst_bin_add(parser) failed";
622 if (isTcpMPEGTS || isUdpMPEGTS) {
623 tsdemux = gst_element_factory_make(
"tsdemux",
nullptr);
625 qCCritical(GstSourceFactoryLog) <<
"gst_element_factory_make('tsdemux') failed";
629 if (!gst_bin_add(GST_BIN(bin), tsdemux)) {
630 qCCritical(GstSourceFactoryLog) <<
"gst_bin_add(tsdemux) failed";
636 if (!gst_element_link(upstream, demux)) {
637 qCCritical(GstSourceFactoryLog) <<
"gst_element_link(source, tsdemux) failed";
661 gst_clear_object(&bin);
662 gst_clear_object(&parser);
663 gst_clear_object(&rtpDepay);
664 gst_clear_object(&tsdemux);
665 gst_clear_object(&source);