aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Lainé <jeremy.laine@m4x.org>2015-08-27 16:08:07 +0200
committerJeremy Lainé <jeremy.laine@m4x.org>2015-08-27 16:28:36 +0200
commit44cbba5d6538b998b5b5fbb4264d78ab135f669f (patch)
treebfc03a4c282c3274bf0ec323d9b547ca99ef58e0
parenta100a8b3cf20c7dc029416e4471a11fea7c51599 (diff)
downloadqxmpp-44cbba5d6538b998b5b5fbb4264d78ab135f669f.tar.gz
add method to parse SDP to QXmppJingleIq::Content
-rw-r--r--src/base/QXmppJingleIq.cpp180
-rw-r--r--src/base/QXmppJingleIq.h4
-rw-r--r--tests/qxmppjingleiq/tst_qxmppjingleiq.cpp105
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()