/* * libjingle * Copyright 2004--2011, 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. */ //this file contains all the json helper methods #include "talk/app/webrtc_json.h" #include #include #include "talk/base/json.h" #include "talk/base/logging.h" #include "talk/session/phone/mediasessionclient.h" #include "talk/session/phone/codec.h" #include "json/json.h" namespace webrtc { static const int kIceComponent = 1; static const int kIceFoundation = 1; bool GetConnectionMediator(const Json::Value& value, std::string& connectionMediator) { if (value.type() != Json::objectValue && value.type() != Json::nullValue) { LOG(LS_WARNING) << "Failed to parse stun values" ; return false; } if (!GetStringFromJsonObject(value, "connectionmediator", &connectionMediator)) { LOG(LS_WARNING) << "Failed to parse JSON for value: " << value.toStyledString(); return false; } return true; } bool GetStunServer(const Json::Value& value, StunServiceDetails& stunServer) { if (value.type() != Json::objectValue && value.type() != Json::nullValue) { LOG(LS_WARNING) << "Failed to parse stun values" ; return false; } Json::Value stun; if (GetValueFromJsonObject(value, "stun_service", &stun)) { if (stun.type() == Json::objectValue) { if (!GetStringFromJsonObject(stun, "host", &stunServer.host) || !GetStringFromJsonObject(stun, "service", &stunServer.service) || !GetStringFromJsonObject(stun, "protocol", &stunServer.protocol)) { LOG(LS_WARNING) << "Failed to parse JSON value: " << value.toStyledString(); return false; } } else { return false; } } return true; } bool GetTurnServer(const Json::Value& value, std::string& turnServer) { if (value.type() != Json::objectValue && value.type() != Json::nullValue) { LOG(LS_WARNING) << "Failed to parse stun values" ; return false; } Json::Value turn; if (GetValueFromJsonObject(value, "turn_service", &turn)) { if (turn.type() == Json::objectValue) { if (!GetStringFromJsonObject(turn, "host", &turnServer)) { LOG(LS_WARNING) << "Failed to parse JSON value: " << value.toStyledString(); return false; } } else { return false; } } return true; } bool GetJSONSignalingMessage( const cricket::SessionDescription* sdp, const std::vector& candidates, std::string* signaling_message) { const cricket::ContentInfo* audio_content = GetFirstAudioContent(sdp); const cricket::ContentInfo* video_content = GetFirstVideoContent(sdp); std::vector media; if (audio_content) { Json::Value value; BuildMediaMessage(audio_content, candidates, false, value); media.push_back(value); } if (video_content) { Json::Value value; BuildMediaMessage(video_content, candidates, true, value); media.push_back(value); } Json::Value signal; Append(signal, "media", media); // now serialize *signaling_message = Serialize(signal); return true; } bool BuildMediaMessage( const cricket::ContentInfo* content_info, const std::vector& candidates, bool video, Json::Value& params) { if (!content_info) { return false; } if (video) { Append(params, "label", 2); //always video 2 } else { Append(params, "label", 1); //always audio 1 } std::vector rtpmap; if (!BuildRtpMapParams(content_info, video, rtpmap)) { return false; } Append(params, "rtpmap", rtpmap); Json::Value attributes; // Append(attributes, "ice-pwd", candidates.front().password()); // Append(attributes, "ice-ufrag", candidates.front().username()); std::vector jcandidates; if (!BuildAttributes(candidates, video, jcandidates)) { return false; } Append(attributes, "candidate", jcandidates); Append(params, "attributes", attributes); return true; } bool BuildRtpMapParams(const cricket::ContentInfo* content_info, bool video, std::vector& rtpmap) { if (!video) { const cricket::AudioContentDescription* audio_offer = static_cast( content_info->description); for (std::vector::const_iterator iter = audio_offer->codecs().begin(); iter != audio_offer->codecs().end(); ++iter) { Json::Value codec; std::string codec_str = std::string("audio/").append(iter->name); Append(codec, "codec", codec_str); Json::Value codec_id; Append(codec_id, talk_base::ToString(iter->id), codec); rtpmap.push_back(codec_id); } } else { const cricket::VideoContentDescription* video_offer = static_cast( content_info->description); for (std::vector::const_iterator iter = video_offer->codecs().begin(); iter != video_offer->codecs().end(); ++iter) { Json::Value codec; std::string codec_str = std::string("video/").append(iter->name); Append(codec, "codec", codec_str); Json::Value codec_id; Append(codec_id, talk_base::ToString(iter->id), codec); rtpmap.push_back(codec_id); } } return true; } bool BuildAttributes(const std::vector& candidates, bool video, std::vector& jcandidates) { for (std::vector::const_iterator iter = candidates.begin(); iter != candidates.end(); ++iter) { if ((video && !iter->name().compare("video_rtp") || (!video && !iter->name().compare("rtp")))) { Json::Value candidate; Append(candidate, "component", kIceComponent); Append(candidate, "foundation", kIceFoundation); Append(candidate, "generation", iter->generation()); Append(candidate, "proto", iter->protocol()); Append(candidate, "priority", iter->preference()); Append(candidate, "ip", iter->address().IPAsString()); Append(candidate, "port", iter->address().PortAsString()); Append(candidate, "type", iter->type()); Append(candidate, "name", iter->name()); Append(candidate, "network_name", iter->network_name()); Append(candidate, "username", iter->username()); Append(candidate, "password", iter->password()); jcandidates.push_back(candidate); } } return true; } std::string Serialize(const Json::Value& value) { Json::StyledWriter writer; return writer.write(value); } bool Deserialize(const std::string& message, Json::Value& value) { Json::Reader reader; return reader.parse(message, value); } bool ParseJSONSignalingMessage(const std::string& signaling_message, cricket::SessionDescription*& sdp, std::vector& candidates) { ASSERT(!sdp); // expect this to NULL // first deserialize message Json::Value value; if (!Deserialize(signaling_message, value)) { return false; } // get media objects std::vector mlines = ReadValues(value, "media"); if (mlines.empty()) { // no m-lines found return false; } sdp = new cricket::SessionDescription(); // get codec information for (size_t i = 0; i < mlines.size(); ++i) { if (mlines[i]["label"].asInt() == 1) { cricket::AudioContentDescription* audio_content = new cricket::AudioContentDescription(); ParseAudioCodec(mlines[i], audio_content); audio_content->SortCodecs(); sdp->AddContent(cricket::CN_AUDIO, cricket::NS_JINGLE_RTP, audio_content); ParseICECandidates(mlines[i], candidates); } else { cricket::VideoContentDescription* video_content = new cricket::VideoContentDescription(); ParseVideoCodec(mlines[i], video_content); video_content->SortCodecs(); sdp->AddContent(cricket::CN_VIDEO, cricket::NS_JINGLE_RTP, video_content); ParseICECandidates(mlines[i], candidates); } } return true; } bool ParseAudioCodec(Json::Value value, cricket::AudioContentDescription* content) { std::vector rtpmap(ReadValues(value, "rtpmap")); if (rtpmap.empty()) return false; for (size_t i = 0; i < rtpmap.size(); ++i) { cricket::AudioCodec codec; std::string pltype = rtpmap[i].begin().memberName(); talk_base::FromString(pltype, &codec.id); Json::Value codec_info = rtpmap[i][pltype]; std::vector tokens; talk_base::split(codec_info["codec"].asString(), '/', &tokens); codec.name = tokens[1]; content->AddCodec(codec); } return true; } bool ParseVideoCodec(Json::Value value, cricket::VideoContentDescription* content) { std::vector rtpmap(ReadValues(value, "rtpmap")); if (rtpmap.empty()) return false; for (size_t i = 0; i < rtpmap.size(); ++i) { cricket::VideoCodec codec; std::string pltype = rtpmap[i].begin().memberName(); talk_base::FromString(pltype, &codec.id); Json::Value codec_info = rtpmap[i][pltype]; std::vector tokens; talk_base::split(codec_info["codec"].asString(), '/', &tokens); codec.name = tokens[1]; content->AddCodec(codec); } return true; } bool ParseICECandidates(Json::Value& value, std::vector& candidates) { Json::Value attributes = ReadValue(value, "attributes"); std::string ice_pwd = ReadString(attributes, "ice-pwd"); std::string ice_ufrag = ReadString(attributes, "ice-ufrag"); std::vector jcandidates = ReadValues(attributes, "candidate"); char buffer[64]; for (size_t i = 0; i < jcandidates.size(); ++i) { cricket::Candidate cand; std::string str; str = ReadUInt(jcandidates[i], "generation"); cand.set_generation_str(str); str = ReadString(jcandidates[i], "proto"); cand.set_protocol(str); double priority = ReadDouble(jcandidates[i], "priority"); #ifdef _DEBUG double as_int = static_cast(priority); ASSERT(as_int == priority); #endif sprintf(buffer, "%i", static_cast(priority)); str = buffer; cand.set_preference_str(str); talk_base::SocketAddress addr; str = ReadString(jcandidates[i], "ip"); addr.SetIP(str); str = ReadString(jcandidates[i], "port"); int port; talk_base::FromString(str, &port); addr.SetPort(port); cand.set_address(addr); str = ReadString(jcandidates[i], "type"); cand.set_type(str); str = ReadString(jcandidates[i], "name"); cand.set_name(str); str = ReadString(jcandidates[i], "network_name"); cand.set_network_name(str); str = ReadString(jcandidates[i], "username"); cand.set_username(str); str = ReadString(jcandidates[i], "password"); cand.set_password(str); candidates.push_back(cand); } return true; } std::vector ReadValues( Json::Value& value, const std::string& key) { std::vector objects; for (size_t i = 0; i < value[key].size(); ++i) { objects.push_back(value[key][i]); } return objects; } Json::Value ReadValue(Json::Value& value, const std::string& key) { return value[key]; } std::string ReadString(Json::Value& value, const std::string& key) { return value[key].asString(); } uint32 ReadUInt(Json::Value& value, const std::string& key) { return value[key].asUInt(); } double ReadDouble(Json::Value& value, const std::string& key) { return value[key].asDouble(); } // Add values void Append(Json::Value& object, const std::string& key, bool value) { object[key] = Json::Value(value); } void Append(Json::Value& object, const std::string& key, char * value) { object[key] = Json::Value(value); } void Append(Json::Value& object, const std::string& key, double value) { object[key] = Json::Value(value); } void Append(Json::Value& object, const std::string& key, float value) { object[key] = Json::Value(value); } void Append(Json::Value& object, const std::string& key, int value) { object[key] = Json::Value(value); } void Append(Json::Value& object, const std::string& key, std::string value) { object[key] = Json::Value(value); } void Append(Json::Value& object, const std::string& key, uint32 value) { object[key] = Json::Value(value); } void Append(Json::Value& object, const std::string& key, Json::Value value) { object[key] = value; } void Append(Json::Value & object, const std::string & key, std::vector& values){ for (std::vector::const_iterator iter = values.begin(); iter != values.end(); ++iter) { object[key].append(*iter); } } } //namespace webrtc