Index: webrtc/libjingle/session/media/mediasessionclient.cc |
diff --git a/webrtc/libjingle/session/media/mediasessionclient.cc b/webrtc/libjingle/session/media/mediasessionclient.cc |
deleted file mode 100644 |
index 7cfcf155c8fbc7fa7d244276ef88e01350501d32..0000000000000000000000000000000000000000 |
--- a/webrtc/libjingle/session/media/mediasessionclient.cc |
+++ /dev/null |
@@ -1,1160 +0,0 @@ |
-/* |
- * 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 <string> |
- |
-#include "webrtc/libjingle/session/media/mediasessionclient.h" |
- |
-#include "talk/media/base/capturemanager.h" |
-#include "talk/media/base/cryptoparams.h" |
-#include "talk/media/sctp/sctpdataengine.h" |
-#include "talk/session/media/srtpfilter.h" |
-#include "webrtc/base/helpers.h" |
-#include "webrtc/base/logging.h" |
-#include "webrtc/base/stringencode.h" |
-#include "webrtc/base/stringutils.h" |
-#include "webrtc/libjingle/session/media/mediamessages.h" |
-#include "webrtc/libjingle/xmllite/qname.h" |
-#include "webrtc/libjingle/xmllite/xmlconstants.h" |
-#include "webrtc/libjingle/xmpp/constants.h" |
-#include "webrtc/p2p/base/constants.h" |
- |
-namespace cricket { |
- |
-#if !defined(DISABLE_MEDIA_ENGINE_FACTORY) |
-MediaSessionClient::MediaSessionClient( |
- const buzz::Jid& jid, SessionManager *manager) |
- : jid_(jid), |
- session_manager_(manager), |
- focus_call_(NULL), |
- channel_manager_(new ChannelManager(session_manager_->worker_thread())), |
- desc_factory_(channel_manager_, |
- session_manager_->transport_desc_factory()), |
- multisession_enabled_(false) { |
- Construct(); |
-} |
-#endif |
- |
-MediaSessionClient::MediaSessionClient( |
- const buzz::Jid& jid, SessionManager *manager, |
- MediaEngineInterface* media_engine, |
- DataEngineInterface* data_media_engine, |
- DeviceManagerInterface* device_manager) |
- : jid_(jid), |
- session_manager_(manager), |
- focus_call_(NULL), |
- channel_manager_(new ChannelManager( |
- media_engine, data_media_engine, |
- device_manager, new CaptureManager(), |
- session_manager_->worker_thread())), |
- desc_factory_(channel_manager_, |
- session_manager_->transport_desc_factory()), |
- multisession_enabled_(false) { |
- Construct(); |
-} |
- |
-void MediaSessionClient::Construct() { |
- // Register ourselves as the handler of audio and video sessions. |
- session_manager_->AddClient(NS_JINGLE_RTP, this); |
- // Forward device notifications. |
- SignalDevicesChange.repeat(channel_manager_->SignalDevicesChange); |
- // Bring up the channel manager. |
- // In previous versions of ChannelManager, this was done automatically |
- // in the constructor. |
- channel_manager_->Init(); |
-} |
- |
-MediaSessionClient::~MediaSessionClient() { |
- // Destroy all calls |
- std::map<uint32, Call *>::iterator it; |
- while (calls_.begin() != calls_.end()) { |
- std::map<uint32, Call *>::iterator it = calls_.begin(); |
- DestroyCall((*it).second); |
- } |
- |
- // Delete channel manager. This will wait for the channels to exit |
- delete channel_manager_; |
- |
- // Remove ourselves from the client map. |
- session_manager_->RemoveClient(NS_JINGLE_RTP); |
-} |
- |
-Call *MediaSessionClient::CreateCall() { |
- Call *call = new Call(this); |
- calls_[call->id()] = call; |
- SignalCallCreate(call); |
- return call; |
-} |
- |
-void MediaSessionClient::OnSessionCreate(Session *session, |
- bool received_initiate) { |
- if (received_initiate) { |
- session->SignalState.connect(this, &MediaSessionClient::OnSessionState); |
- } |
-} |
- |
-void MediaSessionClient::OnSessionState(BaseSession* base_session, |
- BaseSession::State state) { |
- // MediaSessionClient can only be used with a Session*, so it's |
- // safe to cast here. |
- Session* session = static_cast<Session*>(base_session); |
- |
- if (state == Session::STATE_RECEIVEDINITIATE) { |
- // The creation of the call must happen after the session has |
- // processed the initiate message because we need the |
- // remote_description to know what content names to use in the |
- // call. |
- |
- // If our accept would have no codecs, then we must reject this call. |
- const SessionDescription* offer = session->remote_description(); |
- const SessionDescription* accept = CreateAnswer(offer, CallOptions()); |
- const ContentInfo* audio_content = GetFirstAudioContent(accept); |
- bool audio_rejected = (!audio_content) ? true : audio_content->rejected; |
- const AudioContentDescription* audio_desc = (!audio_content) ? NULL : |
- static_cast<const AudioContentDescription*>(audio_content->description); |
- |
- // For some reason, we need a call even if we reject. So, either find a |
- // matching call or create a new one. |
- // The matching of existing calls is used to support the multi-session mode |
- // required for p2p handoffs: ie. once a MUC call is established, a new |
- // session may be established for the same call but is direct between the |
- // clients. To indicate that this is the case, the initiator of the incoming |
- // session is set to be the same as the remote name of the MUC for the |
- // existing session, thus the client can know that this is a new session for |
- // the existing call, rather than a whole new call. |
- Call* call = NULL; |
- if (multisession_enabled_) { |
- call = FindCallByRemoteName(session->initiator_name()); |
- } |
- |
- if (call == NULL) { |
- // Could not find a matching call, so create a new one. |
- call = CreateCall(); |
- } |
- |
- session_map_[session->id()] = call; |
- call->IncomingSession(session, offer); |
- |
- if (audio_rejected || !audio_desc || audio_desc->codecs().size() == 0) { |
- session->Reject(STR_TERMINATE_INCOMPATIBLE_PARAMETERS); |
- } |
- delete accept; |
- } |
-} |
- |
-void MediaSessionClient::DestroyCall(Call *call) { |
- // Change focus away, signal destruction |
- |
- if (call == focus_call_) |
- SetFocus(NULL); |
- SignalCallDestroy(call); |
- |
- // Remove it from calls_ map and delete |
- |
- std::map<uint32, Call *>::iterator it = calls_.find(call->id()); |
- if (it != calls_.end()) |
- calls_.erase(it); |
- |
- delete call; |
-} |
- |
-void MediaSessionClient::OnSessionDestroy(Session *session) { |
- // Find the call this session is in, remove it |
- SessionMap::iterator it = session_map_.find(session->id()); |
- ASSERT(it != session_map_.end()); |
- if (it != session_map_.end()) { |
- Call *call = (*it).second; |
- session_map_.erase(it); |
- call->RemoveSession(session); |
- } |
-} |
- |
-Call *MediaSessionClient::GetFocus() { |
- return focus_call_; |
-} |
- |
-void MediaSessionClient::SetFocus(Call *call) { |
- Call *old_focus_call = focus_call_; |
- if (focus_call_ != call) { |
- if (focus_call_ != NULL) |
- focus_call_->EnableChannels(false); |
- focus_call_ = call; |
- if (focus_call_ != NULL) |
- focus_call_->EnableChannels(true); |
- SignalFocus(focus_call_, old_focus_call); |
- } |
-} |
- |
-void MediaSessionClient::JoinCalls(Call *call_to_join, Call *call) { |
- // Move all sessions from call to call_to_join, delete call. |
- // If call_to_join has focus, added sessions should have enabled channels. |
- |
- if (focus_call_ == call) |
- SetFocus(NULL); |
- call_to_join->Join(call, focus_call_ == call_to_join); |
- DestroyCall(call); |
-} |
- |
-Session *MediaSessionClient::CreateSession(Call *call) { |
- std::string id; |
- return CreateSession(id, call); |
-} |
- |
-Session *MediaSessionClient::CreateSession(const std::string& id, Call* call) { |
- const std::string& type = NS_JINGLE_RTP; |
- Session *session = session_manager_->CreateSession(id, jid().Str(), type); |
- session_map_[session->id()] = call; |
- return session; |
-} |
- |
-Call *MediaSessionClient::FindCallByRemoteName(const std::string &remote_name) { |
- SessionMap::const_iterator call; |
- for (call = session_map_.begin(); call != session_map_.end(); ++call) { |
- std::vector<Session *> sessions = call->second->sessions(); |
- std::vector<Session *>::const_iterator session; |
- for (session = sessions.begin(); session != sessions.end(); ++session) { |
- if (remote_name == (*session)->remote_name()) { |
- return call->second; |
- } |
- } |
- } |
- |
- return NULL; |
-} |
- |
-// TODO(pthatcher): Move all of the parsing and writing functions into |
-// mediamessages.cc, with unit tests. |
-bool ParseGingleAudioCodec(const buzz::XmlElement* element, AudioCodec* out) { |
- int id = GetXmlAttr(element, QN_ID, -1); |
- if (id < 0) |
- return false; |
- |
- std::string name = GetXmlAttr(element, QN_NAME, buzz::STR_EMPTY); |
- int clockrate = GetXmlAttr(element, QN_CLOCKRATE, 0); |
- int bitrate = GetXmlAttr(element, QN_BITRATE, 0); |
- int channels = GetXmlAttr(element, QN_CHANNELS, 1); |
- *out = AudioCodec(id, name, clockrate, bitrate, channels, 0); |
- return true; |
-} |
- |
-bool ParseGingleVideoCodec(const buzz::XmlElement* element, VideoCodec* out) { |
- int id = GetXmlAttr(element, QN_ID, -1); |
- if (id < 0) |
- return false; |
- |
- std::string name = GetXmlAttr(element, QN_NAME, buzz::STR_EMPTY); |
- int width = GetXmlAttr(element, QN_WIDTH, 0); |
- int height = GetXmlAttr(element, QN_HEIGHT, 0); |
- int framerate = GetXmlAttr(element, QN_FRAMERATE, 0); |
- |
- *out = VideoCodec(id, name, width, height, framerate, 0); |
- return true; |
-} |
- |
-// Parses an ssrc string as a legacy stream. If it fails, returns |
-// false and fills an error message. |
-bool ParseSsrcAsLegacyStream(const std::string& ssrc_str, |
- std::vector<StreamParams>* streams, |
- ParseError* error) { |
- if (!ssrc_str.empty()) { |
- uint32 ssrc; |
- if (!rtc::FromString(ssrc_str, &ssrc)) { |
- return BadParse("Missing or invalid ssrc.", error); |
- } |
- |
- streams->push_back(StreamParams::CreateLegacy(ssrc)); |
- } |
- return true; |
-} |
- |
-void ParseGingleSsrc(const buzz::XmlElement* parent_elem, |
- const buzz::QName& name, |
- MediaContentDescription* media) { |
- const buzz::XmlElement* ssrc_elem = parent_elem->FirstNamed(name); |
- if (ssrc_elem) { |
- ParseError error; |
- ParseSsrcAsLegacyStream( |
- ssrc_elem->BodyText(), &(media->mutable_streams()), &error); |
- } |
-} |
- |
-bool ParseCryptoParams(const buzz::XmlElement* element, |
- CryptoParams* out, |
- ParseError* error) { |
- if (!element->HasAttr(QN_CRYPTO_SUITE)) { |
- return BadParse("crypto: crypto-suite attribute missing ", error); |
- } else if (!element->HasAttr(QN_CRYPTO_KEY_PARAMS)) { |
- return BadParse("crypto: key-params attribute missing ", error); |
- } else if (!element->HasAttr(QN_CRYPTO_TAG)) { |
- return BadParse("crypto: tag attribute missing ", error); |
- } |
- |
- const std::string& crypto_suite = element->Attr(QN_CRYPTO_SUITE); |
- const std::string& key_params = element->Attr(QN_CRYPTO_KEY_PARAMS); |
- const int tag = GetXmlAttr(element, QN_CRYPTO_TAG, 0); |
- const std::string& session_params = |
- element->Attr(QN_CRYPTO_SESSION_PARAMS); // Optional. |
- |
- *out = CryptoParams(tag, crypto_suite, key_params, session_params); |
- return true; |
-} |
- |
- |
-// Parse the first encryption element found with a matching 'usage' |
-// element. |
-// <usage/> is specific to Gingle. In Jingle, <crypto/> is already |
-// scoped to a content. |
-// Return false if there was an encryption element and it could not be |
-// parsed. |
-bool ParseGingleEncryption(const buzz::XmlElement* desc, |
- const buzz::QName& usage, |
- MediaContentDescription* media, |
- ParseError* error) { |
- for (const buzz::XmlElement* encryption = desc->FirstNamed(QN_ENCRYPTION); |
- encryption != NULL; |
- encryption = encryption->NextNamed(QN_ENCRYPTION)) { |
- if (encryption->FirstNamed(usage) != NULL) { |
- if (GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false)) { |
- media->set_crypto_required(CT_SDES); |
- } |
- for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO); |
- crypto != NULL; |
- crypto = crypto->NextNamed(QN_CRYPTO)) { |
- CryptoParams params; |
- if (!ParseCryptoParams(crypto, ¶ms, error)) { |
- return false; |
- } |
- media->AddCrypto(params); |
- } |
- break; |
- } |
- } |
- return true; |
-} |
- |
-void ParseBandwidth(const buzz::XmlElement* parent_elem, |
- MediaContentDescription* media) { |
- const buzz::XmlElement* bw_elem = GetXmlChild(parent_elem, LN_BANDWIDTH); |
- int bandwidth_kbps = -1; |
- if (bw_elem && rtc::FromString(bw_elem->BodyText(), &bandwidth_kbps)) { |
- if (bandwidth_kbps >= 0) { |
- media->set_bandwidth(bandwidth_kbps * 1000); |
- } |
- } |
-} |
- |
-bool ParseGingleAudioContent(const buzz::XmlElement* content_elem, |
- ContentDescription** content, |
- ParseError* error) { |
- AudioContentDescription* audio = new AudioContentDescription(); |
- |
- int preference = kMaxPayloadId; |
- if (content_elem->FirstElement()) { |
- for (const buzz::XmlElement* codec_elem = |
- content_elem->FirstNamed(QN_GINGLE_AUDIO_PAYLOADTYPE); |
- codec_elem != NULL; |
- codec_elem = codec_elem->NextNamed(QN_GINGLE_AUDIO_PAYLOADTYPE)) { |
- AudioCodec codec; |
- if (ParseGingleAudioCodec(codec_elem, &codec)) { |
- codec.preference = preference--; |
- audio->AddCodec(codec); |
- } |
- } |
- } else { |
- // For backward compatibility, we can assume the other client is |
- // an old version of Talk if it has no audio payload types at all. |
- audio->AddCodec(AudioCodec(103, "ISAC", 16000, -1, 1, 1)); |
- audio->AddCodec(AudioCodec(0, "PCMU", 8000, 64000, 1, 0)); |
- } |
- |
- ParseGingleSsrc(content_elem, QN_GINGLE_AUDIO_SRCID, audio); |
- |
- if (!ParseGingleEncryption(content_elem, QN_GINGLE_AUDIO_CRYPTO_USAGE, |
- audio, error)) { |
- return false; |
- } |
- |
- *content = audio; |
- return true; |
-} |
- |
-bool ParseGingleVideoContent(const buzz::XmlElement* content_elem, |
- ContentDescription** content, |
- ParseError* error) { |
- VideoContentDescription* video = new VideoContentDescription(); |
- |
- int preference = kMaxPayloadId; |
- for (const buzz::XmlElement* codec_elem = |
- content_elem->FirstNamed(QN_GINGLE_VIDEO_PAYLOADTYPE); |
- codec_elem != NULL; |
- codec_elem = codec_elem->NextNamed(QN_GINGLE_VIDEO_PAYLOADTYPE)) { |
- VideoCodec codec; |
- if (ParseGingleVideoCodec(codec_elem, &codec)) { |
- codec.preference = preference--; |
- video->AddCodec(codec); |
- } |
- } |
- |
- ParseGingleSsrc(content_elem, QN_GINGLE_VIDEO_SRCID, video); |
- ParseBandwidth(content_elem, video); |
- |
- if (!ParseGingleEncryption(content_elem, QN_GINGLE_VIDEO_CRYPTO_USAGE, |
- video, error)) { |
- return false; |
- } |
- |
- *content = video; |
- return true; |
-} |
- |
-void ParsePayloadTypeParameters(const buzz::XmlElement* element, |
- std::map<std::string, std::string>* paramap) { |
- for (const buzz::XmlElement* param = element->FirstNamed(QN_PARAMETER); |
- param != NULL; param = param->NextNamed(QN_PARAMETER)) { |
- std::string name = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_NAME, |
- buzz::STR_EMPTY); |
- std::string value = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_VALUE, |
- buzz::STR_EMPTY); |
- if (!name.empty() && !value.empty()) { |
- paramap->insert(make_pair(name, value)); |
- } |
- } |
-} |
- |
-void ParseFeedbackParams(const buzz::XmlElement* element, |
- FeedbackParams* params) { |
- for (const buzz::XmlElement* param = element->FirstNamed(QN_JINGLE_RTCP_FB); |
- param != NULL; param = param->NextNamed(QN_JINGLE_RTCP_FB)) { |
- std::string type = GetXmlAttr(param, QN_TYPE, buzz::STR_EMPTY); |
- std::string subtype = GetXmlAttr(param, QN_SUBTYPE, buzz::STR_EMPTY); |
- if (!type.empty()) { |
- params->Add(FeedbackParam(type, subtype)); |
- } |
- } |
-} |
- |
-void AddFeedbackParams(const FeedbackParams& additional_params, |
- FeedbackParams* params) { |
- for (size_t i = 0; i < additional_params.params().size(); ++i) { |
- params->Add(additional_params.params()[i]); |
- } |
-} |
- |
-int FindWithDefault(const std::map<std::string, std::string>& map, |
- const std::string& key, const int def) { |
- std::map<std::string, std::string>::const_iterator iter = map.find(key); |
- return (iter == map.end()) ? def : atoi(iter->second.c_str()); |
-} |
- |
- |
-// Parse the first encryption element found. |
-// Return false if there was an encryption element and it could not be |
-// parsed. |
-bool ParseJingleEncryption(const buzz::XmlElement* content_elem, |
- MediaContentDescription* media, |
- ParseError* error) { |
- const buzz::XmlElement* encryption = |
- content_elem->FirstNamed(QN_ENCRYPTION); |
- if (encryption == NULL) { |
- return true; |
- } |
- |
- if (GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false)) { |
- media->set_crypto_required(CT_SDES); |
- } |
- |
- for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO); |
- crypto != NULL; |
- crypto = crypto->NextNamed(QN_CRYPTO)) { |
- CryptoParams params; |
- if (!ParseCryptoParams(crypto, ¶ms, error)) { |
- return false; |
- } |
- media->AddCrypto(params); |
- } |
- return true; |
-} |
- |
-bool ParseJingleAudioCodec(const buzz::XmlElement* elem, AudioCodec* codec) { |
- int id = GetXmlAttr(elem, QN_ID, -1); |
- if (id < 0) |
- return false; |
- |
- std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY); |
- int clockrate = GetXmlAttr(elem, QN_CLOCKRATE, 0); |
- int channels = GetXmlAttr(elem, QN_CHANNELS, 1); |
- |
- std::map<std::string, std::string> paramap; |
- ParsePayloadTypeParameters(elem, ¶map); |
- int bitrate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_BITRATE, 0); |
- |
- *codec = AudioCodec(id, name, clockrate, bitrate, channels, 0); |
- ParseFeedbackParams(elem, &codec->feedback_params); |
- return true; |
-} |
- |
-bool ParseJingleVideoCodec(const buzz::XmlElement* elem, VideoCodec* codec) { |
- int id = GetXmlAttr(elem, QN_ID, -1); |
- if (id < 0) |
- return false; |
- |
- std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY); |
- |
- std::map<std::string, std::string> paramap; |
- ParsePayloadTypeParameters(elem, ¶map); |
- int width = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_WIDTH, 0); |
- int height = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_HEIGHT, 0); |
- int framerate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_FRAMERATE, 0); |
- |
- *codec = VideoCodec(id, name, width, height, framerate, 0); |
- codec->params = paramap; |
- ParseFeedbackParams(elem, &codec->feedback_params); |
- return true; |
-} |
- |
-bool ParseJingleDataCodec(const buzz::XmlElement* elem, DataCodec* codec) { |
- int id = GetXmlAttr(elem, QN_ID, -1); |
- if (id < 0) |
- return false; |
- |
- std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY); |
- |
- *codec = DataCodec(id, name, 0); |
- ParseFeedbackParams(elem, &codec->feedback_params); |
- return true; |
-} |
- |
-bool ParseJingleStreamsOrLegacySsrc(const buzz::XmlElement* desc_elem, |
- MediaContentDescription* media, |
- ParseError* error) { |
- if (HasJingleStreams(desc_elem)) { |
- if (!ParseJingleStreams(desc_elem, &(media->mutable_streams()), error)) { |
- return false; |
- } |
- } else { |
- const std::string ssrc_str = desc_elem->Attr(QN_SSRC); |
- if (!ParseSsrcAsLegacyStream( |
- ssrc_str, &(media->mutable_streams()), error)) { |
- return false; |
- } |
- } |
- return true; |
-} |
- |
-bool ParseJingleAudioContent(const buzz::XmlElement* content_elem, |
- ContentDescription** content, |
- ParseError* error) { |
- rtc::scoped_ptr<AudioContentDescription> audio( |
- new AudioContentDescription()); |
- |
- FeedbackParams content_feedback_params; |
- ParseFeedbackParams(content_elem, &content_feedback_params); |
- |
- int preference = kMaxPayloadId; |
- for (const buzz::XmlElement* payload_elem = |
- content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE); |
- payload_elem != NULL; |
- payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) { |
- AudioCodec codec; |
- if (ParseJingleAudioCodec(payload_elem, &codec)) { |
- AddFeedbackParams(content_feedback_params, &codec.feedback_params); |
- codec.preference = preference--; |
- audio->AddCodec(codec); |
- } |
- } |
- |
- if (!ParseJingleStreamsOrLegacySsrc(content_elem, audio.get(), error)) { |
- return false; |
- } |
- |
- if (!ParseJingleEncryption(content_elem, audio.get(), error)) { |
- return false; |
- } |
- |
- audio->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL); |
- |
- RtpHeaderExtensions hdrexts; |
- if (!ParseJingleRtpHeaderExtensions(content_elem, &hdrexts, error)) { |
- return false; |
- } |
- audio->set_rtp_header_extensions(hdrexts); |
- |
- *content = audio.release(); |
- return true; |
-} |
- |
-bool ParseJingleVideoContent(const buzz::XmlElement* content_elem, |
- ContentDescription** content, |
- ParseError* error) { |
- rtc::scoped_ptr<VideoContentDescription> video( |
- new VideoContentDescription()); |
- |
- FeedbackParams content_feedback_params; |
- ParseFeedbackParams(content_elem, &content_feedback_params); |
- |
- int preference = kMaxPayloadId; |
- for (const buzz::XmlElement* payload_elem = |
- content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE); |
- payload_elem != NULL; |
- payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) { |
- VideoCodec codec; |
- if (ParseJingleVideoCodec(payload_elem, &codec)) { |
- AddFeedbackParams(content_feedback_params, &codec.feedback_params); |
- codec.preference = preference--; |
- video->AddCodec(codec); |
- } |
- } |
- |
- if (!ParseJingleStreamsOrLegacySsrc(content_elem, video.get(), error)) { |
- return false; |
- } |
- ParseBandwidth(content_elem, video.get()); |
- |
- if (!ParseJingleEncryption(content_elem, video.get(), error)) { |
- return false; |
- } |
- |
- video->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL); |
- |
- RtpHeaderExtensions hdrexts; |
- if (!ParseJingleRtpHeaderExtensions(content_elem, &hdrexts, error)) { |
- return false; |
- } |
- video->set_rtp_header_extensions(hdrexts); |
- |
- *content = video.release(); |
- return true; |
-} |
- |
-bool ParseJingleSctpDataContent(const buzz::XmlElement* content_elem, |
- ContentDescription** content, |
- ParseError* error) { |
- rtc::scoped_ptr<DataContentDescription> data( |
- new DataContentDescription()); |
- data->set_protocol(kMediaProtocolSctp); |
- |
- for (const buzz::XmlElement* stream_elem = |
- content_elem->FirstNamed(QN_JINGLE_DRAFT_SCTP_STREAM); |
- stream_elem != NULL; |
- stream_elem = stream_elem->NextNamed(QN_JINGLE_DRAFT_SCTP_STREAM)) { |
- StreamParams stream; |
- stream.groupid = stream_elem->Attr(QN_NICK); |
- stream.id = stream_elem->Attr(QN_NAME); |
- uint32 sid; |
- if (!rtc::FromString(stream_elem->Attr(QN_SID), &sid)) { |
- return BadParse("Missing or invalid sid.", error); |
- } |
- if (sid > kMaxSctpSid) { |
- return BadParse("SID is greater than max value.", error); |
- } |
- |
- stream.ssrcs.push_back(sid); |
- data->mutable_streams().push_back(stream); |
- } |
- |
- *content = data.release(); |
- return true; |
-} |
- |
-bool ParseJingleRtpDataContent(const buzz::XmlElement* content_elem, |
- ContentDescription** content, |
- ParseError* error) { |
- DataContentDescription* data = new DataContentDescription(); |
- |
- FeedbackParams content_feedback_params; |
- ParseFeedbackParams(content_elem, &content_feedback_params); |
- |
- int preference = kMaxPayloadId; |
- for (const buzz::XmlElement* payload_elem = |
- content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE); |
- payload_elem != NULL; |
- payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) { |
- DataCodec codec; |
- if (ParseJingleDataCodec(payload_elem, &codec)) { |
- AddFeedbackParams(content_feedback_params, &codec.feedback_params); |
- codec.preference = preference--; |
- data->AddCodec(codec); |
- } |
- } |
- |
- if (!ParseJingleStreamsOrLegacySsrc(content_elem, data, error)) { |
- return false; |
- } |
- ParseBandwidth(content_elem, data); |
- |
- if (!ParseJingleEncryption(content_elem, data, error)) { |
- return false; |
- } |
- |
- data->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL); |
- |
- *content = data; |
- return true; |
-} |
- |
-bool MediaSessionClient::ParseContent(SignalingProtocol protocol, |
- const buzz::XmlElement* content_elem, |
- ContentDescription** content, |
- ParseError* error) { |
- if (protocol == PROTOCOL_GINGLE) { |
- const std::string& content_type = content_elem->Name().Namespace(); |
- if (NS_GINGLE_AUDIO == content_type) { |
- return ParseGingleAudioContent(content_elem, content, error); |
- } else if (NS_GINGLE_VIDEO == content_type) { |
- return ParseGingleVideoContent(content_elem, content, error); |
- } else { |
- return BadParse("Unknown content type: " + content_type, error); |
- } |
- } else { |
- const std::string& content_type = content_elem->Name().Namespace(); |
- // We use the XMLNS of the <description> element to determine if |
- // it's RTP or SCTP. |
- if (content_type == NS_JINGLE_DRAFT_SCTP) { |
- return ParseJingleSctpDataContent(content_elem, content, error); |
- } |
- |
- std::string media; |
- if (!RequireXmlAttr(content_elem, QN_JINGLE_CONTENT_MEDIA, &media, error)) |
- return false; |
- |
- if (media == JINGLE_CONTENT_MEDIA_AUDIO) { |
- return ParseJingleAudioContent(content_elem, content, error); |
- } else if (media == JINGLE_CONTENT_MEDIA_VIDEO) { |
- return ParseJingleVideoContent(content_elem, content, error); |
- } else if (media == JINGLE_CONTENT_MEDIA_DATA) { |
- return ParseJingleRtpDataContent(content_elem, content, error); |
- } else { |
- return BadParse("Unknown media: " + media, error); |
- } |
- } |
-} |
- |
-buzz::XmlElement* CreateGingleAudioCodecElem(const AudioCodec& codec) { |
- buzz::XmlElement* payload_type = |
- new buzz::XmlElement(QN_GINGLE_AUDIO_PAYLOADTYPE, true); |
- AddXmlAttr(payload_type, QN_ID, codec.id); |
- payload_type->AddAttr(QN_NAME, codec.name); |
- if (codec.clockrate > 0) |
- AddXmlAttr(payload_type, QN_CLOCKRATE, codec.clockrate); |
- if (codec.bitrate > 0) |
- AddXmlAttr(payload_type, QN_BITRATE, codec.bitrate); |
- if (codec.channels > 1) |
- AddXmlAttr(payload_type, QN_CHANNELS, codec.channels); |
- return payload_type; |
-} |
- |
-buzz::XmlElement* CreateGingleVideoCodecElem(const VideoCodec& codec) { |
- buzz::XmlElement* payload_type = |
- new buzz::XmlElement(QN_GINGLE_VIDEO_PAYLOADTYPE, true); |
- AddXmlAttr(payload_type, QN_ID, codec.id); |
- payload_type->AddAttr(QN_NAME, codec.name); |
- AddXmlAttr(payload_type, QN_WIDTH, codec.width); |
- AddXmlAttr(payload_type, QN_HEIGHT, codec.height); |
- AddXmlAttr(payload_type, QN_FRAMERATE, codec.framerate); |
- return payload_type; |
-} |
- |
-buzz::XmlElement* CreateGingleSsrcElem(const buzz::QName& name, uint32 ssrc) { |
- buzz::XmlElement* elem = new buzz::XmlElement(name, true); |
- if (ssrc) { |
- SetXmlBody(elem, ssrc); |
- } |
- return elem; |
-} |
- |
-buzz::XmlElement* CreateBandwidthElem(const buzz::QName& name, int bps) { |
- int kbps = bps / 1000; |
- buzz::XmlElement* elem = new buzz::XmlElement(name); |
- elem->AddAttr(buzz::QN_TYPE, "AS"); |
- SetXmlBody(elem, kbps); |
- return elem; |
-} |
- |
-// For Jingle, usage_qname is empty. |
-buzz::XmlElement* CreateJingleEncryptionElem(const CryptoParamsVec& cryptos, |
- bool required) { |
- buzz::XmlElement* encryption_elem = new buzz::XmlElement(QN_ENCRYPTION); |
- |
- if (required) { |
- encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true"); |
- } |
- |
- for (CryptoParamsVec::const_iterator i = cryptos.begin(); |
- i != cryptos.end(); |
- ++i) { |
- buzz::XmlElement* crypto_elem = new buzz::XmlElement(QN_CRYPTO); |
- |
- AddXmlAttr(crypto_elem, QN_CRYPTO_TAG, i->tag); |
- crypto_elem->AddAttr(QN_CRYPTO_SUITE, i->cipher_suite); |
- crypto_elem->AddAttr(QN_CRYPTO_KEY_PARAMS, i->key_params); |
- if (!i->session_params.empty()) { |
- crypto_elem->AddAttr(QN_CRYPTO_SESSION_PARAMS, i->session_params); |
- } |
- encryption_elem->AddElement(crypto_elem); |
- } |
- return encryption_elem; |
-} |
- |
-buzz::XmlElement* CreateGingleEncryptionElem(const CryptoParamsVec& cryptos, |
- const buzz::QName& usage_qname, |
- bool required) { |
- buzz::XmlElement* encryption_elem = |
- CreateJingleEncryptionElem(cryptos, required); |
- |
- if (required) { |
- encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true"); |
- } |
- |
- buzz::XmlElement* usage_elem = new buzz::XmlElement(usage_qname); |
- encryption_elem->AddElement(usage_elem); |
- |
- return encryption_elem; |
-} |
- |
-buzz::XmlElement* CreateGingleAudioContentElem( |
- const AudioContentDescription* audio, |
- bool crypto_required) { |
- buzz::XmlElement* elem = |
- new buzz::XmlElement(QN_GINGLE_AUDIO_CONTENT, true); |
- |
- for (AudioCodecs::const_iterator codec = audio->codecs().begin(); |
- codec != audio->codecs().end(); ++codec) { |
- elem->AddElement(CreateGingleAudioCodecElem(*codec)); |
- } |
- if (audio->has_ssrcs()) { |
- elem->AddElement(CreateGingleSsrcElem( |
- QN_GINGLE_AUDIO_SRCID, audio->first_ssrc())); |
- } |
- |
- const CryptoParamsVec& cryptos = audio->cryptos(); |
- if (!cryptos.empty()) { |
- elem->AddElement(CreateGingleEncryptionElem(cryptos, |
- QN_GINGLE_AUDIO_CRYPTO_USAGE, |
- crypto_required)); |
- } |
- return elem; |
-} |
- |
-buzz::XmlElement* CreateGingleVideoContentElem( |
- const VideoContentDescription* video, |
- bool crypto_required) { |
- buzz::XmlElement* elem = |
- new buzz::XmlElement(QN_GINGLE_VIDEO_CONTENT, true); |
- |
- for (VideoCodecs::const_iterator codec = video->codecs().begin(); |
- codec != video->codecs().end(); ++codec) { |
- elem->AddElement(CreateGingleVideoCodecElem(*codec)); |
- } |
- if (video->has_ssrcs()) { |
- elem->AddElement(CreateGingleSsrcElem( |
- QN_GINGLE_VIDEO_SRCID, video->first_ssrc())); |
- } |
- if (video->bandwidth() != kAutoBandwidth) { |
- elem->AddElement(CreateBandwidthElem(QN_GINGLE_VIDEO_BANDWIDTH, |
- video->bandwidth())); |
- } |
- |
- const CryptoParamsVec& cryptos = video->cryptos(); |
- if (!cryptos.empty()) { |
- elem->AddElement(CreateGingleEncryptionElem(cryptos, |
- QN_GINGLE_VIDEO_CRYPTO_USAGE, |
- crypto_required)); |
- } |
- |
- return elem; |
-} |
- |
-template <class T> |
-buzz::XmlElement* CreatePayloadTypeParameterElem( |
- const std::string& name, T value) { |
- buzz::XmlElement* elem = new buzz::XmlElement(QN_PARAMETER); |
- |
- elem->AddAttr(QN_PAYLOADTYPE_PARAMETER_NAME, name); |
- AddXmlAttr(elem, QN_PAYLOADTYPE_PARAMETER_VALUE, value); |
- |
- return elem; |
-} |
- |
-void AddRtcpFeedbackElem(buzz::XmlElement* elem, |
- const FeedbackParams& feedback_params) { |
- std::vector<FeedbackParam>::const_iterator it; |
- for (it = feedback_params.params().begin(); |
- it != feedback_params.params().end(); ++it) { |
- buzz::XmlElement* fb_elem = new buzz::XmlElement(QN_JINGLE_RTCP_FB); |
- fb_elem->AddAttr(QN_TYPE, it->id()); |
- fb_elem->AddAttr(QN_SUBTYPE, it->param()); |
- elem->AddElement(fb_elem); |
- } |
-} |
- |
-buzz::XmlElement* CreateJingleAudioCodecElem(const AudioCodec& codec) { |
- buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE); |
- |
- AddXmlAttr(elem, QN_ID, codec.id); |
- elem->AddAttr(QN_NAME, codec.name); |
- if (codec.clockrate > 0) { |
- AddXmlAttr(elem, QN_CLOCKRATE, codec.clockrate); |
- } |
- if (codec.bitrate > 0) { |
- elem->AddElement(CreatePayloadTypeParameterElem( |
- PAYLOADTYPE_PARAMETER_BITRATE, codec.bitrate)); |
- } |
- if (codec.channels > 1) { |
- AddXmlAttr(elem, QN_CHANNELS, codec.channels); |
- } |
- |
- AddRtcpFeedbackElem(elem, codec.feedback_params); |
- |
- return elem; |
-} |
- |
-buzz::XmlElement* CreateJingleVideoCodecElem(const VideoCodec& codec) { |
- buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE); |
- |
- AddXmlAttr(elem, QN_ID, codec.id); |
- elem->AddAttr(QN_NAME, codec.name); |
- elem->AddElement(CreatePayloadTypeParameterElem( |
- PAYLOADTYPE_PARAMETER_WIDTH, codec.width)); |
- elem->AddElement(CreatePayloadTypeParameterElem( |
- PAYLOADTYPE_PARAMETER_HEIGHT, codec.height)); |
- elem->AddElement(CreatePayloadTypeParameterElem( |
- PAYLOADTYPE_PARAMETER_FRAMERATE, codec.framerate)); |
- |
- AddRtcpFeedbackElem(elem, codec.feedback_params); |
- |
- CodecParameterMap::const_iterator param_iter; |
- for (param_iter = codec.params.begin(); param_iter != codec.params.end(); |
- ++param_iter) { |
- elem->AddElement(CreatePayloadTypeParameterElem(param_iter->first, |
- param_iter->second)); |
- } |
- |
- return elem; |
-} |
- |
-buzz::XmlElement* CreateJingleDataCodecElem(const DataCodec& codec) { |
- buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE); |
- |
- AddXmlAttr(elem, QN_ID, codec.id); |
- elem->AddAttr(QN_NAME, codec.name); |
- |
- AddRtcpFeedbackElem(elem, codec.feedback_params); |
- |
- return elem; |
-} |
- |
-void WriteLegacyJingleSsrc(const MediaContentDescription* media, |
- buzz::XmlElement* elem) { |
- if (media->has_ssrcs()) { |
- AddXmlAttr(elem, QN_SSRC, media->first_ssrc()); |
- } |
-} |
- |
-void WriteJingleStreamsOrLegacySsrc(const MediaContentDescription* media, |
- buzz::XmlElement* desc_elem) { |
- if (!media->multistream()) { |
- WriteLegacyJingleSsrc(media, desc_elem); |
- } else { |
- WriteJingleStreams(media->streams(), desc_elem); |
- } |
-} |
- |
-buzz::XmlElement* CreateJingleAudioContentElem( |
- const AudioContentDescription* audio, bool crypto_required) { |
- buzz::XmlElement* elem = |
- new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true); |
- |
- elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_AUDIO); |
- WriteJingleStreamsOrLegacySsrc(audio, elem); |
- |
- for (AudioCodecs::const_iterator codec = audio->codecs().begin(); |
- codec != audio->codecs().end(); ++codec) { |
- elem->AddElement(CreateJingleAudioCodecElem(*codec)); |
- } |
- |
- const CryptoParamsVec& cryptos = audio->cryptos(); |
- if (!cryptos.empty()) { |
- elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required)); |
- } |
- |
- if (audio->rtcp_mux()) { |
- elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX)); |
- } |
- |
- WriteJingleRtpHeaderExtensions(audio->rtp_header_extensions(), elem); |
- |
- return elem; |
-} |
- |
-buzz::XmlElement* CreateJingleVideoContentElem( |
- const VideoContentDescription* video, bool crypto_required) { |
- buzz::XmlElement* elem = |
- new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true); |
- |
- elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_VIDEO); |
- WriteJingleStreamsOrLegacySsrc(video, elem); |
- |
- for (VideoCodecs::const_iterator codec = video->codecs().begin(); |
- codec != video->codecs().end(); ++codec) { |
- elem->AddElement(CreateJingleVideoCodecElem(*codec)); |
- } |
- |
- const CryptoParamsVec& cryptos = video->cryptos(); |
- if (!cryptos.empty()) { |
- elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required)); |
- } |
- |
- if (video->rtcp_mux()) { |
- elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX)); |
- } |
- |
- if (video->bandwidth() != kAutoBandwidth) { |
- elem->AddElement(CreateBandwidthElem(QN_JINGLE_RTP_BANDWIDTH, |
- video->bandwidth())); |
- } |
- |
- WriteJingleRtpHeaderExtensions(video->rtp_header_extensions(), elem); |
- |
- return elem; |
-} |
- |
-buzz::XmlElement* CreateJingleSctpDataContentElem( |
- const DataContentDescription* data) { |
- buzz::XmlElement* content_elem = |
- new buzz::XmlElement(QN_JINGLE_DRAFT_SCTP_CONTENT, true); |
- for (std::vector<StreamParams>::const_iterator |
- stream = data->streams().begin(); |
- stream != data->streams().end(); ++stream) { |
- buzz::XmlElement* stream_elem = |
- new buzz::XmlElement(QN_JINGLE_DRAFT_SCTP_STREAM, false); |
- AddXmlAttrIfNonEmpty(stream_elem, QN_NICK, stream->groupid); |
- AddXmlAttrIfNonEmpty(stream_elem, QN_NAME, stream->id); |
- if (!stream->ssrcs.empty()) { |
- AddXmlAttr(stream_elem, QN_SID, stream->ssrcs[0]); |
- } |
- content_elem->AddElement(stream_elem); |
- } |
- return content_elem;; |
-} |
- |
-buzz::XmlElement* CreateJingleRtpDataContentElem( |
- const DataContentDescription* data, bool crypto_required) { |
- |
- buzz::XmlElement* elem = |
- new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true); |
- |
- elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_DATA); |
- WriteJingleStreamsOrLegacySsrc(data, elem); |
- |
- for (DataCodecs::const_iterator codec = data->codecs().begin(); |
- codec != data->codecs().end(); ++codec) { |
- elem->AddElement(CreateJingleDataCodecElem(*codec)); |
- } |
- |
- const CryptoParamsVec& cryptos = data->cryptos(); |
- if (!cryptos.empty()) { |
- elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required)); |
- } |
- |
- if (data->rtcp_mux()) { |
- elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX)); |
- } |
- |
- if (data->bandwidth() != kAutoBandwidth) { |
- elem->AddElement(CreateBandwidthElem(QN_JINGLE_RTP_BANDWIDTH, |
- data->bandwidth())); |
- } |
- |
- return elem; |
-} |
- |
-bool IsSctp(const DataContentDescription* data) { |
- return (data->protocol() == kMediaProtocolSctp || |
- data->protocol() == kMediaProtocolDtlsSctp); |
-} |
- |
-buzz::XmlElement* CreateJingleDataContentElem( |
- const DataContentDescription* data, bool crypto_required) { |
- if (IsSctp(data)) { |
- return CreateJingleSctpDataContentElem(data); |
- } else { |
- return CreateJingleRtpDataContentElem(data, crypto_required); |
- } |
-} |
- |
-bool MediaSessionClient::IsWritable(SignalingProtocol protocol, |
- const ContentDescription* content) { |
- const MediaContentDescription* media = |
- static_cast<const MediaContentDescription*>(content); |
- if (protocol == PROTOCOL_GINGLE && |
- media->type() == MEDIA_TYPE_DATA) { |
- return false; |
- } |
- return true; |
-} |
- |
-bool MediaSessionClient::WriteContent(SignalingProtocol protocol, |
- const ContentDescription* content, |
- buzz::XmlElement** elem, |
- WriteError* error) { |
- const MediaContentDescription* media = |
- static_cast<const MediaContentDescription*>(content); |
- bool crypto_required = secure() == SEC_REQUIRED; |
- |
- if (media->type() == MEDIA_TYPE_AUDIO) { |
- const AudioContentDescription* audio = |
- static_cast<const AudioContentDescription*>(media); |
- if (protocol == PROTOCOL_GINGLE) { |
- *elem = CreateGingleAudioContentElem(audio, crypto_required); |
- } else { |
- *elem = CreateJingleAudioContentElem(audio, crypto_required); |
- } |
- } else if (media->type() == MEDIA_TYPE_VIDEO) { |
- const VideoContentDescription* video = |
- static_cast<const VideoContentDescription*>(media); |
- if (protocol == PROTOCOL_GINGLE) { |
- *elem = CreateGingleVideoContentElem(video, crypto_required); |
- } else { |
- *elem = CreateJingleVideoContentElem(video, crypto_required); |
- } |
- } else if (media->type() == MEDIA_TYPE_DATA) { |
- const DataContentDescription* data = |
- static_cast<const DataContentDescription*>(media); |
- if (protocol == PROTOCOL_GINGLE) { |
- return BadWrite("Data channel not supported with Gingle.", error); |
- } else { |
- *elem = CreateJingleDataContentElem(data, crypto_required); |
- } |
- } else { |
- return BadWrite("Unknown content type: " + |
- rtc::ToString<int>(media->type()), error); |
- } |
- |
- return true; |
-} |
- |
-} // namespace cricket |