Support bandwidth control in call.exe and support call redirects.
git-svn-id: http://libjingle.googlecode.com/svn/trunk@38 dd674b97-3498-5ee5-1854-bdd07cd0ff33
diff --git a/talk/examples/call/callclient.cc b/talk/examples/call/callclient.cc
index 3657420..92fcea3 100644
--- a/talk/examples/call/callclient.cc
+++ b/talk/examples/call/callclient.cc
@@ -35,6 +35,7 @@
#include "talk/base/network.h"
#include "talk/base/socketaddress.h"
#include "talk/base/stringutils.h"
+#include "talk/base/stringencode.h"
#include "talk/p2p/base/sessionmanager.h"
#include "talk/p2p/client/basicportallocator.h"
#include "talk/p2p/client/sessionmanagertask.h"
@@ -82,6 +83,25 @@
}
}
+std::string GetWord(const std::vector<std::string>& words,
+ size_t index, const std::string& def) {
+ if (words.size() > index) {
+ return words[index];
+ } else {
+ return def;
+ }
+}
+
+int GetInt(const std::vector<std::string>& words, size_t index, int def) {
+ int val;
+ if (words.size() > index && talk_base::FromString(words[index], &val)) {
+ return val;
+ } else {
+ return def;
+ }
+}
+
+
} // namespace
const char* CALL_COMMANDS =
@@ -107,10 +127,10 @@
"\n"
" roster Prints the online friends from your roster.\n"
" friend user Request to add a user to your roster.\n"
-" call [jid] Initiates a call to the user[/room] with the\n"
-" given JID.\n"
-" vcall [jid] Initiates a video call to the user[/room] with\n"
-" the given JID.\n"
+" call [jid] [bw] Initiates a call to the user[/room] with the\n"
+" given JID and with optional bandwidth.\n"
+" vcall [jid] [bw] Initiates a video call to the user[/room] with\n"
+" the given JID and with optional bandwidth.\n"
" voicemail [jid] Leave a voicemail for the user with the given JID.\n"
" join [room] Joins a multi-user-chat.\n"
" invite user [room] Invites a friend to a multi-user-chat.\n"
@@ -142,37 +162,38 @@
}
// Global commands
- if ((words.size() == 1) && (words[0] == "quit")) {
+ const std::string& command = GetWord(words, 0, "");
+ if (command == "quit") {
Quit();
} else if (call_ && incoming_call_) {
- if ((words.size() == 1) && (words[0] == "accept")) {
+ if (command == "accept") {
Accept();
- } else if ((words.size() == 1) && (words[0] == "reject")) {
+ } else if (command == "reject") {
Reject();
} else {
console_->Print(RECEIVE_COMMANDS);
}
} else if (call_) {
- if ((words.size() == 1) && (words[0] == "hangup")) {
+ if (command == "hangup") {
// TODO: do more shutdown here, move to Terminate()
call_->Terminate();
call_ = NULL;
session_ = NULL;
console_->SetPrompt(NULL);
- } else if ((words.size() == 1) && (words[0] == "mute")) {
+ } else if (command == "mute") {
call_->Mute(true);
- } else if ((words.size() == 1) && (words[0] == "unmute")) {
+ } else if (command == "unmute") {
call_->Mute(false);
- } else if ((words.size() == 2) && (words[0] == "dtmf")) {
+ } else if ((command == "dtmf") && (words.size() == 2)) {
int ev = std::string("0123456789*#").find(words[1][0]);
call_->PressDTMF(ev);
} else {
console_->Print(CALL_COMMANDS);
}
} else {
- if ((words.size() == 1) && (words[0] == "roster")) {
+ if (command == "roster") {
PrintRoster();
- } else if ((words.size() >= 2) && (words[0] == "send")) {
+ } else if (command == "send") {
buzz::Jid jid(words[1]);
if (jid.IsValid()) {
last_sent_to_ = words[1];
@@ -183,23 +204,31 @@
console_->Printf(
"Invalid JID. JIDs should be in the form user@domain\n");
}
- } else if ((words.size() == 2) && (words[0] == "friend")) {
+ } else if ((words.size() == 2) && (command == "friend")) {
InviteFriend(words[1]);
- } else if ((words.size() >= 1) && (words[0] == "call")) {
- MakeCallTo((words.size() >= 2) ? words[1] : "", false);
- } else if ((words.size() >= 1) && (words[0] == "vcall")) {
- MakeCallTo((words.size() >= 2) ? words[1] : "", true);
- } else if ((words.size() >= 1) && (words[0] == "join")) {
- JoinMuc((words.size() >= 2) ? words[1] : "");
- } else if ((words.size() >= 2) && (words[0] == "invite")) {
- InviteToMuc(words[1], (words.size() >= 3) ? words[2] : "");
- } else if ((words.size() >= 1) && (words[0] == "leave")) {
- LeaveMuc((words.size() >= 2) ? words[1] : "");
- } else if ((words.size() == 1) && (words[0] == "getdevs")) {
+ } else if (command == "call") {
+ std::string to = GetWord(words, 1, "");
+ MakeCallTo(to, cricket::CallOptions());
+ } else if (command == "vcall") {
+ std::string to = GetWord(words, 1, "");
+ int bandwidth = GetInt(words, 2, -1);
+ cricket::CallOptions options;
+ options.is_video = true;
+ if (bandwidth > 0) {
+ options.video_bandwidth = bandwidth;
+ }
+ MakeCallTo(to, options);
+ } else if (command == "join") {
+ JoinMuc(GetWord(words, 1, ""));
+ } else if ((words.size() >= 2) && (command == "invite")) {
+ InviteToMuc(words[1], GetWord(words, 2, ""));
+ } else if (command == "leave") {
+ LeaveMuc(GetWord(words, 1, ""));
+ } else if (command == "getdevs") {
GetDevices();
- } else if ((words.size() == 2) && (words[0] == "setvol")) {
+ } else if ((words.size() == 2) && (command == "setvol")) {
SetVolume(words[1]);
- } else if ((words.size() >= 1) && (words[0] == "voicemail")) {
+ } else if (command == "voicemail") {
CallVoicemail((words.size() >= 2) ? words[1] : "");
} else {
console_->Print(CONSOLE_COMMANDS);
@@ -505,16 +534,20 @@
console_->Printf("Requesting to befriend %s.\n", name.c_str());
}
-void CallClient::MakeCallTo(const std::string& name, bool video) {
+void CallClient::MakeCallTo(const std::string& name,
+ const cricket::CallOptions& given_options) {
+ // Copy so we can change .is_muc.
+ cricket::CallOptions options = given_options;
+
bool found = false;
- bool is_muc = false;
+ options.is_muc = false;
buzz::Jid callto_jid(name);
buzz::Jid found_jid;
if (name.length() == 0 && mucs_.size() > 0) {
// if no name, and in a MUC, establish audio with the MUC
found_jid = mucs_.begin()->first;
found = true;
- is_muc = true;
+ options.is_muc = true;
} else if (name[0] == '+') {
// if the first character is a +, assume it's a phone number
found_jid = callto_jid;
@@ -539,28 +572,29 @@
mucs_[callto_jid]->state() == buzz::Muc::MUC_JOINED) {
found = true;
found_jid = callto_jid;
- is_muc = true;
+ options.is_muc = true;
}
}
}
if (found) {
- console_->Printf("Found %s '%s'", is_muc ? "room" : "online friend",
+ console_->Printf("Found %s '%s'", options.is_muc ? "room" : "online friend",
found_jid.Str().c_str());
- PlaceCall(found_jid, is_muc, video);
+ PlaceCall(found_jid, options);
} else {
console_->Printf("Could not find online friend '%s'", name.c_str());
}
}
-void CallClient::PlaceCall(const buzz::Jid& jid, bool is_muc, bool video) {
+void CallClient::PlaceCall(const buzz::Jid& jid,
+ const cricket::CallOptions& options) {
media_client_->SignalCallDestroy.connect(
this, &CallClient::OnCallDestroy);
if (!call_) {
- call_ = media_client_->CreateCall(video, is_muc);
+ call_ = media_client_->CreateCall();
console_->SetPrompt(jid.Str().c_str());
- session_ = call_->InitiateSession(jid);
- if (is_muc) {
+ session_ = call_->InitiateSession(jid, options);
+ if (options.is_muc) {
// If people in this room are already in a call, must add all their
// streams.
buzz::Muc::MemberMap& members = mucs_[jid]->members();
@@ -598,7 +632,7 @@
void CallClient::OnFoundVoicemailJid(const buzz::Jid& to,
const buzz::Jid& voicemail) {
console_->Printf("Calling %s's voicemail.\n", to.Str().c_str());
- PlaceCall(voicemail, false, false);
+ PlaceCall(voicemail, cricket::CallOptions());
}
void CallClient::OnVoicemailJidError(const buzz::Jid& to) {
diff --git a/talk/examples/call/callclient.h b/talk/examples/call/callclient.h
index b7dc716..bdae6dc 100644
--- a/talk/examples/call/callclient.h
+++ b/talk/examples/call/callclient.h
@@ -66,6 +66,7 @@
class MediaSessionClient;
class Receiver;
class Call;
+struct CallOptions;
class SessionManagerTask;
enum SignalingProtocol;
}
@@ -148,8 +149,8 @@
static const std::string strerror(buzz::XmppEngine::Error err);
void PrintRoster();
- void MakeCallTo(const std::string& name, bool video);
- void PlaceCall(const buzz::Jid& jid, bool is_muc, bool video);
+ void MakeCallTo(const std::string& name, const cricket::CallOptions& options);
+ void PlaceCall(const buzz::Jid& jid, const cricket::CallOptions& options);
void CallVoicemail(const std::string& name);
void Accept();
void Reject();
diff --git a/talk/main.scons b/talk/main.scons
index 54b863a..a1348ff 100644
--- a/talk/main.scons
+++ b/talk/main.scons
@@ -142,6 +142,9 @@
# this NTDDI version or else the headers
# that LMI includes from it won't compile.
'NTDDI_VERSION=NTDDI_WINXP',
+
+ # npapi.h requires the following:
+ '_WINDOWS',
],
CPPPATH = [
'$THIRD_PARTY/wtl_71/include',
diff --git a/talk/p2p/base/constants.cc b/talk/p2p/base/constants.cc
index 7430140..a0c4ba4 100644
--- a/talk/p2p/base/constants.cc
+++ b/talk/p2p/base/constants.cc
@@ -64,6 +64,10 @@
const std::string GINGLE_ACTION_TERMINATE("terminate");
const std::string GINGLE_ACTION_CANDIDATES("candidates");
+const std::string LN_ERROR("error");
+const buzz::QName QN_GINGLE_REDIRECT(true, NS_GINGLE, "redirect");
+const std::string STR_REDIRECT_PREFIX("xmpp:");
+
// Session Contents (aka Gingle <session><description>
// or Jingle <content><description>)
const std::string LN_DESCRIPTION("description");
@@ -84,6 +88,7 @@
const std::string PAYLOADTYPE_PARAMETER_HEIGHT("height");
const std::string PAYLOADTYPE_PARAMETER_WIDTH("width");
const std::string PAYLOADTYPE_PARAMETER_FRAMERATE("framerate");
+const std::string LN_BANDWIDTH("bandwidth");
const std::string CN_AUDIO("audio");
const std::string CN_VIDEO("video");
@@ -108,7 +113,6 @@
const buzz::QName QN_GINGLE_VIDEO_PAYLOADTYPE(
true, NS_GINGLE_VIDEO, LN_PAYLOADTYPE);
const buzz::QName QN_GINGLE_VIDEO_SRCID(true, NS_GINGLE_VIDEO, "src-id");
-const buzz::QName QN_GINGLE_VIDEO_BANDWIDTH(true, NS_GINGLE_VIDEO, "bandwidth");
// Crypto support.
const buzz::QName QN_ENCRYPTION(true, NS_JINGLE_RTP, "encryption");
diff --git a/talk/p2p/base/constants.h b/talk/p2p/base/constants.h
index d4e600b..8355210 100644
--- a/talk/p2p/base/constants.h
+++ b/talk/p2p/base/constants.h
@@ -80,6 +80,9 @@
extern const std::string GINGLE_ACTION_TERMINATE;
extern const std::string GINGLE_ACTION_CANDIDATES;
+extern const std::string LN_ERROR;
+extern const buzz::QName QN_GINGLE_REDIRECT;
+extern const std::string STR_REDIRECT_PREFIX;
// Session Contents (aka Gingle <session><description>
// or Jingle <content><description>)
@@ -102,6 +105,7 @@
extern const std::string PAYLOADTYPE_PARAMETER_HEIGHT;
extern const std::string PAYLOADTYPE_PARAMETER_WIDTH;
extern const std::string PAYLOADTYPE_PARAMETER_FRAMERATE;
+extern const std::string LN_BANDWIDTH;
// CN_ == "content name". When we initiate a session, we choose the
// name, and when we receive a Gingle session, we provide default
diff --git a/talk/p2p/base/session.cc b/talk/p2p/base/session.cc
index a7bb92d..38b91ff 100644
--- a/talk/p2p/base/session.cc
+++ b/talk/p2p/base/session.cc
@@ -672,6 +672,20 @@
return;
}
+ // If the error is a session redirect, call OnRedirectError, which will
+ // continue the session with a new remote JID.
+ SessionRedirect redirect;
+ if (FindSessionRedirect(error_stanza, &redirect)) {
+ SessionError error;
+ if (!OnRedirectError(redirect, &error)) {
+ // TODO: Should we send a message back? The standard
+ // says nothing about it.
+ LOG(LS_ERROR) << "Failed to redirect: " << error.text;
+ SetError(ERROR_RESPONSE);
+ }
+ return;
+ }
+
std::string error_type = "cancel";
const buzz::XmlElement* error = error_stanza->FirstNamed(buzz::QN_ERROR);
@@ -810,6 +824,32 @@
return true;
}
+bool BareJidsEqual(const std::string& name1,
+ const std::string& name2) {
+ buzz::Jid jid1(name1);
+ buzz::Jid jid2(name2);
+
+ return jid1.IsValid() && jid2.IsValid() && jid1.BareEquals(jid2);
+}
+
+bool Session::OnRedirectError(const SessionRedirect& redirect,
+ SessionError* error) {
+ MessageError message_error;
+ if (!CheckState(STATE_SENTINITIATE, &message_error)) {
+ return BadWrite(message_error.text, error);
+ }
+
+ if (!BareJidsEqual(remote_name_, redirect.target))
+ return BadWrite("Redirection not allowed: must be the same bare jid.",
+ error);
+
+ // When we receive a redirect, we point the session at the new JID
+ // and resend the candidates.
+ remote_name_ = redirect.target;
+ return (SendInitiateMessage(local_description(), error) &&
+ ResendAllTransportInfoMessages(error));
+}
+
bool Session::CheckState(State state, MessageError* error) {
ASSERT(state_ == state);
if (state_ != state) {
@@ -910,6 +950,25 @@
elems, error);
}
+bool Session::ResendAllTransportInfoMessages(SessionError* error) {
+ for (TransportMap::iterator iter = transports_.begin();
+ iter != transports_.end(); ++iter) {
+ TransportProxy* transproxy = iter->second;
+ if (transproxy->sent_candidates().size() > 0) {
+ if (!SendTransportInfoMessage(
+ TransportInfo(
+ transproxy->content_name(),
+ transproxy->type(),
+ transproxy->sent_candidates()),
+ error)) {
+ return false;
+ }
+ transproxy->ClearSentCandidates();
+ }
+ }
+ return true;
+}
+
bool Session::SendMessage(ActionType type, const XmlElements& action_elems,
SessionError* error) {
talk_base::scoped_ptr<buzz::XmlElement> stanza(
diff --git a/talk/p2p/base/session.h b/talk/p2p/base/session.h
index a72f751..ddbc663 100644
--- a/talk/p2p/base/session.h
+++ b/talk/p2p/base/session.h
@@ -439,6 +439,7 @@
bool SendTerminateMessage(const std::string& reason, SessionError* error);
bool SendTransportInfoMessage(const TransportInfo& tinfo,
SessionError* error);
+ bool ResendAllTransportInfoMessages(SessionError* error);
// Both versions of SendMessage send a message of the given type to
// the other client. Can pass either a set of elements or an
@@ -509,6 +510,7 @@
bool OnTerminateMessage(const SessionMessage& msg, MessageError* error);
bool OnTransportInfoMessage(const SessionMessage& msg, MessageError* error);
bool OnTransportAcceptMessage(const SessionMessage& msg, MessageError* error);
+ bool OnRedirectError(const SessionRedirect& redirect, SessionError* error);
// Verifies that we are in the appropriate state to receive this message.
bool CheckState(State state, MessageError* error);
diff --git a/talk/p2p/base/sessionmessages.cc b/talk/p2p/base/sessionmessages.cc
index e65d826..cfd035d 100644
--- a/talk/p2p/base/sessionmessages.cc
+++ b/talk/p2p/base/sessionmessages.cc
@@ -820,4 +820,34 @@
}
}
+bool GetUriTarget(const std::string& prefix, const std::string& str,
+ std::string* after) {
+ size_t pos = str.find(prefix);
+ if (pos == std::string::npos)
+ return false;
+
+ *after = str.substr(pos + prefix.size(), std::string::npos);
+ return true;
+}
+
+bool FindSessionRedirect(const buzz::XmlElement* stanza,
+ SessionRedirect* redirect) {
+ const buzz::XmlElement* error_elem = GetXmlChild(stanza, LN_ERROR);
+ if (error_elem == NULL)
+ return false;
+
+ const buzz::XmlElement* redirect_elem =
+ error_elem->FirstNamed(QN_GINGLE_REDIRECT);
+ if (redirect_elem == NULL)
+ redirect_elem = error_elem->FirstNamed(buzz::QN_STANZA_REDIRECT);
+ if (redirect_elem == NULL)
+ return false;
+
+ if (!GetUriTarget(STR_REDIRECT_PREFIX, redirect_elem->BodyText(),
+ &redirect->target))
+ return false;
+
+ return true;
+}
+
} // namespace cricket
diff --git a/talk/p2p/base/sessionmessages.h b/talk/p2p/base/sessionmessages.h
index ff8641c..eee8452 100644
--- a/talk/p2p/base/sessionmessages.h
+++ b/talk/p2p/base/sessionmessages.h
@@ -149,6 +149,10 @@
std::string debug_reason;
};
+struct SessionRedirect {
+ std::string target;
+};
+
bool IsSessionMessage(const buzz::XmlElement* stanza);
bool ParseSessionMessage(const buzz::XmlElement* stanza,
SessionMessage* msg,
@@ -208,6 +212,9 @@
const TransportParserMap& trans_parsers,
XmlElements* elems,
WriteError* error);
+// Handles both Gingle and Jingle syntax.
+bool FindSessionRedirect(const buzz::XmlElement* stanza,
+ SessionRedirect* redirect);
} // namespace cricket
#endif // TALK_P2P_BASE_SESSIONMESSAGES_H_
diff --git a/talk/session/phone/call.cc b/talk/session/phone/call.cc
index fbd7a85..d72f913 100644
--- a/talk/session/phone/call.cc
+++ b/talk/session/phone/call.cc
@@ -30,6 +30,7 @@
#include "talk/base/logging.h"
#include "talk/base/thread.h"
#include "talk/session/phone/call.h"
+#include "talk/session/phone/mediasessionclient.h"
namespace cricket {
@@ -45,10 +46,13 @@
const int kMediaMonitorInterval = 1000*15;
}
-Call::Call(MediaSessionClient *session_client, bool video, bool mux)
- : id_(talk_base::CreateRandomId()), session_client_(session_client),
- local_renderer_(NULL), video_(video), mux_(mux),
- muted_(false), send_to_voicemail_(true), playing_dtmf_(false) {
+Call::Call(MediaSessionClient* session_client)
+ : id_(talk_base::CreateRandomId()),
+ session_client_(session_client),
+ local_renderer_(NULL),
+ muted_(false),
+ send_to_voicemail_(true),
+ playing_dtmf_(false) {
}
Call::~Call() {
@@ -60,8 +64,9 @@
talk_base::Thread::Current()->Clear(this);
}
-Session *Call::InitiateSession(const buzz::Jid &jid) {
- const SessionDescription* offer = session_client_->CreateOffer(video_, mux_);
+Session *Call::InitiateSession(const buzz::Jid &jid,
+ const CallOptions& options) {
+ const SessionDescription* offer = session_client_->CreateOffer(options);
Session *session = session_client_->CreateSession(this);
AddSession(session, offer);
@@ -196,6 +201,9 @@
VideoChannel *video_channel = NULL;
const ContentInfo* audio_offer = GetFirstAudioContent(offer);
+ const ContentInfo* video_offer = GetFirstVideoContent(offer);
+ video_ = (video_offer != NULL);
+
ASSERT(audio_offer != NULL);
// Create voice channel and start a media monitor
voice_channel = session_client_->channel_manager()->CreateVoiceChannel(
@@ -212,8 +220,6 @@
// If desired, create video channel and start a media monitor
if (video_ && succeeded) {
- const ContentInfo* video_offer = GetFirstVideoContent(offer);
- ASSERT(video_offer != NULL);
video_channel = session_client_->channel_manager()->CreateVideoChannel(
session, video_offer->name, true, voice_channel);
// video_channel can be NULL in case of NullVideoEngine.
diff --git a/talk/session/phone/call.h b/talk/session/phone/call.h
index 8500fc6..f3fcbf1 100644
--- a/talk/session/phone/call.h
+++ b/talk/session/phone/call.h
@@ -36,21 +36,20 @@
#include "talk/p2p/base/session.h"
#include "talk/p2p/client/socketmonitor.h"
#include "talk/xmpp/jid.h"
-#include "talk/session/phone/mediasessionclient.h"
-#include "talk/session/phone/voicechannel.h"
#include "talk/session/phone/audiomonitor.h"
+#include "talk/session/phone/voicechannel.h"
namespace cricket {
class MediaSessionClient;
+struct CallOptions;
class Call : public talk_base::MessageHandler, public sigslot::has_slots<> {
public:
- Call(MediaSessionClient *session_client,
- bool video = false, bool mux = false);
+ Call(MediaSessionClient* session_client);
~Call();
- Session *InitiateSession(const buzz::Jid &jid);
+ Session *InitiateSession(const buzz::Jid &jid, const CallOptions& options);
void AcceptSession(BaseSession *session);
void RejectSession(BaseSession *session);
void TerminateSession(BaseSession *session);
@@ -127,7 +126,6 @@
std::map<std::string, VideoChannel *> video_channel_map_;
VideoRenderer* local_renderer_;
bool video_;
- bool mux_;
bool muted_;
bool send_to_voicemail_;
diff --git a/talk/session/phone/channel.cc b/talk/session/phone/channel.cc
index 526dbec..76c1cb4 100644
--- a/talk/session/phone/channel.cc
+++ b/talk/session/phone/channel.cc
@@ -942,8 +942,9 @@
}
// Set video bandwidth parameters.
if (ret) {
- ret = media_channel()->SetSendBandwidth(video->auto_bandwidth(),
- video->bandwidth_bps());
+ int bandwidth_bps = video->bandwidth();
+ bool auto_bandwidth = (bandwidth_bps == kAutoBandwidth);
+ ret = media_channel()->SetSendBandwidth(auto_bandwidth, bandwidth_bps);
}
if (ret) {
ret = media_channel()->SetSendCodecs(video->codecs());
diff --git a/talk/session/phone/mediachannel.h b/talk/session/phone/mediachannel.h
index 2650ea7..01bd8b5 100644
--- a/talk/session/phone/mediachannel.h
+++ b/talk/session/phone/mediachannel.h
@@ -1,6 +1,6 @@
/*
* libjingle
- * Copyright 2004--2007, Google Inc.
+ * Copyright 2004--2010, Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -237,7 +237,7 @@
// Size of an I420 image of given dimensions when stored as a frame buffer.
static size_t SizeOf(size_t w, size_t h) {
- return w * h * 3 / 2;
+ return w * h + ((w + 1) / 2) * ((h + 1) / 2) * 2;
}
protected:
diff --git a/talk/session/phone/mediasessionclient.cc b/talk/session/phone/mediasessionclient.cc
index 6e9d8b6..645524a 100644
--- a/talk/session/phone/mediasessionclient.cc
+++ b/talk/session/phone/mediasessionclient.cc
@@ -32,6 +32,7 @@
#include "talk/base/helpers.h"
#include "talk/base/logging.h"
#include "talk/base/stringutils.h"
+#include "talk/base/stringencode.h"
#include "talk/p2p/base/constants.h"
#include "talk/p2p/base/parsing.h"
#include "talk/session/phone/cryptoparams.h"
@@ -133,7 +134,8 @@
#endif
}
-SessionDescription* MediaSessionClient::CreateOffer(bool video, bool set_ssrc) {
+SessionDescription* MediaSessionClient::CreateOffer(
+ const CallOptions& options) {
SessionDescription* offer = new SessionDescription();
AudioContentDescription* audio = new AudioContentDescription();
@@ -144,7 +146,7 @@
codec != audio_codecs.end(); ++codec) {
audio->AddCodec(*codec);
}
- if (set_ssrc) {
+ if (options.is_muc) {
audio->set_ssrc(0);
}
audio->SortCodecs();
@@ -168,7 +170,7 @@
offer->AddContent(CN_AUDIO, NS_JINGLE_RTP, audio);
// add video codecs, if this is a video call
- if (video) {
+ if (options.is_video) {
VideoContentDescription* video = new VideoContentDescription();
VideoCodecs video_codecs;
channel_manager_->GetSupportedVideoCodecs(&video_codecs);
@@ -176,9 +178,10 @@
codec != video_codecs.end(); ++codec) {
video->AddCodec(*codec);
}
- if (set_ssrc) {
+ if (options.is_muc) {
video->set_ssrc(0);
}
+ video->set_bandwidth(options.video_bandwidth);
video->SortCodecs();
if (secure() != SEC_DISABLED) {
@@ -329,8 +332,8 @@
return accept;
}
-Call *MediaSessionClient::CreateCall(bool video, bool mux) {
- Call *call = new Call(this, video, mux);
+Call *MediaSessionClient::CreateCall() {
+ Call *call = new Call(this);
calls_[call->id()] = call;
SignalCallCreate(call);
return call;
@@ -359,13 +362,12 @@
const SessionDescription* offer = session->remote_description();
const SessionDescription* accept = CreateAnswer(offer);
const ContentInfo* audio_content = GetFirstAudioContent(accept);
- const ContentInfo* video_content = GetFirstVideoContent(accept);
const AudioContentDescription* audio_accept = (!audio_content) ? NULL :
static_cast<const AudioContentDescription*>(audio_content->description);
// For some reason, we need to create the call even when we
// reject.
- Call *call = CreateCall(video_content != NULL);
+ Call *call = CreateCall();
session_map_[session->id()] = call;
call->IncomingSession(session, offer);
@@ -526,6 +528,17 @@
return true;
}
+void ParseBandwidth(const buzz::XmlElement* parent_elem,
+ MediaContentDescription* media) {
+ const buzz::XmlElement* bw_elem = GetXmlChild(parent_elem, LN_BANDWIDTH);
+ int bandwidth_kbps;
+ if (bw_elem && FromString(bw_elem->BodyText(), &bandwidth_kbps)) {
+ if (bandwidth_kbps >= 0) {
+ media->set_bandwidth(bandwidth_kbps * 1000);
+ }
+ }
+}
+
bool ParseGingleAudioContent(const buzz::XmlElement* content_elem,
const ContentDescription** content,
ParseError* error) {
@@ -575,6 +588,7 @@
}
ParseGingleSsrc(content_elem, QN_GINGLE_VIDEO_SRCID, video);
+ ParseBandwidth(content_elem, video);
if (!ParseGingleEncryption(content_elem, QN_GINGLE_VIDEO_CRYPTO_USAGE,
video, error)) {
@@ -705,6 +719,8 @@
}
}
+ ParseBandwidth(content_elem, video);
+
if (!ParseJingleEncryption(content_elem, video, error)) {
return false;
}
@@ -774,6 +790,15 @@
return elem;
}
+buzz::XmlElement* CreateBandwidthElem(int bps) {
+ int kbps = bps / 1000;
+ buzz::XmlElement* elem = new buzz::XmlElement(
+ buzz::QName(true, "", LN_BANDWIDTH), true);
+ 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) {
@@ -815,7 +840,6 @@
return encryption_elem;
}
-
buzz::XmlElement* CreateGingleAudioContentElem(
const AudioContentDescription* audio,
bool crypto_required) {
@@ -856,6 +880,9 @@
elem->AddElement(CreateGingleSsrcElem(
QN_GINGLE_VIDEO_SRCID, video->ssrc()));
}
+ if (video->bandwidth() != kAutoBandwidth) {
+ elem->AddElement(CreateBandwidthElem(video->bandwidth()));
+ }
const CryptoParamsVec& cryptos = video->cryptos();
if (!cryptos.empty()) {
@@ -949,6 +976,10 @@
elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
}
+ if (video->bandwidth() != kAutoBandwidth) {
+ elem->AddElement(CreateBandwidthElem(video->bandwidth()));
+ }
+
// TODO: Figure out how to integrate SSRC into Jingle.
return elem;
}
diff --git a/talk/session/phone/mediasessionclient.h b/talk/session/phone/mediasessionclient.h
index 6534259..8f157ee 100644
--- a/talk/session/phone/mediasessionclient.h
+++ b/talk/session/phone/mediasessionclient.h
@@ -65,6 +65,21 @@
// in the offer.)
enum SecureMediaPolicy {SEC_DISABLED, SEC_ENABLED, SEC_REQUIRED};
+const int kAutoBandwidth = -1;
+
+struct CallOptions {
+ CallOptions() :
+ is_video(false),
+ is_muc(false),
+ video_bandwidth(kAutoBandwidth) {
+ }
+
+ bool is_video;
+ bool is_muc;
+ // bps. -1 == auto.
+ int video_bandwidth;
+};
+
class MediaSessionClient: public SessionClient, public sigslot::has_slots<> {
public:
@@ -81,7 +96,7 @@
int GetCapabilities() { return channel_manager_->GetCapabilities(); }
- Call *CreateCall(bool video = false, bool mux = false);
+ Call *CreateCall();
void DestroyCall(Call *call);
Call *GetFocus();
@@ -115,7 +130,7 @@
sigslot::signal1<Call *> SignalCallDestroy;
sigslot::repeater0<> SignalDevicesChange;
- SessionDescription* CreateOffer(bool video = false, bool set_ssrc = false);
+ SessionDescription* CreateOffer(const CallOptions& options);
SessionDescription* CreateAnswer(const SessionDescription* offer);
SecureMediaPolicy secure() const { return secure_; }
@@ -153,10 +168,14 @@
class MediaContentDescription : public ContentDescription {
public:
- MediaContentDescription() : ssrc_(0), bandwidth_bps_(-1),
- ssrc_set_(false), auto_bandwidth_(true),
- rtcp_mux_(false), rtp_headers_disabled_(false),
- crypto_required_(false) {}
+ MediaContentDescription()
+ : ssrc_(0),
+ ssrc_set_(false),
+ rtcp_mux_(false),
+ rtp_headers_disabled_(false),
+ crypto_required_(false),
+ bandwidth_(kAutoBandwidth) {
+ }
virtual MediaType type() const = 0;
@@ -186,21 +205,17 @@
crypto_required_ = crypto;
}
- int bandwidth_bps() const { return bandwidth_bps_; }
- void set_bandwidth_bps(int bps) { bandwidth_bps_ = bps; }
-
- bool auto_bandwidth() const { return auto_bandwidth_; }
- void set_auto_bandwidth(bool enable) { auto_bandwidth_ = enable; }
+ int bandwidth() const { return bandwidth_; }
+ void set_bandwidth(int bandwidth) { bandwidth_ = bandwidth; }
protected:
uint32 ssrc_;
- int bandwidth_bps_; // fixed or max video bandwidth
bool ssrc_set_;
- bool auto_bandwidth_; // if true, bandwidth_bps_ < 0 flags default limits
bool rtcp_mux_;
bool rtp_headers_disabled_;
std::vector<CryptoParams> cryptos_;
bool crypto_required_;
+ int bandwidth_;
};
template <class C>