blob: 2aaac97eaabe0754dafb4788f3ae0ac01a0a1933 [file] [log] [blame]
/*
* libjingle
* Copyright 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.
*/
#include "talk/app/webrtc/webrtcjson.h"
#include <stdio.h>
#include <string>
#include <vector>
#include "talk/base/json.h"
#include "talk/base/logging.h"
#include "talk/base/stringutils.h"
#include "talk/session/phone/codec.h"
#include "talk/session/phone/cryptoparams.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 std::vector<Json::Value> ReadValues(const Json::Value& value,
const std::string& key);
static void BuildContent(
const cricket::SessionDescription* sdp,
const cricket::ContentInfo& content_info,
const std::vector<cricket::Candidate>& candidates,
bool video,
Json::Value* content);
static void BuildCandidate(const std::vector<cricket::Candidate>& candidates,
bool video,
std::vector<Json::Value>* jcandidates);
static void BuildRtpMapParams(const cricket::ContentInfo& content_info,
bool video,
std::vector<Json::Value>* rtpmap);
static void BuildCrypto(const cricket::ContentInfo& content_info,
bool video,
std::vector<Json::Value>* cryptos);
static void BuildTrack(const cricket::SessionDescription* sdp,
bool video,
std::vector<Json::Value>* track);
bool ParseContent(const Json::Value& jmessage,
cricket::SessionDescription* sdp,
std::vector<cricket::Candidate>* candidates);
static bool ParseAudioCodec(const Json::Value& value,
cricket::AudioContentDescription* content);
static bool ParseVideoCodec(const Json::Value& value,
cricket::VideoContentDescription* content);
static bool ParseCrypto(const Json::Value& content,
cricket::MediaContentDescription* desc);
static bool ParseCandidates(const Json::Value& content,
std::vector<cricket::Candidate>* 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,
const char* value);
static void Append(Json::Value* object, const std::string& key, int value);
static void Append(Json::Value* object, const std::string& key,
const std::string& value);
static void Append(Json::Value* object, const std::string& key, uint32 value);
static void Append(Json::Value* object, const std::string& key,
const Json::Value& value);
static void Append(Json::Value* object,
const std::string& key,
const std::vector<Json::Value>& values);
void JsonSerializeSessionDescription(
const cricket::SessionDescription* sdp,
const std::vector<cricket::Candidate>& candidates,
Json::Value* message) {
const cricket::ContentInfo* audio_content = GetFirstAudioContent(sdp);
const cricket::ContentInfo* video_content = GetFirstVideoContent(sdp);
std::vector<Json::Value> together;
together.push_back("audio");
together.push_back("video");
std::vector<Json::Value> contents;
if (audio_content) {
Json::Value content;
BuildContent(sdp, *audio_content, candidates, false, &content);
contents.push_back(content);
}
if (video_content) {
Json::Value content;
BuildContent(sdp, *video_content, candidates, true, &content);
contents.push_back(content);
}
Append(message, "content", contents);
Append(message, "TOGETHER", together);
}
void BuildContent(
const cricket::SessionDescription* sdp,
const cricket::ContentInfo& content_info,
const std::vector<cricket::Candidate>& candidates,
bool video,
Json::Value* content) {
std::string label("media");
// TODO: Use enum instead of bool video to prepare for other
// media types such as the data media stream.
if (video) {
Append(content, label, "video");
} else {
Append(content, label, "audio");
}
const cricket::MediaContentDescription* media_info =
static_cast<const cricket::MediaContentDescription*> (
content_info.description);
if (media_info->rtcp_mux()) {
Append(content, "rtcp_mux", true);
}
// rtpmap
std::vector<Json::Value> rtpmap;
BuildRtpMapParams(content_info, video, &rtpmap);
Append(content, "rtpmap", rtpmap);
// crypto
std::vector<Json::Value> crypto;
BuildCrypto(content_info, video, &crypto);
Append(content, "crypto", crypto);
// candidate
std::vector<Json::Value> jcandidates;
BuildCandidate(candidates, video, &jcandidates);
Append(content, "candidate", jcandidates);
// track
std::vector<Json::Value> track;
BuildTrack(sdp, video, &track);
Append(content, "track", track);
}
void BuildRtpMapParams(const cricket::ContentInfo& content_info,
bool video,
std::vector<Json::Value>* rtpmap) {
if (!video) {
const cricket::AudioContentDescription* audio =
static_cast<const cricket::AudioContentDescription*>(
content_info.description);
std::vector<cricket::AudioCodec>::const_iterator iter =
audio->codecs().begin();
std::vector<cricket::AudioCodec>::const_iterator iter_end =
audio->codecs().end();
for (; iter != iter_end; ++iter) {
Json::Value codec;
std::string codec_str(std::string("audio/").append(iter->name));
// adding clockrate
Append(&codec, "clockrate", iter->clockrate);
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 =
static_cast<const cricket::VideoContentDescription*>(
content_info.description);
std::vector<cricket::VideoCodec>::const_iterator iter =
video->codecs().begin();
std::vector<cricket::VideoCodec>::const_iterator iter_end =
video->codecs().end();
for (; iter != iter_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);
}
}
}
void BuildCrypto(const cricket::ContentInfo& content_info,
bool video,
std::vector<Json::Value>* cryptos) {
const cricket::MediaContentDescription* content_desc =
static_cast<const cricket::MediaContentDescription*>(
content_info.description);
std::vector<cricket::CryptoParams>::const_iterator iter =
content_desc->cryptos().begin();
std::vector<cricket::CryptoParams>::const_iterator iter_end =
content_desc->cryptos().end();
for (; iter != iter_end; ++iter) {
Json::Value crypto;
Append(&crypto, "cipher_suite", iter->cipher_suite);
Append(&crypto, "key_params", iter->key_params);
cryptos->push_back(crypto);
}
}
void BuildCandidate(const std::vector<cricket::Candidate>& candidates,
bool video,
std::vector<Json::Value>* jcandidates) {
std::vector<cricket::Candidate>::const_iterator iter =
candidates.begin();
std::vector<cricket::Candidate>::const_iterator iter_end =
candidates.end();
for (; iter != iter_end; ++iter) {
if ((video && (!iter->name().compare("video_rtcp") ||
(!iter->name().compare("video_rtp")))) ||
(!video && (!iter->name().compare("rtp") ||
(!iter->name().compare("rtcp"))))) {
Json::Value jcandidate;
Append(&jcandidate, "component", kIceComponent);
Append(&jcandidate, "foundation", kIceFoundation);
Append(&jcandidate, "generation", iter->generation());
Append(&jcandidate, "proto", iter->protocol());
Append(&jcandidate, "priority", iter->preference_str());
Append(&jcandidate, "ip", iter->address().IPAsString());
Append(&jcandidate, "port", iter->address().PortAsString());
Append(&jcandidate, "type", iter->type());
Append(&jcandidate, "name", iter->name());
Append(&jcandidate, "network_name", iter->network_name());
Append(&jcandidate, "username", iter->username());
Append(&jcandidate, "password", iter->password());
jcandidates->push_back(jcandidate);
}
}
}
void BuildTrack(const cricket::SessionDescription* sdp,
bool video,
std::vector<Json::Value>* tracks) {
const cricket::ContentInfo* content;
if (video)
content = GetFirstVideoContent(sdp);
else
content = GetFirstAudioContent(sdp);
if (!content)
return;
const cricket::MediaContentDescription* desc =
static_cast<const cricket::MediaContentDescription*>(
content->description);
for (cricket::StreamParamsVec::const_iterator it = desc->streams().begin();
it != desc->streams().end();
++it) {
// TODO: Support ssrcsgroups.
Json::Value track;
Append(&track, "ssrc", it->ssrcs[0]);
Append(&track, "cname", it->cname);
Append(&track, "stream_label", it->sync_label);
Append(&track, "label", it->name);
tracks->push_back(track);
}
}
bool JsonDeserializeSessionDescription(
const Json::Value& message,
cricket::SessionDescription* sdp,
std::vector<cricket::Candidate>* candidates) {
ASSERT(sdp != NULL);
ASSERT(candidates != NULL);
if (sdp == NULL || candidates == NULL)
return false;
// Get content
std::vector<Json::Value> contents = ReadValues(message, "content");
if (contents.size() == 0)
return false;
for (size_t i = 0; i < contents.size(); ++i) {
Json::Value content = contents[i];
// 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();
if (!ParseAudioCodec(content, audio_content))
return false;
audio_content->set_rtcp_mux(rtcp_mux);
audio_content->SortCodecs();
// crypto
if (!ParseCrypto(content, audio_content))
return false;
// 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();
if (!ParseVideoCodec(content, video_content))
return false;
video_content->set_rtcp_mux(rtcp_mux);
video_content->SortCodecs();
// crypto
if (!ParseCrypto(content, video_content))
return false;
if (!ParseTrack(content, video_content))
return false;
(sdp)->AddContent(cricket::CN_VIDEO,
cricket::NS_JINGLE_RTP, video_content);
}
}
return true;
}
bool ParseAudioCodec(const Json::Value& value,
cricket::AudioContentDescription* content) {
std::vector<Json::Value> rtpmap(ReadValues(value, "rtpmap"));
// When there's no codecs in common, rtpmap can be empty.
if (rtpmap.empty())
return true;
std::vector<Json::Value>::const_iterator iter =
rtpmap.begin();
std::vector<Json::Value>::const_iterator iter_end =
rtpmap.end();
for (; iter != iter_end; ++iter) {
cricket::AudioCodec codec;
std::string pltype(iter->begin().memberName());
talk_base::FromString(pltype, &codec.id);
Json::Value codec_info((*iter)[pltype]);
std::string codec_name;
if (!GetStringFromJsonObject(codec_info, "codec", &codec_name))
continue;
std::vector<std::string> tokens;
talk_base::split(codec_name, '/', &tokens);
codec.name = tokens[1];
GetIntFromJsonObject(codec_info, "clockrate", &codec.clockrate);
content->AddCodec(codec);
}
return true;
}
bool ParseVideoCodec(const Json::Value& value,
cricket::VideoContentDescription* content) {
std::vector<Json::Value> rtpmap(ReadValues(value, "rtpmap"));
// When there's no codecs in common, rtpmap can be empty.
if (rtpmap.empty())
return true;
std::vector<Json::Value>::const_iterator iter =
rtpmap.begin();
std::vector<Json::Value>::const_iterator iter_end =
rtpmap.end();
for (; iter != iter_end; ++iter) {
cricket::VideoCodec codec;
std::string pltype(iter->begin().memberName());
talk_base::FromString(pltype, &codec.id);
Json::Value codec_info((*iter)[pltype]);
std::vector<std::string> tokens;
talk_base::split(codec_info["codec"].asString(), '/', &tokens);
codec.name = tokens[1];
content->AddCodec(codec);
}
return true;
}
bool ParseCandidates(const Json::Value& content,
std::vector<cricket::Candidate>* candidates) {
std::vector<Json::Value> jcandidates(ReadValues(content, "candidate"));
std::vector<Json::Value>::const_iterator iter =
jcandidates.begin();
std::vector<Json::Value>::const_iterator iter_end =
jcandidates.end();
for (; iter != iter_end; ++iter) {
cricket::Candidate cand;
unsigned int generation;
if (!GetUIntFromJsonObject(*iter, "generation", &generation))
return false;
cand.set_generation_str(talk_base::ToString(generation));
std::string proto;
if (!GetStringFromJsonObject(*iter, "proto", &proto))
return false;
cand.set_protocol(proto);
std::string priority;
if (!GetStringFromJsonObject(*iter, "priority", &priority))
return false;
cand.set_preference_str(priority);
std::string str;
talk_base::SocketAddress addr;
if (!GetStringFromJsonObject(*iter, "ip", &str))
return false;
addr.SetIP(str);
if (!GetStringFromJsonObject(*iter, "port", &str))
return false;
int port;
if (!talk_base::FromString(str, &port))
return false;
addr.SetPort(port);
cand.set_address(addr);
if (!GetStringFromJsonObject(*iter, "type", &str))
return false;
cand.set_type(str);
if (!GetStringFromJsonObject(*iter, "name", &str))
return false;
cand.set_name(str);
if (!GetStringFromJsonObject(*iter, "network_name", &str))
return false;
cand.set_network_name(str);
if (!GetStringFromJsonObject(*iter, "username", &str))
return false;
cand.set_username(str);
if (!GetStringFromJsonObject(*iter, "password", &str))
return false;
cand.set_password(str);
candidates->push_back(cand);
}
return true;
}
bool ParseCrypto(const Json::Value& content,
cricket::MediaContentDescription* desc) {
std::vector<Json::Value> jcryptos(ReadValues(content, "crypto"));
std::vector<Json::Value>::const_iterator iter =
jcryptos.begin();
std::vector<Json::Value>::const_iterator iter_end =
jcryptos.end();
for (; iter != iter_end; ++iter) {
cricket::CryptoParams crypto;
std::string cipher_suite;
if (!GetStringFromJsonObject(*iter, "cipher_suite", &cipher_suite))
return false;
crypto.cipher_suite = cipher_suite;
std::string key_params;
if (!GetStringFromJsonObject(*iter, "key_params", &key_params))
return false;
crypto.key_params= key_params;
desc->AddCrypto(crypto);
}
return true;
}
bool ParseTrack(const Json::Value& content,
cricket::MediaContentDescription* content_desc) {
ASSERT(content_desc != NULL);
if (!content_desc)
return false;
std::vector<Json::Value> tracks(ReadValues(content, "track"));
std::vector<Json::Value>::const_iterator iter =
tracks.begin();
std::vector<Json::Value>::const_iterator iter_end =
tracks.end();
for (; iter != iter_end; ++iter) {
uint32 ssrc;
std::string label;
std::string cname;
std::string stream_label;
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;
// stream_label is optional, it will be the same as cname if it
// doesn't exist.
GetStringFromJsonObject(*iter, "stream_label", &stream_label);
if (stream_label.empty())
stream_label = cname;
cricket::StreamParams stream;
stream.name = label;
stream.cname = cname;
stream.sync_label = stream_label;
stream.ssrcs.push_back(ssrc);
content_desc->AddStream(stream);
}
return true;
}
std::vector<Json::Value> ReadValues(
const Json::Value& value, const std::string& key) {
std::vector<Json::Value> objects;
for (Json::Value::ArrayIndex i = 0; i < value[key].size(); ++i) {
objects.push_back(value[key][i]);
}
return objects;
}
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, const char* 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,
const 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,
const Json::Value& value) {
(*object)[key] = value;
}
void Append(Json::Value* object,
const std::string & key,
const std::vector<Json::Value>& values) {
for (std::vector<Json::Value>::const_iterator iter = values.begin();
iter != values.end(); ++iter) {
(*object)[key].append(*iter);
}
}
} // namespace webrtc