/* * libjingle * Copyright 2004 Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "talk/media/base/fakemediaengine.h" #include "talk/media/base/testutils.h" #include "talk/media/devices/fakedevicemanager.h" #include "webrtc/p2p/base/constants.h" #include "webrtc/p2p/client/basicportallocator.h" #include "talk/session/media/mediasessionclient.h" #include "webrtc/libjingle/xmllite/xmlbuilder.h" #include "webrtc/libjingle/xmllite/xmlelement.h" #include "webrtc/libjingle/xmllite/xmlprinter.h" #include "webrtc/libjingle/xmpp/constants.h" #include "webrtc/base/gunit.h" #include "webrtc/base/logging.h" #include "webrtc/base/scoped_ptr.h" #include "webrtc/base/ssladapter.h" using cricket::AudioCodec; using cricket::AudioContentDescription; using cricket::Codec; using cricket::DataCodec; using cricket::DataContentDescription; using cricket::FeedbackParam; using cricket::FeedbackParams; using cricket::VideoCodec; using cricket::VideoContentDescription; // The codecs that our FakeMediaEngine will support. Order is important, since // the tests check that our messages have codecs in the correct order. static const cricket::AudioCodec kAudioCodecs[] = { cricket::AudioCodec(103, "ISAC", 16000, -1, 1, 18), cricket::AudioCodec(104, "ISAC", 32000, -1, 1, 17), cricket::AudioCodec(119, "ISACLC", 16000, 40000, 1, 16), cricket::AudioCodec(99, "speex", 16000, 22000, 1, 15), cricket::AudioCodec(97, "IPCMWB", 16000, 80000, 1, 14), cricket::AudioCodec(9, "G722", 8000, 64000, 1, 13), cricket::AudioCodec(102, "iLBC", 8000, 13300, 1, 12), cricket::AudioCodec(98, "speex", 8000, 11000, 1, 11), cricket::AudioCodec(3, "GSM", 8000, 13000, 1, 10), cricket::AudioCodec(100, "EG711U", 8000, 64000, 1, 9), cricket::AudioCodec(101, "EG711A", 8000, 64000, 1, 8), cricket::AudioCodec(0, "PCMU", 8000, 64000, 1, 7), cricket::AudioCodec(8, "PCMA", 8000, 64000, 1, 6), cricket::AudioCodec(126, "CN", 32000, 0, 1, 5), cricket::AudioCodec(105, "CN", 16000, 0, 1, 4), cricket::AudioCodec(13, "CN", 8000, 0, 1, 3), cricket::AudioCodec(117, "red", 8000, 0, 1, 2), cricket::AudioCodec(106, "telephone-event", 8000, 0, 1, 1) }; // The codecs that our FakeMediaEngine will support with a different order of // supported codecs. static const cricket::AudioCodec kAudioCodecsDifferentPreference[] = { cricket::AudioCodec(104, "ISAC", 32000, -1, 1, 17), cricket::AudioCodec(97, "IPCMWB", 16000, 80000, 1, 14), cricket::AudioCodec(9, "G722", 8000, 64000, 1, 13), cricket::AudioCodec(119, "ISACLC", 16000, 40000, 1, 16), cricket::AudioCodec(103, "ISAC", 16000, -1, 1, 18), cricket::AudioCodec(99, "speex", 16000, 22000, 1, 15), cricket::AudioCodec(100, "EG711U", 8000, 64000, 1, 9), cricket::AudioCodec(101, "EG711A", 8000, 64000, 1, 8), cricket::AudioCodec(0, "PCMU", 8000, 64000, 1, 7), cricket::AudioCodec(8, "PCMA", 8000, 64000, 1, 6), cricket::AudioCodec(102, "iLBC", 8000, 13300, 1, 12), cricket::AudioCodec(3, "GSM", 8000, 13000, 1, 10), cricket::AudioCodec(98, "speex", 8000, 11000, 1, 11), cricket::AudioCodec(126, "CN", 32000, 0, 1, 5), cricket::AudioCodec(105, "CN", 16000, 0, 1, 4), cricket::AudioCodec(13, "CN", 8000, 0, 1, 3), cricket::AudioCodec(117, "red", 8000, 0, 1, 2), cricket::AudioCodec(106, "telephone-event", 8000, 0, 1, 1) }; static const cricket::VideoCodec kVideoCodecs[] = { cricket::VideoCodec(96, "H264-SVC", 320, 200, 30, 1) }; static const cricket::DataCodec kDataCodecs[] = { cricket::DataCodec(127, "google-data", 0) }; const std::string kGingleCryptoOffer = \ " " \ " " \ " " \ " " \ " "; // Jingle offer does not have any element. const std::string kJingleCryptoOffer = \ " " \ " " \ " " \ " "; const std::string kGingleRequiredCryptoOffer = \ " "\ " " \ " " \ " " \ " "; const std::string kJingleRequiredCryptoOffer = \ " "\ " " \ " " \ " "; const std::string kGingleUnsupportedCryptoOffer = \ " " \ " " \ " " \ " " \ " "; const std::string kJingleUnsupportedCryptoOffer = \ " " \ " " \ " " \ " "; // With unsupported but with required="true" const std::string kGingleRequiredUnsupportedCryptoOffer = \ "" \ " " \ " " \ " " \ " "; const std::string kJingleRequiredUnsupportedCryptoOffer = \ "" \ " " \ " " \ " "; const std::string kGingleInitiate( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); const std::string kJingleInitiate( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); const std::string kJingleInitiateWithRtcpFb( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); const std::string kGingleVideoInitiate( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); const std::string kJingleVideoInitiate( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); const std::string kJingleVideoInitiateWithRtpData( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); const std::string kJingleVideoInitiateWithSctpData( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); const std::string kJingleVideoInitiateWithBandwidth( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " 42 " \ " " \ " " \ " " \ " " \ " "); const std::string kJingleVideoInitiateWithRtcpMux( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); // Initiate string with a combination of supported and unsupported codecs // Should accept the supported ones const std::string kGingleInitiateSomeUnsupported( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); const std::string kJingleInitiateSomeUnsupported( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); const std::string kGingleVideoInitiateWithBandwidth( " " \ " " \ " " \ " " \ " " \ " 42 " \ " " \ " " \ " "); // Initiate string without any supported codecs. Should send a reject. const std::string kGingleInitiateNoSupportedAudioCodecs( " " \ " " \ " " \ " " \ " " \ " " \ " "); const std::string kJingleInitiateNoSupportedAudioCodecs( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); // Initiate string without any codecs. Assumes ancient version of Cricket // and tries a session with ISAC and PCMU const std::string kGingleInitiateNoAudioCodecs( " " \ " " \ " " \ " " \ " " \ " "); const std::string kJingleInitiateNoAudioCodecs( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); // The codecs are supported, but not at the given clockrates. Should send // a reject. const std::string kGingleInitiateWrongClockrates( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); const std::string kJingleInitiateWrongClockrates( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); // The codecs are supported, but not with the given number of channels. // Should send a reject. const std::string kGingleInitiateWrongChannels( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); const std::string kJingleInitiateWrongChannels( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); // Initiate with a dynamic codec not using webrtc default payload id. Should // accept with provided payload id. const std::string kGingleInitiateDynamicAudioCodecs( " " \ " " \ " " \ " " \ " " \ " " \ " "); const std::string kJingleInitiateDynamicAudioCodecs( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); // Initiate string with nothing but static codec id's. Should accept. const std::string kGingleInitiateStaticAudioCodecs( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); const std::string kJingleInitiateStaticAudioCodecs( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); // Initiate with payload type-less codecs. Should reject. const std::string kGingleInitiateNoPayloadTypes( " " \ " " \ " " \ " " \ " " \ " " \ " "); const std::string kJingleInitiateNoPayloadTypes( " " \ " " \ " sid='abcdef' initiator='me@domain.com/resource'> " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); // Initiate with unnamed dynamic codces. Should reject. const std::string kGingleInitiateDynamicWithoutNames( " " \ " " \ " " \ " " \ " " \ " " \ " "); const std::string kJingleInitiateDynamicWithoutNames( " " \ " " \ " sid='abcdef' initiator='me@domain.com/resource'> " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); const uint32 kAudioSsrc = 4294967295U; const uint32 kVideoSsrc = 87654321; const uint32 kDataSsrc = 1010101; // Note that this message does not specify a session ID. It must be populated // before use. const std::string kGingleAcceptWithSsrcs( " " \ " " \ " " \ " " \ " " \ " " \ " 4294967295 " \ " 87654321 " \ " " \ " " \ " "); const std::string kJingleAcceptWithSsrcs( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); const std::string kJingleAcceptWithRtpDataSsrcs( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); const std::string kJingleAcceptWithSctpData( " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ " "); std::string JingleView(const std::string& ssrc, const std::string& width, const std::string& height, const std::string& framerate) { // We have some slightly weird whitespace formatting to make the // actual XML generated match the expected XML here. return \ "" "" "" "" "" "" ""; } std::string JingleStreamAdd(const std::string& content_name, const std::string& nick, const std::string& name, const std::string& ssrc) { return \ "" " " " " " " " " " " " " + ssrc + "" " " " " " " " " " " ""; } std::string JingleOutboundStreamRemove(const std::string& sid, const std::string& content_name, const std::string& name) { return \ "" "" "" "" "" "" "" "" "" "" "" ""; } std::string JingleOutboundStreamAdd(const std::string& sid, const std::string& content_name, const std::string& name, const std::string& ssrc) { return \ "" "" "" "" "" "" "" + ssrc + "" "" "" "" "" "" ""; } std::string JingleStreamAddWithoutSsrc(const std::string& content_name, const std::string& nick, const std::string& name) { return \ "" " " " " " " " " " " " " " " " " " " " " ""; } std::string JingleStreamRemove(const std::string& content_name, const std::string& nick, const std::string& name) { return \ "" " " " " " " " " " " " " " " " " " " ""; } // Convenience function to get CallOptions that have audio enabled, // but not video or data. static cricket::CallOptions AudioCallOptions() { cricket::CallOptions options; options.recv_audio = true; options.recv_video = false; options.data_channel_type = cricket::DCT_NONE; return options; } // Convenience function to get CallOptions that have audio and video // enabled, but not data. static cricket::CallOptions VideoCallOptions() { cricket::CallOptions options; options.recv_audio = true; options.recv_video = true; options.data_channel_type = cricket::DCT_NONE; return options; } static buzz::XmlElement* CopyElement(const buzz::XmlElement* elem) { return new buzz::XmlElement(*elem); } static std::string AddEncryption(std::string stanza, std::string encryption) { std::string::size_type pos = stanza.find(""); while (pos != std::string::npos) { stanza = stanza.insert(pos, encryption); pos = stanza.find("", pos + encryption.length() + 1); } return stanza; } static int IntFromJingleCodecParameter(const buzz::XmlElement* parameter, const std::string& expected_name) { if (parameter) { const std::string& actual_name = parameter->Attr(cricket::QN_PAYLOADTYPE_PARAMETER_NAME); EXPECT_EQ(expected_name, actual_name) << "wrong parameter name. Expected '" << expected_name << "'. Actually '" << actual_name << "'."; return atoi(parameter->Attr( cricket::QN_PAYLOADTYPE_PARAMETER_VALUE).c_str()); } return 0; } template static void VerifyCodecFbParams(const FeedbackParams& expected, const DescriptionClass* desc) { if (!expected.params().empty()) { ASSERT_TRUE(desc != NULL); const std::vector codecs = desc->codecs(); for (size_t i = 0; i < codecs.size(); ++i) { EXPECT_EQ(expected, codecs[i].feedback_params); } } } // Parses and extracts payload and codec info from test XML. Since // that XML will be in various contents (Gingle and Jingle), we need an // abstract parser with one concrete implementation per XML content. class MediaSessionTestParser { public: virtual buzz::XmlElement* ActionFromStanza(buzz::XmlElement* stanza) = 0; virtual buzz::XmlElement* ContentFromAction(buzz::XmlElement* action) = 0; virtual buzz::XmlElement* NextContent(buzz::XmlElement* content) = 0; virtual buzz::XmlElement* PayloadTypeFromContent( buzz::XmlElement* content) = 0; virtual buzz::XmlElement* NextFromPayloadType( buzz::XmlElement* payload_type) = 0; virtual cricket::AudioCodec AudioCodecFromPayloadType( const buzz::XmlElement* payload_type) = 0; virtual cricket::VideoCodec VideoCodecFromPayloadType( const buzz::XmlElement* payload_type) = 0; virtual cricket::DataCodec DataCodecFromPayloadType( const buzz::XmlElement* payload_type) = 0; virtual buzz::XmlElement* EncryptionFromContent( buzz::XmlElement* content) = 0; virtual buzz::XmlElement* NextFromEncryption( buzz::XmlElement* encryption) = 0; virtual const buzz::XmlElement* BandwidthFromContent( buzz::XmlElement* content) = 0; virtual const buzz::XmlElement* RtcpMuxFromContent( buzz::XmlElement* content) = 0; virtual bool ActionIsTerminate(const buzz::XmlElement* action) = 0; virtual ~MediaSessionTestParser() {} }; class JingleSessionTestParser : public MediaSessionTestParser { public: JingleSessionTestParser() {} ~JingleSessionTestParser() { } buzz::XmlElement* ActionFromStanza(buzz::XmlElement* stanza) { return stanza->FirstNamed(cricket::QN_JINGLE); } buzz::XmlElement* ContentFromAction(buzz::XmlElement* action) { // We need to be able to use multiple contents, but the action // gets deleted before we can call NextContent, so we need to // stash away a copy. action_.reset(CopyElement(action)); return action_->FirstNamed(cricket::QN_JINGLE_CONTENT); } buzz::XmlElement* NextContent(buzz::XmlElement* content) { // For some reason, content->NextNamed(cricket::QN_JINGLE_CONTENT) // doesn't work. return action_->FirstNamed(cricket::QN_JINGLE_CONTENT) ->NextNamed(cricket::QN_JINGLE_CONTENT); } buzz::XmlElement* PayloadTypeFromContent(buzz::XmlElement* content) { buzz::XmlElement* content_desc = content->FirstNamed(cricket::QN_JINGLE_RTP_CONTENT); if (!content_desc) return NULL; return content_desc->FirstNamed(cricket::QN_JINGLE_RTP_PAYLOADTYPE); } buzz::XmlElement* NextFromPayloadType(buzz::XmlElement* payload_type) { return payload_type->NextNamed(cricket::QN_JINGLE_RTP_PAYLOADTYPE); } void ParsePayloadTypeFeedbackParameters(const buzz::XmlElement* element, FeedbackParams* params) { const buzz::XmlElement* param = element->FirstNamed(cricket::QN_JINGLE_RTCP_FB); for (; param != NULL; param = param->NextNamed(cricket::QN_JINGLE_RTCP_FB)) { std::string type = param->Attr(cricket::QN_TYPE); std::string subtype = param->Attr(cricket::QN_SUBTYPE); if (!type.empty()) { params->Add(FeedbackParam(type, subtype)); } } } cricket::AudioCodec AudioCodecFromPayloadType( const buzz::XmlElement* payload_type) { int id = 0; if (payload_type->HasAttr(cricket::QN_ID)) id = atoi(payload_type->Attr(cricket::QN_ID).c_str()); std::string name; if (payload_type->HasAttr(cricket::QN_NAME)) name = payload_type->Attr(cricket::QN_NAME); int clockrate = 0; if (payload_type->HasAttr(cricket::QN_CLOCKRATE)) clockrate = atoi(payload_type->Attr(cricket::QN_CLOCKRATE).c_str()); int bitrate = IntFromJingleCodecParameter( payload_type->FirstNamed(cricket::QN_PARAMETER), "bitrate"); int channels = 1; if (payload_type->HasAttr(cricket::QN_CHANNELS)) channels = atoi(payload_type->Attr( cricket::QN_CHANNELS).c_str()); AudioCodec codec = AudioCodec(id, name, clockrate, bitrate, channels, 0); ParsePayloadTypeFeedbackParameters(payload_type, &codec.feedback_params); return codec; } cricket::VideoCodec VideoCodecFromPayloadType( const buzz::XmlElement* payload_type) { int id = 0; if (payload_type->HasAttr(cricket::QN_ID)) id = atoi(payload_type->Attr(cricket::QN_ID).c_str()); std::string name; if (payload_type->HasAttr(cricket::QN_NAME)) name = payload_type->Attr(cricket::QN_NAME); int width = 0; int height = 0; int framerate = 0; const buzz::XmlElement* param = payload_type->FirstNamed(cricket::QN_PARAMETER); if (param) { width = IntFromJingleCodecParameter(param, "width"); param = param->NextNamed(cricket::QN_PARAMETER); if (param) { height = IntFromJingleCodecParameter(param, "height"); param = param->NextNamed(cricket::QN_PARAMETER); if (param) { framerate = IntFromJingleCodecParameter(param, "framerate"); } } } VideoCodec codec = VideoCodec(id, name, width, height, framerate, 0); ParsePayloadTypeFeedbackParameters(payload_type, &codec.feedback_params); return codec; } cricket::DataCodec DataCodecFromPayloadType( const buzz::XmlElement* payload_type) { int id = 0; if (payload_type->HasAttr(cricket::QN_ID)) id = atoi(payload_type->Attr(cricket::QN_ID).c_str()); std::string name; if (payload_type->HasAttr(cricket::QN_NAME)) name = payload_type->Attr(cricket::QN_NAME); DataCodec codec = DataCodec(id, name, 0); ParsePayloadTypeFeedbackParameters(payload_type, &codec.feedback_params); return codec; } bool ActionIsTerminate(const buzz::XmlElement* action) { return (action->HasAttr(cricket::QN_ACTION) && action->Attr(cricket::QN_ACTION) == "session-terminate"); } buzz::XmlElement* EncryptionFromContent(buzz::XmlElement* content) { buzz::XmlElement* content_desc = content->FirstNamed(cricket::QN_JINGLE_RTP_CONTENT); if (!content_desc) return NULL; return content_desc->FirstNamed(cricket::QN_ENCRYPTION); } buzz::XmlElement* NextFromEncryption(buzz::XmlElement* encryption) { return encryption->NextNamed(cricket::QN_ENCRYPTION); } const buzz::XmlElement* BandwidthFromContent(buzz::XmlElement* content) { buzz::XmlElement* content_desc = content->FirstNamed(cricket::QN_JINGLE_RTP_CONTENT); if (!content_desc) return NULL; return content_desc->FirstNamed(cricket::QN_JINGLE_RTP_BANDWIDTH); } const buzz::XmlElement* RtcpMuxFromContent(buzz::XmlElement* content) { return content->FirstNamed(cricket::QN_JINGLE_RTCP_MUX); } private: rtc::scoped_ptr action_; }; class GingleSessionTestParser : public MediaSessionTestParser { public: GingleSessionTestParser() : found_content_count_(0) {} buzz::XmlElement* ActionFromStanza(buzz::XmlElement* stanza) { return stanza->FirstNamed(cricket::QN_GINGLE_SESSION); } buzz::XmlElement* ContentFromAction(buzz::XmlElement* session) { buzz::XmlElement* content = session->FirstNamed(cricket::QN_GINGLE_AUDIO_CONTENT); if (content == NULL) content = session->FirstNamed(cricket::QN_GINGLE_VIDEO_CONTENT); return content; } // Assumes contents are in order of audio, and then video. buzz::XmlElement* NextContent(buzz::XmlElement* content) { found_content_count_++; return content; } buzz::XmlElement* PayloadTypeFromContent(buzz::XmlElement* content) { if (found_content_count_ > 0) { return content->FirstNamed(cricket::QN_GINGLE_VIDEO_PAYLOADTYPE); } else { return content->FirstNamed(cricket::QN_GINGLE_AUDIO_PAYLOADTYPE); } } buzz::XmlElement* NextFromPayloadType(buzz::XmlElement* payload_type) { if (found_content_count_ > 0) { return payload_type->NextNamed(cricket::QN_GINGLE_VIDEO_PAYLOADTYPE); } else { return payload_type->NextNamed(cricket::QN_GINGLE_AUDIO_PAYLOADTYPE); } } cricket::AudioCodec AudioCodecFromPayloadType( const buzz::XmlElement* payload_type) { int id = 0; if (payload_type->HasAttr(cricket::QN_ID)) id = atoi(payload_type->Attr(cricket::QN_ID).c_str()); std::string name; if (payload_type->HasAttr(cricket::QN_NAME)) name = payload_type->Attr(cricket::QN_NAME); int clockrate = 0; if (payload_type->HasAttr(cricket::QN_CLOCKRATE)) clockrate = atoi(payload_type->Attr(cricket::QN_CLOCKRATE).c_str()); int bitrate = 0; if (payload_type->HasAttr(cricket::QN_BITRATE)) bitrate = atoi(payload_type->Attr(cricket::QN_BITRATE).c_str()); int channels = 1; if (payload_type->HasAttr(cricket::QN_CHANNELS)) channels = atoi(payload_type->Attr(cricket::QN_CHANNELS).c_str()); return cricket::AudioCodec(id, name, clockrate, bitrate, channels, 0); } cricket::VideoCodec VideoCodecFromPayloadType( const buzz::XmlElement* payload_type) { int id = 0; if (payload_type->HasAttr(cricket::QN_ID)) id = atoi(payload_type->Attr(cricket::QN_ID).c_str()); std::string name; if (payload_type->HasAttr(cricket::QN_NAME)) name = payload_type->Attr(cricket::QN_NAME); int width = 0; if (payload_type->HasAttr(cricket::QN_WIDTH)) width = atoi(payload_type->Attr(cricket::QN_WIDTH).c_str()); int height = 0; if (payload_type->HasAttr(cricket::QN_HEIGHT)) height = atoi(payload_type->Attr(cricket::QN_HEIGHT).c_str()); int framerate = 1; if (payload_type->HasAttr(cricket::QN_FRAMERATE)) framerate = atoi(payload_type->Attr(cricket::QN_FRAMERATE).c_str()); return cricket::VideoCodec(id, name, width, height, framerate, 0); } cricket::DataCodec DataCodecFromPayloadType( const buzz::XmlElement* payload_type) { // Gingle can't do data codecs. return cricket::DataCodec(0, "", 0); } buzz::XmlElement* EncryptionFromContent( buzz::XmlElement* content) { return content->FirstNamed(cricket::QN_ENCRYPTION); } buzz::XmlElement* NextFromEncryption(buzz::XmlElement* encryption) { return encryption->NextNamed(cricket::QN_ENCRYPTION); } const buzz::XmlElement* BandwidthFromContent(buzz::XmlElement* content) { return content->FirstNamed(cricket::QN_GINGLE_VIDEO_BANDWIDTH); } const buzz::XmlElement* RtcpMuxFromContent(buzz::XmlElement* content) { return NULL; } bool ActionIsTerminate(const buzz::XmlElement* session) { return (session->HasAttr(buzz::QN_TYPE) && session->Attr(buzz::QN_TYPE) == "terminate"); } int found_content_count_; }; class MediaSessionClientTest : public sigslot::has_slots<> { public: explicit MediaSessionClientTest(MediaSessionTestParser* parser, cricket::SignalingProtocol initial_protocol) { nm_ = new rtc::BasicNetworkManager(); pa_ = new cricket::BasicPortAllocator(nm_); sm_ = new cricket::SessionManager(pa_, NULL); fme_ = new cricket::FakeMediaEngine(); fdme_ = new cricket::FakeDataEngine(); FeedbackParams params_nack_fir; params_nack_fir.Add(FeedbackParam(cricket::kRtcpFbParamCcm, cricket::kRtcpFbCcmParamFir)); params_nack_fir.Add(FeedbackParam(cricket::kRtcpFbParamNack)); FeedbackParams params_nack; params_nack.Add(FeedbackParam(cricket::kRtcpFbParamNack)); std::vector audio_codecs(kAudioCodecs, kAudioCodecs + ARRAY_SIZE(kAudioCodecs)); SetCodecFeedbackParams(&audio_codecs, params_nack); fme_->SetAudioCodecs(audio_codecs); std::vector video_codecs(kVideoCodecs, kVideoCodecs + ARRAY_SIZE(kVideoCodecs)); SetCodecFeedbackParams(&video_codecs, params_nack_fir); fme_->SetVideoCodecs(video_codecs); std::vector data_codecs(kDataCodecs, kDataCodecs + ARRAY_SIZE(kDataCodecs)); SetCodecFeedbackParams(&data_codecs, params_nack); fdme_->SetDataCodecs(data_codecs); client_ = new cricket::MediaSessionClient( buzz::Jid("user@domain.com/resource"), sm_, fme_, fdme_, new cricket::FakeDeviceManager()); client_->session_manager()->SignalOutgoingMessage.connect( this, &MediaSessionClientTest::OnSendStanza); client_->session_manager()->SignalSessionCreate.connect( this, &MediaSessionClientTest::OnSessionCreate); client_->SignalCallCreate.connect( this, &MediaSessionClientTest::OnCallCreate); client_->SignalCallDestroy.connect( this, &MediaSessionClientTest::OnCallDestroy); call_ = NULL; parser_ = parser; initial_protocol_ = initial_protocol; expect_incoming_crypto_ = false; expect_outgoing_crypto_ = false; expected_video_bandwidth_ = cricket::kAutoBandwidth; expected_video_rtcp_mux_ = false; } ~MediaSessionClientTest() { delete client_; delete sm_; delete pa_; delete nm_; delete parser_; ClearStanzas(); } buzz::XmlElement* ActionFromStanza(buzz::XmlElement* stanza) { return parser_->ActionFromStanza(stanza); } buzz::XmlElement* ContentFromAction(buzz::XmlElement* action) { return parser_->ContentFromAction(action); } buzz::XmlElement* PayloadTypeFromContent(buzz::XmlElement* payload) { return parser_->PayloadTypeFromContent(payload); } buzz::XmlElement* NextFromPayloadType(buzz::XmlElement* payload_type) { return parser_->NextFromPayloadType(payload_type); } buzz::XmlElement* EncryptionFromContent(buzz::XmlElement* content) { return parser_->EncryptionFromContent(content); } buzz::XmlElement* NextFromEncryption(buzz::XmlElement* encryption) { return parser_->NextFromEncryption(encryption); } cricket::AudioCodec AudioCodecFromPayloadType( const buzz::XmlElement* payload_type) { return parser_->AudioCodecFromPayloadType(payload_type); } cricket::VideoCodec VideoCodecFromPayloadType( const buzz::XmlElement* payload_type) { return parser_->VideoCodecFromPayloadType(payload_type); } cricket::DataCodec DataCodecFromPayloadType( const buzz::XmlElement* payload_type) { return parser_->DataCodecFromPayloadType(payload_type); } const AudioContentDescription* GetFirstAudioContentDescription( const cricket::SessionDescription* sdesc) { const cricket::ContentInfo* content = cricket::GetFirstAudioContent(sdesc); if (content == NULL) return NULL; return static_cast(content->description); } const cricket::VideoContentDescription* GetFirstVideoContentDescription( const cricket::SessionDescription* sdesc) { const cricket::ContentInfo* content = cricket::GetFirstVideoContent(sdesc); if (content == NULL) return NULL; return static_cast( content->description); } void CheckCryptoFromGoodIncomingInitiate(const cricket::Session* session) { ASSERT_TRUE(session != NULL); const AudioContentDescription* content = GetFirstAudioContentDescription(session->remote_description()); ASSERT_TRUE(content != NULL); ASSERT_EQ(2U, content->cryptos().size()); ASSERT_EQ(145, content->cryptos()[0].tag); ASSERT_EQ("AES_CM_128_HMAC_SHA1_32", content->cryptos()[0].cipher_suite); ASSERT_EQ("inline:hsWuSQJxx7przmb8HM+ZkeNcG3HezSNID7LmfDa9", content->cryptos()[0].key_params); ASSERT_EQ(51, content->cryptos()[1].tag); ASSERT_EQ("AES_CM_128_HMAC_SHA1_80", content->cryptos()[1].cipher_suite); ASSERT_EQ("inline:J4lfdUL8W1F7TNJKcbuygaQuA429SJy2e9JctPUy", content->cryptos()[1].key_params); } void CheckCryptoForGoodOutgoingAccept(const cricket::Session* session) { const AudioContentDescription* content = GetFirstAudioContentDescription(session->local_description()); ASSERT_EQ(1U, content->cryptos().size()); ASSERT_EQ(145, content->cryptos()[0].tag); ASSERT_EQ("AES_CM_128_HMAC_SHA1_32", content->cryptos()[0].cipher_suite); ASSERT_EQ(47U, content->cryptos()[0].key_params.size()); } void CheckBadCryptoFromIncomingInitiate(const cricket::Session* session) { const AudioContentDescription* content = GetFirstAudioContentDescription(session->remote_description()); ASSERT_EQ(1U, content->cryptos().size()); ASSERT_EQ(145, content->cryptos()[0].tag); ASSERT_EQ("NOT_SUPPORTED", content->cryptos()[0].cipher_suite); ASSERT_EQ("inline:hsWuSQJxx7przmb8HM+ZkeNcG3HezSNID7LmfDa9", content->cryptos()[0].key_params); } void CheckNoCryptoForOutgoingAccept(const cricket::Session* session) { const AudioContentDescription* content = GetFirstAudioContentDescription(session->local_description()); ASSERT_TRUE(content->cryptos().empty()); } void CheckRtcpFb(const cricket::SessionDescription* sdesc) { VerifyCodecFbParams(expected_audio_fb_params_, GetFirstAudioContentDescription(sdesc)); VerifyCodecFbParams(expected_video_fb_params_, GetFirstVideoContentDescription(sdesc)); VerifyCodecFbParams(expected_data_fb_params_, GetFirstDataContentDescription(sdesc)); } void CheckVideoBandwidth(int expected_bandwidth, const cricket::SessionDescription* sdesc) { const cricket::VideoContentDescription* video = GetFirstVideoContentDescription(sdesc); if (video != NULL) { ASSERT_EQ(expected_bandwidth, video->bandwidth()); } } void CheckVideoRtcpMux(bool expected_video_rtcp_mux, const cricket::SessionDescription* sdesc) { const cricket::VideoContentDescription* video = GetFirstVideoContentDescription(sdesc); if (video != NULL) { ASSERT_EQ(expected_video_rtcp_mux, video->rtcp_mux()); } } virtual void CheckRtpDataContent(buzz::XmlElement* content) { if (initial_protocol_) { // Gingle can not write out data content. return; } buzz::XmlElement* e = PayloadTypeFromContent(content); ASSERT_TRUE(e != NULL); cricket::DataCodec codec = DataCodecFromPayloadType(e); EXPECT_EQ(127, codec.id); EXPECT_EQ("google-data", codec.name); EXPECT_EQ(expected_data_fb_params_, codec.feedback_params); CheckDataRtcpMux(true, call_->sessions()[0]->local_description()); CheckDataRtcpMux(true, call_->sessions()[0]->remote_description()); if (expect_outgoing_crypto_) { content = parser_->NextContent(content); buzz::XmlElement* encryption = EncryptionFromContent(content); ASSERT_TRUE(encryption != NULL); // TODO(pthatcher): Check encryption parameters? } } virtual void CheckSctpDataContent(buzz::XmlElement* content) { if (initial_protocol_) { // Gingle can not write out data content. return; } buzz::XmlElement* payload_type = PayloadTypeFromContent(content); ASSERT_TRUE(payload_type == NULL); buzz::XmlElement* encryption = EncryptionFromContent(content); ASSERT_TRUE(encryption == NULL); // TODO(pthatcher): Check for . } void CheckDataRtcpMux(bool expected_data_rtcp_mux, const cricket::SessionDescription* sdesc) { const cricket::DataContentDescription* data = GetFirstDataContentDescription(sdesc); if (data != NULL) { ASSERT_EQ(expected_data_rtcp_mux, data->rtcp_mux()); } } void CheckAudioSsrcForIncomingAccept(const cricket::Session* session) { const AudioContentDescription* audio = GetFirstAudioContentDescription(session->remote_description()); ASSERT_TRUE(audio != NULL); ASSERT_EQ(kAudioSsrc, audio->first_ssrc()); } void CheckVideoSsrcForIncomingAccept(const cricket::Session* session) { const cricket::VideoContentDescription* video = GetFirstVideoContentDescription(session->remote_description()); ASSERT_TRUE(video != NULL); ASSERT_EQ(kVideoSsrc, video->first_ssrc()); } void CheckDataSsrcForIncomingAccept(const cricket::Session* session) { const cricket::DataContentDescription* data = GetFirstDataContentDescription(session->remote_description()); ASSERT_TRUE(data != NULL); ASSERT_EQ(kDataSsrc, data->first_ssrc()); } void TestGoodIncomingInitiate(const std::string& initiate_string, const cricket::CallOptions& options, buzz::XmlElement** element) { *element = NULL; rtc::scoped_ptr el( buzz::XmlElement::ForStr(initiate_string)); client_->session_manager()->OnIncomingMessage(el.get()); ASSERT_TRUE(call_ != NULL); ASSERT_TRUE(call_->sessions()[0] != NULL); ASSERT_EQ(cricket::Session::STATE_RECEIVEDINITIATE, call_->sessions()[0]->state()); ASSERT_EQ(1U, stanzas_.size()); ASSERT_TRUE(buzz::QN_IQ == stanzas_[0]->Name()); ASSERT_TRUE(stanzas_[0]->HasAttr(buzz::QN_TYPE)); ASSERT_EQ(std::string(buzz::STR_RESULT), stanzas_[0]->Attr(buzz::QN_TYPE)); ClearStanzas(); CheckVideoBandwidth(expected_video_bandwidth_, call_->sessions()[0]->remote_description()); CheckVideoRtcpMux(expected_video_rtcp_mux_, call_->sessions()[0]->remote_description()); CheckRtcpFb(call_->sessions()[0]->remote_description()); if (expect_incoming_crypto_) { CheckCryptoFromGoodIncomingInitiate(call_->sessions()[0]); } // TODO(pthatcher): Add tests for sending in accept. call_->AcceptSession(call_->sessions()[0], options); ASSERT_EQ(cricket::Session::STATE_SENTACCEPT, call_->sessions()[0]->state()); ASSERT_EQ(1U, stanzas_.size()); ASSERT_TRUE(buzz::QN_IQ == stanzas_[0]->Name()); ASSERT_TRUE(stanzas_[0]->HasAttr(buzz::QN_TYPE)); ASSERT_EQ(std::string(buzz::STR_SET), stanzas_[0]->Attr(buzz::QN_TYPE)); buzz::XmlElement* e = ActionFromStanza(stanzas_[0]); ASSERT_TRUE(e != NULL); ASSERT_TRUE(ContentFromAction(e) != NULL); *element = CopyElement(ContentFromAction(e)); ASSERT_TRUE(*element != NULL); ClearStanzas(); if (expect_outgoing_crypto_) { CheckCryptoForGoodOutgoingAccept(call_->sessions()[0]); } if (options.data_channel_type == cricket::DCT_RTP) { CheckDataRtcpMux(true, call_->sessions()[0]->local_description()); CheckDataRtcpMux(true, call_->sessions()[0]->remote_description()); // TODO(pthatcher): Check rtcpmux and crypto? } call_->Terminate(); ASSERT_EQ(cricket::Session::STATE_SENTTERMINATE, call_->sessions()[0]->state()); ASSERT_EQ(1U, stanzas_.size()); ASSERT_TRUE(buzz::QN_IQ == stanzas_[0]->Name()); ASSERT_TRUE(stanzas_[0]->HasAttr(buzz::QN_TYPE)); ASSERT_EQ(std::string(buzz::STR_SET), stanzas_[0]->Attr(buzz::QN_TYPE)); e = ActionFromStanza(stanzas_[0]); ASSERT_TRUE(e != NULL); ASSERT_TRUE(parser_->ActionIsTerminate(e)); ClearStanzas(); } void TestRejectOffer(const std::string &initiate_string, const cricket::CallOptions& options, buzz::XmlElement** element) { *element = NULL; rtc::scoped_ptr el( buzz::XmlElement::ForStr(initiate_string)); client_->session_manager()->OnIncomingMessage(el.get()); ASSERT_TRUE(call_ != NULL); ASSERT_TRUE(call_->sessions()[0] != NULL); ASSERT_EQ(cricket::Session::STATE_RECEIVEDINITIATE, call_->sessions()[0]->state()); ASSERT_EQ(1U, stanzas_.size()); ASSERT_TRUE(buzz::QN_IQ == stanzas_[0]->Name()); ASSERT_TRUE(stanzas_[0]->HasAttr(buzz::QN_TYPE)); ASSERT_EQ(std::string(buzz::STR_RESULT), stanzas_[0]->Attr(buzz::QN_TYPE)); ClearStanzas(); call_->AcceptSession(call_->sessions()[0], options); ASSERT_EQ(cricket::Session::STATE_SENTACCEPT, call_->sessions()[0]->state()); ASSERT_EQ(1U, stanzas_.size()); ASSERT_TRUE(buzz::QN_IQ == stanzas_[0]->Name()); ASSERT_TRUE(stanzas_[0]->HasAttr(buzz::QN_TYPE)); ASSERT_EQ(std::string(buzz::STR_SET), stanzas_[0]->Attr(buzz::QN_TYPE)); buzz::XmlElement* e = ActionFromStanza(stanzas_[0]); ASSERT_TRUE(e != NULL); ASSERT_TRUE(ContentFromAction(e) != NULL); *element = CopyElement(ContentFromAction(e)); ASSERT_TRUE(*element != NULL); ClearStanzas(); buzz::XmlElement* content = *element; // The NextContent method actually returns the second content. So we // can't handle the case when audio, video and data are all enabled. But // since we are testing rejection, it won't be the case. if (options.has_audio()) { ASSERT_TRUE(content != NULL); ASSERT_EQ("test audio", content->Attr(buzz::QName("", "name"))); content = parser_->NextContent(content); } if (options.has_video()) { ASSERT_TRUE(content != NULL); ASSERT_EQ("test video", content->Attr(buzz::QName("", "name"))); content = parser_->NextContent(content); } if (options.has_data()) { ASSERT_TRUE(content != NULL); ASSERT_EQ("test data", content->Attr(buzz::QName("", "name"))); content = parser_->NextContent(content); } call_->Terminate(); ASSERT_EQ(cricket::Session::STATE_SENTTERMINATE, call_->sessions()[0]->state()); ASSERT_EQ(1U, stanzas_.size()); ASSERT_TRUE(buzz::QN_IQ == stanzas_[0]->Name()); ASSERT_TRUE(stanzas_[0]->HasAttr(buzz::QN_TYPE)); ASSERT_EQ(std::string(buzz::STR_SET), stanzas_[0]->Attr(buzz::QN_TYPE)); e = ActionFromStanza(stanzas_[0]); ASSERT_TRUE(e != NULL); ASSERT_TRUE(parser_->ActionIsTerminate(e)); ClearStanzas(); } void TestBadIncomingInitiate(const std::string& initiate_string) { rtc::scoped_ptr el( buzz::XmlElement::ForStr(initiate_string)); client_->session_manager()->OnIncomingMessage(el.get()); ASSERT_TRUE(call_ != NULL); ASSERT_TRUE(call_->sessions()[0] != NULL); ASSERT_EQ(cricket::Session::STATE_SENTREJECT, call_->sessions()[0]->state()); ASSERT_EQ(2U, stanzas_.size()); ASSERT_TRUE(buzz::QN_IQ == stanzas_[0]->Name()); ASSERT_TRUE(stanzas_[1]->HasAttr(buzz::QN_TYPE)); ASSERT_EQ(std::string(buzz::STR_RESULT), stanzas_[1]->Attr(buzz::QN_TYPE)); ClearStanzas(); } void VerifyAudioCodec(const AudioCodec& codec, int id, const std::string& name, int clockrate, int bitrate, int channels) { ASSERT_EQ(id, codec.id); ASSERT_EQ(name, codec.name); ASSERT_EQ(clockrate, codec.clockrate); ASSERT_EQ(bitrate, codec.bitrate); ASSERT_EQ(channels, codec.channels); ASSERT_EQ(expected_audio_fb_params_, codec.feedback_params); } void TestGoodOutgoingInitiate(const cricket::CallOptions& options) { if (initial_protocol_ == cricket::PROTOCOL_JINGLE) { // rtcp fb is only implemented for jingle. ExpectRtcpFb(); } client_->CreateCall(); ASSERT_TRUE(call_ != NULL); call_->InitiateSession(buzz::Jid("me@mydomain.com"), buzz::Jid("me@mydomain.com"), options); ASSERT_TRUE(call_->sessions()[0] != NULL); ASSERT_EQ(cricket::Session::STATE_SENTINITIATE, call_->sessions()[0]->state()); ASSERT_EQ(1U, stanzas_.size()); ASSERT_TRUE(buzz::QN_IQ == stanzas_[0]->Name()); ASSERT_TRUE(stanzas_[0]->HasAttr(buzz::QN_TYPE)); ASSERT_EQ(std::string(buzz::STR_SET), stanzas_[0]->Attr(buzz::QN_TYPE)); buzz::XmlElement* action = ActionFromStanza(stanzas_[0]); ASSERT_TRUE(action != NULL); buzz::XmlElement* content = ContentFromAction(action); ASSERT_TRUE(content != NULL); buzz::XmlElement* e = PayloadTypeFromContent(content); ASSERT_TRUE(e != NULL); cricket::AudioCodec codec = AudioCodecFromPayloadType(e); VerifyAudioCodec(codec, 103, "ISAC", 16000, 0, 1); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); VerifyAudioCodec(codec, 104, "ISAC", 32000, 0, 1); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); VerifyAudioCodec(codec, 119, "ISACLC", 16000, 40000, 1); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); VerifyAudioCodec(codec, 99, "speex", 16000, 22000, 1); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); VerifyAudioCodec(codec, 97, "IPCMWB", 16000, 80000, 1); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); VerifyAudioCodec(codec, 9, "G722", 8000, 64000, 1); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); VerifyAudioCodec(codec, 102, "iLBC", 8000, 13300, 1); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); VerifyAudioCodec(codec, 98, "speex", 8000, 11000, 1); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); VerifyAudioCodec(codec, 3, "GSM", 8000, 13000, 1); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); VerifyAudioCodec(codec, 100, "EG711U", 8000, 64000, 1); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); VerifyAudioCodec(codec, 101, "EG711A", 8000, 64000, 1); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); VerifyAudioCodec(codec, 0, "PCMU", 8000, 64000, 1); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); VerifyAudioCodec(codec, 8, "PCMA", 8000, 64000, 1); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); VerifyAudioCodec(codec, 126, "CN", 32000, 0, 1); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); VerifyAudioCodec(codec, 105, "CN", 16000, 0, 1); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); VerifyAudioCodec(codec, 13, "CN", 8000, 0, 1); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); VerifyAudioCodec(codec, 117, "red", 8000, 0, 1); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); VerifyAudioCodec(codec, 106, "telephone-event", 8000, 0, 1); e = NextFromPayloadType(e); ASSERT_TRUE(e == NULL); if (expect_outgoing_crypto_) { buzz::XmlElement* encryption = EncryptionFromContent(content); ASSERT_TRUE(encryption != NULL); if (client_->secure() == cricket::SEC_REQUIRED) { ASSERT_TRUE(cricket::GetXmlAttr( encryption, cricket::QN_ENCRYPTION_REQUIRED, false)); } if (content->Name().Namespace() == cricket::NS_GINGLE_AUDIO) { e = encryption->FirstNamed(cricket::QN_GINGLE_AUDIO_CRYPTO_USAGE); ASSERT_TRUE(e != NULL); ASSERT_TRUE( e->NextNamed(cricket::QN_GINGLE_AUDIO_CRYPTO_USAGE) == NULL); ASSERT_TRUE( e->FirstNamed(cricket::QN_GINGLE_VIDEO_CRYPTO_USAGE) == NULL); } e = encryption->FirstNamed(cricket::QN_CRYPTO); ASSERT_TRUE(e != NULL); ASSERT_EQ("0", e->Attr(cricket::QN_CRYPTO_TAG)); ASSERT_EQ("AES_CM_128_HMAC_SHA1_32", e->Attr(cricket::QN_CRYPTO_SUITE)); std::string key_0 = e->Attr(cricket::QN_CRYPTO_KEY_PARAMS); ASSERT_EQ(47U, key_0.length()); ASSERT_EQ("inline:", key_0.substr(0, 7)); e = e->NextNamed(cricket::QN_CRYPTO); ASSERT_TRUE(e != NULL); ASSERT_EQ("1", e->Attr(cricket::QN_CRYPTO_TAG)); ASSERT_EQ("AES_CM_128_HMAC_SHA1_80", e->Attr(cricket::QN_CRYPTO_SUITE)); std::string key_1 = e->Attr(cricket::QN_CRYPTO_KEY_PARAMS); ASSERT_EQ(47U, key_1.length()); ASSERT_EQ("inline:", key_1.substr(0, 7)); ASSERT_NE(key_0, key_1); encryption = NextFromEncryption(encryption); ASSERT_TRUE(encryption == NULL); } if (options.has_video()) { CheckVideoBandwidth(options.video_bandwidth, call_->sessions()[0]->local_description()); CheckVideoRtcpMux(expected_video_rtcp_mux_, call_->sessions()[0]->remote_description()); content = parser_->NextContent(content); const buzz::XmlElement* bandwidth = parser_->BandwidthFromContent(content); if (options.video_bandwidth == cricket::kAutoBandwidth) { ASSERT_TRUE(bandwidth == NULL); } else { ASSERT_TRUE(bandwidth != NULL); ASSERT_EQ("AS", bandwidth->Attr(buzz::QName("", "type"))); ASSERT_EQ(rtc::ToString(options.video_bandwidth / 1000), bandwidth->BodyText()); } buzz::XmlElement* e = PayloadTypeFromContent(content); ASSERT_TRUE(e != NULL); VideoCodec codec = VideoCodecFromPayloadType(e); VideoCodec expected_codec = kVideoCodecs[0]; expected_codec.preference = codec.preference; expected_codec.feedback_params = expected_video_fb_params_; EXPECT_EQ(expected_codec, codec); } if (options.data_channel_type == cricket::DCT_RTP) { content = parser_->NextContent(content); CheckRtpDataContent(content); } if (options.data_channel_type == cricket::DCT_SCTP) { content = parser_->NextContent(content); CheckSctpDataContent(content); } ClearStanzas(); } void TestHasAllSupportedAudioCodecs(buzz::XmlElement* e) { ASSERT_TRUE(e != NULL); e = PayloadTypeFromContent(e); ASSERT_TRUE(e != NULL); cricket::AudioCodec codec = AudioCodecFromPayloadType(e); ASSERT_EQ(103, codec.id); ASSERT_EQ("ISAC", codec.name); ASSERT_EQ(16000, codec.clockrate); ASSERT_EQ(1, codec.channels); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(104, codec.id); ASSERT_EQ("ISAC", codec.name); ASSERT_EQ(32000, codec.clockrate); ASSERT_EQ(1, codec.channels); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(119, codec.id); ASSERT_EQ("ISACLC", codec.name); ASSERT_EQ(16000, codec.clockrate); ASSERT_EQ(40000, codec.bitrate); ASSERT_EQ(1, codec.channels); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(99, codec.id); ASSERT_EQ("speex", codec.name); ASSERT_EQ(16000, codec.clockrate); ASSERT_EQ(22000, codec.bitrate); ASSERT_EQ(1, codec.channels); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(97, codec.id); ASSERT_EQ("IPCMWB", codec.name); ASSERT_EQ(16000, codec.clockrate); ASSERT_EQ(80000, codec.bitrate); ASSERT_EQ(1, codec.channels); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(9, codec.id); ASSERT_EQ("G722", codec.name); ASSERT_EQ(8000, codec.clockrate); ASSERT_EQ(64000, codec.bitrate); ASSERT_EQ(1, codec.channels); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(102, codec.id); ASSERT_EQ("iLBC", codec.name); ASSERT_EQ(8000, codec.clockrate); ASSERT_EQ(13300, codec.bitrate); ASSERT_EQ(1, codec.channels); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(98, codec.id); ASSERT_EQ("speex", codec.name); ASSERT_EQ(8000, codec.clockrate); ASSERT_EQ(11000, codec.bitrate); ASSERT_EQ(1, codec.channels); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(3, codec.id); ASSERT_EQ("GSM", codec.name); ASSERT_EQ(8000, codec.clockrate); ASSERT_EQ(13000, codec.bitrate); ASSERT_EQ(1, codec.channels); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(100, codec.id); ASSERT_EQ("EG711U", codec.name); ASSERT_EQ(8000, codec.clockrate); ASSERT_EQ(64000, codec.bitrate); ASSERT_EQ(1, codec.channels); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(101, codec.id); ASSERT_EQ("EG711A", codec.name); ASSERT_EQ(8000, codec.clockrate); ASSERT_EQ(64000, codec.bitrate); ASSERT_EQ(1, codec.channels); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(0, codec.id); ASSERT_EQ("PCMU", codec.name); ASSERT_EQ(8000, codec.clockrate); ASSERT_EQ(64000, codec.bitrate); ASSERT_EQ(1, codec.channels); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(8, codec.id); ASSERT_EQ("PCMA", codec.name); ASSERT_EQ(8000, codec.clockrate); ASSERT_EQ(64000, codec.bitrate); ASSERT_EQ(1, codec.channels); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(126, codec.id); ASSERT_EQ("CN", codec.name); ASSERT_EQ(32000, codec.clockrate); ASSERT_EQ(1, codec.channels); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(105, codec.id); ASSERT_EQ("CN", codec.name); ASSERT_EQ(16000, codec.clockrate); ASSERT_EQ(1, codec.channels); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(13, codec.id); ASSERT_EQ("CN", codec.name); ASSERT_EQ(8000, codec.clockrate); ASSERT_EQ(1, codec.channels); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(117, codec.id); ASSERT_EQ("red", codec.name); ASSERT_EQ(8000, codec.clockrate); ASSERT_EQ(1, codec.channels); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(106, codec.id); ASSERT_EQ("telephone-event", codec.name); ASSERT_EQ(8000, codec.clockrate); ASSERT_EQ(1, codec.channels); e = NextFromPayloadType(e); ASSERT_TRUE(e == NULL); } void TestCodecsOfVideoInitiate(buzz::XmlElement* content) { ASSERT_TRUE(content != NULL); buzz::XmlElement* payload_type = PayloadTypeFromContent(content); ASSERT_TRUE(payload_type != NULL); cricket::AudioCodec codec = AudioCodecFromPayloadType(payload_type); ASSERT_EQ(103, codec.id); ASSERT_EQ("ISAC", codec.name); ASSERT_EQ(16000, codec.clockrate); ASSERT_EQ(1, codec.channels); content = parser_->NextContent(content); ASSERT_TRUE(content != NULL); payload_type = PayloadTypeFromContent(content); ASSERT_TRUE(payload_type != NULL); cricket::VideoCodec vcodec = parser_->VideoCodecFromPayloadType(payload_type); ASSERT_EQ(99, vcodec.id); ASSERT_EQ("H264-SVC", vcodec.name); ASSERT_EQ(320, vcodec.width); ASSERT_EQ(200, vcodec.height); ASSERT_EQ(30, vcodec.framerate); } void TestHasAudioCodecsFromInitiateSomeUnsupported(buzz::XmlElement* e) { ASSERT_TRUE(e != NULL); e = PayloadTypeFromContent(e); ASSERT_TRUE(e != NULL); cricket::AudioCodec codec = AudioCodecFromPayloadType(e); ASSERT_EQ(103, codec.id); ASSERT_EQ("ISAC", codec.name); ASSERT_EQ(16000, codec.clockrate); ASSERT_EQ(1, codec.channels); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(100, codec.id); ASSERT_EQ("EG711U", codec.name); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(101, codec.id); ASSERT_EQ("EG711A", codec.name); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(0, codec.id); ASSERT_EQ("PCMU", codec.name); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(13, codec.id); ASSERT_EQ("CN", codec.name); e = NextFromPayloadType(e); ASSERT_TRUE(e == NULL); } void TestHasAudioCodecsFromInitiateDynamicAudioCodecs( buzz::XmlElement* e) { ASSERT_TRUE(e != NULL); e = PayloadTypeFromContent(e); ASSERT_TRUE(e != NULL); cricket::AudioCodec codec = AudioCodecFromPayloadType(e); ASSERT_EQ(123, codec.id); ASSERT_EQ(16000, codec.clockrate); ASSERT_EQ(1, codec.channels); e = NextFromPayloadType(e); ASSERT_TRUE(e == NULL); } void TestHasDefaultAudioCodecs(buzz::XmlElement* e) { ASSERT_TRUE(e != NULL); e = PayloadTypeFromContent(e); ASSERT_TRUE(e != NULL); cricket::AudioCodec codec = AudioCodecFromPayloadType(e); ASSERT_EQ(103, codec.id); ASSERT_EQ("ISAC", codec.name); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(0, codec.id); ASSERT_EQ("PCMU", codec.name); e = NextFromPayloadType(e); ASSERT_TRUE(e == NULL); } void TestHasAudioCodecsFromInitiateStaticAudioCodecs( buzz::XmlElement* e) { ASSERT_TRUE(e != NULL); e = PayloadTypeFromContent(e); ASSERT_TRUE(e != NULL); cricket::AudioCodec codec = AudioCodecFromPayloadType(e); ASSERT_EQ(3, codec.id); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(0, codec.id); e = NextFromPayloadType(e); ASSERT_TRUE(e != NULL); codec = AudioCodecFromPayloadType(e); ASSERT_EQ(8, codec.id); e = NextFromPayloadType(e); ASSERT_TRUE(e == NULL); } void TestGingleInitiateWithUnsupportedCrypto( const std::string &initiate_string, buzz::XmlElement** element) { *element = NULL; rtc::scoped_ptr el( buzz::XmlElement::ForStr(initiate_string)); client_->session_manager()->OnIncomingMessage(el.get()); ASSERT_EQ(cricket::Session::STATE_RECEIVEDINITIATE, call_->sessions()[0]->state()); ClearStanzas(); CheckBadCryptoFromIncomingInitiate(call_->sessions()[0]); call_->AcceptSession(call_->sessions()[0], cricket::CallOptions()); ClearStanzas(); CheckNoCryptoForOutgoingAccept(call_->sessions()[0]); call_->Terminate(); ASSERT_EQ(cricket::Session::STATE_SENTTERMINATE, call_->sessions()[0]->state()); ClearStanzas(); } void TestIncomingAcceptWithSsrcs( const std::string& accept_string, cricket::CallOptions& options) { client_->CreateCall(); ASSERT_TRUE(call_ != NULL); call_->InitiateSession(buzz::Jid("me@mydomain.com"), buzz::Jid("me@mydomain.com"), options); ASSERT_TRUE(call_->sessions()[0] != NULL); ASSERT_EQ(cricket::Session::STATE_SENTINITIATE, call_->sessions()[0]->state()); ASSERT_EQ(1U, stanzas_.size()); ASSERT_TRUE(buzz::QN_IQ == stanzas_[0]->Name()); ASSERT_TRUE(stanzas_[0]->HasAttr(buzz::QN_TYPE)); ASSERT_EQ(std::string(buzz::STR_SET), stanzas_[0]->Attr(buzz::QN_TYPE)); buzz::XmlElement* action = ActionFromStanza(stanzas_[0]); ASSERT_TRUE(action != NULL); buzz::XmlElement* content = ContentFromAction(action); ASSERT_TRUE(content != NULL); if (initial_protocol_ == cricket::PROTOCOL_JINGLE) { buzz::XmlElement* content_desc = content->FirstNamed(cricket::QN_JINGLE_RTP_CONTENT); ASSERT_TRUE(content_desc != NULL); ASSERT_EQ("", content_desc->Attr(cricket::QN_SSRC)); } ClearStanzas(); // We need to insert the session ID into the session accept message. rtc::scoped_ptr el( buzz::XmlElement::ForStr(accept_string)); const std::string sid = call_->sessions()[0]->id(); if (initial_protocol_ == cricket::PROTOCOL_JINGLE) { buzz::XmlElement* jingle = el->FirstNamed(cricket::QN_JINGLE); jingle->SetAttr(cricket::QN_SID, sid); } else { buzz::XmlElement* session = el->FirstNamed(cricket::QN_GINGLE_SESSION); session->SetAttr(cricket::QN_ID, sid); } client_->session_manager()->OnIncomingMessage(el.get()); ASSERT_EQ(cricket::Session::STATE_RECEIVEDACCEPT, call_->sessions()[0]->state()); ASSERT_EQ(1U, stanzas_.size()); ASSERT_TRUE(buzz::QN_IQ == stanzas_[0]->Name()); ASSERT_TRUE(stanzas_[0]->HasAttr(buzz::QN_TYPE)); ASSERT_EQ(std::string(buzz::STR_RESULT), stanzas_[0]->Attr(buzz::QN_TYPE)); ClearStanzas(); CheckAudioSsrcForIncomingAccept(call_->sessions()[0]); CheckVideoSsrcForIncomingAccept(call_->sessions()[0]); if (options.data_channel_type == cricket::DCT_RTP) { CheckDataSsrcForIncomingAccept(call_->sessions()[0]); } // TODO(pthatcher): Check kDataSid if DCT_SCTP. // const uint32 kDataSid = 0; } size_t ClearStanzas() { size_t size = stanzas_.size(); for (size_t i = 0; i < size; i++) { delete stanzas_[i]; } stanzas_.clear(); return size; } buzz::XmlElement* SetJingleSid(buzz::XmlElement* stanza) { buzz::XmlElement* jingle = stanza->FirstNamed(cricket::QN_JINGLE); jingle->SetAttr(cricket::QN_SID, call_->sessions()[0]->id()); return stanza; } void TestSendVideoStreamUpdate() { cricket::CallOptions options = VideoCallOptions(); options.is_muc = true; client_->CreateCall(); call_->InitiateSession(buzz::Jid("me@mydomain.com"), buzz::Jid("me@mydomain.com"), options); ClearStanzas(); cricket::StreamParams stream; stream.id = "test-stream"; stream.ssrcs.push_back(1001); rtc::scoped_ptr expected_stream_add( buzz::XmlElement::ForStr( JingleOutboundStreamAdd( call_->sessions()[0]->id(), "video", stream.id, "1001"))); rtc::scoped_ptr expected_stream_remove( buzz::XmlElement::ForStr( JingleOutboundStreamRemove( call_->sessions()[0]->id(), "video", stream.id))); call_->SendVideoStreamUpdate(call_->sessions()[0], call_->CreateVideoStreamUpdate(stream)); ASSERT_EQ(1U, stanzas_.size()); EXPECT_EQ(expected_stream_add->Str(), stanzas_[0]->Str()); ClearStanzas(); stream.ssrcs.clear(); call_->SendVideoStreamUpdate(call_->sessions()[0], call_->CreateVideoStreamUpdate(stream)); ASSERT_EQ(1U, stanzas_.size()); EXPECT_EQ(expected_stream_remove->Str(), stanzas_[0]->Str()); ClearStanzas(); } void TestStreamsUpdateAndViewRequests() { cricket::CallOptions options = VideoCallOptions(); options.is_muc = true; client_->CreateCall(); call_->InitiateSession(buzz::Jid("me@mydomain.com"), buzz::Jid("me@mydomain.com"), options); ASSERT_EQ(1U, ClearStanzas()); ASSERT_EQ(0U, last_streams_added_.audio().size()); ASSERT_EQ(0U, last_streams_added_.video().size()); ASSERT_EQ(0U, last_streams_removed_.audio().size()); ASSERT_EQ(0U, last_streams_removed_.video().size()); rtc::scoped_ptr accept_stanza( buzz::XmlElement::ForStr(kJingleAcceptWithSsrcs)); SetJingleSid(accept_stanza.get()); client_->session_manager()->OnIncomingMessage(accept_stanza.get()); ASSERT_EQ(cricket::Session::STATE_RECEIVEDACCEPT, call_->sessions()[0]->state()); ASSERT_EQ(1U, stanzas_.size()); ASSERT_EQ(std::string(buzz::STR_RESULT), stanzas_[0]->Attr(buzz::QN_TYPE)); ClearStanzas(); // Need to clear the added streams, because they are populated when // receiving an accept message now. last_streams_added_.mutable_video()->clear(); last_streams_added_.mutable_audio()->clear(); call_->sessions()[0]->SetState(cricket::Session::STATE_INPROGRESS); rtc::scoped_ptr streams_stanza( buzz::XmlElement::ForStr( JingleStreamAdd("video", "Bob", "video1", "ABC"))); SetJingleSid(streams_stanza.get()); client_->session_manager()->OnIncomingMessage(streams_stanza.get()); // First one is ignored because of bad syntax. ASSERT_EQ(1U, stanzas_.size()); // TODO(pthatcher): Figure out how to make this an ERROR rather than RESULT. ASSERT_EQ(std::string(buzz::STR_ERROR), stanzas_[0]->Attr(buzz::QN_TYPE)); ClearStanzas(); ASSERT_EQ(0U, last_streams_added_.audio().size()); ASSERT_EQ(0U, last_streams_added_.video().size()); ASSERT_EQ(0U, last_streams_removed_.audio().size()); ASSERT_EQ(0U, last_streams_removed_.video().size()); streams_stanza.reset(buzz::XmlElement::ForStr( JingleStreamAdd("audio", "Bob", "audio1", "1234"))); SetJingleSid(streams_stanza.get()); client_->session_manager()->OnIncomingMessage(streams_stanza.get()); ASSERT_EQ(1U, last_streams_added_.audio().size()); ASSERT_EQ("Bob", last_streams_added_.audio()[0].groupid); ASSERT_EQ(1U, last_streams_added_.audio()[0].ssrcs.size()); ASSERT_EQ(1234U, last_streams_added_.audio()[0].first_ssrc()); // Ignores adds without ssrcs. streams_stanza.reset(buzz::XmlElement::ForStr( JingleStreamAddWithoutSsrc("audio", "Bob", "audioX"))); SetJingleSid(streams_stanza.get()); client_->session_manager()->OnIncomingMessage(streams_stanza.get()); ASSERT_EQ(1U, last_streams_added_.audio().size()); ASSERT_EQ(1234U, last_streams_added_.audio()[0].first_ssrc()); // Ignores stream updates with unknown content names. (Don't terminate). streams_stanza.reset(buzz::XmlElement::ForStr( JingleStreamAddWithoutSsrc("foo", "Bob", "foo"))); SetJingleSid(streams_stanza.get()); client_->session_manager()->OnIncomingMessage(streams_stanza.get()); streams_stanza.reset(buzz::XmlElement::ForStr( JingleStreamAdd("audio", "Joe", "audio1", "2468"))); SetJingleSid(streams_stanza.get()); client_->session_manager()->OnIncomingMessage(streams_stanza.get()); ASSERT_EQ(1U, last_streams_added_.audio().size()); ASSERT_EQ("Joe", last_streams_added_.audio()[0].groupid); ASSERT_EQ(1U, last_streams_added_.audio()[0].ssrcs.size()); ASSERT_EQ(2468U, last_streams_added_.audio()[0].first_ssrc()); streams_stanza.reset(buzz::XmlElement::ForStr( JingleStreamAdd("video", "Bob", "video1", "5678"))); SetJingleSid(streams_stanza.get()); client_->session_manager()->OnIncomingMessage(streams_stanza.get()); ASSERT_EQ(1U, last_streams_added_.video().size()); ASSERT_EQ("Bob", last_streams_added_.video()[0].groupid); ASSERT_EQ(1U, last_streams_added_.video()[0].ssrcs.size()); ASSERT_EQ(5678U, last_streams_added_.video()[0].first_ssrc()); // We're testing that a "duplicate" is effectively ignored. last_streams_added_.mutable_video()->clear(); last_streams_removed_.mutable_video()->clear(); streams_stanza.reset(buzz::XmlElement::ForStr( JingleStreamAdd("video", "Bob", "video1", "5678"))); SetJingleSid(streams_stanza.get()); client_->session_manager()->OnIncomingMessage(streams_stanza.get()); ASSERT_EQ(0U, last_streams_added_.video().size()); ASSERT_EQ(0U, last_streams_removed_.video().size()); streams_stanza.reset(buzz::XmlElement::ForStr( JingleStreamAdd("video", "Bob", "video2", "5679"))); SetJingleSid(streams_stanza.get()); client_->session_manager()->OnIncomingMessage(streams_stanza.get()); ASSERT_EQ(1U, last_streams_added_.video().size()); ASSERT_EQ("Bob", last_streams_added_.video()[0].groupid); ASSERT_EQ(1U, last_streams_added_.video()[0].ssrcs.size()); ASSERT_EQ(5679U, last_streams_added_.video()[0].first_ssrc()); cricket::FakeVoiceMediaChannel* voice_channel = fme_->GetVoiceChannel(0); ASSERT_TRUE(voice_channel != NULL); ASSERT_TRUE(voice_channel->HasRecvStream(1234U)); ASSERT_TRUE(voice_channel->HasRecvStream(2468U)); cricket::FakeVideoMediaChannel* video_channel = fme_->GetVideoChannel(0); ASSERT_TRUE(video_channel != NULL); ASSERT_TRUE(video_channel->HasRecvStream(5678U)); ClearStanzas(); cricket::ViewRequest viewRequest; cricket::StaticVideoView staticVideoView( cricket::StreamSelector(5678U), 640, 480, 30); viewRequest.static_video_views.push_back(staticVideoView); rtc::scoped_ptr expected_view_elem( buzz::XmlElement::ForStr(JingleView("5678", "640", "480", "30"))); SetJingleSid(expected_view_elem.get()); ASSERT_TRUE( call_->SendViewRequest(call_->sessions()[0], viewRequest)); ASSERT_EQ(1U, stanzas_.size()); ASSERT_EQ(expected_view_elem->Str(), stanzas_[0]->Str()); ClearStanzas(); streams_stanza.reset(buzz::XmlElement::ForStr( JingleStreamRemove("audio", "Bob", "audio1"))); SetJingleSid(streams_stanza.get()); client_->session_manager()->OnIncomingMessage(streams_stanza.get()); ASSERT_EQ(1U, last_streams_removed_.audio().size()); ASSERT_EQ(1U, last_streams_removed_.audio()[0].ssrcs.size()); EXPECT_EQ(1234U, last_streams_removed_.audio()[0].first_ssrc()); streams_stanza.reset(buzz::XmlElement::ForStr( JingleStreamRemove("video", "Bob", "video1"))); SetJingleSid(streams_stanza.get()); client_->session_manager()->OnIncomingMessage(streams_stanza.get()); ASSERT_EQ(1U, last_streams_removed_.video().size()); ASSERT_EQ(1U, last_streams_removed_.video()[0].ssrcs.size()); EXPECT_EQ(5678U, last_streams_removed_.video()[0].first_ssrc()); streams_stanza.reset(buzz::XmlElement::ForStr( JingleStreamRemove("video", "Bob", "video2"))); SetJingleSid(streams_stanza.get()); client_->session_manager()->OnIncomingMessage(streams_stanza.get()); ASSERT_EQ(1U, last_streams_removed_.video().size()); ASSERT_EQ(1U, last_streams_removed_.video()[0].ssrcs.size()); EXPECT_EQ(5679U, last_streams_removed_.video()[0].first_ssrc()); // Duplicate removal: should be ignored. last_streams_removed_.mutable_audio()->clear(); streams_stanza.reset(buzz::XmlElement::ForStr( JingleStreamRemove("audio", "Bob", "audio1"))); SetJingleSid(streams_stanza.get()); client_->session_manager()->OnIncomingMessage(streams_stanza.get()); ASSERT_EQ(0U, last_streams_removed_.audio().size()); // Duplicate removal: should be ignored. last_streams_removed_.mutable_video()->clear(); streams_stanza.reset(buzz::XmlElement::ForStr( JingleStreamRemove("video", "Bob", "video1"))); SetJingleSid(streams_stanza.get()); client_->session_manager()->OnIncomingMessage(streams_stanza.get()); ASSERT_EQ(0U, last_streams_removed_.video().size()); voice_channel = fme_->GetVoiceChannel(0); ASSERT_TRUE(voice_channel != NULL); ASSERT_FALSE(voice_channel->HasRecvStream(1234U)); ASSERT_TRUE(voice_channel->HasRecvStream(2468U)); video_channel = fme_->GetVideoChannel(0); ASSERT_TRUE(video_channel != NULL); ASSERT_FALSE(video_channel->HasRecvStream(5678U)); // Fails because ssrc is now invalid. ASSERT_FALSE( call_->SendViewRequest(call_->sessions()[0], viewRequest)); ClearStanzas(); } void MakeSignalingSecure(cricket::SecurePolicy secure) { client_->set_secure(secure); } void ExpectCrypto(cricket::SecurePolicy secure) { MakeSignalingSecure(secure); expect_incoming_crypto_ = true; #ifdef HAVE_SRTP expect_outgoing_crypto_ = true; #endif } void ExpectVideoBandwidth(int bandwidth) { expected_video_bandwidth_ = bandwidth; } void ExpectVideoRtcpMux(bool rtcp_mux) { expected_video_rtcp_mux_ = rtcp_mux; } template void SetCodecFeedbackParams(std::vector* codecs, const FeedbackParams& fb_params) { for (size_t i = 0; i < codecs->size(); ++i) { codecs->at(i).feedback_params = fb_params; } } void ExpectRtcpFb() { FeedbackParams params_nack_fir; params_nack_fir.Add(FeedbackParam(cricket::kRtcpFbParamCcm, cricket::kRtcpFbCcmParamFir)); params_nack_fir.Add(FeedbackParam(cricket::kRtcpFbParamNack)); FeedbackParams params_nack; params_nack.Add(FeedbackParam(cricket::kRtcpFbParamNack)); expected_audio_fb_params_ = params_nack; expected_video_fb_params_ = params_nack_fir; expected_data_fb_params_ = params_nack; } cricket::FakeMediaEngine* fme() { return fme_; } private: void OnSendStanza(cricket::SessionManager* manager, const buzz::XmlElement* stanza) { LOG(LS_INFO) << stanza->Str(); stanzas_.push_back(new buzz::XmlElement(*stanza)); } void OnSessionCreate(cricket::Session* session, bool initiate) { session->set_current_protocol(initial_protocol_); } void OnCallCreate(cricket::Call *call) { call_ = call; call->SignalMediaStreamsUpdate.connect( this, &MediaSessionClientTest::OnMediaStreamsUpdate); } void OnCallDestroy(cricket::Call *call) { call_ = NULL; } void OnMediaStreamsUpdate(cricket::Call *call, cricket::Session *session, const cricket::MediaStreams& added, const cricket::MediaStreams& removed) { last_streams_added_.CopyFrom(added); last_streams_removed_.CopyFrom(removed); } rtc::NetworkManager* nm_; cricket::PortAllocator* pa_; cricket::SessionManager* sm_; cricket::FakeMediaEngine* fme_; cricket::FakeDataEngine* fdme_; cricket::MediaSessionClient* client_; cricket::Call* call_; std::vector stanzas_; MediaSessionTestParser* parser_; cricket::SignalingProtocol initial_protocol_; bool expect_incoming_crypto_; bool expect_outgoing_crypto_; int expected_video_bandwidth_; bool expected_video_rtcp_mux_; FeedbackParams expected_audio_fb_params_; FeedbackParams expected_video_fb_params_; FeedbackParams expected_data_fb_params_; cricket::MediaStreams last_streams_added_; cricket::MediaStreams last_streams_removed_; }; MediaSessionClientTest* GingleTest() { return new MediaSessionClientTest(new GingleSessionTestParser(), cricket::PROTOCOL_GINGLE); } MediaSessionClientTest* JingleTest() { return new MediaSessionClientTest(new JingleSessionTestParser(), cricket::PROTOCOL_JINGLE); } class MediaSessionTest : public ::testing::Test {}; TEST_F(MediaSessionTest, JingleGoodInitiateWithRtcpFb) { rtc::scoped_ptr test(JingleTest()); rtc::scoped_ptr elem; cricket::CallOptions options = VideoCallOptions(); options.data_channel_type = cricket::DCT_SCTP; test->ExpectRtcpFb(); test->TestGoodIncomingInitiate( kJingleInitiateWithRtcpFb, options, elem.use()); } TEST_F(MediaSessionTest, JingleGoodVideoInitiate) { rtc::scoped_ptr test(JingleTest()); rtc::scoped_ptr elem; test->TestGoodIncomingInitiate( kJingleVideoInitiate, VideoCallOptions(), elem.use()); test->TestCodecsOfVideoInitiate(elem.get()); } TEST_F(MediaSessionTest, JingleGoodVideoInitiateWithBandwidth) { rtc::scoped_ptr test(JingleTest()); rtc::scoped_ptr elem; test->ExpectVideoBandwidth(42000); test->TestGoodIncomingInitiate( kJingleVideoInitiateWithBandwidth, VideoCallOptions(), elem.use()); } TEST_F(MediaSessionTest, JingleGoodVideoInitiateWithRtcpMux) { rtc::scoped_ptr test(JingleTest()); rtc::scoped_ptr elem; test->ExpectVideoRtcpMux(true); test->TestGoodIncomingInitiate( kJingleVideoInitiateWithRtcpMux, VideoCallOptions(), elem.use()); } TEST_F(MediaSessionTest, JingleGoodVideoInitiateWithRtpData) { rtc::scoped_ptr test(JingleTest()); rtc::scoped_ptr elem; cricket::CallOptions options = VideoCallOptions(); options.data_channel_type = cricket::DCT_RTP; test->TestGoodIncomingInitiate( AddEncryption(kJingleVideoInitiateWithRtpData, kJingleCryptoOffer), options, elem.use()); } TEST_F(MediaSessionTest, JingleGoodVideoInitiateWithSctpData) { rtc::scoped_ptr test(JingleTest()); rtc::scoped_ptr elem; cricket::CallOptions options = VideoCallOptions(); options.data_channel_type = cricket::DCT_SCTP; test->TestGoodIncomingInitiate(kJingleVideoInitiateWithSctpData, options, elem.use()); } TEST_F(MediaSessionTest, JingleRejectAudio) { rtc::scoped_ptr test(JingleTest()); rtc::scoped_ptr elem; cricket::CallOptions options = VideoCallOptions(); options.recv_audio = false; options.data_channel_type = cricket::DCT_RTP; test->TestRejectOffer(kJingleVideoInitiateWithRtpData, options, elem.use()); } TEST_F(MediaSessionTest, JingleRejectVideo) { rtc::scoped_ptr test(JingleTest()); rtc::scoped_ptr elem; cricket::CallOptions options = AudioCallOptions(); options.data_channel_type = cricket::DCT_RTP; test->TestRejectOffer(kJingleVideoInitiateWithRtpData, options, elem.use()); } TEST_F(MediaSessionTest, JingleRejectData) { rtc::scoped_ptr test(JingleTest()); rtc::scoped_ptr elem; test->TestRejectOffer( kJingleVideoInitiateWithRtpData, VideoCallOptions(), elem.use()); } TEST_F(MediaSessionTest, JingleRejectVideoAndData) { rtc::scoped_ptr test(JingleTest()); rtc::scoped_ptr elem; test->TestRejectOffer( kJingleVideoInitiateWithRtpData, AudioCallOptions(), elem.use()); } TEST_F(MediaSessionTest, JingleGoodInitiateAllSupportedAudioCodecs) { rtc::scoped_ptr test(JingleTest()); rtc::scoped_ptr elem; test->TestGoodIncomingInitiate( kJingleInitiate, AudioCallOptions(), elem.use()); test->TestHasAllSupportedAudioCodecs(elem.get()); } // Changes the codecs that our FakeMediaEngine will support with a different // preference order than the incoming offer. // Verifies the answer accepts the preference order of the remote peer. TEST_F(MediaSessionTest, JingleGoodInitiateDifferentPreferenceAudioCodecs) { rtc::scoped_ptr test(JingleTest()); test->fme()->SetAudioCodecs(MAKE_VECTOR(kAudioCodecsDifferentPreference)); rtc::scoped_ptr elem; test->TestGoodIncomingInitiate( kJingleInitiate, AudioCallOptions(), elem.use()); test->TestHasAllSupportedAudioCodecs(elem.get()); } TEST_F(MediaSessionTest, JingleGoodInitiateSomeUnsupportedAudioCodecs) { rtc::scoped_ptr test(JingleTest()); rtc::scoped_ptr elem; test->TestGoodIncomingInitiate( kJingleInitiateSomeUnsupported, AudioCallOptions(), elem.use()); test->TestHasAudioCodecsFromInitiateSomeUnsupported(elem.get()); } TEST_F(MediaSessionTest, JingleGoodInitiateDynamicAudioCodecs) { rtc::scoped_ptr test(JingleTest()); rtc::scoped_ptr elem; test->TestGoodIncomingInitiate( kJingleInitiateDynamicAudioCodecs, AudioCallOptions(), elem.use()); test->TestHasAudioCodecsFromInitiateDynamicAudioCodecs(elem.get()); } TEST_F(MediaSessionTest, JingleGoodInitiateStaticAudioCodecs) { rtc::scoped_ptr test(JingleTest()); rtc::scoped_ptr elem; test->TestGoodIncomingInitiate( kJingleInitiateStaticAudioCodecs, AudioCallOptions(), elem.use()); test->TestHasAudioCodecsFromInitiateStaticAudioCodecs(elem.get()); } TEST_F(MediaSessionTest, JingleBadInitiateNoAudioCodecs) { rtc::scoped_ptr test(JingleTest()); test->TestBadIncomingInitiate(kJingleInitiateNoAudioCodecs); } TEST_F(MediaSessionTest, JingleBadInitiateNoSupportedAudioCodecs) { rtc::scoped_ptr test(JingleTest()); test->TestBadIncomingInitiate(kJingleInitiateNoSupportedAudioCodecs); } TEST_F(MediaSessionTest, JingleBadInitiateWrongClockrates) { rtc::scoped_ptr test(JingleTest()); test->TestBadIncomingInitiate(kJingleInitiateWrongClockrates); } TEST_F(MediaSessionTest, JingleBadInitiateWrongChannels) { rtc::scoped_ptr test(JingleTest()); test->TestBadIncomingInitiate(kJingleInitiateWrongChannels); } TEST_F(MediaSessionTest, JingleBadInitiateNoPayloadTypes) { rtc::scoped_ptr test(JingleTest()); test->TestBadIncomingInitiate(kJingleInitiateNoPayloadTypes); } TEST_F(MediaSessionTest, JingleBadInitiateDynamicWithoutNames) { rtc::scoped_ptr test(JingleTest()); test->TestBadIncomingInitiate(kJingleInitiateDynamicWithoutNames); } TEST_F(MediaSessionTest, JingleGoodOutgoingInitiate) { rtc::scoped_ptr test(JingleTest()); test->TestGoodOutgoingInitiate(AudioCallOptions()); } TEST_F(MediaSessionTest, JingleGoodOutgoingInitiateWithBandwidth) { rtc::scoped_ptr test(JingleTest()); cricket::CallOptions options = VideoCallOptions(); options.video_bandwidth = 42000; test->TestGoodOutgoingInitiate(options); } TEST_F(MediaSessionTest, JingleGoodOutgoingInitiateWithRtcpMux) { rtc::scoped_ptr test(JingleTest()); cricket::CallOptions options = VideoCallOptions(); options.rtcp_mux_enabled = true; test->TestGoodOutgoingInitiate(options); } TEST_F(MediaSessionTest, JingleGoodOutgoingInitiateWithRtpData) { rtc::scoped_ptr test(JingleTest()); cricket::CallOptions options; options.data_channel_type = cricket::DCT_RTP; test->ExpectCrypto(cricket::SEC_ENABLED); test->TestGoodOutgoingInitiate(options); } TEST_F(MediaSessionTest, JingleGoodOutgoingInitiateWithSctpData) { rtc::scoped_ptr test(JingleTest()); cricket::CallOptions options; options.data_channel_type = cricket::DCT_SCTP; test->TestGoodOutgoingInitiate(options); } // Crypto related tests. // Offer has crypto but the session is not secured, just ignore it. TEST_F(MediaSessionTest, JingleInitiateWithCryptoIsIgnoredWhenNotSecured) { rtc::scoped_ptr test(JingleTest()); rtc::scoped_ptr elem; test->TestGoodIncomingInitiate( AddEncryption(kJingleVideoInitiate, kJingleCryptoOffer), VideoCallOptions(), elem.use()); } // Offer has crypto required but the session is not secure, fail. TEST_F(MediaSessionTest, JingleInitiateWithCryptoRequiredWhenNotSecured) { rtc::scoped_ptr test(JingleTest()); test->TestBadIncomingInitiate(AddEncryption(kJingleVideoInitiate, kJingleRequiredCryptoOffer)); } // Offer has no crypto but the session is secure required, fail. TEST_F(MediaSessionTest, JingleInitiateWithNoCryptoFailsWhenSecureRequired) { rtc::scoped_ptr test(JingleTest()); test->ExpectCrypto(cricket::SEC_REQUIRED); test->TestBadIncomingInitiate(kJingleInitiate); } // Offer has crypto and session is secure, expect crypto in the answer. TEST_F(MediaSessionTest, JingleInitiateWithCryptoWhenSecureEnabled) { rtc::scoped_ptr test(JingleTest()); rtc::scoped_ptr elem; test->ExpectCrypto(cricket::SEC_ENABLED); test->TestGoodIncomingInitiate( AddEncryption(kJingleVideoInitiate, kJingleCryptoOffer), VideoCallOptions(), elem.use()); } // Offer has crypto and session is secure required, expect crypto in // the answer. TEST_F(MediaSessionTest, JingleInitiateWithCryptoWhenSecureRequired) { rtc::scoped_ptr test(JingleTest()); rtc::scoped_ptr elem; test->ExpectCrypto(cricket::SEC_REQUIRED); test->TestGoodIncomingInitiate( AddEncryption(kJingleVideoInitiate, kJingleCryptoOffer), VideoCallOptions(), elem.use()); } // Offer has unsupported crypto and session is secure, no crypto in // the answer. TEST_F(MediaSessionTest, JingleInitiateWithUnsupportedCrypto) { rtc::scoped_ptr test(JingleTest()); rtc::scoped_ptr elem; test->MakeSignalingSecure(cricket::SEC_ENABLED); test->TestGoodIncomingInitiate( AddEncryption(kJingleInitiate, kJingleUnsupportedCryptoOffer), VideoCallOptions(), elem.use()); } // Offer has unsupported REQUIRED crypto and session is not secure, fail. TEST_F(MediaSessionTest, JingleInitiateWithRequiredUnsupportedCrypto) { rtc::scoped_ptr test(JingleTest()); test->TestBadIncomingInitiate( AddEncryption(kJingleInitiate, kJingleRequiredUnsupportedCryptoOffer)); } // Offer has unsupported REQUIRED crypto and session is secure, fail. TEST_F(MediaSessionTest, JingleInitiateWithRequiredUnsupportedCryptoWhenSecure) { rtc::scoped_ptr test(JingleTest()); test->MakeSignalingSecure(cricket::SEC_ENABLED); test->TestBadIncomingInitiate( AddEncryption(kJingleInitiate, kJingleRequiredUnsupportedCryptoOffer)); } // Offer has unsupported REQUIRED crypto and session is required secure, fail. TEST_F(MediaSessionTest, JingleInitiateWithRequiredUnsupportedCryptoWhenSecureRequired) { rtc::scoped_ptr test(JingleTest()); test->MakeSignalingSecure(cricket::SEC_REQUIRED); test->TestBadIncomingInitiate( AddEncryption(kJingleInitiate, kJingleRequiredUnsupportedCryptoOffer)); } TEST_F(MediaSessionTest, JingleGoodOutgoingInitiateWithCrypto) { rtc::scoped_ptr test(JingleTest()); test->ExpectCrypto(cricket::SEC_ENABLED); test->TestGoodOutgoingInitiate(AudioCallOptions()); } TEST_F(MediaSessionTest, JingleGoodOutgoingInitiateWithCryptoRequired) { rtc::scoped_ptr test(JingleTest()); test->ExpectCrypto(cricket::SEC_REQUIRED); test->TestGoodOutgoingInitiate(AudioCallOptions()); } TEST_F(MediaSessionTest, JingleIncomingAcceptWithSsrcs) { rtc::scoped_ptr test(JingleTest()); cricket::CallOptions options = VideoCallOptions(); options.is_muc = true; test->TestIncomingAcceptWithSsrcs(kJingleAcceptWithSsrcs, options); } TEST_F(MediaSessionTest, JingleIncomingAcceptWithRtpDataSsrcs) { rtc::scoped_ptr test(JingleTest()); cricket::CallOptions options = VideoCallOptions(); options.is_muc = true; options.data_channel_type = cricket::DCT_RTP; test->TestIncomingAcceptWithSsrcs(kJingleAcceptWithRtpDataSsrcs, options); } TEST_F(MediaSessionTest, JingleIncomingAcceptWithSctpData) { rtc::scoped_ptr test(JingleTest()); cricket::CallOptions options = VideoCallOptions(); options.is_muc = true; options.data_channel_type = cricket::DCT_SCTP; test->TestIncomingAcceptWithSsrcs(kJingleAcceptWithSctpData, options); } TEST_F(MediaSessionTest, JingleStreamsUpdateAndView) { rtc::scoped_ptr test(JingleTest()); test->TestStreamsUpdateAndViewRequests(); } TEST_F(MediaSessionTest, JingleSendVideoStreamUpdate) { rtc::scoped_ptr test(JingleTest()); test->TestSendVideoStreamUpdate(); } // Gingle tests TEST_F(MediaSessionTest, GingleGoodVideoInitiate) { rtc::scoped_ptr elem; rtc::scoped_ptr test(GingleTest()); test->TestGoodIncomingInitiate( kGingleVideoInitiate, VideoCallOptions(), elem.use()); test->TestCodecsOfVideoInitiate(elem.get()); } TEST_F(MediaSessionTest, GingleGoodVideoInitiateWithBandwidth) { rtc::scoped_ptr elem; rtc::scoped_ptr test(GingleTest()); test->ExpectVideoBandwidth(42000); test->TestGoodIncomingInitiate( kGingleVideoInitiateWithBandwidth, VideoCallOptions(), elem.use()); } TEST_F(MediaSessionTest, GingleGoodInitiateAllSupportedAudioCodecs) { rtc::scoped_ptr elem; rtc::scoped_ptr test(GingleTest()); test->TestGoodIncomingInitiate( kGingleInitiate, AudioCallOptions(), elem.use()); test->TestHasAllSupportedAudioCodecs(elem.get()); } TEST_F(MediaSessionTest, GingleGoodInitiateAllSupportedAudioCodecsWithCrypto) { rtc::scoped_ptr elem; rtc::scoped_ptr test(GingleTest()); test->ExpectCrypto(cricket::SEC_ENABLED); test->TestGoodIncomingInitiate( AddEncryption(kGingleInitiate, kGingleCryptoOffer), AudioCallOptions(), elem.use()); test->TestHasAllSupportedAudioCodecs(elem.get()); } // Changes the codecs that our FakeMediaEngine will support with a different // preference order than the incoming offer. // Verifies the answer accepts the preference order of the remote peer. TEST_F(MediaSessionTest, GingleGoodInitiateDifferentPreferenceAudioCodecs) { rtc::scoped_ptr test(GingleTest()); test->fme()->SetAudioCodecs(MAKE_VECTOR(kAudioCodecsDifferentPreference)); rtc::scoped_ptr elem; test->TestGoodIncomingInitiate( kGingleInitiate, AudioCallOptions(), elem.use()); test->TestHasAllSupportedAudioCodecs(elem.get()); } TEST_F(MediaSessionTest, GingleGoodInitiateSomeUnsupportedAudioCodecs) { rtc::scoped_ptr elem; rtc::scoped_ptr test(GingleTest()); test->TestGoodIncomingInitiate( kGingleInitiateSomeUnsupported, AudioCallOptions(), elem.use()); test->TestHasAudioCodecsFromInitiateSomeUnsupported(elem.get()); } TEST_F(MediaSessionTest, GingleGoodInitiateDynamicAudioCodecs) { rtc::scoped_ptr elem; rtc::scoped_ptr test(GingleTest()); test->TestGoodIncomingInitiate( kGingleInitiateDynamicAudioCodecs, AudioCallOptions(), elem.use()); test->TestHasAudioCodecsFromInitiateDynamicAudioCodecs(elem.get()); } TEST_F(MediaSessionTest, GingleGoodInitiateStaticAudioCodecs) { rtc::scoped_ptr elem; rtc::scoped_ptr test(GingleTest()); test->TestGoodIncomingInitiate( kGingleInitiateStaticAudioCodecs, AudioCallOptions(), elem.use()); test->TestHasAudioCodecsFromInitiateStaticAudioCodecs(elem.get()); } TEST_F(MediaSessionTest, GingleGoodInitiateNoAudioCodecs) { rtc::scoped_ptr elem; rtc::scoped_ptr test(GingleTest()); test->TestGoodIncomingInitiate( kGingleInitiateNoAudioCodecs, AudioCallOptions(), elem.use()); test->TestHasDefaultAudioCodecs(elem.get()); } TEST_F(MediaSessionTest, GingleBadInitiateNoSupportedAudioCodecs) { rtc::scoped_ptr test(GingleTest()); test->TestBadIncomingInitiate(kGingleInitiateNoSupportedAudioCodecs); } TEST_F(MediaSessionTest, GingleBadInitiateWrongClockrates) { rtc::scoped_ptr test(GingleTest()); test->TestBadIncomingInitiate(kGingleInitiateWrongClockrates); } TEST_F(MediaSessionTest, GingleBadInitiateWrongChannels) { rtc::scoped_ptr test(GingleTest()); test->TestBadIncomingInitiate(kGingleInitiateWrongChannels); } TEST_F(MediaSessionTest, GingleBadInitiateNoPayloadTypes) { rtc::scoped_ptr test(GingleTest()); test->TestBadIncomingInitiate(kGingleInitiateNoPayloadTypes); } TEST_F(MediaSessionTest, GingleBadInitiateDynamicWithoutNames) { rtc::scoped_ptr test(GingleTest()); test->TestBadIncomingInitiate(kGingleInitiateDynamicWithoutNames); } TEST_F(MediaSessionTest, GingleGoodOutgoingInitiate) { rtc::scoped_ptr test(GingleTest()); test->TestGoodOutgoingInitiate(AudioCallOptions()); } TEST_F(MediaSessionTest, GingleGoodOutgoingInitiateWithBandwidth) { rtc::scoped_ptr test(GingleTest()); cricket::CallOptions options = VideoCallOptions(); options.video_bandwidth = 42000; test->TestGoodOutgoingInitiate(options); } // Crypto related tests. // Offer has crypto but the session is not secured, just ignore it. TEST_F(MediaSessionTest, GingleInitiateWithCryptoIsIgnoredWhenNotSecured) { rtc::scoped_ptr elem; rtc::scoped_ptr test(GingleTest()); test->TestGoodIncomingInitiate( AddEncryption(kGingleInitiate, kGingleCryptoOffer), VideoCallOptions(), elem.use()); } // Offer has crypto required but the session is not secure, fail. TEST_F(MediaSessionTest, GingleInitiateWithCryptoRequiredWhenNotSecured) { rtc::scoped_ptr test(GingleTest()); test->TestBadIncomingInitiate(AddEncryption(kGingleInitiate, kGingleRequiredCryptoOffer)); } // Offer has no crypto but the session is secure required, fail. TEST_F(MediaSessionTest, GingleInitiateWithNoCryptoFailsWhenSecureRequired) { rtc::scoped_ptr test(GingleTest()); test->ExpectCrypto(cricket::SEC_REQUIRED); test->TestBadIncomingInitiate(kGingleInitiate); } // Offer has crypto and session is secure, expect crypto in the answer. TEST_F(MediaSessionTest, GingleInitiateWithCryptoWhenSecureEnabled) { rtc::scoped_ptr elem; rtc::scoped_ptr test(GingleTest()); test->ExpectCrypto(cricket::SEC_ENABLED); test->TestGoodIncomingInitiate( AddEncryption(kGingleInitiate, kGingleCryptoOffer), VideoCallOptions(), elem.use()); } // Offer has crypto and session is secure required, expect crypto in // the answer. TEST_F(MediaSessionTest, GingleInitiateWithCryptoWhenSecureRequired) { rtc::scoped_ptr elem; rtc::scoped_ptr test(GingleTest()); test->ExpectCrypto(cricket::SEC_REQUIRED); test->TestGoodIncomingInitiate( AddEncryption(kGingleInitiate, kGingleCryptoOffer), VideoCallOptions(), elem.use()); } // Offer has unsupported crypto and session is secure, no crypto in // the answer. TEST_F(MediaSessionTest, GingleInitiateWithUnsupportedCrypto) { rtc::scoped_ptr elem; rtc::scoped_ptr test(GingleTest()); test->MakeSignalingSecure(cricket::SEC_ENABLED); test->TestGoodIncomingInitiate( AddEncryption(kGingleInitiate, kGingleUnsupportedCryptoOffer), VideoCallOptions(), elem.use()); } // Offer has unsupported REQUIRED crypto and session is not secure, fail. TEST_F(MediaSessionTest, GingleInitiateWithRequiredUnsupportedCrypto) { rtc::scoped_ptr test(GingleTest()); test->TestBadIncomingInitiate( AddEncryption(kGingleInitiate, kGingleRequiredUnsupportedCryptoOffer)); } // Offer has unsupported REQUIRED crypto and session is secure, fail. TEST_F(MediaSessionTest, GingleInitiateWithRequiredUnsupportedCryptoWhenSecure) { rtc::scoped_ptr test(GingleTest()); test->MakeSignalingSecure(cricket::SEC_ENABLED); test->TestBadIncomingInitiate( AddEncryption(kGingleInitiate, kGingleRequiredUnsupportedCryptoOffer)); } // Offer has unsupported REQUIRED crypto and session is required secure, fail. TEST_F(MediaSessionTest, GingleInitiateWithRequiredUnsupportedCryptoWhenSecureRequired) { rtc::scoped_ptr test(GingleTest()); test->MakeSignalingSecure(cricket::SEC_REQUIRED); test->TestBadIncomingInitiate( AddEncryption(kGingleInitiate, kGingleRequiredUnsupportedCryptoOffer)); } TEST_F(MediaSessionTest, GingleGoodOutgoingInitiateWithCrypto) { rtc::scoped_ptr test(GingleTest()); test->ExpectCrypto(cricket::SEC_ENABLED); test->TestGoodOutgoingInitiate(AudioCallOptions()); } TEST_F(MediaSessionTest, GingleGoodOutgoingInitiateWithCryptoRequired) { rtc::scoped_ptr test(GingleTest()); test->ExpectCrypto(cricket::SEC_REQUIRED); test->TestGoodOutgoingInitiate(AudioCallOptions()); } TEST_F(MediaSessionTest, GingleIncomingAcceptWithSsrcs) { rtc::scoped_ptr test(GingleTest()); cricket::CallOptions options = VideoCallOptions(); options.is_muc = true; test->TestIncomingAcceptWithSsrcs(kGingleAcceptWithSsrcs, options); } TEST_F(MediaSessionTest, GingleGoodOutgoingInitiateWithRtpData) { rtc::scoped_ptr test(GingleTest()); cricket::CallOptions options; options.data_channel_type = cricket::DCT_RTP; test->ExpectCrypto(cricket::SEC_ENABLED); test->TestGoodOutgoingInitiate(options); }