From c93e36346bbce9f9d4716936d78aafc09c67a8e6 Mon Sep 17 00:00:00 2001 From: "wu@webrtc.org" Date: Fri, 30 Sep 2011 18:08:51 +0000 Subject: [PATCH] * Add Deserize for PeerConnectionMessage BUG= TEST= Review URL: http://webrtc-codereview.appspot.com/189001 git-svn-id: http://webrtc.googlecode.com/svn/trunk@671 4adac7df-926f-26a2-2b94-8c16560cd09d --- .../app/webrtc_dev/peerconnectionmessage.cc | 27 ++- .../app/webrtc_dev/peerconnectionmessage.h | 14 +- .../peerconnectionmessage_unittest.cc | 91 +++++++- .../source/talk/app/webrtc_dev/webrtcjson.cc | 211 ++++++++++++------ .../source/talk/app/webrtc_dev/webrtcjson.h | 12 +- 5 files changed, 259 insertions(+), 96 deletions(-) diff --git a/third_party_mods/libjingle/source/talk/app/webrtc_dev/peerconnectionmessage.cc b/third_party_mods/libjingle/source/talk/app/webrtc_dev/peerconnectionmessage.cc index f7680a7b2..2af08c6fc 100644 --- a/third_party_mods/libjingle/source/talk/app/webrtc_dev/peerconnectionmessage.cc +++ b/third_party_mods/libjingle/source/talk/app/webrtc_dev/peerconnectionmessage.cc @@ -36,14 +36,19 @@ namespace webrtc { scoped_refptr PeerConnectionMessage::Create( PeerConnectionMessageType type, - const cricket::SessionDescription* desc, + cricket::SessionDescription* desc, const std::vector& candidates) { return new RefCountImpl (type, desc, candidates); } scoped_refptr PeerConnectionMessage::Create( - std::string message) { - return new RefCountImpl (message); + const std::string& message) { + scoped_refptrpc_message(new + RefCountImpl ()); + if (pc_message->Deserialize(message)) + return pc_message; + else + return NULL; } scoped_refptr PeerConnectionMessage::CreateErrorMessage( @@ -53,7 +58,7 @@ scoped_refptr PeerConnectionMessage::CreateErrorMessage( PeerConnectionMessage::PeerConnectionMessage( PeerConnectionMessageType type, - const cricket::SessionDescription* desc, + cricket::SessionDescription* desc, const std::vector& candidates) : type_(type), error_code_(kNoError), @@ -61,8 +66,10 @@ PeerConnectionMessage::PeerConnectionMessage( candidates_(candidates) { } -PeerConnectionMessage::PeerConnectionMessage(std::string message) { - // TODO(ronghuawu): implement +PeerConnectionMessage::PeerConnectionMessage() + : type_(kOffer), + error_code_(kNoError), + desc_(new cricket::SessionDescription()) { } PeerConnectionMessage::PeerConnectionMessage(ErrorCode error) @@ -72,7 +79,13 @@ PeerConnectionMessage::PeerConnectionMessage(ErrorCode error) } bool PeerConnectionMessage::Serialize(std::string* message) { - return JsonSerialize(desc_.get(), candidates_, message); + return JsonSerialize(type_, error_code_, + desc_.get(), candidates_, message); +} + +bool PeerConnectionMessage::Deserialize(std::string message) { + return JsonDeserialize(&type_, &error_code_, desc_.get(), + &candidates_, message); } } // namespace webrtc diff --git a/third_party_mods/libjingle/source/talk/app/webrtc_dev/peerconnectionmessage.h b/third_party_mods/libjingle/source/talk/app/webrtc_dev/peerconnectionmessage.h index a4872308f..e46f77274 100644 --- a/third_party_mods/libjingle/source/talk/app/webrtc_dev/peerconnectionmessage.h +++ b/third_party_mods/libjingle/source/talk/app/webrtc_dev/peerconnectionmessage.h @@ -65,10 +65,11 @@ class PeerConnectionMessage : public RefCount { static scoped_refptr Create( PeerConnectionMessageType type, - const cricket::SessionDescription* desc, + cricket::SessionDescription* desc, const std::vector& candidates); - static scoped_refptr Create(std::string message); + static scoped_refptr Create( + const std::string& message); static scoped_refptr CreateErrorMessage( ErrorCode error); @@ -81,16 +82,19 @@ class PeerConnectionMessage : public RefCount { protected: PeerConnectionMessage(PeerConnectionMessageType type, - const cricket::SessionDescription* desc, + cricket::SessionDescription* desc, const std::vector& candidates); - explicit PeerConnectionMessage(std::string message); + PeerConnectionMessage(); explicit PeerConnectionMessage(ErrorCode error); + bool Deserialize(std::string message); + private: PeerConnectionMessageType type_; ErrorCode error_code_; - talk_base::scoped_ptr desc_; + talk_base::scoped_ptr desc_; std::vector candidates_; + }; } // namespace webrtc diff --git a/third_party_mods/libjingle/source/talk/app/webrtc_dev/peerconnectionmessage_unittest.cc b/third_party_mods/libjingle/source/talk/app/webrtc_dev/peerconnectionmessage_unittest.cc index a223a60d8..f55566d9f 100644 --- a/third_party_mods/libjingle/source/talk/app/webrtc_dev/peerconnectionmessage_unittest.cc +++ b/third_party_mods/libjingle/source/talk/app/webrtc_dev/peerconnectionmessage_unittest.cc @@ -80,18 +80,91 @@ class PeerConnectionMessageTest: public testing::Test { }; TEST_F(PeerConnectionMessageTest, Serialize) { - talk_base::scoped_ptr offer( - session_description_factory_->CreateOffer(options_)); - std::vector candidates; // TODO(ronghuawu): Populate the test candidates. std::string message; - scoped_refptr offer_message = - PeerConnectionMessage::Create(PeerConnectionMessage::kOffer, - offer.release(), - candidates); - EXPECT_TRUE(offer_message->Serialize(&message)); - LOG(LS_ERROR) << message; + scoped_refptr pc_message; + + // Offer + talk_base::scoped_ptr offer( + session_description_factory_->CreateOffer(options_)); + pc_message = PeerConnectionMessage::Create(PeerConnectionMessage::kOffer, + offer.get(), candidates); + EXPECT_TRUE(pc_message->Serialize(&message)); + pc_message.release(); + LOG(LS_INFO) << message; + + // Answer + talk_base::scoped_ptr answer( + session_description_factory_->CreateAnswer(offer.get(), options_)); + pc_message = PeerConnectionMessage::Create(PeerConnectionMessage::kAnswer, + answer.get(), candidates); + EXPECT_TRUE(pc_message->Serialize(&message)); + pc_message.release(); + LOG(LS_INFO) << message; + + // Error + pc_message = PeerConnectionMessage::CreateErrorMessage( + PeerConnectionMessage::kParseError); + EXPECT_TRUE(pc_message->Serialize(&message)); + pc_message.release(); + LOG(LS_INFO) << message; + // TODO(ronghuawu): Verify the serialized message. } + +TEST_F(PeerConnectionMessageTest, Deserialize) { + std::vector candidates; + // TODO(ronghuawu): Populate the test candidates. + + std::string message_ref; + std::string message_result; + scoped_refptr pc_message; + + // Offer + talk_base::scoped_ptr offer( + session_description_factory_->CreateOffer(options_)); + pc_message = PeerConnectionMessage::Create(PeerConnectionMessage::kOffer, + offer.get(), candidates); + EXPECT_TRUE(pc_message->Serialize(&message_ref)); + pc_message.release(); + LOG(LS_INFO) << "The reference message: " << message_ref; + + // Deserialize Offer + pc_message = PeerConnectionMessage::Create(message_ref); + EXPECT_TRUE(pc_message->Serialize(&message_result)); + pc_message.release(); + LOG(LS_INFO) << "The result message: " << message_result; + EXPECT_EQ(message_ref, message_result); + + // Answer + talk_base::scoped_ptr answer( + session_description_factory_->CreateAnswer(offer.get(), options_)); + pc_message = PeerConnectionMessage::Create(PeerConnectionMessage::kAnswer, + answer.get(), candidates); + EXPECT_TRUE(pc_message->Serialize(&message_ref)); + pc_message.release(); + LOG(LS_INFO) << "The reference message: " << message_ref; + + // Deserialize Answer + pc_message = PeerConnectionMessage::Create(message_ref); + EXPECT_TRUE(pc_message->Serialize(&message_result)); + pc_message.release(); + LOG(LS_INFO) << "The result message: " << message_result; + EXPECT_EQ(message_ref, message_result); + + // Error + pc_message = PeerConnectionMessage::CreateErrorMessage( + PeerConnectionMessage::kParseError); + EXPECT_TRUE(pc_message->Serialize(&message_ref)); + pc_message.release(); + LOG(LS_INFO) << "The reference message: " << message_ref; + + // Deserialize Error + pc_message = PeerConnectionMessage::Create(message_ref); + EXPECT_TRUE(pc_message->Serialize(&message_result)); + pc_message.release(); + LOG(LS_INFO) << "The result message: " << message_result; + EXPECT_EQ(message_ref, message_result); +} diff --git a/third_party_mods/libjingle/source/talk/app/webrtc_dev/webrtcjson.cc b/third_party_mods/libjingle/source/talk/app/webrtc_dev/webrtcjson.cc index 73984095b..a4184b323 100644 --- a/third_party_mods/libjingle/source/talk/app/webrtc_dev/webrtcjson.cc +++ b/third_party_mods/libjingle/source/talk/app/webrtc_dev/webrtcjson.cc @@ -34,11 +34,17 @@ #include "talk/base/logging.h" #include "talk/base/stringutils.h" #include "talk/session/phone/codec.h" +#include "talk/session/phone/mediasession.h" #include "talk/session/phone/mediasessionclient.h" namespace webrtc { static const int kIceComponent = 1; static const int kIceFoundation = 1; +static const char* kMessageType[] = { + "OFFER", + "ANSWER", + "ERROR", +}; static std::vector ReadValues(const Json::Value& value, const std::string& key); @@ -66,17 +72,19 @@ static std::string Serialize(const Json::Value& value); static bool Deserialize(const std::string& message, Json::Value& value); -static bool ParseRtcpMux(const Json::Value& value); +bool ParseContent(const Json::Value& jmessage, + cricket::SessionDescription* sdp, + std::vector* candidates); + static bool ParseAudioCodec(const Json::Value& value, cricket::AudioContentDescription* content); static bool ParseVideoCodec(const Json::Value& value, cricket::VideoContentDescription* content); -static bool ParseIceCandidates(const Json::Value& value, - std::vector* candidates); -static Json::Value ReadValue(const Json::Value& value, const std::string& key); -static std::string ReadString(const Json::Value& value, const std::string& key); -static double ReadDouble(const Json::Value& value, const std::string& key); -static uint32 ReadUInt(const Json::Value& value, const std::string& key); +static bool ParseCandidates(const Json::Value& content, + std::vector* candidates); + +static bool ParseTrack(const Json::Value& content, + cricket::MediaContentDescription* content_desc); static void Append(Json::Value* object, const std::string& key, bool value); static void Append(Json::Value* object, const std::string& key, @@ -92,13 +100,24 @@ static void Append(Json::Value* object, const std::vector& values); bool JsonSerialize( + const webrtc::PeerConnectionMessage::PeerConnectionMessageType type, + int error_code, const cricket::SessionDescription* sdp, const std::vector& candidates, std::string* signaling_message) { + Json::Value media; + // TODO(ronghuawu): Replace magic strings. + Append(&media, "SDP", kMessageType[type]); + + if (type == webrtc::PeerConnectionMessage::kError) { + Append(&media, "error_code", error_code); + *signaling_message = Serialize(media); + return true; + } + const cricket::ContentInfo* audio_content = GetFirstAudioContent(sdp); const cricket::ContentInfo* video_content = GetFirstVideoContent(sdp); - Json::Value media; std::vector together; together.push_back("audio"); together.push_back("video"); @@ -266,6 +285,7 @@ bool BuildTrack(const cricket::SessionDescription* sdp, Json::Value track; Append(&track, "ssrc", it->ssrc); Append(&track, "cname", it->cname); + Append(&track, "label", it->description); tracks->push_back(track); } return true; @@ -281,66 +301,103 @@ bool Deserialize(const std::string& message, Json::Value* value) { return reader.parse(message, *value); } -bool JsonDeserialize(const std::string& signaling_message, - cricket::SessionDescription** sdp, - std::vector* candidates) { - ASSERT(!(*sdp)); // expect this to be NULL +bool JsonDeserialize( + webrtc::PeerConnectionMessage::PeerConnectionMessageType* type, + webrtc::PeerConnectionMessage::ErrorCode* error_code, + cricket::SessionDescription* sdp, + std::vector* candidates, + const std::string& signaling_message) { + ASSERT(type); + ASSERT(error_code); + ASSERT(sdp); + ASSERT(candidates); + if (type == NULL || error_code == NULL || sdp == NULL || candidates == NULL) + return false; + // first deserialize message - Json::Value value; - if (!Deserialize(signaling_message, &value)) { + Json::Value jmessage; + if (!Deserialize(signaling_message, &jmessage)) { return false; } - // get media objects - std::vector mlines = ReadValues(value, "media"); - if (mlines.empty()) { - // no m-lines found + // Get the message type + std::string message_type; + bool valid_message_type = false; + if (!GetStringFromJsonObject(jmessage, "SDP", &message_type)) return false; + for (int i = 0; i < ARRAY_SIZE(kMessageType); i++) { + if (message_type.compare(kMessageType[i]) == 0) { + *type = static_cast< + webrtc::PeerConnectionMessage::PeerConnectionMessageType>(i); + valid_message_type = true; + break; + } + } + if (!valid_message_type) + return false; + if (*type == webrtc::PeerConnectionMessage::kError) { + int code; + if (!GetIntFromJsonObject(jmessage, "error_code", &code)) + return false; + *error_code = static_cast(code); + return true; } - *sdp = new cricket::SessionDescription(); + return ParseContent(jmessage, sdp, candidates); +} - // get codec information - for (size_t i = 0; i < mlines.size(); ++i) { - if (mlines[i]["label"].asInt() == 1) { +bool ParseContent(const Json::Value& jmessage, + cricket::SessionDescription* sdp, + std::vector* candidates) { + // Get content + std::vector contents = ReadValues(jmessage, "content"); + if (contents.size() == 0) + return false; + for (size_t i = 0; i < contents.size(); ++i) { + Json::Value content = contents[i]; + // TODO(ronghuawu): crypto + // candidates + if (!ParseCandidates(content, candidates)) + return false; + // rtcp_mux + bool rtcp_mux; + if (!GetBoolFromJsonObject(content, "rtcp_mux", &rtcp_mux)) + rtcp_mux = false; + // rtpmap + if (content["media"].asString().compare("audio") == 0) { cricket::AudioContentDescription* audio_content = new cricket::AudioContentDescription(); - ParseAudioCodec(mlines[i], audio_content); - audio_content->set_rtcp_mux(ParseRtcpMux(mlines[i])); + if (!ParseAudioCodec(content, audio_content)) + return false; + audio_content->set_rtcp_mux(rtcp_mux); audio_content->SortCodecs(); - (*sdp)->AddContent(cricket::CN_AUDIO, - cricket::NS_JINGLE_RTP, audio_content); - ParseIceCandidates(mlines[i], candidates); - } else { + // tracks + if (!ParseTrack(content, audio_content)) + return false; + (sdp)->AddContent(cricket::CN_AUDIO, + cricket::NS_JINGLE_RTP, audio_content); + } else if (content["media"].asString().compare("video") == 0) { cricket::VideoContentDescription* video_content = new cricket::VideoContentDescription(); - ParseVideoCodec(mlines[i], video_content); - - video_content->set_rtcp_mux(ParseRtcpMux(mlines[i])); + if (!ParseVideoCodec(content, video_content)) + return false; + video_content->set_rtcp_mux(rtcp_mux); video_content->SortCodecs(); - (*sdp)->AddContent(cricket::CN_VIDEO, - cricket::NS_JINGLE_RTP, video_content); - ParseIceCandidates(mlines[i], candidates); + if (!ParseTrack(content, video_content)) + return false; + (sdp)->AddContent(cricket::CN_VIDEO, + cricket::NS_JINGLE_RTP, video_content); } } return true; } -bool ParseRtcpMux(const Json::Value& value) { - Json::Value rtcp_mux(ReadValue(value, "rtcp_mux")); - if (!rtcp_mux.empty()) { - if (rtcp_mux.asBool()) { - return true; - } - } - return false; -} - bool ParseAudioCodec(const Json::Value& value, cricket::AudioContentDescription* content) { std::vector rtpmap(ReadValues(value, "rtpmap")); + // When there's no codecs in common, rtpmap can be empty. if (rtpmap.empty()) - return false; + return true; std::vector::const_iterator iter = rtpmap.begin(); @@ -351,11 +408,13 @@ bool ParseAudioCodec(const Json::Value& value, std::string pltype(iter->begin().memberName()); talk_base::FromString(pltype, &codec.id); Json::Value codec_info((*iter)[pltype]); - std::string codec_name(ReadString(codec_info, "codec")); + std::string codec_name; + if (!GetStringFromJsonObject(codec_info, "codec", &codec_name)) + continue; std::vector tokens; talk_base::split(codec_name, '/', &tokens); codec.name = tokens[1]; - codec.clockrate = ReadUInt(codec_info, "clockrate"); + GetIntFromJsonObject(codec_info, "clockrate", &codec.clockrate); content->AddCodec(codec); } @@ -365,8 +424,9 @@ bool ParseAudioCodec(const Json::Value& value, bool ParseVideoCodec(const Json::Value& value, cricket::VideoContentDescription* content) { std::vector rtpmap(ReadValues(value, "rtpmap")); + // When there's no codecs in common, rtpmap can be empty. if (rtpmap.empty()) - return false; + return true; std::vector::const_iterator iter = rtpmap.begin(); @@ -385,14 +445,9 @@ bool ParseVideoCodec(const Json::Value& value, return true; } -bool ParseIceCandidates(const 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")); - +bool ParseCandidates(const Json::Value& content, + std::vector* candidates) { + std::vector jcandidates(ReadValues(content, "candidate")); std::vector::const_iterator iter = jcandidates.begin(); std::vector::const_iterator iter_end = @@ -453,6 +508,34 @@ bool ParseIceCandidates(const Json::Value& value, return true; } +bool ParseTrack(const Json::Value& content, + cricket::MediaContentDescription* content_desc) { + ASSERT(content_desc); + if (!content_desc) + return false; + + std::vector tracks(ReadValues(content, "track")); + std::vector::const_iterator iter = + tracks.begin(); + std::vector::const_iterator iter_end = + tracks.end(); + cricket::Sources sources; + for (; iter != iter_end; ++iter) { + uint32 ssrc; + std::string label; + std::string cname; + if (!GetUIntFromJsonObject(*iter, "ssrc", &ssrc)) + return false; + // label is optional, it will be empty string if doesn't exist + GetStringFromJsonObject(*iter, "label", &label); + if (!GetStringFromJsonObject(*iter, "cname", &cname)) + return false; + sources.push_back(cricket::SourceParam(ssrc, label, cname)); + } + content_desc->set_sources(sources); + return true; +} + std::vector ReadValues( const Json::Value& value, const std::string& key) { std::vector objects; @@ -462,22 +545,6 @@ std::vector ReadValues( return objects; } -Json::Value ReadValue(const Json::Value& value, const std::string& key) { - return value[key]; -} - -std::string ReadString(const Json::Value& value, const std::string& key) { - return value[key].asString(); -} - -uint32 ReadUInt(const Json::Value& value, const std::string& key) { - return value[key].asUInt(); -} - -double ReadDouble(const Json::Value& value, const std::string& key) { - return value[key].asDouble(); -} - void Append(Json::Value* object, const std::string& key, bool value) { (*object)[key] = Json::Value(value); } diff --git a/third_party_mods/libjingle/source/talk/app/webrtc_dev/webrtcjson.h b/third_party_mods/libjingle/source/talk/app/webrtc_dev/webrtcjson.h index d456fd5c2..4c9944f80 100644 --- a/third_party_mods/libjingle/source/talk/app/webrtc_dev/webrtcjson.h +++ b/third_party_mods/libjingle/source/talk/app/webrtc_dev/webrtcjson.h @@ -35,6 +35,7 @@ #else #include "third_party/jsoncpp/json.h" #endif +#include "talk/app/webrtc_dev/peerconnectionmessage.h" #include "talk/p2p/base/candidate.h" namespace cricket { @@ -44,13 +45,18 @@ class SessionDescription; namespace webrtc { bool JsonSerialize( + const webrtc::PeerConnectionMessage::PeerConnectionMessageType type, + int error_code, const cricket::SessionDescription* sdp, const std::vector& candidates, std::string* signaling_message); -bool JsonDeserialize(const std::string& signaling_message, - cricket::SessionDescription** sdp, - std::vector* candidates); +bool JsonDeserialize( + webrtc::PeerConnectionMessage::PeerConnectionMessageType* type, + webrtc::PeerConnectionMessage::ErrorCode* error_code, + cricket::SessionDescription* sdp, + std::vector* candidates, + const std::string& signaling_message); } #endif // TALK_APP_WEBRTC_WEBRTCJSON_H_