diff options
| author | Jeremy Lainé <jeremy.laine@m4x.org> | 2015-08-27 16:08:07 +0200 |
|---|---|---|
| committer | Jeremy Lainé <jeremy.laine@m4x.org> | 2015-08-27 16:28:36 +0200 |
| commit | 44cbba5d6538b998b5b5fbb4264d78ab135f669f (patch) | |
| tree | bfc03a4c282c3274bf0ec323d9b547ca99ef58e0 | |
| parent | a100a8b3cf20c7dc029416e4471a11fea7c51599 (diff) | |
| download | qxmpp-44cbba5d6538b998b5b5fbb4264d78ab135f669f.tar.gz | |
add method to parse SDP to QXmppJingleIq::Content
| -rw-r--r-- | src/base/QXmppJingleIq.cpp | 180 | ||||
| -rw-r--r-- | src/base/QXmppJingleIq.h | 4 | ||||
| -rw-r--r-- | tests/qxmppjingleiq/tst_qxmppjingleiq.cpp | 105 |
3 files changed, 230 insertions, 59 deletions
diff --git a/src/base/QXmppJingleIq.cpp b/src/base/QXmppJingleIq.cpp index 17dbfb9f..3422734d 100644 --- a/src/base/QXmppJingleIq.cpp +++ b/src/base/QXmppJingleIq.cpp @@ -79,6 +79,37 @@ static QString addressToSdp(const QHostAddress &host) host.toString()); } +static bool candidateParseSdp(QXmppJingleCandidate *candidate, const QString &sdp) +{ + if (!sdp.startsWith("candidate:")) + return false; + + const QStringList bits = sdp.mid(10).split(" "); + if (bits.size() < 6) + return false; + + candidate->setFoundation(bits[0]); + candidate->setComponent(bits[1].toInt()); + candidate->setProtocol(bits[2].toLower()); + candidate->setPriority(bits[3].toInt()); + candidate->setHost(QHostAddress(bits[4])); + candidate->setPort(bits[5].toInt()); + for (int i = 6; i < bits.size() - 1; i += 2) { + if (bits[i] == "typ") { + bool ok; + candidate->setType(QXmppJingleCandidate::typeFromString(bits[i + 1], &ok)); + if (!ok) + return false; + } else if (bits[i] == "generation") { + candidate->setGeneration(bits[i + 1].toInt()); + } else { + qWarning() << "Candidate SDP contains unknown attribute" << bits[i]; + return false; + } + } + return true; +} + static QString candidateToSdp(const QXmppJingleCandidate &candidate) { return QString("candidate:%1 %2 %3 %4 %5 %6 typ %7 generation %8").arg( @@ -88,7 +119,7 @@ static QString candidateToSdp(const QXmppJingleCandidate &candidate) QString::number(candidate.priority()), candidate.host().toString(), QString::number(candidate.port()), - "host", + QXmppJingleCandidate::typeToString(candidate.type()), QString::number(candidate.generation()) ); } @@ -233,6 +264,117 @@ void QXmppJingleIq::Content::parse(const QDomElement &element) } } +void QXmppJingleIq::Content::toXml(QXmlStreamWriter *writer) const +{ + if (m_creator.isEmpty() || m_name.isEmpty()) + return; + + writer->writeStartElement("content"); + helperToXmlAddAttribute(writer, "creator", m_creator); + helperToXmlAddAttribute(writer, "disposition", m_disposition); + helperToXmlAddAttribute(writer, "name", m_name); + helperToXmlAddAttribute(writer, "senders", m_senders); + + // description + if (!m_descriptionType.isEmpty() || !m_payloadTypes.isEmpty()) + { + writer->writeStartElement("description"); + writer->writeAttribute("xmlns", m_descriptionType); + helperToXmlAddAttribute(writer, "media", m_descriptionMedia); + if (m_descriptionSsrc) + writer->writeAttribute("ssrc", QString::number(m_descriptionSsrc)); + foreach (const QXmppJinglePayloadType &payload, m_payloadTypes) + payload.toXml(writer); + writer->writeEndElement(); + } + + // transport + if (!m_transportType.isEmpty() || !m_transportCandidates.isEmpty()) + { + writer->writeStartElement("transport"); + writer->writeAttribute("xmlns", m_transportType); + helperToXmlAddAttribute(writer, "ufrag", m_transportUser); + helperToXmlAddAttribute(writer, "pwd", m_transportPassword); + foreach (const QXmppJingleCandidate &candidate, m_transportCandidates) + candidate.toXml(writer); + writer->writeEndElement(); + } + writer->writeEndElement(); +} + +bool QXmppJingleIq::Content::parseSdp(const QString &sdp) +{ + QList<QXmppJinglePayloadType> payloads; + foreach (const QString &line, sdp.split("\r\n")) { + if (line.startsWith("a=")) { + int idx = line.indexOf(':'); + const QString attrName = idx != -1 ? line.mid(2, idx - 2) : line.mid(2); + const QString attrValue = idx != -1 ? line.mid(idx + 1) : ""; + + if (attrName == "candidate") { + QXmppJingleCandidate candidate; + if (!candidateParseSdp(&candidate, line.mid(2))) { + qWarning() << "Could not parse candidate" << line; + return false; + } + addTransportCandidate(candidate); + } else if (attrName == "rtpmap") { + // payload type map + const QStringList bits = attrValue.split(' '); + if (bits.size() != 2) + continue; + bool ok = false; + const int id = bits[0].toInt(&ok); + if (!ok) + continue; + + const QStringList args = bits[1].split('/'); + for (int i = 0; i < payloads.size(); ++i) { + if (payloads[i].id() == id) { + payloads[i].setName(args[0]); + if (args.size() > 1) + payloads[i].setClockrate(args[1].toInt()); + if (args.size() > 2) + payloads[i].setChannels(args[2].toInt()); + } + } + } else if (attrName == "ice-ufrag") { + m_transportUser = attrValue; + } else if (attrName == "ice-pwd") { + m_transportPassword = attrValue; + } else if (attrName == "ssrc") { + const QStringList bits = attrValue.split(' '); + if (bits.isEmpty()) { + qWarning() << "Could not parse ssrc" << line; + return false; + } + m_descriptionSsrc = bits[0].toULong(); + } + } else if (line.startsWith("m=")) { + // FIXME: what do we do with the profile (bits[2]) ? + QStringList bits = line.mid(2).split(' '); + if (bits.size() < 3) { + qWarning() << "Could not parse media" << line; + return false; + } + m_descriptionMedia = bits[0]; + + // parse payload types + for (int i = 3; i < bits.size(); ++i) { + bool ok = false; + int id = bits[i].toInt(&ok); + if (!ok) + continue; + QXmppJinglePayloadType payload; + payload.setId(id); + payloads << payload; + } + } + } + setPayloadTypes(payloads); + return true; +} + QString QXmppJingleIq::Content::toSdp() const { const quint32 ntpSeconds = QDateTime(QDate(1900, 1, 1)).secsTo(QDateTime::currentDateTime()); @@ -285,43 +427,7 @@ QString QXmppJingleIq::Content::toSdp() const return sdp.join("\r\n") + "\r\n"; } -void QXmppJingleIq::Content::toXml(QXmlStreamWriter *writer) const -{ - if (m_creator.isEmpty() || m_name.isEmpty()) - return; - writer->writeStartElement("content"); - helperToXmlAddAttribute(writer, "creator", m_creator); - helperToXmlAddAttribute(writer, "disposition", m_disposition); - helperToXmlAddAttribute(writer, "name", m_name); - helperToXmlAddAttribute(writer, "senders", m_senders); - - // description - if (!m_descriptionType.isEmpty() || !m_payloadTypes.isEmpty()) - { - writer->writeStartElement("description"); - writer->writeAttribute("xmlns", m_descriptionType); - helperToXmlAddAttribute(writer, "media", m_descriptionMedia); - if (m_descriptionSsrc) - writer->writeAttribute("ssrc", QString::number(m_descriptionSsrc)); - foreach (const QXmppJinglePayloadType &payload, m_payloadTypes) - payload.toXml(writer); - writer->writeEndElement(); - } - - // transport - if (!m_transportType.isEmpty() || !m_transportCandidates.isEmpty()) - { - writer->writeStartElement("transport"); - writer->writeAttribute("xmlns", m_transportType); - helperToXmlAddAttribute(writer, "ufrag", m_transportUser); - helperToXmlAddAttribute(writer, "pwd", m_transportPassword); - foreach (const QXmppJingleCandidate &candidate, m_transportCandidates) - candidate.toXml(writer); - writer->writeEndElement(); - } - writer->writeEndElement(); -} /// \endcond QXmppJingleIq::Reason::Reason() diff --git a/src/base/QXmppJingleIq.h b/src/base/QXmppJingleIq.h index b0854495..7bb0e80a 100644 --- a/src/base/QXmppJingleIq.h +++ b/src/base/QXmppJingleIq.h @@ -217,8 +217,10 @@ public: /// \cond void parse(const QDomElement &element); - QString toSdp() const; void toXml(QXmlStreamWriter *writer) const; + + bool parseSdp(const QString &sdp); + QString toSdp() const; /// \endcond private: diff --git a/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp b/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp index f73f32e2..ad20e083 100644 --- a/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp +++ b/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp @@ -32,6 +32,7 @@ class tst_QXmppJingleIq : public QObject private slots: void testCandidate(); void testRtpSession(); + void testRtpSessionSdp(); void testSession(); void testTerminate(); void testAudioPayloadType(); @@ -69,24 +70,6 @@ void tst_QXmppJingleIq::testCandidate() void tst_QXmppJingleIq::testRtpSession() { - const QString sdp( - "v=0\r\n" - "o=- NTPSTAMP NTPSTAMP IN IP4 0.0.0.0\r\n" - "s=-\r\n" - "t=0 0\r\n" - "m=audio 8998 RTP/AVP 96 97 18 0 103 98\r\n" - "c=IN IP4 10.0.1.1\r\n" - "a=rtpmap:96 speex/16000\r\n" - "a=rtpmap:97 speex/8000\r\n" - "a=rtpmap:18 G729/0\r\n" - "a=rtpmap:0 PCMU/0\r\n" - "a=rtpmap:103 L16/16000/2\r\n" - "a=rtpmap:98 x-ISAC/8000\r\n" - "a=candidate:1 1 udp 2130706431 10.0.1.1 8998 typ host generation 0\r\n" - "a=candidate:2 1 udp 1694498815 192.0.2.3 45664 typ host generation 0\r\n" - "a=ice-ufrag:8hhy\r\n" - "a=ice-pwd:asd88fgpdd777uzjYhagZg\r\n"); - const QByteArray xml( "<iq" " id=\"ih28sx61\"" @@ -139,13 +122,93 @@ void tst_QXmppJingleIq::testRtpSession() QCOMPARE(session.action(), QXmppJingleIq::SessionInitiate); QCOMPARE(session.initiator(), QLatin1String("romeo@montague.lit/orchard")); QCOMPARE(session.sid(), QLatin1String("a73sjjvkla37jfea")); - QCOMPARE(session.content().creator(), QLatin1String("initiator")); - QCOMPARE(session.content().name(), QLatin1String("voice")); + + const QXmppJingleIq::Content &content = session.content(); + QCOMPARE(content.creator(), QLatin1String("initiator")); + QCOMPARE(content.name(), QLatin1String("voice")); + QCOMPARE(content.descriptionMedia(), QLatin1String("audio")); + QCOMPARE(content.descriptionSsrc(), quint32(0)); + QCOMPARE(content.payloadTypes().size(), 6); + QCOMPARE(content.payloadTypes()[0].id(), quint8(96)); + QCOMPARE(content.payloadTypes()[1].id(), quint8(97)); + QCOMPARE(content.payloadTypes()[2].id(), quint8(18)); + QCOMPARE(content.payloadTypes()[3].id(), quint8(0)); + QCOMPARE(content.payloadTypes()[4].id(), quint8(103)); + QCOMPARE(content.payloadTypes()[5].id(), quint8(98)); + QCOMPARE(content.transportCandidates().size(), 2); + QCOMPARE(content.transportCandidates()[0].component(), 1); + QCOMPARE(content.transportCandidates()[0].foundation(), QLatin1String("1")); + QCOMPARE(content.transportCandidates()[0].host(), QHostAddress("10.0.1.1")); + QCOMPARE(content.transportCandidates()[0].port(), quint16(8998)); + QCOMPARE(content.transportCandidates()[0].priority(), 2130706431); + QCOMPARE(content.transportCandidates()[0].protocol(), QLatin1String("udp")); + QCOMPARE(content.transportCandidates()[0].type(), QXmppJingleCandidate::HostType); + QCOMPARE(content.transportCandidates()[1].component(), 1); + QCOMPARE(content.transportCandidates()[1].foundation(), QLatin1String("2")); + QCOMPARE(content.transportCandidates()[1].host(), QHostAddress("192.0.2.3")); + QCOMPARE(content.transportCandidates()[1].port(), quint16(45664)); + QCOMPARE(content.transportCandidates()[1].priority(), 1694498815); + QCOMPARE(content.transportCandidates()[1].protocol(), QLatin1String("udp")); + QCOMPARE(content.transportCandidates()[1].type(), QXmppJingleCandidate::ServerReflexiveType); + QCOMPARE(content.transportUser(), QLatin1String("8hhy")); + QCOMPARE(content.transportPassword(), QLatin1String("asd88fgpdd777uzjYhagZg")); + QCOMPARE(session.reason().text(), QString()); QCOMPARE(session.reason().type(), QXmppJingleIq::Reason::None); serializePacket(session, xml); +} + +void tst_QXmppJingleIq::testRtpSessionSdp() +{ + const QString sdp( + "v=0\r\n" + "o=- NTPSTAMP NTPSTAMP IN IP4 0.0.0.0\r\n" + "s=-\r\n" + "t=0 0\r\n" + "m=audio 8998 RTP/AVP 96 97 18 0 103 98\r\n" + "c=IN IP4 10.0.1.1\r\n" + "a=rtpmap:96 speex/16000\r\n" + "a=rtpmap:97 speex/8000\r\n" + "a=rtpmap:18 G729/0\r\n" + "a=rtpmap:0 PCMU/0\r\n" + "a=rtpmap:103 L16/16000/2\r\n" + "a=rtpmap:98 x-ISAC/8000\r\n" + "a=candidate:1 1 udp 2130706431 10.0.1.1 8998 typ host generation 0\r\n" + "a=candidate:2 1 udp 1694498815 192.0.2.3 45664 typ srflx generation 0\r\n" + "a=ice-ufrag:8hhy\r\n" + "a=ice-pwd:asd88fgpdd777uzjYhagZg\r\n"); + + QXmppJingleIq::Content content; + QVERIFY(content.parseSdp(sdp)); + + QCOMPARE(content.descriptionMedia(), QLatin1String("audio")); + QCOMPARE(content.descriptionSsrc(), quint32(0)); + QCOMPARE(content.payloadTypes().size(), 6); + QCOMPARE(content.payloadTypes()[0].id(), quint8(96)); + QCOMPARE(content.payloadTypes()[1].id(), quint8(97)); + QCOMPARE(content.payloadTypes()[2].id(), quint8(18)); + QCOMPARE(content.payloadTypes()[3].id(), quint8(0)); + QCOMPARE(content.payloadTypes()[4].id(), quint8(103)); + QCOMPARE(content.payloadTypes()[5].id(), quint8(98)); + QCOMPARE(content.transportCandidates().size(), 2); + QCOMPARE(content.transportCandidates()[0].component(), 1); + QCOMPARE(content.transportCandidates()[0].foundation(), QLatin1String("1")); + QCOMPARE(content.transportCandidates()[0].host(), QHostAddress("10.0.1.1")); + QCOMPARE(content.transportCandidates()[0].port(), quint16(8998)); + QCOMPARE(content.transportCandidates()[0].priority(), 2130706431); + QCOMPARE(content.transportCandidates()[0].protocol(), QLatin1String("udp")); + QCOMPARE(content.transportCandidates()[0].type(), QXmppJingleCandidate::HostType); + QCOMPARE(content.transportCandidates()[1].component(), 1); + QCOMPARE(content.transportCandidates()[1].foundation(), QLatin1String("2")); + QCOMPARE(content.transportCandidates()[1].host(), QHostAddress("192.0.2.3")); + QCOMPARE(content.transportCandidates()[1].port(), quint16(45664)); + QCOMPARE(content.transportCandidates()[1].priority(), 1694498815); + QCOMPARE(content.transportCandidates()[1].protocol(), QLatin1String("udp")); + QCOMPARE(content.transportCandidates()[1].type(), QXmppJingleCandidate::ServerReflexiveType); + QCOMPARE(content.transportUser(), QLatin1String("8hhy")); + QCOMPARE(content.transportPassword(), QLatin1String("asd88fgpdd777uzjYhagZg")); - QCOMPARE(session.content().toSdp().replace(QRegExp("o=- [0-9]+ [0-9]+"), "o=- NTPSTAMP NTPSTAMP"), sdp); + QCOMPARE(content.toSdp().replace(QRegExp("o=- [0-9]+ [0-9]+"), "o=- NTPSTAMP NTPSTAMP"), sdp); } void tst_QXmppJingleIq::testSession() |
