diff --git a/src/base/QXmppConstants.cpp b/src/base/QXmppConstants.cpp index 94aec45ac..86f2fca84 100644 --- a/src/base/QXmppConstants.cpp +++ b/src/base/QXmppConstants.cpp @@ -135,6 +135,8 @@ const char* ns_message_correct = "urn:xmpp:message-correct:0"; const char* ns_mam = "urn:xmpp:mam:2"; // XEP-0319: Last User Interaction in Presence const char* ns_idle = "urn:xmpp:idle:1"; +// XEP-0320: Use of DTLS-SRTP in Jingle Sessions +const char* ns_jingle_dtls = "urn:xmpp:jingle:apps:dtls:0"; // XEP-0333: Chat Markers const char* ns_chat_markers = "urn:xmpp:chat-markers:0"; // XEP-0334: Message Processing Hints diff --git a/src/base/QXmppConstants_p.h b/src/base/QXmppConstants_p.h index 85d3eaa0f..210fc18e7 100644 --- a/src/base/QXmppConstants_p.h +++ b/src/base/QXmppConstants_p.h @@ -147,6 +147,8 @@ extern const char* ns_message_correct; extern const char* ns_mam; // XEP-0319: Last User Interaction in Presence extern const char* ns_idle; +// XEP-0320: Use of DTLS-SRTP in Jingle Sessions +extern const char* ns_jingle_dtls; // XEP-0333: Char Markers extern const char* ns_chat_markers; // XEP-0334: Message Processing Hints: diff --git a/src/base/QXmppJingleIq.cpp b/src/base/QXmppJingleIq.cpp index 64650d29f..4273e4e56 100644 --- a/src/base/QXmppJingleIq.cpp +++ b/src/base/QXmppJingleIq.cpp @@ -34,7 +34,6 @@ static const int RTP_COMPONENT = 1; static const char *ns_jingle_rtp_info = "urn:xmpp:jingle:apps:rtp:info:1"; -static const char *ns_jingle_dtls = "urn:xmpp:jingle:apps:dtls:0"; static const char *jingle_actions[] = { "content-accept", diff --git a/src/client/QXmppCall.cpp b/src/client/QXmppCall.cpp index 670d5cd6b..f990656ea 100644 --- a/src/client/QXmppCall.cpp +++ b/src/client/QXmppCall.cpp @@ -113,18 +113,9 @@ void QXmppCallPrivate::padAdded(GstPad *pad) if (nameParts.size() < 4) { return; } - if (nameParts[0] == QLatin1String("send") && - nameParts[1] == QLatin1String("rtp") && + if (nameParts[0] == QLatin1String("recv") || + nameParts[1] == QLatin1String("rtp") || nameParts[2] == QLatin1String("src")) { - if (nameParts.size() != 4) { - return; - } - int sessionId = nameParts[3].toInt(); - auto stream = findStreamById(sessionId); - stream->d->addRtpSender(pad); - } else if (nameParts[0] == QLatin1String("recv") || - nameParts[1] == QLatin1String("rtp") || - nameParts[2] == QLatin1String("src")) { if (nameParts.size() != 6) { return; } @@ -331,6 +322,10 @@ void QXmppCallPrivate::handleRequest(const QXmppJingleIq &iq) return; } + if (useDtls && content.transportFingerprintSetup() == QLatin1String("passive")) { + stream->d->toDtlsClientMode(); + } + // check for call establishment setState(QXmppCall::ActiveState); @@ -401,7 +396,8 @@ void QXmppCallPrivate::handleRequest(const QXmppJingleIq &iq) iq.setType(QXmppIq::Set); iq.setAction(QXmppJingleIq::ContentAccept); iq.setSid(q->sid()); - iq.addContent(localContent(stream)); + iq.addContent(localContent(stream, QLatin1String("active"))); + stream->d->toDtlsClientMode(); sendRequest(iq); } else if (iq.action() == QXmppJingleIq::TransportInfo) { @@ -435,7 +431,7 @@ QXmppCallStream *QXmppCallPrivate::createStream(const QString &media, const QStr return nullptr; } - QXmppCallStream *stream = new QXmppCallStream(pipeline, rtpbin, media, creator, name, ++nextId); + QXmppCallStream *stream = new QXmppCallStream(pipeline, rtpbin, media, creator, name, ++nextId, useDtls); // Fill local payload payload types auto &codecs = media == AUDIO_MEDIA ? audioCodecs : videoCodecs; @@ -470,7 +466,7 @@ QXmppCallStream *QXmppCallPrivate::createStream(const QString &media, const QStr return stream; } -QXmppJingleIq::Content QXmppCallPrivate::localContent(QXmppCallStream *stream) const +QXmppJingleIq::Content QXmppCallPrivate::localContent(QXmppCallStream *stream, QString dtlsSetup) const { QXmppJingleIq::Content content; content.setCreator(stream->creator()); @@ -487,6 +483,13 @@ QXmppJingleIq::Content QXmppCallPrivate::localContent(QXmppCallStream *stream) c content.setTransportPassword(stream->d->connection->localPassword()); content.setTransportCandidates(stream->d->connection->localCandidates()); + // encryption + if (useDtls) { + content.setTransportFingerprint(stream->d->digest); + content.setTransportFingerprintHash("sha-256"); + content.setTransportFingerprintSetup(dtlsSetup); + } + return content; } @@ -514,7 +517,7 @@ bool QXmppCallPrivate::sendInvite() iq.setAction(QXmppJingleIq::SessionInitiate); iq.setInitiator(ownJid); iq.setSid(sid); - iq.addContent(localContent(stream)); + iq.addContent(localContent(stream, QLatin1String("actpass"))); return sendRequest(iq); } @@ -562,13 +565,14 @@ void QXmppCallPrivate::terminate(QXmppJingleIq::Reason::Type reasonType) QTimer::singleShot(5000, q, SLOT(terminated())); } -QXmppCall::QXmppCall(const QString &jid, QXmppCall::Direction direction, QXmppCallManager *parent) +QXmppCall::QXmppCall(const QString &jid, QXmppCall::Direction direction, bool useDtls, QXmppCallManager *parent) : QXmppLoggable(parent) { d = new QXmppCallPrivate(this); d->direction = direction; d->jid = jid; d->ownJid = parent->client()->configuration().jid(); + d->useDtls = true; d->manager = parent; } @@ -593,7 +597,8 @@ void QXmppCall::accept() iq.setAction(QXmppJingleIq::SessionAccept); iq.setResponder(d->ownJid); iq.setSid(d->sid); - iq.addContent(d->localContent(stream)); + iq.addContent(d->localContent(stream, QLatin1String("active"))); + stream->d->toDtlsClientMode(); d->sendRequest(iq); // notify user @@ -680,7 +685,7 @@ void QXmppCall::localCandidatesChanged() iq.setType(QXmppIq::Set); iq.setAction(QXmppJingleIq::TransportInfo); iq.setSid(d->sid); - iq.addContent(d->localContent(stream)); + iq.addContent(d->localContent(stream, QLatin1String())); d->sendRequest(iq); } @@ -734,6 +739,13 @@ void QXmppCall::addVideo() iq.setType(QXmppIq::Set); iq.setAction(QXmppJingleIq::ContentAdd); iq.setSid(d->sid); - iq.addContent(d->localContent(stream)); + iq.addContent(d->localContent(stream, QLatin1String("actpass"))); d->sendRequest(iq); } + +// Returns if the call is encrypted + +bool QXmppCall::isEncrypted() const +{ + return d->useDtls; +} diff --git a/src/client/QXmppCall.h b/src/client/QXmppCall.h index 8bc91b71b..6d9b0ce74 100644 --- a/src/client/QXmppCall.h +++ b/src/client/QXmppCall.h @@ -75,6 +75,8 @@ class QXMPP_EXPORT QXmppCall : public QXmppLoggable QXmppCallStream *audioStream() const; QXmppCallStream *videoStream() const; + bool isEncrypted() const; + signals: /// \brief This signal is emitted when a call is connected. /// @@ -108,7 +110,7 @@ private slots: void terminated(); private: - QXmppCall(const QString &jid, QXmppCall::Direction direction, QXmppCallManager *parent); + QXmppCall(const QString &jid, QXmppCall::Direction direction, bool useDtls, QXmppCallManager *parent); QXmppCallPrivate *d; friend class QXmppCallManager; diff --git a/src/client/QXmppCallManager.cpp b/src/client/QXmppCallManager.cpp index dba059751..ebdac0caa 100644 --- a/src/client/QXmppCallManager.cpp +++ b/src/client/QXmppCallManager.cpp @@ -29,6 +29,7 @@ #include "QXmppClient.h" #include "QXmppConstants_p.h" #include "QXmppJingleIq.h" +#include "QXmppRosterManager.h" #include "QXmppStun.h" #include "QXmppUtils.h" @@ -43,6 +44,21 @@ QXmppCallManagerPrivate::QXmppCallManagerPrivate(QXmppCallManager *qq) { // Initialize GStreamer gst_init(nullptr, nullptr); + + supportsDtls = true; + GstElementFactory *factory; + factory = gst_element_factory_find("dtlssrtpenc"); + if (!factory) { + supportsDtls = false; + } else { + g_object_unref(factory); + } + factory = gst_element_factory_find("dtlssrtpdec"); + if (!factory) { + supportsDtls = false; + } else { + g_object_unref(factory); + } } QXmppCall *QXmppCallManagerPrivate::findCall(const QString &sid) const @@ -80,12 +96,17 @@ QXmppCallManager::~QXmppCallManager() /// \cond QStringList QXmppCallManager::discoveryFeatures() const { - return QStringList() + QStringList features; + features << ns_jingle // XEP-0166 : Jingle << ns_jingle_rtp // XEP-0167 : Jingle RTP Sessions << ns_jingle_rtp_audio << ns_jingle_rtp_video << ns_jingle_ice_udp; // XEP-0176 : Jingle ICE-UDP Transport Method + if (d->supportsDtls) { + features << ns_jingle_dtls; // XEP-0320: Use of DTLS-SRTP in Jingle Sessions + } + return features; } bool QXmppCallManager::handleStanza(const QDomElement &element) @@ -136,7 +157,12 @@ QXmppCall *QXmppCallManager::call(const QString &jid) return nullptr; } - QXmppCall *call = new QXmppCall(jid, QXmppCall::OutgoingDirection, this); + /* Determine support for XEP-0320: Use of DTLS-SRTP in Jingle Sessions */ + QXmppRosterManager *rosterManager = client()->findExtension(); + QXmppPresence presence = rosterManager->getPresence(QXmppUtils::jidToBareJid(jid), QXmppUtils::jidToResource(jid)); + bool remoteSupportsDtls = presence.capabilityExt().contains(ns_jingle_dtls); + + QXmppCall *call = new QXmppCall(jid, QXmppCall::OutgoingDirection, remoteSupportsDtls && d->supportsDtls, this); QXmppCallStream *stream = call->d->createStream("audio", "initiator", "microphone"); call->d->streams << stream; call->d->sid = QXmppUtils::generateStanzaHash(); @@ -247,14 +273,23 @@ void QXmppCallManager::_q_jingleIqReceived(const QXmppJingleIq &iq) return; if (iq.action() == QXmppJingleIq::SessionInitiate) { + const QXmppJingleIq::Content content = iq.contents().isEmpty() ? QXmppJingleIq::Content() : iq.contents().first(); + // build call - QXmppCall *call = new QXmppCall(iq.from(), QXmppCall::IncomingDirection, this); + bool useDtls = !content.transportFingerprint().isEmpty(); + QXmppCall *call = new QXmppCall(iq.from(), QXmppCall::IncomingDirection, useDtls, this); call->d->sid = iq.sid(); - const QXmppJingleIq::Content content = iq.contents().isEmpty() ? QXmppJingleIq::Content() : iq.contents().first(); + if (useDtls && !d->supportsDtls) { + call->d->terminate(QXmppJingleIq::Reason::FailedApplication); + return; + } + QXmppCallStream *stream = call->d->createStream(content.descriptionMedia(), content.creator(), content.name()); - if (!stream) + if (!stream) { + call->d->terminate(QXmppJingleIq::Reason::FailedApplication); return; + } call->d->streams << stream; // send ack diff --git a/src/client/QXmppCallManager_p.h b/src/client/QXmppCallManager_p.h index 66781911b..d82561013 100644 --- a/src/client/QXmppCallManager_p.h +++ b/src/client/QXmppCallManager_p.h @@ -55,6 +55,8 @@ class QXmppCallManagerPrivate QString turnUser; QString turnPassword; + bool supportsDtls; + private: QXmppCallManager *q; }; diff --git a/src/client/QXmppCallStream.cpp b/src/client/QXmppCallStream.cpp index 386906b83..fde5a0387 100644 --- a/src/client/QXmppCallStream.cpp +++ b/src/client/QXmppCallStream.cpp @@ -28,12 +28,14 @@ #include "QXmppStun.h" #include - #include +#include +#include + QXmppCallStreamPrivate::QXmppCallStreamPrivate(QXmppCallStream *parent, GstElement *pipeline_, GstElement *rtpbin_, QString media_, QString creator_, - QString name_, int id_) + QString name_, int id_, bool useDtls_) : QObject(parent), q(parent), pipeline(pipeline_), @@ -47,7 +49,9 @@ QXmppCallStreamPrivate::QXmppCallStreamPrivate(QXmppCallStream *parent, GstEleme media(media_), creator(creator_), name(name_), - id(id_) + id(id_), + useDtls(useDtls_), + dtlsHandshakeComplete(false) { localSsrc = qrand(); @@ -62,6 +66,60 @@ QXmppCallStreamPrivate::QXmppCallStreamPrivate(QXmppCallStream *parent, GstEleme qFatal("Failed to add pads to send bin"); } + /* Create DTLS SRTP elements */ + if (useDtls) { + QString dtlsRtpId = QUuid::createUuid().toString(QUuid::WithoutBraces); + QString dtlsRtcpId = QUuid::createUuid().toString(QUuid::WithoutBraces); + + dtlsrtpdecoder = gst_element_factory_make("dtlssrtpdec", nullptr); + dtlsrtcpdecoder = gst_element_factory_make("dtlssrtpdec", nullptr); + if (!dtlsrtpdecoder || !dtlsrtcpdecoder) { + qFatal("Failed to create dtls srtp decoders"); + } + + g_object_set(dtlsrtpdecoder, "async-handling", true, "connection-id", dtlsRtpId.toLatin1().data(), nullptr); + g_object_set(dtlsrtcpdecoder, "async-handling", true, "connection-id", dtlsRtcpId.toLatin1().data(), nullptr); + gchar *pem; + g_object_get(dtlsrtpdecoder, "pem", &pem, nullptr); + /* Copy the certificate to the RTCP decoder so that they both share the same fingerprint. */ + //g_object_set(dtlsrtcpdecoder, "pem", pem, nullptr); //TODO why does this fail? + /* Calculate the fingerprint to transmit to the remote party. */ + QSslCertificate certificate(pem); + digest = certificate.digest(QCryptographicHash::Sha256); + g_free(pem); + + dtlsrtpencoder = gst_element_factory_make("dtlssrtpenc", nullptr); + dtlsrtcpencoder = gst_element_factory_make("dtlssrtpenc", nullptr); + if (!dtlsrtpencoder || !dtlsrtcpencoder) { + qFatal("Failed to create dtls srtp encoders"); + } + + g_object_set(dtlsrtpencoder, "async-handling", true, "connection-id", dtlsRtpId.toLatin1().data(), "is-client", false, nullptr); + g_object_set(dtlsrtcpencoder, "async-handling", true, "connection-id", dtlsRtcpId.toLatin1().data(), "is-client", false, nullptr); + + g_signal_connect_swapped(dtlsrtpencoder, "on-key-set", + G_CALLBACK(+[](QXmppCallStreamPrivate *p) { + // TODO check remote fingerprint (peer-pem on decoders) + qWarning("================ ON_KEY_SET =============="); + p->dtlsHandshakeComplete = true; + if (p->sendPadCB && p->encoderBin) { + p->sendPadCB(p->sendPad); + } + if (p->receivePadCB && p->decoderBin) { + p->receivePadCB(p->receivePad); + } + }), + this); + + if (!gst_bin_add(GST_BIN(iceReceiveBin), dtlsrtpdecoder) || + !gst_bin_add(GST_BIN(iceReceiveBin), dtlsrtcpdecoder) || + !gst_bin_add(GST_BIN(iceSendBin), dtlsrtpencoder) || + !gst_bin_add(GST_BIN(iceSendBin), dtlsrtcpencoder)) { + qFatal("Failed to add dtls elements to corresponding bins"); + } + } + + /* Create appsrc / appsink elements */ connection = new QXmppIceConnection(this); connection->addComponent(RTP_COMPONENT); connection->addComponent(RTCP_COMPONENT); @@ -100,14 +158,65 @@ QXmppCallStreamPrivate::QXmppCallStreamPrivate(QXmppCallStream *parent, GstEleme [&](const QByteArray &datagram) { datagramReceived(datagram, apprtcpsrc); }); if (!gst_bin_add(GST_BIN(iceReceiveBin), apprtpsrc) || - !gst_bin_add(GST_BIN(iceReceiveBin), apprtcpsrc)) { - qFatal("Failed to add appsrcs to receive bin"); - } - - if (!gst_element_link_pads(apprtpsrc, "src", rtpbin, QStringLiteral("recv_rtp_sink_%1").arg(id).toLatin1().data()) || - !gst_element_link_pads(apprtcpsrc, "src", rtpbin, QStringLiteral("recv_rtcp_sink_%1").arg(id).toLatin1().data())) { - qFatal("Failed to link receive pads"); - } + !gst_bin_add(GST_BIN(iceReceiveBin), apprtcpsrc) || + !gst_bin_add(GST_BIN(iceSendBin), apprtpsink) || + !gst_bin_add(GST_BIN(iceSendBin), apprtcpsink)) { + qFatal("Failed to add appsrc / appsink elements to respective bins"); + } + + /* Trigger creation of necessary pads */ + GstPad *dummyPad = gst_element_get_request_pad(rtpbin, QStringLiteral("send_rtp_sink_%1").arg(id).toLatin1().data()); + gst_object_unref(dummyPad); + + /* Link pads - receiving side */ + GstPad *rtpRecvPad = gst_element_get_static_pad(apprtpsrc, "src"); + GstPad *rtcpRecvPad = gst_element_get_static_pad(apprtcpsrc, "src"); + + if (useDtls) { + GstPad *dtlsRtpSinkPad = gst_element_get_static_pad(dtlsrtpdecoder, "sink"); + GstPad *dtlsRtcpSinkPad = gst_element_get_static_pad(dtlsrtcpdecoder, "sink"); + gst_pad_link(rtpRecvPad, dtlsRtpSinkPad); + gst_pad_link(rtcpRecvPad, dtlsRtcpSinkPad); + gst_object_unref(dtlsRtpSinkPad); + gst_object_unref(dtlsRtcpSinkPad); + gst_object_unref(rtpRecvPad); + gst_object_unref(rtcpRecvPad); + rtpRecvPad = gst_element_get_static_pad(dtlsrtpdecoder, "rtp_src"); + rtcpRecvPad = gst_element_get_static_pad(dtlsrtcpdecoder, "rtcp_src"); + } + + GstPad *rtpSinkPad = gst_element_get_request_pad(rtpbin, QStringLiteral("recv_rtp_sink_%1").arg(id).toLatin1().data()); + GstPad *rtcpSinkPad = gst_element_get_request_pad(rtpbin, QStringLiteral("recv_rtp_sink_%1").arg(id).toLatin1().data()); + gst_pad_link(rtpRecvPad, rtpSinkPad); + gst_pad_link(rtcpRecvPad, rtcpSinkPad); + gst_object_unref(rtpSinkPad); + gst_object_unref(rtcpSinkPad); + gst_object_unref(rtpRecvPad); + gst_object_unref(rtcpRecvPad); + + /* Link pads - sending side */ + GstPad *rtpSendPad = gst_element_get_static_pad(apprtpsink, "sink"); + GstPad *rtcpSendPad = gst_element_get_static_pad(apprtcpsink, "sink"); + + if (useDtls) { + GstPad *dtlsRtpSrcPad = gst_element_get_static_pad(dtlsrtpencoder, "src"); + GstPad *dtlsRtcpSrcPad = gst_element_get_static_pad(dtlsrtcpencoder, "src"); + gst_pad_link(dtlsRtpSrcPad, rtpSendPad); + gst_pad_link(dtlsRtcpSrcPad, rtcpSendPad); + gst_object_unref(dtlsRtpSrcPad); + gst_object_unref(dtlsRtcpSrcPad); + gst_object_unref(rtpSendPad); + gst_object_unref(rtcpSendPad); + rtpSendPad = gst_element_get_request_pad(dtlsrtpencoder, QStringLiteral("rtp_sink_%1").arg(id).toLatin1().data()); + rtcpSendPad = gst_element_get_request_pad(dtlsrtcpencoder, QStringLiteral("rtcp_sink_%1").arg(id).toLatin1().data()); + } + + if (!gst_ghost_pad_set_target(GST_GHOST_PAD(internalRtpPad), rtpSendPad) || + !gst_ghost_pad_set_target(GST_GHOST_PAD(internalRtcpPad), rtcpSendPad)) { + qFatal("Failed to link rtp send pads to internal ghost pads"); + } + gst_object_unref(rtpSendPad); + gst_object_unref(rtcpSendPad); // We need frequent RTCP reports for the bandwidth controller GstElement *rtpSession; @@ -116,6 +225,15 @@ QXmppCallStreamPrivate::QXmppCallStreamPrivate(QXmppCallStream *parent, GstEleme gst_element_sync_state_with_parent(iceReceiveBin); gst_element_sync_state_with_parent(iceSendBin); + + GstPad *rtpbinRtpSendPad = gst_element_get_static_pad(rtpbin, QStringLiteral("send_rtp_src_%1").arg(id).toLatin1().data()); + GstPad *rtpbinRtcpSendPad = gst_element_get_request_pad(rtpbin, QStringLiteral("send_rtcp_src_%1").arg(id).toLatin1().data()); + if (gst_pad_link(rtpbinRtpSendPad, internalRtpPad) != GST_PAD_LINK_OK || + gst_pad_link(rtpbinRtcpSendPad, internalRtcpPad) != GST_PAD_LINK_OK) { + qFatal("Failed to link rtp pads"); + } + gst_object_unref(rtpbinRtpSendPad); + gst_object_unref(rtpbinRtcpSendPad); } QXmppCallStreamPrivate::~QXmppCallStreamPrivate() @@ -226,18 +344,18 @@ void QXmppCallStreamPrivate::addEncoder(QXmppCallPrivate::GstCodec &codec) return; } - if (!gst_ghost_pad_set_target(GST_GHOST_PAD(sendPad), gst_element_get_static_pad(queue, "sink"))) { + GstPad *targetPad = gst_element_get_static_pad(queue, "sink"); + if (!gst_ghost_pad_set_target(GST_GHOST_PAD(sendPad), targetPad)) { qFatal("Failed to set send pad"); return; } + gst_object_unref(targetPad); - if (sendPadCB) { + if (sendPadCB && (dtlsHandshakeComplete || !useDtls)) { sendPadCB(sendPad); } gst_element_sync_state_with_parent(encoderBin); - - addRtcpSender(gst_element_get_request_pad(rtpbin, QStringLiteral("send_rtcp_src_%1").arg(id).toLatin1().data())); } void QXmppCallStreamPrivate::addDecoder(GstPad *pad, QXmppCallPrivate::GstCodec &codec) @@ -280,49 +398,35 @@ void QXmppCallStreamPrivate::addDecoder(GstPad *pad, QXmppCallPrivate::GstCodec gst_bin_add_many(GST_BIN(decoderBin), depay, decoder, queue, nullptr); - if (!gst_ghost_pad_set_target(GST_GHOST_PAD(internalReceivePad), gst_element_get_static_pad(depay, "sink")) || - gst_pad_link(pad, internalReceivePad) != GST_PAD_LINK_OK || - !gst_element_link_many(depay, decoder, queue, nullptr) || - !gst_ghost_pad_set_target(GST_GHOST_PAD(receivePad), gst_element_get_static_pad(queue, "src"))) { + GstPad *targetPad = gst_element_get_static_pad(depay, "sink"); + if (!gst_ghost_pad_set_target(GST_GHOST_PAD(internalReceivePad), targetPad)) { + qFatal("Failed to set receive pad"); + } + gst_object_unref(targetPad); + + targetPad = gst_element_get_static_pad(queue, "src"); + if (!gst_ghost_pad_set_target(GST_GHOST_PAD(receivePad), targetPad)) { + qFatal("Failed to set receive pad"); + } + gst_object_unref(targetPad); + + if (gst_pad_link(pad, internalReceivePad) != GST_PAD_LINK_OK || + !gst_element_link_many(depay, decoder, queue, nullptr)) { qFatal("Could not link all decoder pads"); return; } gst_element_sync_state_with_parent(decoderBin); - if (receivePadCB) { + if (receivePadCB && (dtlsHandshakeComplete || !useDtls)) { receivePadCB(receivePad); } } -void QXmppCallStreamPrivate::addRtpSender(GstPad *pad) -{ - if (!gst_bin_add(GST_BIN(iceSendBin), apprtpsink)) { - qFatal("Failed to add rtp sink to send bin"); - } - gst_element_sync_state_with_parent(apprtpsink); - if (!gst_ghost_pad_set_target(GST_GHOST_PAD(internalRtpPad), gst_element_get_static_pad(apprtpsink, "sink")) || - gst_pad_link(pad, internalRtpPad) != GST_PAD_LINK_OK) { - qFatal("Failed to link rtp pads"); - } -} - -void QXmppCallStreamPrivate::addRtcpSender(GstPad *pad) -{ - if (!gst_bin_add(GST_BIN(iceSendBin), apprtcpsink)) { - qFatal("Failed to add rtcp sink to send bin"); - } - gst_element_sync_state_with_parent(apprtcpsink); - if (!gst_ghost_pad_set_target(GST_GHOST_PAD(internalRtcpPad), gst_element_get_static_pad(apprtcpsink, "sink")) || - gst_pad_link(pad, internalRtcpPad) != GST_PAD_LINK_OK) { - qFatal("Failed to link rtcp pads"); - } -} - QXmppCallStream::QXmppCallStream(GstElement *pipeline, GstElement *rtpbin, - QString media, QString creator, QString name, int id) + QString media, QString creator, QString name, int id, bool useDtls) { - d = new QXmppCallStreamPrivate(this, pipeline, rtpbin, media, creator, name, id); + d = new QXmppCallStreamPrivate(this, pipeline, rtpbin, media, creator, name, id, useDtls); } QString QXmppCallStream::creator() const @@ -360,3 +464,13 @@ void QXmppCallStream::setSendPadCallback(std::function cb) d->sendPadCB(d->sendPad); } } + +void QXmppCallStreamPrivate::toDtlsClientMode() +{ + gst_element_set_state(dtlsrtpencoder, GST_STATE_READY); + gst_element_set_state(dtlsrtcpencoder, GST_STATE_READY); + g_object_set(dtlsrtpencoder, "is-client", true, nullptr); + g_object_set(dtlsrtcpencoder, "is-client", true, nullptr); + gst_element_set_state(dtlsrtpencoder, GST_STATE_PLAYING); + gst_element_set_state(dtlsrtcpencoder, GST_STATE_PLAYING); +} diff --git a/src/client/QXmppCallStream.h b/src/client/QXmppCallStream.h index 71b27b847..dc8f5c82f 100644 --- a/src/client/QXmppCallStream.h +++ b/src/client/QXmppCallStream.h @@ -26,9 +26,8 @@ #include -#include - #include +#include #include @@ -57,7 +56,7 @@ class QXMPP_EXPORT QXmppCallStream : public QObject private: QXmppCallStream(GstElement *pipeline, GstElement *rtpbin, - QString media, QString creator, QString name, int id); + QString media, QString creator, QString name, int id, bool useDtls); QXmppCallStreamPrivate *d; diff --git a/src/client/QXmppCallStream_p.h b/src/client/QXmppCallStream_p.h index 9b58970d6..fbd8a1141 100644 --- a/src/client/QXmppCallStream_p.h +++ b/src/client/QXmppCallStream_p.h @@ -57,7 +57,7 @@ class QXmppCallStreamPrivate : public QObject public: QXmppCallStreamPrivate(QXmppCallStream *parent, GstElement *pipeline_, GstElement *rtpbin_, - QString media_, QString creator_, QString name_, int id_); + QString media_, QString creator_, QString name_, int id_, bool useDtls_); ~QXmppCallStreamPrivate(); GstFlowReturn sendDatagram(GstElement *appsink, int component); @@ -65,8 +65,7 @@ class QXmppCallStreamPrivate : public QObject void addEncoder(QXmppCallPrivate::GstCodec &codec); void addDecoder(GstPad *pad, QXmppCallPrivate::GstCodec &codec); - void addRtpSender(GstPad *pad); - void addRtcpSender(GstPad *pad); + void toDtlsClientMode(); QXmppCallStream *q; @@ -87,6 +86,11 @@ class QXmppCallStreamPrivate : public QObject GstElement *apprtcpsrc; GstElement *apprtpsink; GstElement *apprtcpsink; + GstElement *dtlsrtpencoder; + GstElement *dtlsrtcpencoder; + GstElement *dtlsrtpdecoder; + GstElement *dtlsrtcpdecoder; + QByteArray digest; std::function sendPadCB; std::function receivePadCB; @@ -96,6 +100,8 @@ class QXmppCallStreamPrivate : public QObject QString creator; QString name; int id; + bool useDtls; + bool dtlsHandshakeComplete; QList payloadTypes; }; diff --git a/src/client/QXmppCall_p.h b/src/client/QXmppCall_p.h index b3e052c8f..9cf7bd575 100644 --- a/src/client/QXmppCall_p.h +++ b/src/client/QXmppCall_p.h @@ -77,7 +77,7 @@ class QXmppCallPrivate : public QObject QXmppCallStream *findStreamByMedia(const QString &media); QXmppCallStream *findStreamByName(const QString &name); QXmppCallStream *findStreamById(const int id); - QXmppJingleIq::Content localContent(QXmppCallStream *stream) const; + QXmppJingleIq::Content localContent(QXmppCallStream *stream, QString dtlsSetup) const; void handleAck(const QXmppIq &iq); bool handleDescription(QXmppCallStream *stream, const QXmppJingleIq::Content &content); @@ -92,6 +92,7 @@ class QXmppCallPrivate : public QObject QXmppCall::Direction direction; QString jid; QString ownJid; + bool useDtls; QXmppCallManager *manager; QList requests; QString sid; @@ -106,7 +107,7 @@ class QXmppCallPrivate : public QObject // Supported codecs QList videoCodecs = { - { .pt = 100, .name = "H264", .channels = 1, .clockrate = 90000, .gstPay = "rtph264pay", .gstDepay = "rtph264depay", .gstEnc = "x264enc", .gstDec = "avdec_h264", .encProps = { { "tune", 4 }, { "speed-preset", 3 }, {"byte-stream", true}, { "bitrate", 512 } } }, + { .pt = 100, .name = "H264", .channels = 1, .clockrate = 90000, .gstPay = "rtph264pay", .gstDepay = "rtph264depay", .gstEnc = "x264enc", .gstDec = "avdec_h264", .encProps = { { "tune", 4 }, { "speed-preset", 3 }, { "byte-stream", true }, { "bitrate", 512 } } }, { .pt = 99, .name = "VP8", .channels = 1, .clockrate = 90000, .gstPay = "rtpvp8pay", .gstDepay = "rtpvp8depay", .gstEnc = "vp8enc", .gstDec = "vp8dec", .encProps = { { "deadline", 20000 }, { "target-bitrate", 512000 } } }, // vp9enc and x265enc seem to be very slow. Give them a lower priority for now. { .pt = 102, .name = "H265", .channels = 1, .clockrate = 90000, .gstPay = "rtph265pay", .gstDepay = "rtph265depay", .gstEnc = "x265enc", .gstDec = "avdec_h265", .encProps = { { "tune", 4 }, { "speed-preset", 3 }, { "bitrate", 512 } } },