diff options
| author | Melvin Keskin <melvo@olomono.de> | 2022-09-27 22:27:20 +0200 |
|---|---|---|
| committer | Linus Jahn <lnj@kaidan.im> | 2022-10-01 19:50:31 +0200 |
| commit | 1814f67c9a41eb750a56fdccd28843b53be17b0d (patch) | |
| tree | 6b224b53f6fae15609752c4d30c1eb3b155b5efe | |
| parent | 58646ad17d331f980fa1e03e3831fadea847c2b2 (diff) | |
| download | qxmpp-1814f67c9a41eb750a56fdccd28843b53be17b0d.tar.gz | |
Implement XEP-0294: Jingle RTP Header Extensions Negotiation stanzas
| -rw-r--r-- | doc/doap.xml | 9 | ||||
| -rw-r--r-- | src/base/QXmppConstants.cpp | 2 | ||||
| -rw-r--r-- | src/base/QXmppConstants_p.h | 2 | ||||
| -rw-r--r-- | src/base/QXmppJingleIq.cpp | 263 | ||||
| -rw-r--r-- | src/base/QXmppJingleIq.h | 46 | ||||
| -rw-r--r-- | tests/qxmppjingleiq/tst_qxmppjingleiq.cpp | 171 |
6 files changed, 492 insertions, 1 deletions
diff --git a/doc/doap.xml b/doc/doap.xml index 733fdc65..b3d2c4e0 100644 --- a/doc/doap.xml +++ b/doc/doap.xml @@ -420,6 +420,15 @@ SPDX-License-Identifier: CC0-1.0 </implements> <implements> <xmpp:SupportedXep> + <xmpp:xep rdf:resource='https://xmpp.org/extensions/xep-0294.html'/> + <xmpp:status>complete</xmpp:status> + <xmpp:version>1.1</xmpp:version> + <xmpp:since>1.5</xmpp:since> + <xmpp:note>Manager functionality missing</xmpp:note> + </xmpp:SupportedXep> + </implements> + <implements> + <xmpp:SupportedXep> <xmpp:xep rdf:resource='https://xmpp.org/extensions/xep-0300.html'/> <xmpp:status>complete</xmpp:status> <xmpp:version>1.0</xmpp:version> diff --git a/src/base/QXmppConstants.cpp b/src/base/QXmppConstants.cpp index a98e4754..1729d226 100644 --- a/src/base/QXmppConstants.cpp +++ b/src/base/QXmppConstants.cpp @@ -135,6 +135,8 @@ const char *ns_muji = "urn:xmpp:jingle:muji:0"; const char *ns_carbons = "urn:xmpp:carbons:2"; // XEP-0293: Jingle RTP Feedback Negotiation const char *ns_jingle_rtp_feedback_negotiation = "urn:xmpp:jingle:apps:rtp:rtcp-fb:0"; +// XEP-0294: Jingle RTP Header Extensions Negotiation +const char *ns_jingle_rtp_header_extensions_negotiation = "urn:xmpp:jingle:apps:rtp:rtp-hdrext:0"; // XEP-0297: Stanza Forwarding const char *ns_forwarding = "urn:xmpp:forward:0"; // XEP-0300: Use of Cryptographic Hash Functions in XMPP diff --git a/src/base/QXmppConstants_p.h b/src/base/QXmppConstants_p.h index 8db12563..1f067b2b 100644 --- a/src/base/QXmppConstants_p.h +++ b/src/base/QXmppConstants_p.h @@ -147,6 +147,8 @@ extern const char *ns_muji; extern const char *ns_carbons; // XEP-0293: Jingle RTP Feedback Negotiation extern const char *ns_jingle_rtp_feedback_negotiation; +// XEP-0294: Jingle RTP Header Extensions Negotiation +extern const char *ns_jingle_rtp_header_extensions_negotiation; // XEP-0297: Stanza Forwarding extern const char *ns_forwarding; // XEP-0300: Use of Cryptographic Hash Functions in XMPP diff --git a/src/base/QXmppJingleIq.cpp b/src/base/QXmppJingleIq.cpp index c6ae9ede..ff92993e 100644 --- a/src/base/QXmppJingleIq.cpp +++ b/src/base/QXmppJingleIq.cpp @@ -57,6 +57,12 @@ static const char *jingle_reasons[] = { "unsupported-transports", }; +static const QStringList JINGLE_RTP_HEADER_EXTENSIONS_SENDERS = { + QStringLiteral("both"), + QStringLiteral("initiator"), + QStringLiteral("responder") +}; + static QString formatFingerprint(const QByteArray &digest) { QString fingerprint; @@ -136,7 +142,8 @@ static void parseSdpParameters(const QDomElement &parent, QVector<QXmppSdpParame } // Serializes the SDP parameters. -static void sdpParametersToXml(QXmlStreamWriter *writer, const QVector<QXmppSdpParameter> ¶meters) { +static void sdpParametersToXml(QXmlStreamWriter *writer, const QVector<QXmppSdpParameter> ¶meters) +{ for (const auto ¶meter : parameters) { parameter.toXml(writer); } @@ -173,6 +180,37 @@ static void jingleRtpFeedbackNegotiationElementsToXml(QXmlStreamWriter *writer, } } +// Parses all found RTP Header Extensions Negotiation elements inside of parent into properties and +// isRtpHeaderExtensionMixingAllowed. +static void parseJingleRtpHeaderExtensionsNegotiationElements(const QDomElement &parent, QVector<QXmppJingleRtpHeaderExtensionProperty> &properties, bool &isRtpHeaderExtensionMixingAllowed) +{ + for (auto child = parent.firstChildElement(); + !child.isNull(); + child = child.nextSiblingElement()) { + if (QXmppJingleRtpHeaderExtensionProperty::isJingleRtpHeaderExtensionProperty(child)) { + QXmppJingleRtpHeaderExtensionProperty property; + property.parse(child); + properties.append(property); + } else if (child.tagName() == QStringLiteral("extmap-allow-mixed") && child.namespaceURI() == ns_jingle_rtp_header_extensions_negotiation) { + isRtpHeaderExtensionMixingAllowed = true; + } + } +} + +// Serializes the RTP header extension properties and isRtpHeaderExtensionMixingAllowed. +static void jingleRtpHeaderExtensionsNegotiationElementsToXml(QXmlStreamWriter *writer, const QVector<QXmppJingleRtpHeaderExtensionProperty> &properties, bool isRtpHeaderExtensionMixingAllowed) +{ + for (const auto &property : properties) { + property.toXml(writer); + } + + if (isRtpHeaderExtensionMixingAllowed) { + writer->writeStartElement(QStringLiteral("extmap-allow-mixed")); + writer->writeDefaultNamespace(ns_jingle_rtp_header_extensions_negotiation); + writer->writeEndElement(); + } +} + class QXmppJingleIqContentPrivate : public QSharedData { public: @@ -202,6 +240,10 @@ public: // XEP-0293: Jingle RTP Feedback Negotiation QVector<QXmppJingleRtpFeedbackProperty> rtpFeedbackProperties; QVector<QXmppJingleRtpFeedbackInterval> rtpFeedbackIntervals; + + // XEP-0294: Jingle RTP Header Extensions Negotiation + QVector<QXmppJingleRtpHeaderExtensionProperty> rtpHeaderExtensionProperties; + bool isRtpHeaderExtensionMixingAllowed = false; }; QXmppJingleIqContentPrivate::QXmppJingleIqContentPrivate() @@ -481,6 +523,58 @@ void QXmppJingleIq::Content::setRtpFeedbackIntervals(const QVector<QXmppJingleRt } /// +/// Returns the RTP header extension properties. +/// +/// \return the RTP header extension properties +/// +/// \since QXmpp 1.5 +/// +QVector<QXmppJingleRtpHeaderExtensionProperty> QXmppJingleIq::Content::rtpHeaderExtensionProperties() const +{ + return d->rtpHeaderExtensionProperties; +} + +/// +/// Sets the RTP header extension properties. +/// +/// \param rtpHeaderExtensionProperties RTP header extension properties +/// +/// \since QXmpp 1.5 +/// +void QXmppJingleIq::Content::setRtpHeaderExtensionProperties(const QVector<QXmppJingleRtpHeaderExtensionProperty> &rtpHeaderExtensionProperties) +{ + d->rtpHeaderExtensionProperties = rtpHeaderExtensionProperties; +} + +/// +/// Returns whether mixing of RTP header extensions is allowed corresponding to the +/// "extmap-allow-mixed" element as specified by +/// \xep{0293, Jingle RTP Header Extensions Negotiation}. +/// +/// \return whether mixing of RTP header extensions is allowed +/// +/// \since QXmpp 1.5 +/// +bool QXmppJingleIq::Content::isRtpHeaderExtensionMixingAllowed() const +{ + return d->isRtpHeaderExtensionMixingAllowed; +} + +/// +/// Sets whether mixing of RTP header extensions is allowed corresponding to the +/// "extmap-allow-mixed" element as specified by +/// \xep{0293, Jingle RTP Header Extensions Negotiation}. +/// +/// \param isAllowed whether mixing of RTP header extensions is allowed +/// +/// \since QXmpp 1.5 +/// +void QXmppJingleIq::Content::setRtpHeaderExtensionMixingAllowed(bool isRtpHeaderExtensionMixingAllowed) +{ + d->isRtpHeaderExtensionMixingAllowed = isRtpHeaderExtensionMixingAllowed; +} + +/// /// Returns the fingerprint hash value for the transport key. /// /// This is used for DTLS-SRTP as defined in \xep{0320}. @@ -568,6 +662,7 @@ void QXmppJingleIq::Content::parse(const QDomElement &element) d->isRtpMultiplexingSupported = !descriptionElement.firstChildElement(QStringLiteral("rtcp-mux")).isNull(); parseJingleRtpFeedbackNegotiationElements(descriptionElement, d->rtpFeedbackProperties, d->rtpFeedbackIntervals); + parseJingleRtpHeaderExtensionsNegotiationElements(descriptionElement, d->rtpHeaderExtensionProperties, d->isRtpHeaderExtensionMixingAllowed); QDomElement child = descriptionElement.firstChildElement(QStringLiteral("payload-type")); while (!child.isNull()) { @@ -626,6 +721,7 @@ void QXmppJingleIq::Content::toXml(QXmlStreamWriter *writer) const } jingleRtpFeedbackNegotiationElementsToXml(writer, d->rtpFeedbackProperties, d->rtpFeedbackIntervals); + jingleRtpHeaderExtensionsNegotiationElementsToXml(writer, d->rtpHeaderExtensionProperties, d->isRtpHeaderExtensionMixingAllowed); for (const auto &payload : d->payloadTypes) { payload.toXml(writer); @@ -2162,3 +2258,168 @@ bool QXmppJingleRtpFeedbackInterval::isJingleRtpFeedbackInterval(const QDomEleme return element.tagName() == QStringLiteral("rtcp-fb-trr-int") && element.namespaceURI() == ns_jingle_rtp_feedback_negotiation; } + +class QXmppJingleRtpHeaderExtensionPropertyPrivate : public QSharedData +{ +public: + uint32_t id = 0; + QString uri; + QXmppJingleRtpHeaderExtensionProperty::Senders senders = QXmppJingleRtpHeaderExtensionProperty::Both; + QVector<QXmppSdpParameter> parameters; +}; + +/// +/// \enum QXmppJingleRtpHeaderExtensionProperty::Senders +/// +/// Parties that are allowed to send the negotiated RTP header extension +/// + +/// +/// \class QXmppJingleRtpHeaderExtensionProperty +/// +/// \brief The QXmppJingleRtpHeaderExtensionProperty class represents the +/// \xep{0294, Jingle RTP Header Extensions Negotiation} "rtp-hdrext" element. +/// +/// \since QXmpp 1.5 +/// + +/// +/// Constructs a Jingle RTP header extension property. +/// +QXmppJingleRtpHeaderExtensionProperty::QXmppJingleRtpHeaderExtensionProperty() + : d(new QXmppJingleRtpHeaderExtensionPropertyPrivate()) +{ +} + +QXMPP_PRIVATE_DEFINE_RULE_OF_SIX(QXmppJingleRtpHeaderExtensionProperty) + +/// +/// Returns the ID of the RTP header extension. +/// +/// The ID is 0 if it is unset. +/// +/// \return the RTP header extension's ID +/// +uint32_t QXmppJingleRtpHeaderExtensionProperty::id() const +{ + return d->id; +} + +/// +/// Sets the ID of the RTP header extension. +/// +/// The ID must either be at least 1 and at most 256 or at least 4096 and at most 4351. +/// +/// \param id RTP header extension's ID +/// +void QXmppJingleRtpHeaderExtensionProperty::setId(uint32_t id) +{ + d->id = id; +} + +/// +/// Returns the URI defning the RTP header extension. +/// +/// \return the RTP header extension's URI +/// +QString QXmppJingleRtpHeaderExtensionProperty::uri() const +{ + return d->uri; +} + +/// +/// Sets the URI defning the RTP header extension. +/// +/// \param uri RTP header extension's URI +/// +void QXmppJingleRtpHeaderExtensionProperty::setUri(const QString &uri) +{ + d->uri = uri; +} + +/// +/// Returns the parties that are allowed to send the negotiated RTP header extensions. +/// +/// \return the parties that are allowed to send the RTP header extensions +/// +QXmppJingleRtpHeaderExtensionProperty::Senders QXmppJingleRtpHeaderExtensionProperty::senders() const +{ + return d->senders; +} + +/// +/// Sets the parties that are allowed to send the negotiated RTP header extensions. +/// +/// \param senders parties that are allowed to send the RTP header extensions +/// +void QXmppJingleRtpHeaderExtensionProperty::setSenders(Senders senders) +{ + d->senders = senders; +} + +/// +/// Returns the parameters of the RTP header extension. +/// +/// \return the RTP header extension's parameters +/// +QVector<QXmppSdpParameter> QXmppJingleRtpHeaderExtensionProperty::parameters() const +{ + return d->parameters; +} + +/// +/// Sets the parameters of the RTP header extension. +/// +/// Additional parameters can be set by this method. +/// +/// \param parameters RTP header extension's parameters +/// +void QXmppJingleRtpHeaderExtensionProperty::setParameters(const QVector<QXmppSdpParameter> ¶meters) +{ + d->parameters = parameters; +} + +/// \cond +void QXmppJingleRtpHeaderExtensionProperty::parse(const QDomElement &element) +{ + if (element.tagName() == QStringLiteral("rtp-hdrext") && element.namespaceURI() == ns_jingle_rtp_header_extensions_negotiation) { + d->id = element.attribute(QStringLiteral("id")).toUInt(); + d->uri = element.attribute(QStringLiteral("uri")); + + if (const auto senders = JINGLE_RTP_HEADER_EXTENSIONS_SENDERS.indexOf(element.attribute(QStringLiteral("senders"))); senders > QXmppJingleRtpHeaderExtensionProperty::Both) { + d->senders = static_cast<QXmppJingleRtpHeaderExtensionProperty::Senders>(senders); + } + + parseSdpParameters(element, d->parameters); + } +} + +void QXmppJingleRtpHeaderExtensionProperty::toXml(QXmlStreamWriter *writer) const +{ + writer->writeStartElement(QStringLiteral("rtp-hdrext")); + writer->writeDefaultNamespace(ns_jingle_rtp_header_extensions_negotiation); + helperToXmlAddAttribute(writer, QStringLiteral("id"), QString::number(d->id)); + helperToXmlAddAttribute(writer, QStringLiteral("uri"), d->uri); + + if (d->senders != QXmppJingleRtpHeaderExtensionProperty::Both) { + helperToXmlAddAttribute(writer, QStringLiteral("senders"), JINGLE_RTP_HEADER_EXTENSIONS_SENDERS.at(d->senders)); + } + + sdpParametersToXml(writer, d->parameters); + + writer->writeEndElement(); +} +/// \endcond + +/// +/// Determines whether the given DOM element is an RTP header extensions property element. +/// +/// \param element DOM element being checked +/// +/// \return whether element is an RTP header extension property element +/// +bool QXmppJingleRtpHeaderExtensionProperty::isJingleRtpHeaderExtensionProperty(const QDomElement &element) +{ + return element.tagName() == QStringLiteral("rtp-hdrext") && + element.namespaceURI() == ns_jingle_rtp_header_extensions_negotiation; +} diff --git a/src/base/QXmppJingleIq.h b/src/base/QXmppJingleIq.h index 02ecc9a5..8dd3e68f 100644 --- a/src/base/QXmppJingleIq.h +++ b/src/base/QXmppJingleIq.h @@ -17,6 +17,7 @@ class QXmppJingleIqContentPrivate; class QXmppJingleIqPrivate; class QXmppJinglePayloadTypePrivate; class QXmppJingleRtpFeedbackPropertyPrivate; +class QXmppJingleRtpHeaderExtensionPropertyPrivate; class QXmppSdpParameterPrivate; class QXMPP_EXPORT QXmppSdpParameter @@ -91,6 +92,45 @@ private: uint64_t m_value; }; +class QXMPP_EXPORT QXmppJingleRtpHeaderExtensionProperty +{ +public: + enum Senders { + /// The initiator and the sender are allowed. + Both, + /// Only the initiator is allowed. + Initiator, + /// Only the responder is allowed. + Responder + }; + + QXmppJingleRtpHeaderExtensionProperty(); + + QXMPP_PRIVATE_DECLARE_RULE_OF_SIX(QXmppJingleRtpHeaderExtensionProperty) + + uint32_t id() const; + void setId(uint32_t id); + + QString uri() const; + void setUri(const QString &uri); + + Senders senders() const; + void setSenders(Senders senders); + + QVector<QXmppSdpParameter> parameters() const; + void setParameters(const QVector<QXmppSdpParameter> ¶meters); + + /// \cond + void parse(const QDomElement &element); + void toXml(QXmlStreamWriter *writer) const; + /// \endcond + + static bool isJingleRtpHeaderExtensionProperty(const QDomElement &element); + +private: + QSharedDataPointer<QXmppJingleRtpHeaderExtensionPropertyPrivate> d; +}; + /// /// \brief The QXmppJinglePayloadType class represents a payload type /// as specified by \xep{0167}: Jingle RTP Sessions and RFC 5245. @@ -330,6 +370,12 @@ public: QVector<QXmppJingleRtpFeedbackInterval> rtpFeedbackIntervals() const; void setRtpFeedbackIntervals(const QVector<QXmppJingleRtpFeedbackInterval> &rtpFeedbackIntervals); + QVector<QXmppJingleRtpHeaderExtensionProperty> rtpHeaderExtensionProperties() const; + void setRtpHeaderExtensionProperties(const QVector<QXmppJingleRtpHeaderExtensionProperty> &rtpHeaderExtensionProperties); + + bool isRtpHeaderExtensionMixingAllowed() const; + void setRtpHeaderExtensionMixingAllowed(bool isRtpHeaderExtensionMixingAllowed); + // XEP-0320: Use of DTLS-SRTP in Jingle Sessions QByteArray transportFingerprint() const; void setTransportFingerprint(const QByteArray &fingerprint); diff --git a/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp b/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp index be9e3d4c..aaa6822d 100644 --- a/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp +++ b/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp @@ -24,6 +24,11 @@ private slots: void testIsRtpFeedbackInterval_data(); void testIsRtpFeedbackInterval(); void testRtpFeedbackInterval(); + void testIsRtpHeaderExtensionProperty_data(); + void testIsRtpHeaderExtensionProperty(); + void testRtpHeaderExtensionProperty(); + void testRtpHeaderExtensionPropertyWithSenders(); + void testRtpHeaderExtensionPropertyWithParameters(); void testCandidate(); void testContent(); void testContentFingerprint(); @@ -32,6 +37,7 @@ private slots: void testContentSdpFingerprint(); void testContentSdpParameters(); void testContentRtpFeedbackNegotiation(); + void testContentRtpHeaderExtensionsNegotiation(); void testSession(); void testTerminate(); void testRtpSessionState_data(); @@ -229,6 +235,115 @@ void tst_QXmppJingleIq::testRtpFeedbackInterval() serializePacket(interval2, xml); } +void tst_QXmppJingleIq::testIsRtpHeaderExtensionProperty_data() +{ + QTest::addColumn<QByteArray>("xml"); + QTest::addColumn<bool>("isValid"); + + QTest::newRow("valid") + << QByteArrayLiteral("<rtp-hdrext xmlns=\"urn:xmpp:jingle:apps:rtp:rtp-hdrext:0\"/>") + << true; + QTest::newRow("invalidTag") + << QByteArrayLiteral("<invalid xmlns=\"urn:xmpp:jingle:apps:rtp:rtp-hdrext:0\"/>") + << false; + QTest::newRow("invalidNamespace") + << QByteArrayLiteral("<rtp-hdrext xmlns=\"invalid\"/>") + << false; +} + +void tst_QXmppJingleIq::testIsRtpHeaderExtensionProperty() +{ + QFETCH(QByteArray, xml); + QFETCH(bool, isValid); + + QCOMPARE(QXmppJingleRtpHeaderExtensionProperty::isJingleRtpHeaderExtensionProperty(xmlToDom(xml)), isValid); +} + +void tst_QXmppJingleIq::testRtpHeaderExtensionProperty() +{ + const QByteArray xml("<rtp-hdrext xmlns=\"urn:xmpp:jingle:apps:rtp:rtp-hdrext:0\" id=\"1\" uri=\"urn:ietf:params:rtp-hdrext:toffset\"/>"); + + QXmppJingleRtpHeaderExtensionProperty property1; + QCOMPARE(property1.id(), 0); + QVERIFY(property1.uri().isEmpty()); + QCOMPARE(property1.senders(), QXmppJingleRtpHeaderExtensionProperty::Both); + + parsePacket(property1, xml); + QCOMPARE(property1.id(), 1); + QCOMPARE(property1.uri(), QStringLiteral("urn:ietf:params:rtp-hdrext:toffset")); + QCOMPARE(property1.senders(), QXmppJingleRtpHeaderExtensionProperty::Both); + + serializePacket(property1, xml); + + QXmppJingleRtpHeaderExtensionProperty property2; + property2.setId(1); + property2.setUri(QStringLiteral("urn:ietf:params:rtp-hdrext:toffset")); + property2.setSenders(QXmppJingleRtpHeaderExtensionProperty::Both); + + QCOMPARE(property1.id(), 1); + QCOMPARE(property1.uri(), QStringLiteral("urn:ietf:params:rtp-hdrext:toffset")); + QCOMPARE(property1.senders(), QXmppJingleRtpHeaderExtensionProperty::Both); + + serializePacket(property2, xml); +} + +void tst_QXmppJingleIq::testRtpHeaderExtensionPropertyWithSenders() +{ + const QByteArray xml("<rtp-hdrext xmlns=\"urn:xmpp:jingle:apps:rtp:rtp-hdrext:0\" id=\"1\" uri=\"urn:ietf:params:rtp-hdrext:toffset\" senders=\"initiator\"/>"); + + QXmppJingleRtpHeaderExtensionProperty property1; + + parsePacket(property1, xml); + QCOMPARE(property1.senders(), QXmppJingleRtpHeaderExtensionProperty::Initiator); + + serializePacket(property1, xml); + + QXmppJingleRtpHeaderExtensionProperty property2; + property2.setId(1); + property2.setUri(QStringLiteral("urn:ietf:params:rtp-hdrext:toffset")); + property2.setSenders(QXmppJingleRtpHeaderExtensionProperty::Initiator); + + QCOMPARE(property1.senders(), QXmppJingleRtpHeaderExtensionProperty::Initiator); + + serializePacket(property2, xml); +} + +void tst_QXmppJingleIq::testRtpHeaderExtensionPropertyWithParameters() +{ + const QByteArray xml( + "<rtp-hdrext xmlns=\"urn:xmpp:jingle:apps:rtp:rtp-hdrext:0\" id=\"1\" uri=\"urn:ietf:params:rtp-hdrext:toffset\">" + "<parameter name=\"test-name-1\"/>" + "<parameter name=\"test-name-2\"/>" + "</rtp-hdrext>"); + + QXmppJingleRtpHeaderExtensionProperty property1; + + parsePacket(property1, xml); + QCOMPARE(property1.parameters().size(), 2); + QCOMPARE(property1.parameters().at(0).name(), QStringLiteral("test-name-1")); + QCOMPARE(property1.parameters().at(1).name(), QStringLiteral("test-name-2")); + + serializePacket(property1, xml); + + QXmppJingleRtpHeaderExtensionProperty property2; + property2.setId(1); + property2.setUri(QStringLiteral("urn:ietf:params:rtp-hdrext:toffset")); + + QXmppSdpParameter parameter1; + parameter1.setName(QStringLiteral("test-name-1")); + + QXmppSdpParameter parameter2; + parameter2.setName(QStringLiteral("test-name-2")); + + property2.setParameters({ parameter1, parameter2 }); + + QCOMPARE(property2.parameters().size(), 2); + QCOMPARE(property2.parameters().at(0).name(), QStringLiteral("test-name-1")); + QCOMPARE(property2.parameters().at(1).name(), QStringLiteral("test-name-2")); + + serializePacket(property2, xml); +} + void tst_QXmppJingleIq::testCandidate() { const QByteArray xml( @@ -641,6 +756,62 @@ void tst_QXmppJingleIq::testContentRtpFeedbackNegotiation() serializePacket(content2, xml); } +void tst_QXmppJingleIq::testContentRtpHeaderExtensionsNegotiation() +{ + const QByteArray xml( + "<content creator=\"initiator\" name=\"voice\">" + "<description xmlns=\"urn:xmpp:jingle:apps:rtp:1\">" + "<rtp-hdrext xmlns=\"urn:xmpp:jingle:apps:rtp:rtp-hdrext:0\" id=\"1\" uri=\"urn:ietf:params:rtp-hdrext:toffset\"/>" + "<rtp-hdrext xmlns=\"urn:xmpp:jingle:apps:rtp:rtp-hdrext:0\" id=\"2\" uri=\"urn:ietf:params:rtp-hdrext:ntp-64\"/>" + "<extmap-allow-mixed xmlns=\"urn:xmpp:jingle:apps:rtp:rtp-hdrext:0\"/>" + "<payload-type id=\"96\" name=\"speex\"/>" + "</description>" + "</content>"); + + QXmppJingleIq::Content content1; + QVERIFY(content1.rtpHeaderExtensionProperties().isEmpty()); + QVERIFY(!content1.isRtpHeaderExtensionMixingAllowed()); + parsePacket(content1, xml); + + const auto rtpHeaderExtensionProperties1 = content1.rtpHeaderExtensionProperties(); + QCOMPARE(rtpHeaderExtensionProperties1.size(), 2); + QCOMPARE(rtpHeaderExtensionProperties1[0].id(), 1); + QCOMPARE(rtpHeaderExtensionProperties1[1].id(), 2); + + QVERIFY(content1.isRtpHeaderExtensionMixingAllowed()); + + serializePacket(content1, xml); + + QXmppJingleRtpHeaderExtensionProperty rtpHeaderExtensionProperty1; + rtpHeaderExtensionProperty1.setId(1); + rtpHeaderExtensionProperty1.setUri(QStringLiteral("urn:ietf:params:rtp-hdrext:toffset")); + + QXmppJingleRtpHeaderExtensionProperty rtpHeaderExtensionProperty2; + rtpHeaderExtensionProperty2.setId(2); + rtpHeaderExtensionProperty2.setUri(QStringLiteral("urn:ietf:params:rtp-hdrext:ntp-64")); + + + QXmppJinglePayloadType payloadType; + payloadType.setId(96); + payloadType.setName(QStringLiteral("speex")); + + QXmppJingleIq::Content content2; + content2.setCreator(QStringLiteral("initiator")); + content2.setName(QStringLiteral("voice")); + content2.addPayloadType(payloadType); + content2.setRtpHeaderExtensionProperties({ rtpHeaderExtensionProperty1, rtpHeaderExtensionProperty2 }); + content2.setRtpHeaderExtensionMixingAllowed(true); + + const auto rtpHeaderExtensionProperties2 = content2.rtpHeaderExtensionProperties(); + QCOMPARE(rtpHeaderExtensionProperties2.size(), 2); + QCOMPARE(rtpHeaderExtensionProperties2[0].id(), 1); + QCOMPARE(rtpHeaderExtensionProperties2[1].id(), 2); + + QVERIFY(content2.isRtpHeaderExtensionMixingAllowed()); + + serializePacket(content2, xml); +} + void tst_QXmppJingleIq::testSession() { const QByteArray xml( |
