diff options
Diffstat (limited to 'Swiften/Parser')
8 files changed, 281 insertions, 9 deletions
diff --git a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp index a40e8f6..e6f3f60 100644 --- a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp +++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp @@ -67,6 +67,8 @@ #include <Swiften/Parser/PayloadParsers/DeliveryReceiptParserFactory.h> #include <Swiften/Parser/PayloadParsers/DeliveryReceiptRequestParserFactory.h> #include <Swiften/Parser/PayloadParsers/WhiteboardParser.h> +#include <Swiften/Parser/PayloadParsers/RTPPayloadTypeParser.h> +#include <Swiften/Parser/PayloadParsers/JingleRTPDescriptionParserFactory.h> using namespace boost; @@ -128,6 +130,8 @@ FullPayloadParserFactoryCollection::FullPayloadParserFactoryCollection() { factories_.push_back(boost::make_shared<GenericPayloadParserFactory<WhiteboardParser> >("wb", "http://swift.im/whiteboard")); factories_.push_back(boost::make_shared<DeliveryReceiptParserFactory>()); factories_.push_back(boost::make_shared<DeliveryReceiptRequestParserFactory>()); + factories_.push_back(boost::make_shared<JingleRTPDescriptionParserFactory>(this)); + factories_.push_back(boost::make_shared<GenericPayloadParserFactory<RTPPayloadTypeParser> >("payload-type")); foreach(shared_ptr<PayloadParserFactory> factory, factories_) { addFactory(factory.get()); diff --git a/Swiften/Parser/PayloadParsers/JingleRTPDescriptionParser.cpp b/Swiften/Parser/PayloadParsers/JingleRTPDescriptionParser.cpp new file mode 100644 index 0000000..7429b39 --- /dev/null +++ b/Swiften/Parser/PayloadParsers/JingleRTPDescriptionParser.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2012 Yoann Blein + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "JingleRTPDescriptionParser.h" + +#include <Swiften/Parser/PayloadParserFactoryCollection.h> +#include <Swiften/Parser/PayloadParserFactory.h> + +#include <boost/optional.hpp> + +namespace Swift { + +JingleRTPDescriptionParser::JingleRTPDescriptionParser(PayloadParserFactoryCollection* factories) : + factories(factories), level(0), parsingBandwidth(false) { + +} + +void JingleRTPDescriptionParser::handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) { + if (level == 0) { + const std::string& media = attributes.getAttributeValue("media").get_value_or(""); + getPayloadInternal()->setMedia(mediaTypeFromString(media)); + } + + if (level == 1) { + if (element == "bandwidth") { + parsingBandwidth = true; + bandwidthType = attributes.getAttributeValue("type").get_value_or(""); + } else { + PayloadParserFactory* payloadParserFactory = factories->getPayloadParserFactory(element, ns, attributes); + if (payloadParserFactory) { + currentPayloadParser.reset(payloadParserFactory->createPayloadParser()); + } + } + } + + if (level >= 1 && currentPayloadParser && !parsingBandwidth) { + currentPayloadParser->handleStartElement(element, ns, attributes); + } + + ++level; +} + +void JingleRTPDescriptionParser::handleEndElement(const std::string& element, const std::string& ns) { + --level; + if (element == "bandwidth") { + parsingBandwidth = false; + getPayloadInternal()->setBandwidth(bandwidthType, bandwidthValue); + } else if (currentPayloadParser) { + if (level >= 1) { + currentPayloadParser->handleEndElement(element, ns); + } + if (level == 1) { + RTPPayloadType::ref payloadType = boost::dynamic_pointer_cast<RTPPayloadType>(currentPayloadParser->getPayload()); + if (payloadType) { + getPayloadInternal()->addPayloadType(*payloadType); + } + } + } +} + +void JingleRTPDescriptionParser::handleCharacterData(const std::string& data) { + if (level == 2 && parsingBandwidth) { + bandwidthValue += data; + } +} + +JingleRTPDescription::MediaType JingleRTPDescriptionParser::mediaTypeFromString(const std::string& media) const { + if (media == "audio") { + return JingleRTPDescription::Audio; + } else if (media == "video") { + return JingleRTPDescription::Video; + } else { + return JingleRTPDescription::Unknown; + } +} + +} diff --git a/Swiften/Parser/PayloadParsers/JingleRTPDescriptionParser.h b/Swiften/Parser/PayloadParsers/JingleRTPDescriptionParser.h new file mode 100644 index 0000000..d250754 --- /dev/null +++ b/Swiften/Parser/PayloadParsers/JingleRTPDescriptionParser.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2012 Yoann Blein + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Elements/JingleRTPDescription.h> +#include <Swiften/Parser/GenericPayloadParser.h> +#include <Swiften/Parser/PayloadParser.h> + +namespace Swift { + +class PayloadParserFactoryCollection; + +class JingleRTPDescriptionParser : public GenericPayloadParser<JingleRTPDescription> { + public: + JingleRTPDescriptionParser(PayloadParserFactoryCollection* factories); + + virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes); + virtual void handleEndElement(const std::string& element, const std::string& ns); + virtual void handleCharacterData(const std::string& data); + + private: + JingleRTPDescription::MediaType mediaTypeFromString(const std::string& media) const; + + private: + PayloadParserFactoryCollection* factories; + int level; + //CurrentParseElement currentElement; + boost::shared_ptr<PayloadParser> currentPayloadParser; + bool parsingBandwidth; + std::string bandwidthType; + std::string bandwidthValue; +}; + +} diff --git a/Swiften/Parser/PayloadParsers/JingleRTPDescriptionParserFactory.h b/Swiften/Parser/PayloadParsers/JingleRTPDescriptionParserFactory.h new file mode 100644 index 0000000..90d32eb --- /dev/null +++ b/Swiften/Parser/PayloadParsers/JingleRTPDescriptionParserFactory.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2012 Yoann Blein + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Parser/GenericPayloadParserFactory.h> +#include <Swiften/Parser/PayloadParsers/JingleRTPDescriptionParser.h> + +namespace Swift { + + class PayloadParserFactoryCollection; + + class JingleRTPDescriptionParserFactory : public PayloadParserFactory { + public: + JingleRTPDescriptionParserFactory(PayloadParserFactoryCollection* factories) : factories(factories) { + } + + virtual bool canParse(const std::string& element, const std::string& ns, const AttributeMap&) const { + return element == "description" && ns == "urn:xmpp:jingle:apps:rtp:1"; + } + + virtual PayloadParser* createPayloadParser() { + return new JingleRTPDescriptionParser(factories); + } + + private: + PayloadParserFactoryCollection* factories; + + }; +} + + diff --git a/Swiften/Parser/PayloadParsers/RTPPayloadTypeParser.cpp b/Swiften/Parser/PayloadParsers/RTPPayloadTypeParser.cpp index 56fe109..f9d9bb3 100644 --- a/Swiften/Parser/PayloadParsers/RTPPayloadTypeParser.cpp +++ b/Swiften/Parser/PayloadParsers/RTPPayloadTypeParser.cpp @@ -8,9 +8,7 @@ #include <boost/optional.hpp> #include <boost/lexical_cast.hpp> - -//#include <Swiften/Base/DateTime.h> -//#include <Swiften/Base/Log.h> +#include <boost/numeric/conversion/cast.hpp> namespace Swift { @@ -20,7 +18,7 @@ RTPPayloadTypeParser::RTPPayloadTypeParser() : level(0) { void RTPPayloadTypeParser::handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes) { if (level == 0) { - // channel + // channels try { getPayloadInternal()->setChannels(boost::lexical_cast<boost::uint32_t>(attributes.getAttributeValue("channels").get_value_or("1"))); } catch (boost::bad_lexical_cast &) { @@ -34,9 +32,9 @@ void RTPPayloadTypeParser::handleStartElement(const std::string& element, const } // ID try { - getPayloadInternal()->setID(boost::lexical_cast<boost::uint8_t>(attributes.getAttributeValue("id").get_value_or("0"))); + getPayloadInternal()->setID(boost::numeric_cast<boost::uint8_t>(boost::lexical_cast<int>(attributes.getAttributeValue("id").get_value_or("0")))); } catch (boost::bad_lexical_cast &) { - getPayloadInternal()->setID(0); + getPayloadInternal()->setID(151); } // maxptime try { @@ -52,14 +50,14 @@ void RTPPayloadTypeParser::handleStartElement(const std::string& element, const } catch (boost::bad_lexical_cast &) { getPayloadInternal()->setPTime(0); } - } else if (level == 1) { + } else if (level == 1 && element == "parameter") { getPayloadInternal()->addParameter(attributes.getAttributeValue("name").get_value_or(""), attributes.getAttributeValue("value").get_value_or("")); } ++level; } -void RTPPayloadTypeParser::handleEndElement(const std::string& element, const std::string&) { +void RTPPayloadTypeParser::handleEndElement(const std::string&, const std::string&) { --level; } diff --git a/Swiften/Parser/PayloadParsers/RTPPayloadTypeParser.h b/Swiften/Parser/PayloadParsers/RTPPayloadTypeParser.h index 8dee938..f632aca 100644 --- a/Swiften/Parser/PayloadParsers/RTPPayloadTypeParser.h +++ b/Swiften/Parser/PayloadParsers/RTPPayloadTypeParser.h @@ -16,7 +16,7 @@ namespace Swift { RTPPayloadTypeParser(); virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes); - virtual void handleEndElement(const std::string& element, const std::string&); + virtual void handleEndElement(const std::string&, const std::string&); virtual void handleCharacterData(const std::string&) {}; private: diff --git a/Swiften/Parser/PayloadParsers/UnitTest/JingleParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/JingleParserTest.cpp index 8719a5d..18f38b2 100644 --- a/Swiften/Parser/PayloadParsers/UnitTest/JingleParserTest.cpp +++ b/Swiften/Parser/PayloadParsers/UnitTest/JingleParserTest.cpp @@ -15,6 +15,8 @@ #include <Swiften/Elements/StreamInitiationFileInfo.h> #include <Swiften/Elements/JingleFileTransferReceived.h> #include <Swiften/Elements/JingleFileTransferHash.h> +//#include <Swiften/Elements/RTPPayloadType.h> +#include <Swiften/Elements/JingleRTPDescription.h> #include <Swiften/Base/DateTime.h> #include <Swiften/Base/Log.h> @@ -41,6 +43,10 @@ class JingleParserTest : public CppUnit::TestFixture { CPPUNIT_TEST(testParse_Xep0260_Example1); CPPUNIT_TEST(testParse_Xep0260_Example3); + + CPPUNIT_TEST(testParse_Xep0167_Example1); + CPPUNIT_TEST(testParse_Xep0167_Example48); + CPPUNIT_TEST_SUITE_END(); public: @@ -704,6 +710,115 @@ class JingleParserTest : public CppUnit::TestFixture { CPPUNIT_ASSERT_EQUAL(8257606, candidate.priority); CPPUNIT_ASSERT_EQUAL(JingleS5BTransportPayload::Candidate::DirectType, candidate.type); } + + // http://xmpp.org/extensions/xep-0167.html#example-1 + void testParse_Xep0167_Example1() { + PayloadsParserTester parser; + CPPUNIT_ASSERT(parser.parse( + "<jingle action=\"session-initiate\"" + " initiator=\"romeo@montague.lit/orchard\"" + " sid=\"a73sjjvkla37jfea\"" + " xmlns=\"urn:xmpp:jingle:1\">" + "<content creator=\"initiator\" name=\"voice\">" + "<description media=\"audio\" xmlns=\"urn:xmpp:jingle:apps:rtp:1\">" + "<payload-type clockrate=\"16000\" id=\"96\" name=\"speex\"/>" + "<payload-type id=\"18\" name=\"G729\"/>" + "<payload-type channels=\"2\" clockrate=\"16000\" id=\"103\" name=\"L16\"/>" + "</description>" + //"<transport [...] >" + "</content>" + "</jingle>" + )); + + JinglePayload::ref jingle = parser.getPayload<JinglePayload>(); + CPPUNIT_ASSERT(jingle); + CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionInitiate, jingle->getAction()); + CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator()); + CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID()); + + JingleContentPayload::ref content = jingle->getPayload<JingleContentPayload>(); + CPPUNIT_ASSERT(content); + CPPUNIT_ASSERT_EQUAL(std::string("voice"), content->getName()); + + JingleRTPDescription::ref desc = content->getDescription<JingleRTPDescription>(); + CPPUNIT_ASSERT(desc); + CPPUNIT_ASSERT_EQUAL(JingleRTPDescription::Audio, desc->getMedia()); + + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), desc->getPayloadTypes().size()); + + const RTPPayloadType& payloadType1 = desc->getPayloadTypes()[0]; + CPPUNIT_ASSERT_EQUAL(static_cast<boost::uint32_t>(16000), payloadType1.getClockrate()); + CPPUNIT_ASSERT_EQUAL(static_cast<boost::uint8_t>(96), payloadType1.getID()); + CPPUNIT_ASSERT_EQUAL(std::string("speex"), payloadType1.getName()); + + const RTPPayloadType& payloadType2 = desc->getPayloadTypes()[1]; + CPPUNIT_ASSERT_EQUAL(static_cast<boost::uint8_t>(18), payloadType2.getID()); + CPPUNIT_ASSERT_EQUAL(std::string("G729"), payloadType2.getName()); + + const RTPPayloadType& payloadType3 = desc->getPayloadTypes()[2]; + CPPUNIT_ASSERT_EQUAL(static_cast<boost::uint32_t>(2), payloadType3.getChannels()); + CPPUNIT_ASSERT_EQUAL(static_cast<boost::uint32_t>(16000), payloadType3.getClockrate()); + CPPUNIT_ASSERT_EQUAL(static_cast<boost::uint8_t>(103), payloadType3.getID()); + CPPUNIT_ASSERT_EQUAL(std::string("L16"), payloadType3.getName()); + } + + // http://xmpp.org/extensions/xep-0167.html#example-48 + void testParse_Xep0167_Example48() { + PayloadsParserTester parser; + CPPUNIT_ASSERT(parser.parse( + "<jingle action=\"content-accept\"" + " initiator=\"romeo@montague.lit/orchard\"" + " sid=\"a73sjjvkla37jfea\"" + " xmlns=\"urn:xmpp:jingle:1\">" + "<content creator=\"initiator\" name=\"webcam\">" + "<description media=\"video\" xmlns=\"urn:xmpp:jingle:apps:rtp:1\">" + "<payload-type clockrate=\"90000\" id=\"98\" name=\"theora\">" + "<parameter name=\"configuration\" value=\"somebase16string\"/>" + "<parameter name=\"delivery-method\" value=\"inline\"/>" + "<parameter name=\"height\" value=\"600\"/>" + "<parameter name=\"sampling\" value=\"YCbCr-4:2:2\"/>" + "<parameter name=\"width\" value=\"800\"/>" + "</payload-type>" + "<bandwidth type=\"AS\">128</bandwidth>" + "</description>" + //"<transport xmlns=\"urn:xmpp:jingle:transports:ice-udp:0\"/>" + "</content>" + "</jingle>" + )); + + JinglePayload::ref jingle = parser.getPayload<JinglePayload>(); + CPPUNIT_ASSERT(jingle); + CPPUNIT_ASSERT_EQUAL(JinglePayload::ContentAccept, jingle->getAction()); + CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator()); + CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID()); + + JingleContentPayload::ref content = jingle->getPayload<JingleContentPayload>(); + CPPUNIT_ASSERT(content); + CPPUNIT_ASSERT_EQUAL(std::string("webcam"), content->getName()); + + JingleRTPDescription::ref desc = content->getDescription<JingleRTPDescription>(); + CPPUNIT_ASSERT(desc); + CPPUNIT_ASSERT_EQUAL(JingleRTPDescription::Video, desc->getMedia()); + + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), desc->getPayloadTypes().size()); + + const RTPPayloadType& payloadType = desc->getPayloadTypes()[0]; + CPPUNIT_ASSERT_EQUAL(static_cast<boost::uint32_t>(90000), payloadType.getClockrate()); + CPPUNIT_ASSERT_EQUAL(static_cast<boost::uint8_t>(98), payloadType.getID()); + CPPUNIT_ASSERT_EQUAL(std::string("theora"), payloadType.getName()); + + RTPPayloadType::ParameterMap params = payloadType.getParameters(); // Explicit copy for easy testing + CPPUNIT_ASSERT_EQUAL(params["configuration"], std::string("somebase16string")); + CPPUNIT_ASSERT_EQUAL(params["delivery-method"], std::string("inline")); + CPPUNIT_ASSERT_EQUAL(params["height"], std::string("600")); + CPPUNIT_ASSERT_EQUAL(params["sampling"], std::string("YCbCr-4:2:2")); + CPPUNIT_ASSERT_EQUAL(params["width"], std::string("800")); + + std::string type, value; + desc->getBandwidth(type, value); + CPPUNIT_ASSERT_EQUAL(std::string("AS"), type); + CPPUNIT_ASSERT_EQUAL(std::string("128"), value); + } }; CPPUNIT_TEST_SUITE_REGISTRATION(JingleParserTest); diff --git a/Swiften/Parser/SConscript b/Swiften/Parser/SConscript index 64e9eb9..1ce84b9 100644 --- a/Swiften/Parser/SConscript +++ b/Swiften/Parser/SConscript @@ -73,6 +73,8 @@ sources = [ "PayloadParsers/DeliveryReceiptParser.cpp", "PayloadParsers/DeliveryReceiptRequestParser.cpp", "PayloadParsers/WhiteboardParser.cpp", + "PayloadParsers/RTPPayloadTypeParser.cpp", + "PayloadParsers/JingleRTPDescriptionParser.cpp", "PlatformXMLParserFactory.cpp", "PresenceParser.cpp", "SerializingParser.cpp", |