146 g_object_set(buffer,
"latency",
latencyMs,
"do-lost", TRUE,
"do-retransmission",
152 g_object_set(buffer,
"rtx-delay", 25,
"rtx-max-retries", 1,
nullptr);
157struct DynamicLinkContext
165void linkPad(
GstElement* element, GstPad* pad, gpointer data)
167 const auto* ctx =
static_cast<const DynamicLinkContext*
>(data);
171 if (GST_PAD_DIRECTION(pad) != GST_PAD_SRC) {
175 bool isVideo =
false;
177 GstCaps* caps = gst_pad_get_current_caps(pad);
179 caps = gst_pad_query_caps(pad,
nullptr);
182 const guint n = gst_caps_get_size(caps);
183 for (guint i = 0; i < n; ++i) {
184 const GstStructure* st = gst_caps_get_structure(caps, i);
185 const gchar* sname = gst_structure_get_name(st);
189 if (g_str_has_prefix(sname,
"video/")) {
191 }
else if (g_str_equal(sname,
"application/x-rtp")) {
197 gst_clear_caps(&caps);
205 if (GstPad* parserSink = gst_element_get_static_pad(ctx->binParser,
"sink")) {
206 const gboolean alreadyLinked = gst_pad_is_linked(parserSink);
207 gst_object_unref(parserSink);
217 const auto removeDynamicBuffer = [&dynamicBuffer] {
218 if (!dynamicBuffer) {
222 GstObject* parent = gst_object_get_parent(GST_OBJECT(dynamicBuffer));
224 gst_bin_remove(GST_BIN(parent), dynamicBuffer);
225 gst_object_unref(parent);
227 dynamicBuffer =
nullptr;
230 if (isRtp && ctx->allowJitterBuffer && (ctx->config.jitterBuffer !=
JitterBuffer::None)) {
231 GstElement* bin = GST_ELEMENT(gst_object_get_parent(GST_OBJECT(ctx->binParser)));
233 GstElement* buffer = gst_element_factory_make(
"rtpjitterbuffer",
nullptr);
234 if (buffer && gst_bin_add(GST_BIN(bin), buffer)) {
235 configureJitterBuffer(buffer, ctx->config, ctx->latencyMs);
236 if (gst_element_sync_state_with_parent(buffer) && gst_element_link(buffer, ctx->binParser)) {
238 dynamicBuffer = buffer;
240 qCWarning(GstSourceFactoryLog)
241 <<
"dynamic rtpjitterbuffer link/sync failed; linking pad straight to parser";
242 gst_bin_remove(GST_BIN(bin), buffer);
246 qCWarning(GstSourceFactoryLog) <<
"dynamic rtpjitterbuffer create/add failed; skipping jitter buffer";
248 gst_object_unref(buffer);
251 gst_object_unref(bin);
255 gchar* name = gst_pad_get_name(pad);
257 qCWarning(GstSourceFactoryLog) <<
"gst_pad_get_name() failed";
258 removeDynamicBuffer();
262 if (!gst_element_link_pads(element, name, downstream,
"sink")) {
263 qCWarning(GstSourceFactoryLog) <<
"gst_element_link_pads() failed for" << name;
264 removeDynamicBuffer();
267 g_clear_pointer(&name, g_free);
276 qCCritical(GstSourceFactoryLog) <<
"Invalid RTSP URI:" << sourceUrl.toDisplayString(QUrl::RemoveUserInfo);
280 GstElement* source = gst_element_factory_make(
"rtspsrc",
"source");
282 qCCritical(GstSourceFactoryLog) <<
"gst_element_factory_make('rtspsrc') failed";
286 QUrl cleanUrl(sourceUrl);
287 cleanUrl.setUserInfo(QString());
288 const QByteArray cleanLocation = cleanUrl.toEncoded();
292 constexpr GstRTSPLowerTrans kRtspProtocols =
293 static_cast<GstRTSPLowerTrans
>(GST_RTSP_LOWER_TRANS_UDP | GST_RTSP_LOWER_TRANS_TCP);
298 g_object_set(source,
"location", cleanLocation.constData(),
"latency",
latencyMs,
"do-rtcp", TRUE,
300 "udp-reconnect", TRUE,
"drop-on-latency", dropOnLatency,
"retry", kRtspRetry,
"protocols",
301 kRtspProtocols, nullptr);
303 const QString rtspUser = sourceUrl.userName(QUrl::FullyDecoded);
304 const QString rtspPassword = sourceUrl.password(QUrl::FullyDecoded);
305 if (!rtspUser.isEmpty()) {
306 g_object_set(source,
"user-id", rtspUser.toUtf8().constData(),
"user-pw",
307 rtspPassword.toUtf8().constData(),
nullptr);
312GstElement* buildTcpSource(
const QUrl& sourceUrl)
314 const int port = sourceUrl.port();
315 if (!validPort(port)) {
316 qCCritical(GstSourceFactoryLog) <<
"Invalid TCP port" << port <<
"in" << sourceUrl.toDisplayString(QUrl::RemoveUserInfo);
319 const QString host = sourceUrl.host();
320 if (host.isEmpty()) {
321 qCCritical(GstSourceFactoryLog) <<
"Missing host in TCP URI" << sourceUrl.toDisplayString(QUrl::RemoveUserInfo);
325 GstElement* source = gst_element_factory_make(
"tcpclientsrc",
"source");
327 qCCritical(GstSourceFactoryLog) <<
"gst_element_factory_make('tcpclientsrc') failed";
331 g_object_set(source,
"host", host.toUtf8().constData(),
"port",
static_cast<int>(port),
nullptr);
335GstElement* buildUdpSource(
const QUrl& sourceUrl,
bool isUdpH264,
bool isUdpH265)
337 const int port = sourceUrl.port();
338 if (!validPort(port)) {
339 qCCritical(GstSourceFactoryLog) <<
"Invalid UDP port" << port <<
"in" << sourceUrl.toDisplayString(QUrl::RemoveUserInfo);
343 GstElement* source = gst_element_factory_make(
"udpsrc",
"source");
345 qCCritical(GstSourceFactoryLog) <<
"gst_element_factory_make('udpsrc') failed";
350 QUrl udpUrl(sourceUrl);
351 udpUrl.setScheme(QStringLiteral(
"udp"));
352 udpUrl.setUserInfo(QString());
353 const QByteArray udpUri = udpUrl.toEncoded();
357 int udpBufferSize = kUdpBufferSizeBytes;
360 static const int s_rmemMax = [] {
361 QFile rmemMaxFile(QStringLiteral(
"/proc/sys/net/core/rmem_max"));
362 if (rmemMaxFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
364 const int sysMax = rmemMaxFile.readAll().trimmed().toInt(&ok);
365 if (ok && (sysMax > 0)) {
371 if ((s_rmemMax > 0) && (s_rmemMax < udpBufferSize)) {
372 qCDebug(GstSourceFactoryLog) <<
"Clamping UDP buffer-size from" << udpBufferSize <<
"to net.core.rmem_max"
374 udpBufferSize = s_rmemMax;
377 g_object_set(source,
"uri", udpUri.constData(),
"buffer-size", udpBufferSize,
nullptr);
379 GstCaps* caps =
nullptr;
381 caps = gst_caps_from_string(
382 "application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)H264");
383 }
else if (isUdpH265) {
384 caps = gst_caps_from_string(
385 "application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)H265");
388 if ((isUdpH264 || isUdpH265) && !caps) {
389 qCCritical(GstSourceFactoryLog) <<
"gst_caps_from_string() failed";
390 gst_object_unref(source);
395 g_object_set(source,
"caps", caps,
nullptr);
396 gst_clear_caps(&caps);
404 guint
latencyMs,
bool isMpegTs,
bool isRtsp)
410 (void) gst_element_foreach_src_pad(upstream, padProbe, &probeRes);
415 GstElement* buffer = gst_element_factory_make(
"rtpjitterbuffer",
nullptr);
417 qCCritical(GstSourceFactoryLog) <<
"gst_element_factory_make('rtpjitterbuffer') failed";
423 if (!gst_bin_add(GST_BIN(bin), buffer)) {
424 qCCritical(GstSourceFactoryLog) <<
"gst_bin_add(rtpjitterbuffer) failed";
425 gst_object_unref(buffer);
429 if (!gst_element_link_many(upstream, buffer,
binParser,
nullptr)) {
430 qCCritical(GstSourceFactoryLog) <<
"gst_element_link_many(source, rtpjitterbuffer, parser) failed";
434 if (!gst_element_link(upstream,
binParser)) {
435 qCCritical(GstSourceFactoryLog) <<
"gst_element_link(source, parser) failed";
443 g_object_set_data_full(G_OBJECT(
binParser),
"qgc-dynamic-link-ctx", ctx,
444 [](gpointer p) {
delete static_cast<DynamicLinkContext*
>(p); });
445 (void) g_signal_connect(upstream,
"pad-added", G_CALLBACK(linkPad), ctx);
450 (void) g_signal_connect(
binParser,
"pad-added", G_CALLBACK(wrapWithGhostPad),
nullptr);
458 if (!gst_element_link_many(source, depay, parser,
nullptr)) {
459 qCCritical(GstSourceFactoryLog) <<
"gst_element_link_many(source, depay, parser) failed";
465 GstElement* buffer = gst_element_factory_make(
"rtpjitterbuffer",
nullptr);
467 qCCritical(GstSourceFactoryLog) <<
"gst_element_factory_make('rtpjitterbuffer') failed";
473 if (!gst_bin_add(GST_BIN(bin), buffer)) {
474 qCCritical(GstSourceFactoryLog) <<
"gst_bin_add(rtpjitterbuffer) failed";
475 gst_object_unref(buffer);
479 if (!gst_element_link_many(source, buffer, depay, parser,
nullptr)) {
480 qCCritical(GstSourceFactoryLog) <<
"gst_element_link_many(source, rtpjitterbuffer, depay, parser) failed";
492 qCCritical(GstSourceFactoryLog) <<
"Failed because URI is not specified";
498 const QUrl sourceUrl(uri);
499 const QString scheme = sourceUrl.scheme().toLower();
501 const bool isRtsp = scheme.startsWith(QLatin1String(
"rtsp"));
502 const bool isUdpH264 = (scheme == QLatin1String(
"udp"));
503 const bool isUdpH265 = (scheme == QLatin1String(
"udp265"));
504 const bool isUdpMPEGTS = (scheme == QLatin1String(
"mpegts"));
505 const bool isTcpMPEGTS = (scheme == QLatin1String(
"tcp"));
507 if (!isRtsp && !isUdpH264 && !isUdpH265 && !isUdpMPEGTS && !isTcpMPEGTS) {
508 qCWarning(GstSourceFactoryLog) <<
"Unsupported URI scheme:" << scheme <<
"in" << sourceUrl.toDisplayString(QUrl::RemoveUserInfo);
524 }
else if (isTcpMPEGTS) {
525 source = buildTcpSource(sourceUrl);
527 source = buildUdpSource(sourceUrl, isUdpH264, isUdpH265);
533 bin = gst_bin_new(
"sourcebin");
535 qCCritical(GstSourceFactoryLog) <<
"gst_bin_new('sourcebin') failed";
539 parser = gst_element_factory_make(isUdpH265 ?
"h265parse" :
"parsebin",
"parser");
541 qCCritical(GstSourceFactoryLog) <<
"gst_element_factory_make("
542 << (isUdpH265 ?
"'h265parse'" :
"'parsebin'") <<
") failed";
550 rtpDepay = gst_element_factory_make(
"rtph265depay",
nullptr);
552 qCCritical(GstSourceFactoryLog) <<
"gst_element_factory_make('rtph265depay') failed";
555 g_object_set(parser,
"config-interval", -1,
nullptr);
560#if defined(QGC_GST_ENABLE_LEGACY_PARSEBIN_CAPS_FILTER)
562 (void) g_signal_connect(parser,
"autoplug-query", G_CALLBACK(filterParserCaps),
nullptr);
568 if (!gst_bin_add(GST_BIN(bin), source)) {
569 qCCritical(GstSourceFactoryLog) <<
"gst_bin_add(source) failed";
575 if (rtpDepay && !gst_bin_add(GST_BIN(bin), rtpDepay)) {
576 qCCritical(GstSourceFactoryLog) <<
"gst_bin_add(rtph265depay) failed";
582 if (!gst_bin_add(GST_BIN(bin), parser)) {
583 qCCritical(GstSourceFactoryLog) <<
"gst_bin_add(parser) failed";
590 if (isTcpMPEGTS || isUdpMPEGTS) {
591 tsdemux = gst_element_factory_make(
"tsdemux",
nullptr);
593 qCCritical(GstSourceFactoryLog) <<
"gst_element_factory_make('tsdemux') failed";
597 if (!gst_bin_add(GST_BIN(bin), tsdemux)) {
598 qCCritical(GstSourceFactoryLog) <<
"gst_bin_add(tsdemux) failed";
604 if (!gst_element_link(upstream, demux)) {
605 qCCritical(GstSourceFactoryLog) <<
"gst_element_link(source, tsdemux) failed";
629 gst_clear_object(&bin);
630 gst_clear_object(&parser);
631 gst_clear_object(&rtpDepay);
632 gst_clear_object(&tsdemux);
633 gst_clear_object(&source);