Update to 0.5.3.
git-svn-id: http://libjingle.googlecode.com/svn/trunk@60 dd674b97-3498-5ee5-1854-bdd07cd0ff33
diff --git a/CHANGELOG b/CHANGELOG
index e0c746e..73ffc71 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,11 @@
Libjingle
+0.5.3 - May 10, 2011
+ - Stream notification and selection.
+ - Better XEP-0045 support.
+ - Easier to create composite media engines where one part is fake.
+ - Make GtkVideoRenderer thread-safe.
+
0.5.2 - Jan 11, 2010
- Fixed build on Windows 7 with VS 2010
- Fixed build on Windows x64
diff --git a/talk/base/linux.cc b/talk/base/linux.cc
index b9f657e..056e0a0 100644
--- a/talk/base/linux.cc
+++ b/talk/base/linux.cc
@@ -129,6 +129,17 @@
#endif
}
+bool ProcCpuInfo::GetCpuFamily(int* id) {
+ int cpu_family = 0;
+
+ GetSectionIntValue(0, "cpu family", &cpu_family);
+
+ if (id) {
+ *id = cpu_family;
+ }
+ return true;
+}
+
bool ProcCpuInfo::GetSectionStringValue(size_t section_num,
const std::string& key,
std::string* result) {
diff --git a/talk/base/linux.h b/talk/base/linux.h
index 32f2cb6..c6151a4 100644
--- a/talk/base/linux.h
+++ b/talk/base/linux.h
@@ -100,6 +100,9 @@
// Obtains the number of physical CPU cores and places the value num.
virtual bool GetNumPhysicalCpus(int* num);
+ // Obtains the CPU family id.
+ virtual bool GetCpuFamily(int* id);
+
// Obtains the number of sections in /proc/cpuinfo, which may be greater
// than the number of CPUs (e.g. on ARM)
virtual bool GetSectionCount(size_t* count);
diff --git a/talk/base/nethelpers.cc b/talk/base/nethelpers.cc
index b877d78..b408749 100644
--- a/talk/base/nethelpers.cc
+++ b/talk/base/nethelpers.cc
@@ -57,22 +57,8 @@
}
}
-// The functions below are used to do gethostbyname, but with an allocated
-// instead of a static buffer.
-hostent* SafeGetHostByName(const char* hostname, int* herrno) {
- if (NULL == hostname || NULL == herrno) {
- return NULL;
- }
- hostent* result = NULL;
-#if defined(WIN32)
- // On Windows we have to allocate a buffer, and manually copy the hostent,
- // along with its embedded pointers.
- hostent* ent = gethostbyname(hostname);
- if (!ent) {
- *herrno = WSAGetLastError();
- return NULL;
- }
-
+#if defined(WIN32) || defined(ANDROID)
+static hostent* DeepCopyHostent(const hostent* ent) {
// Get the total number of bytes we need to copy, and allocate our buffer.
int num_aliases = 0, num_addrs = 0;
int total_len = sizeof(hostent);
@@ -88,7 +74,7 @@
}
total_len += sizeof(char*);
- result = static_cast<hostent*>(malloc(total_len));
+ hostent* result = static_cast<hostent*>(malloc(total_len));
if (NULL == result) {
return NULL;
}
@@ -119,7 +105,27 @@
p += ent->h_length;
}
result->h_addr_list[num_addrs] = NULL;
+
+ return result;
+}
+#endif
+// The functions below are used to do gethostbyname, but with an allocated
+// instead of a static buffer.
+hostent* SafeGetHostByName(const char* hostname, int* herrno) {
+ if (NULL == hostname || NULL == herrno) {
+ return NULL;
+ }
+ hostent* result = NULL;
+#if defined(WIN32)
+ // On Windows we have to allocate a buffer, and manually copy the hostent,
+ // along with its embedded pointers.
+ hostent* ent = gethostbyname(hostname);
+ if (!ent) {
+ *herrno = WSAGetLastError();
+ return NULL;
+ }
+ result = DeepCopyHostent(ent);
*herrno = 0;
#elif defined(LINUX) || defined(ANDROID)
// gethostbyname() is not thread safe, so we need to call gethostbyname_r()
@@ -150,6 +156,14 @@
free(buf);
return NULL;
}
+#if defined(ANDROID)
+ // Note that Android's version of gethostbyname_r has a bug such that the
+ // returned hostent contains pointers into thread-local storage. (See bug
+ // 4383723.) So we deep copy the result before returning.
+ hostent* deep_copy = DeepCopyHostent(result);
+ FreeHostEnt(result);
+ result = deep_copy;
+#endif
*herrno = 0;
#elif defined(OSX) || defined(IOS)
// Mac OS returns an object with everything allocated.
diff --git a/talk/base/sigslot.h b/talk/base/sigslot.h
index e9b85c7..e4af30e 100644
--- a/talk/base/sigslot.h
+++ b/talk/base/sigslot.h
@@ -78,8 +78,9 @@
#ifndef TALK_BASE_SIGSLOT_H__
#define TALK_BASE_SIGSLOT_H__
-#include <set>
#include <list>
+#include <set>
+#include <stdlib.h>
// On our copy of sigslot.h, we force single threading
#define SIGSLOT_PURE_ISO
diff --git a/talk/examples/call/callclient.cc b/talk/examples/call/callclient.cc
index 2bb51cd..0dd58a9 100644
--- a/talk/examples/call/callclient.cc
+++ b/talk/examples/call/callclient.cc
@@ -225,6 +225,7 @@
pmuc_domain_("groupchat.google.com"),
local_renderer_(NULL),
remote_renderer_(NULL),
+ static_views_accumulated_count_(0),
roster_(new RosterMap),
portallocator_flags_(0),
allow_local_ips_(false),
@@ -277,6 +278,7 @@
delete local_renderer_;
local_renderer_ = NULL;
}
+ RemoveAllStaticRenderedViews();
console_->SetPrompt(NULL);
console_->Print("call destroyed");
call_ = NULL;
@@ -371,6 +373,8 @@
void CallClient::OnCallCreate(cricket::Call* call) {
call->SignalSessionState.connect(this, &CallClient::OnSessionState);
+ call->SignalMediaSourcesUpdate.connect(
+ this, &CallClient::OnMediaSourcesUpdate);
}
void CallClient::OnSessionState(cricket::Call* call,
@@ -574,23 +578,13 @@
call_ = media_client_->CreateCall();
console_->SetPrompt(jid.Str().c_str());
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();
- for (buzz::Muc::MemberMap::iterator elem = members.begin();
- elem != members.end();
- ++elem) {
- AddStream(elem->second.audio_src_id(), elem->second.video_src_id());
- }
- }
}
media_client_->SetFocus(call_);
if (call_->video()) {
- call_->SetLocalRenderer(local_renderer_);
- // TODO: Call this once for every different remote SSRC
- // once we get to testing multiway video.
- call_->SetVideoRenderer(session_, 0, remote_renderer_);
+ if (!options.is_muc) {
+ call_->SetLocalRenderer(local_renderer_);
+ call_->SetVideoRenderer(session_, 0, remote_renderer_);
+ }
}
}
@@ -619,20 +613,6 @@
console_->Printf("Unable to voicemail %s.\n", to.Str().c_str());
}
-void CallClient::AddStream(uint32 audio_src_id, uint32 video_src_id) {
- if (audio_src_id || video_src_id) {
- console_->Printf("Adding stream (%u, %u)\n", audio_src_id, video_src_id);
- call_->AddStream(session_, audio_src_id, video_src_id);
- }
-}
-
-void CallClient::RemoveStream(uint32 audio_src_id, uint32 video_src_id) {
- if (audio_src_id || video_src_id) {
- console_->Printf("Removing stream (%u, %u)\n", audio_src_id, video_src_id);
- call_->RemoveStream(session_, audio_src_id, video_src_id);
- }
-}
-
void CallClient::Accept(const cricket::CallOptions& options) {
ASSERT(call_ && incoming_call_);
ASSERT(call_->sessions().size() == 1);
@@ -744,53 +724,8 @@
}
if (!status.available()) {
- // User is leaving the room.
- buzz::Muc::MemberMap::iterator elem =
- muc->members().find(status.jid().resource());
-
- ASSERT(elem != muc->members().end());
-
- // If user had src-ids, they have the left the room without explicitly
- // hanging-up; must tear down the stream if in a call to this room.
- if (call_ && session_->remote_name() == muc->jid().Str()) {
- RemoveStream(elem->second.audio_src_id(), elem->second.video_src_id());
- }
-
// Remove them from the room.
- muc->members().erase(elem);
- } else {
- // Either user has joined or something changed about them.
- // Note: The [] operator here will create a new entry if it does not
- // exist, which is what we want.
- buzz::MucStatus& member_status(
- muc->members()[status.jid().resource()]);
- if (call_ && session_->remote_name() == muc->jid().Str()) {
- // We are in a call to this muc. Must potentially update our streams.
- // The following code will correctly update our streams regardless of
- // whether the SSRCs have been removed, added, or changed and regardless
- // of whether that has been done to both or just one. This relies on the
- // fact that AddStream/RemoveStream do nothing for SSRC arguments that are
- // zero.
- uint32 remove_audio_src_id = 0;
- uint32 remove_video_src_id = 0;
- uint32 add_audio_src_id = 0;
- uint32 add_video_src_id = 0;
- if (member_status.audio_src_id() != status.audio_src_id()) {
- remove_audio_src_id = member_status.audio_src_id();
- add_audio_src_id = status.audio_src_id();
- }
- if (member_status.video_src_id() != status.video_src_id()) {
- remove_video_src_id = member_status.video_src_id();
- add_video_src_id = status.video_src_id();
- }
- // Remove the old SSRCs, if any.
- RemoveStream(remove_audio_src_id, remove_video_src_id);
- // Add the new SSRCs, if any.
- AddStream(add_audio_src_id, add_video_src_id);
- }
- // Update the status. This will use the compiler-generated copy
- // constructor, which is perfectly adequate for this class.
- member_status = status;
+ muc->members().erase(status.jid().resource());
}
}
@@ -905,3 +840,66 @@
void CallClient::SetVolume(const std::string& level) {
media_client_->SetOutputVolume(strtol(level.c_str(), NULL, 10));
}
+
+void CallClient::OnMediaSourcesUpdate(cricket::Call* call,
+ cricket::Session* session,
+ const cricket::MediaSources& sources) {
+ for (cricket::NamedSources::const_iterator it = sources.video.begin();
+ it != sources.video.end(); ++it) {
+ if (it->removed) {
+ RemoveStaticRenderedView(it->ssrc);
+ } else {
+ // TODO: Make dimensions and positions more configurable.
+ int offset = (50 * static_views_accumulated_count_) % 300;
+ AddStaticRenderedView(session, it->ssrc, 640, 400, 30,
+ offset, offset);
+ }
+ }
+
+ SendViewRequest(session);
+}
+
+// TODO: Would these methods to add and remove views make
+// more sense in call.cc? Would other clients use them?
+void CallClient::AddStaticRenderedView(
+ cricket::Session* session,
+ uint32 ssrc, int width, int height, int framerate,
+ int x_offset, int y_offset) {
+ StaticRenderedView rendered_view(
+ cricket::StaticVideoView(ssrc, width, height, framerate),
+ cricket::VideoRendererFactory::CreateGuiVideoRenderer(
+ x_offset, y_offset));
+ rendered_view.renderer->SetSize(width, height, 0);
+ call_->SetVideoRenderer(session, ssrc, rendered_view.renderer);
+ static_rendered_views_.push_back(rendered_view);
+ ++static_views_accumulated_count_;
+}
+
+bool CallClient::RemoveStaticRenderedView(uint32 ssrc) {
+ for (StaticRenderedViews::iterator it = static_rendered_views_.begin();
+ it != static_rendered_views_.end(); ++it) {
+ if (it->view.ssrc == ssrc) {
+ delete it->renderer;
+ static_rendered_views_.erase(it);
+ return true;
+ }
+ }
+ return false;
+}
+
+void CallClient::RemoveAllStaticRenderedViews() {
+ for (StaticRenderedViews::iterator it = static_rendered_views_.begin();
+ it != static_rendered_views_.end(); ++it) {
+ delete it->renderer;
+ }
+ static_rendered_views_.clear();
+}
+
+void CallClient::SendViewRequest(cricket::Session* session) {
+ cricket::ViewRequest request;
+ for (StaticRenderedViews::iterator it = static_rendered_views_.begin();
+ it != static_rendered_views_.end(); ++it) {
+ request.static_video_views.push_back(it->view);
+ }
+ call_->SendViewRequest(session, request);
+}
diff --git a/talk/examples/call/callclient.h b/talk/examples/call/callclient.h
index 12456cd..4c2da17 100644
--- a/talk/examples/call/callclient.h
+++ b/talk/examples/call/callclient.h
@@ -74,6 +74,19 @@
std::string status;
};
+struct StaticRenderedView {
+ StaticRenderedView(const cricket::StaticVideoView& view,
+ cricket::VideoRenderer* renderer) :
+ view(view),
+ renderer(renderer) {
+ }
+
+ cricket::StaticVideoView view;
+ cricket::VideoRenderer* renderer;
+};
+
+typedef std::vector<StaticRenderedView> StaticRenderedViews;
+
class CallClient: public sigslot::has_slots<> {
public:
explicit CallClient(buzz::XmppClient* xmpp_client);
@@ -144,6 +157,18 @@
void OnDevicesChange();
void OnFoundVoicemailJid(const buzz::Jid& to, const buzz::Jid& voicemail);
void OnVoicemailJidError(const buzz::Jid& to);
+ void OnMediaSourcesUpdate(cricket::Call* call,
+ cricket::Session* session,
+ const cricket::MediaSources& sources);
+
+ void AddStaticRenderedView(
+ cricket::Session* session,
+ uint32 ssrc, int width, int height, int framerate,
+ int x_offset, int y_offset);
+ bool RemoveStaticRenderedView(uint32 ssrc);
+ void RemoveAllStaticRenderedViews();
+ void SendViewRequest(cricket::Session* session);
+
static const std::string strerror(buzz::XmppEngine::Error err);
@@ -180,6 +205,8 @@
std::string pmuc_domain_;
cricket::VideoRenderer* local_renderer_;
cricket::VideoRenderer* remote_renderer_;
+ StaticRenderedViews static_rendered_views_;
+ uint32 static_views_accumulated_count_;
buzz::Status my_status_;
buzz::PresencePushTask* presence_push_;
diff --git a/talk/examples/call/presencepushtask.cc b/talk/examples/call/presencepushtask.cc
index f820030..ca7416d 100644
--- a/talk/examples/call/presencepushtask.cc
+++ b/talk/examples/call/presencepushtask.cc
@@ -213,42 +213,7 @@
void PresencePushTask::FillMucStatus(const Jid& from, const XmlElement* stanza,
MucStatus* s) {
- // First get the normal user status info. Happily, this is in the same
- // format as it is for user presence.
FillStatus(from, stanza, s);
-
- // Now look for src IDs, which will be present if this user is in a
- // multiway call to this MUC.
- const XmlElement* xstanza = stanza->FirstNamed(QN_MUC_USER_X);
- if (xstanza) {
- const XmlElement* media;
- for (media = xstanza->FirstNamed(QN_GOOGLE_MUC_USER_MEDIA);
- media; media = media->NextNamed(QN_GOOGLE_MUC_USER_MEDIA)) {
-
- const XmlElement* type = media->FirstNamed(QN_GOOGLE_MUC_USER_TYPE);
- if (!type) continue; // Shouldn't happen
-
- const XmlElement* src_id = media->FirstNamed(QN_GOOGLE_MUC_USER_SRC_ID);
- if (!src_id) continue; // Shouldn't happen
-
- char *endptr;
- uint32 src_id_num = strtoul(src_id->BodyText().c_str(), &endptr, 10);
- if (src_id->BodyText().c_str()[0] == '\0' || endptr[0] != '\0') {
- // String is not composed exclusively of leading whitespace plus a
- // number (shouldn't happen). Ignore it.
- continue;
- }
- // Else it's valid. Set it.
-
- if (type->BodyText() == "audio") {
- // This is the audio media element. Get the src-id.
- s->set_audio_src_id(src_id_num);
- } else if (type->BodyText() == "video") {
- // This is the video media element. Get the src-id.
- s->set_video_src_id(src_id_num);
- }
- }
- }
}
}
diff --git a/talk/examples/call/status.h b/talk/examples/call/status.h
index be4c2bd..92fe88c 100644
--- a/talk/examples/call/status.h
+++ b/talk/examples/call/status.h
@@ -224,19 +224,6 @@
};
class MucStatus : public Status {
-public:
- MucStatus() : audio_src_id_(0), video_src_id_(0) {}
- uint32 audio_src_id() const { return audio_src_id_; }
- uint32 video_src_id() const { return video_src_id_; }
- void set_audio_src_id(uint32 audio_src_id) {
- audio_src_id_ = audio_src_id;
- }
- void set_video_src_id(uint32 video_src_id) {
- video_src_id_ = video_src_id;
- }
-private:
- uint32 audio_src_id_;
- uint32 video_src_id_;
};
}
diff --git a/talk/libjingle.scons b/talk/libjingle.scons
index 5d77429..cc7947c 100644
--- a/talk/libjingle.scons
+++ b/talk/libjingle.scons
@@ -183,6 +183,7 @@
"session/phone/devicemanager.cc",
"session/phone/filemediaengine.cc",
"session/phone/mediaengine.cc",
+ "session/phone/mediamessages.cc",
"session/phone/mediamonitor.cc",
"session/phone/mediasessionclient.cc",
"session/phone/rtpdump.cc",
diff --git a/talk/p2p/base/constants.cc b/talk/p2p/base/constants.cc
index b069c74..e7025a0 100644
--- a/talk/p2p/base/constants.cc
+++ b/talk/p2p/base/constants.cc
@@ -34,6 +34,7 @@
const std::string NS_EMPTY("");
const std::string NS_JINGLE("urn:xmpp:jingle:1");
+const std::string NS_JINGLE_DRAFT("google:jingle");
const std::string NS_GINGLE("http://www.google.com/session");
// actions (aka <session> or <jingle>)
@@ -64,9 +65,7 @@
const std::string GINGLE_ACTION_REJECT("reject");
const std::string GINGLE_ACTION_TERMINATE("terminate");
const std::string GINGLE_ACTION_CANDIDATES("candidates");
-const std::string GINGLE_ACTION_NOTIFY("notify");
const std::string GINGLE_ACTION_UPDATE("update");
-const std::string GINGLE_ACTION_VIEW("view");
const std::string LN_ERROR("error");
const buzz::QName QN_GINGLE_REDIRECT(true, NS_GINGLE, "redirect");
@@ -194,33 +193,33 @@
const std::string STR_TERMINATE_INTERNAL_SERVER_ERROR("internal-server-error");
const std::string STR_TERMINATE_UNKNOWN_ERROR("unknown-error");
-// Session notify messages
-const buzz::QName QN_GINGLE_NOTIFY(true, NS_GINGLE, "notify");
-const buzz::QName QN_GINGLE_NOTIFY_NICK(true, cricket::NS_EMPTY, "nick");
-const buzz::QName QN_GINGLE_NOTIFY_SOURCE(true, NS_GINGLE, "source");
-const buzz::QName QN_GINGLE_NOTIFY_SOURCE_MTYPE(
- true, cricket::NS_EMPTY, "mtype");
-const buzz::QName QN_GINGLE_NOTIFY_SOURCE_SSRC(true, cricket::NS_EMPTY, "ssrc");
-const std::string GINGLE_NOTIFY_SOURCE_MTYPE_AUDIO("audio");
-const std::string GINGLE_NOTIFY_SOURCE_MTYPE_VIDEO("video");
-
-// Session view messages
-const buzz::QName QN_GINGLE_VIEW(true, cricket::NS_EMPTY, "view");
-const buzz::QName QN_GINGLE_VIEW_TYPE(true, cricket::NS_EMPTY, "type");
-const buzz::QName QN_GINGLE_VIEW_NICK(true, cricket::NS_EMPTY, "nick");
-const buzz::QName QN_GINGLE_VIEW_MEDIA_TYPE(true, cricket::NS_EMPTY, "mtype");
-const buzz::QName QN_GINGLE_VIEW_SSRC(true, cricket::NS_EMPTY, "ssrc");
-const std::string GINGLE_VIEW_TYPE_STATIC("static");
-const std::string GINGLE_VIEW_TYPE_DYNAMIC("dynamic");
-const std::string GINGLE_VIEW_MEDIA_TYPE_AUDIO("audio");
-const std::string GINGLE_VIEW_MEDIA_TYPE_VIDEO("video");
-const buzz::QName QN_GINGLE_VIEW_PARAMS(true, cricket::NS_EMPTY, "params");
-const buzz::QName QN_GINGLE_VIEW_PARAMS_WIDTH(true, cricket::NS_EMPTY, "width");
-const buzz::QName QN_GINGLE_VIEW_PARAMS_HEIGHT(
+// Draft view and notify messages.
+const buzz::QName QN_JINGLE_DRAFT_CONTENT_NAME(true, cricket::NS_EMPTY, "name");
+const std::string STR_JINGLE_DRAFT_CONTENT_NAME_VIDEO("video");
+const std::string STR_JINGLE_DRAFT_CONTENT_NAME_AUDIO("audio");
+const buzz::QName QN_JINGLE_DRAFT_NOTIFY(true, NS_JINGLE_DRAFT, "notify");
+const buzz::QName QN_JINGLE_DRAFT_SOURCE(
+ true, NS_JINGLE_DRAFT, "source");
+const buzz::QName QN_JINGLE_DRAFT_SOURCE_NICK(true, cricket::NS_EMPTY, "nick");
+const buzz::QName QN_JINGLE_DRAFT_SOURCE_NAME(true, cricket::NS_EMPTY, "name");
+const buzz::QName QN_JINGLE_DRAFT_SOURCE_USAGE(true, cricket::NS_EMPTY, "usage");
+const buzz::QName QN_JINGLE_DRAFT_SOURCE_STATE(true, cricket::NS_EMPTY, "state");
+const std::string STR_JINGLE_DRAFT_SOURCE_STATE_REMOVED("removed");
+const buzz::QName QN_JINGLE_DRAFT_SOURCE_SSRC(true, NS_JINGLE_DRAFT, "ssrc");
+const buzz::QName QN_JINGLE_DRAFT_VIEW(true, NS_JINGLE_DRAFT, "view");
+const buzz::QName QN_JINGLE_DRAFT_VIEW_TYPE(true, cricket::NS_EMPTY, "type");
+const std::string STR_JINGLE_DRAFT_VIEW_TYPE_NONE("none");
+const std::string STR_JINGLE_DRAFT_VIEW_TYPE_STATIC("static");
+const buzz::QName QN_JINGLE_DRAFT_VIEW_SSRC(true, cricket::NS_EMPTY, "ssrc");
+const buzz::QName QN_JINGLE_DRAFT_VIEW_PARAMS(true, NS_JINGLE_DRAFT, "params");
+const buzz::QName QN_JINGLE_DRAFT_VIEW_PARAMS_WIDTH(
+ true, cricket::NS_EMPTY, "width");
+const buzz::QName QN_JINGLE_DRAFT_VIEW_PARAMS_HEIGHT(
true, cricket::NS_EMPTY, "height");
-const buzz::QName QN_GINGLE_VIEW_PARAMS_FRAMERATE(
+const buzz::QName QN_JINGLE_DRAFT_VIEW_PARAMS_FRAMERATE(
true, cricket::NS_EMPTY, "framerate");
-
+const buzz::QName QN_JINGLE_DRAFT_VIEW_PARAMS_PREFERENCE(
+ true, cricket::NS_EMPTY, "preference");
// old stuff
#ifdef FEATURE_ENABLE_VOICEMAIL
diff --git a/talk/p2p/base/constants.h b/talk/p2p/base/constants.h
index 9dd4d24..5ffeca8 100644
--- a/talk/p2p/base/constants.h
+++ b/talk/p2p/base/constants.h
@@ -44,6 +44,7 @@
extern const std::string NS_EMPTY;
extern const std::string NS_JINGLE;
+extern const std::string NS_JINGLE_DRAFT;
extern const std::string NS_GINGLE;
enum SignalingProtocol {
@@ -80,9 +81,7 @@
extern const std::string GINGLE_ACTION_REJECT;
extern const std::string GINGLE_ACTION_TERMINATE;
extern const std::string GINGLE_ACTION_CANDIDATES;
-extern const std::string GINGLE_ACTION_NOTIFY;
extern const std::string GINGLE_ACTION_UPDATE;
-extern const std::string GINGLE_ACTION_VIEW;
extern const std::string LN_ERROR;
extern const buzz::QName QN_GINGLE_REDIRECT;
@@ -209,29 +208,29 @@
extern const std::string STR_TERMINATE_INTERNAL_SERVER_ERROR;
extern const std::string STR_TERMINATE_UNKNOWN_ERROR;
-// Session notify messages
-extern const buzz::QName QN_GINGLE_NOTIFY;
-extern const buzz::QName QN_GINGLE_NOTIFY_NICK;
-extern const buzz::QName QN_GINGLE_NOTIFY_SOURCE;
-extern const buzz::QName QN_GINGLE_NOTIFY_SOURCE_MTYPE;
-extern const buzz::QName QN_GINGLE_NOTIFY_SOURCE_SSRC;
-extern const std::string GINGLE_NOTIFY_SOURCE_MTYPE_AUDIO;
-extern const std::string GINGLE_NOTIFY_SOURCE_MTYPE_VIDEO;
-
-// Session view messages
-extern const buzz::QName QN_GINGLE_VIEW;
-extern const buzz::QName QN_GINGLE_VIEW_TYPE;
-extern const buzz::QName QN_GINGLE_VIEW_NICK;
-extern const buzz::QName QN_GINGLE_VIEW_MEDIA_TYPE;
-extern const buzz::QName QN_GINGLE_VIEW_SSRC;
-extern const std::string GINGLE_VIEW_TYPE_STATIC;
-extern const std::string GINGLE_VIEW_TYPE_DYNAMIC;
-extern const std::string GINGLE_VIEW_MEDIA_TYPE_AUDIO;
-extern const std::string GINGLE_VIEW_MEDIA_TYPE_VIDEO;
-extern const buzz::QName QN_GINGLE_VIEW_PARAMS;
-extern const buzz::QName QN_GINGLE_VIEW_PARAMS_WIDTH;
-extern const buzz::QName QN_GINGLE_VIEW_PARAMS_HEIGHT;
-extern const buzz::QName QN_GINGLE_VIEW_PARAMS_FRAMERATE;
+// Draft view and notify messages.
+extern const buzz::QName QN_JINGLE_DRAFT_CONTENT_NAME;
+extern const std::string STR_JINGLE_DRAFT_CONTENT_NAME_VIDEO;
+extern const std::string STR_JINGLE_DRAFT_CONTENT_NAME_AUDIO;
+extern const buzz::QName QN_JINGLE_DRAFT_NOTIFY;
+extern const buzz::QName QN_JINGLE_DRAFT_SOURCE;
+extern const buzz::QName QN_JINGLE_DRAFT_SOURCE_NICK;
+extern const buzz::QName QN_JINGLE_DRAFT_SOURCE_NAME;
+extern const buzz::QName QN_JINGLE_DRAFT_SOURCE_USAGE;
+extern const buzz::QName QN_JINGLE_DRAFT_SOURCE_STATE;
+extern const std::string STR_JINGLE_DRAFT_SOURCE_STATE_REMOVED;
+extern const buzz::QName QN_JINGLE_DRAFT_SOURCE_SSRC;
+extern const buzz::QName QN_JINGLE_DRAFT_VIEW;
+extern const buzz::QName QN_JINGLE_DRAFT_VIEW_NAME;
+extern const buzz::QName QN_JINGLE_DRAFT_VIEW_TYPE;
+extern const std::string STR_JINGLE_DRAFT_VIEW_TYPE_NONE;
+extern const std::string STR_JINGLE_DRAFT_VIEW_TYPE_STATIC;
+extern const buzz::QName QN_JINGLE_DRAFT_VIEW_SSRC;
+extern const buzz::QName QN_JINGLE_DRAFT_VIEW_PARAMS;
+extern const buzz::QName QN_JINGLE_DRAFT_VIEW_PARAMS_WIDTH;
+extern const buzz::QName QN_JINGLE_DRAFT_VIEW_PARAMS_HEIGHT;
+extern const buzz::QName QN_JINGLE_DRAFT_VIEW_PARAMS_FRAMERATE;
+extern const buzz::QName QN_JINGLE_DRAFT_VIEW_PARAMS_PREFERENCE;
// old stuff
#ifdef FEATURE_ENABLE_VOICEMAIL
diff --git a/talk/p2p/base/p2ptransportchannel.cc b/talk/p2p/base/p2ptransportchannel.cc
index c5d4a53..17f52a7 100644
--- a/talk/p2p/base/p2ptransportchannel.cc
+++ b/talk/p2p/base/p2ptransportchannel.cc
@@ -169,6 +169,7 @@
transport_(transport),
allocator_(allocator),
worker_thread_(talk_base::Thread::Current()),
+ incoming_only_(false),
waiting_for_signaling_(false),
error_(0),
best_connection_(NULL),
@@ -282,8 +283,9 @@
std::vector<RemoteCandidate>::iterator iter;
for (iter = remote_candidates_.begin(); iter != remote_candidates_.end();
- ++iter)
+ ++iter) {
CreateConnection(port, *iter, iter->origin_port(), false);
+ }
SortConnections();
}
@@ -419,6 +421,12 @@
}
} else {
Port::CandidateOrigin origin = GetOrigin(port, origin_port);
+
+ // Don't create connection if this is a candidate we received in a
+ // message and we are not allowed to make outgoing connections.
+ if (origin == cricket::Port::ORIGIN_MESSAGE && incoming_only_)
+ return false;
+
connection = port->CreateConnection(remote_candidate, origin);
if (!connection)
return false;
diff --git a/talk/p2p/base/p2ptransportchannel.h b/talk/p2p/base/p2ptransportchannel.h
index 805e159..0083d23 100644
--- a/talk/p2p/base/p2ptransportchannel.h
+++ b/talk/p2p/base/p2ptransportchannel.h
@@ -93,6 +93,8 @@
const std::vector<Connection *>& connections() const { return connections_; }
Connection* best_connection() const { return best_connection_; }
+ void set_incoming_only(bool value) { incoming_only_ = value; }
+
// Handler for internal messages.
virtual void OnMessage(talk_base::Message *pmsg);
@@ -141,6 +143,7 @@
P2PTransport* transport_;
PortAllocator *allocator_;
talk_base::Thread *worker_thread_;
+ bool incoming_only_;
bool waiting_for_signaling_;
int error_;
std::vector<PortAllocatorSession*> allocator_sessions_;
diff --git a/talk/p2p/base/parsing.h b/talk/p2p/base/parsing.h
index 4143c2c..7931431 100644
--- a/talk/p2p/base/parsing.h
+++ b/talk/p2p/base/parsing.h
@@ -36,6 +36,8 @@
namespace cricket {
+typedef std::vector<buzz::XmlElement*> XmlElements;
+
// We decided "bool Parse(in, out*, error*)" is generally the best
// parse signature. "out Parse(in)" doesn't allow for errors.
// "error* Parse(in, out*)" doesn't allow flexible memory management.
diff --git a/talk/p2p/base/port.cc b/talk/p2p/base/port.cc
index e6bab9c..800b9b9 100644
--- a/talk/p2p/base/port.cc
+++ b/talk/p2p/base/port.cc
@@ -213,13 +213,15 @@
} else if (msg->type() == STUN_BINDING_REQUEST) {
SignalUnknownAddress(this, addr, msg, remote_username);
} else {
- // NOTE(tschmelcher): This is benign. It occurs if we pruned a
- // connection for this port while it had STUN requests in flight, because
- // we then get back responses for them, which this code correctly does not
- // handle.
- LOG_J(LS_INFO, this) << "Received unexpected STUN message type ("
- << msg->type() << ") from unknown address ("
- << addr.ToString() << ")";
+ // NOTE(tschmelcher): STUN_BINDING_RESPONSE is benign. It occurs if we
+ // pruned a connection for this port while it had STUN requests in flight,
+ // because we then get back responses for them, which this code correctly
+ // does not handle.
+ if (msg->type() != STUN_BINDING_RESPONSE) {
+ LOG_J(LS_ERROR, this) << "Received unexpected STUN message type ("
+ << msg->type() << ") from unknown address ("
+ << addr.ToString() << ")";
+ }
delete msg;
}
}
diff --git a/talk/p2p/base/session.cc b/talk/p2p/base/session.cc
index 1839118..eb34e0a 100644
--- a/talk/p2p/base/session.cc
+++ b/talk/p2p/base/session.cc
@@ -644,13 +644,8 @@
case ACTION_TRANSPORT_ACCEPT:
valid = OnTransportAcceptMessage(msg, &error);
break;
- case ACTION_NOTIFY:
case ACTION_UPDATE:
- // TODO: Process these non-standard messages, but
- // only once we figure out how in a jingle-specific way (or
- // remove the need altogether). For now, just don't send an
- // error back, because it disrupts call establishment.
- valid = true;
+ valid = OnUpdateMessage(msg, &error);
break;
default:
valid = BadMessage(buzz::QN_STANZA_BAD_REQUEST,
@@ -791,9 +786,8 @@
return true;
}
-// Only used by app/win32/fileshare.cc.
bool Session::OnInfoMessage(const SessionMessage& msg) {
- SignalInfoMessage(this, CopyOfXmlChildren(msg.action_elem));
+ SignalInfoMessage(this, msg.action_elem);
return true;
}
@@ -833,6 +827,13 @@
return true;
}
+bool Session::OnUpdateMessage(const SessionMessage& msg,
+ MessageError* error) {
+ // TODO: Once someone needs it, parse the message
+ // into a data structure and signal out.
+ return true;
+}
+
bool BareJidsEqual(const std::string& name1,
const std::string& name2) {
buzz::Jid jid1(name1);
diff --git a/talk/p2p/base/session.h b/talk/p2p/base/session.h
index 95a201b..6a8750c 100644
--- a/talk/p2p/base/session.h
+++ b/talk/p2p/base/session.h
@@ -339,11 +339,12 @@
virtual bool Reject(const std::string& reason);
virtual bool TerminateWithReason(const std::string& reason);
- // The two clients in the session may also send one another arbitrary XML
- // messages, which are called "info" messages. Both of these functions take
- // ownership of the XmlElements and delete them when done.
+ // The two clients in the session may also send one another
+ // arbitrary XML messages, which are called "info" messages. Sending
+ // takes ownership of the given elements. The signal does not; the
+ // parent element will be deleted after the signal.
bool SendInfoMessage(const XmlElements& elems);
- sigslot::signal2<Session*, const XmlElements&> SignalInfoMessage;
+ sigslot::signal2<Session*, const buzz::XmlElement*> SignalInfoMessage;
// Maps passed to serialization functions.
TransportParserMap GetTransportParsers();
@@ -512,6 +513,7 @@
bool OnTerminateMessage(const SessionMessage& msg, MessageError* error);
bool OnTransportInfoMessage(const SessionMessage& msg, MessageError* error);
bool OnTransportAcceptMessage(const SessionMessage& msg, MessageError* error);
+ bool OnUpdateMessage(const SessionMessage& msg, MessageError* error);
bool OnRedirectError(const SessionRedirect& redirect, SessionError* error);
// Verifies that we are in the appropriate state to receive this message.
diff --git a/talk/p2p/base/sessionmessages.cc b/talk/p2p/base/sessionmessages.cc
index 5c91460..d15c9e3 100644
--- a/talk/p2p/base/sessionmessages.cc
+++ b/talk/p2p/base/sessionmessages.cc
@@ -32,8 +32,6 @@
#include "talk/base/logging.h"
#include "talk/base/scoped_ptr.h"
#include "talk/base/stringutils.h"
-#include "talk/xmllite/xmlconstants.h"
-#include "talk/xmpp/constants.h"
#include "talk/p2p/base/constants.h"
#include "talk/p2p/base/p2ptransport.h"
#include "talk/p2p/base/parsing.h"
@@ -41,6 +39,7 @@
#include "talk/p2p/base/sessiondescription.h"
#include "talk/p2p/base/transport.h"
#include "talk/xmllite/xmlconstants.h"
+#include "talk/xmpp/constants.h"
namespace cricket {
@@ -73,8 +72,8 @@
return ACTION_TRANSPORT_INFO;
if (type == JINGLE_ACTION_TRANSPORT_ACCEPT)
return ACTION_TRANSPORT_ACCEPT;
- if (type == GINGLE_ACTION_NOTIFY)
- return ACTION_NOTIFY;
+ if (type == JINGLE_ACTION_DESCRIPTION_INFO)
+ return ACTION_UPDATE;
if (type == GINGLE_ACTION_UPDATE)
return ACTION_UPDATE;
diff --git a/talk/p2p/base/sessionmessages.h b/talk/p2p/base/sessionmessages.h
index 82e63bd..ef7cc69 100644
--- a/talk/p2p/base/sessionmessages.h
+++ b/talk/p2p/base/sessionmessages.h
@@ -32,10 +32,11 @@
#include <vector>
#include <map>
-#include "talk/xmllite/xmlelement.h"
-#include "talk/p2p/base/constants.h"
-#include "talk/p2p/base/sessiondescription.h" // Needed to delete contents.
#include "talk/base/basictypes.h"
+#include "talk/p2p/base/constants.h"
+#include "talk/p2p/base/parsing.h"
+#include "talk/p2p/base/sessiondescription.h" // Needed to delete contents.
+#include "talk/xmllite/xmlelement.h"
namespace cricket {
@@ -45,7 +46,6 @@
class ContentParser;
class TransportParser;
-typedef std::vector<buzz::XmlElement*> XmlElements;
typedef std::vector<Candidate> Candidates;
typedef std::map<std::string, ContentParser*> ContentParserMap;
typedef std::map<std::string, TransportParser*> TransportParserMap;
@@ -62,10 +62,6 @@
ACTION_TRANSPORT_INFO,
ACTION_TRANSPORT_ACCEPT,
- // TODO: Make better names for these when we think of a
- // "jingley" way of signaling them. Even better, remove them from
- // being needed at all.
- ACTION_NOTIFY,
ACTION_UPDATE,
};
@@ -160,6 +156,7 @@
std::string target;
};
+
bool IsSessionMessage(const buzz::XmlElement* stanza);
bool ParseSessionMessage(const buzz::XmlElement* stanza,
SessionMessage* msg,
diff --git a/talk/session/phone/call.cc b/talk/session/phone/call.cc
index f2c3960..a150688 100644
--- a/talk/session/phone/call.cc
+++ b/talk/session/phone/call.cc
@@ -132,6 +132,30 @@
TerminateSession(*it);
}
+bool Call::SendViewRequest(Session* session,
+ const ViewRequest& view_request) {
+ StaticVideoViews::const_iterator it;
+ for (it = view_request.static_video_views.begin();
+ it != view_request.static_video_views.end(); ++it) {
+ const NamedSource* found_source =
+ media_sources.GetVideoSourceBySsrc(it->ssrc);
+ if (!found_source) {
+ LOG(LS_WARNING) <<
+ "Tried sending view request for bad ssrc: " << it->ssrc;
+ return false;
+ }
+ }
+
+ XmlElements elems;
+ WriteError error;
+ if (!WriteViewRequest(CN_VIDEO, view_request, &elems, &error)) {
+ LOG(LS_ERROR) << "Couldn't write out view request: " << error.text;
+ return false;
+ }
+
+ return session->SendInfoMessage(elems);
+}
+
void Call::SetLocalRenderer(VideoRenderer* renderer) {
local_renderer_ = renderer;
if (session_client_->GetFocus() == this) {
@@ -147,25 +171,31 @@
}
}
-void Call::AddStream(BaseSession *session,
- uint32 voice_ssrc, uint32 video_ssrc) {
+void Call::AddVoiceStream(BaseSession *session, uint32 voice_ssrc) {
VoiceChannel *voice_channel = GetVoiceChannel(session);
- VideoChannel *video_channel = GetVideoChannel(session);
if (voice_channel && voice_ssrc) {
voice_channel->AddStream(voice_ssrc);
}
+}
+
+void Call::AddVideoStream(BaseSession *session, uint32 video_ssrc) {
+ VideoChannel *video_channel = GetVideoChannel(session);
if (video_channel && video_ssrc) {
- video_channel->AddStream(video_ssrc, voice_ssrc);
+ // TODO: Do we need the audio_ssrc here?
+ // It doesn't seem to be used.
+ video_channel->AddStream(video_ssrc, 0U);
}
}
-void Call::RemoveStream(BaseSession *session,
- uint32 voice_ssrc, uint32 video_ssrc) {
+void Call::RemoveVoiceStream(BaseSession *session, uint32 voice_ssrc) {
VoiceChannel *voice_channel = GetVoiceChannel(session);
- VideoChannel *video_channel = GetVideoChannel(session);
if (voice_channel && voice_ssrc) {
voice_channel->RemoveStream(voice_ssrc);
}
+}
+
+void Call::RemoveVideoStream(BaseSession *session, uint32 video_ssrc) {
+ VideoChannel *video_channel = GetVideoChannel(session);
if (video_channel && video_ssrc) {
video_channel->RemoveStream(video_ssrc);
}
@@ -240,6 +270,7 @@
sessions_.push_back(session);
session->SignalState.connect(this, &Call::OnSessionState);
session->SignalError.connect(this, &Call::OnSessionError);
+ session->SignalInfoMessage.connect(this, &Call::OnSessionInfo);
session->SignalReceivedTerminateReason
.connect(this, &Call::OnReceivedTerminateReason);
@@ -490,6 +521,78 @@
SignalSessionError(this, session, error);
}
+void Call::OnSessionInfo(Session *session,
+ const buzz::XmlElement* action_elem) {
+ // We have a different list of "updates" because we only want to
+ // signal the sources that were added or removed. We want to filter
+ // out un-changed sources.
+ cricket::MediaSources updates;
+
+ if (IsSourcesNotify(action_elem)) {
+ MediaSources sources;
+ ParseError error;
+ if (!ParseSourcesNotify(action_elem, session->remote_description(),
+ &sources, &error)) {
+ // TODO: Is there a way we can signal an IQ error
+ // back to the sender?
+ LOG(LS_WARNING) << "Invalid sources notify message: " << error.text;
+ return;
+ }
+
+ NamedSources::iterator it;
+ for (it = sources.audio.begin(); it != sources.audio.end(); ++it) {
+ const NamedSource* found;
+ if (it->ssrc_set) {
+ found = media_sources.GetAudioSourceBySsrc(it->ssrc);
+ } else {
+ // For backwards compatibility, we remove by nick.
+ // TODO: Remove once all senders use explicit remove by ssrc.
+ found = media_sources.GetFirstAudioSourceByNick(it->nick);
+ it->SetSsrc(found->ssrc);
+ it->removed = true;
+ }
+ if (it->removed && found) {
+ LOG(LS_INFO) << "Remove voice stream: " << found->ssrc;
+ RemoveVoiceStream(session, found->ssrc);
+ media_sources.RemoveAudioSourceBySsrc(it->ssrc);
+ updates.audio.push_back(*it);
+ } else if (!it->removed && !found) {
+ LOG(LS_INFO) << "Add voice stream: " << it->ssrc;
+ AddVoiceStream(session, it->ssrc);
+ media_sources.AddAudioSource(*it);
+ updates.audio.push_back(*it);
+ }
+ }
+ for (it = sources.video.begin(); it != sources.video.end(); ++it) {
+ const NamedSource* found;
+ if (it->ssrc_set) {
+ found = media_sources.GetVideoSourceBySsrc(it->ssrc);
+ } else {
+ // For backwards compatibility, we remove by nick.
+ // TODO: Remove once all senders use explicit remove by ssrc.
+ found = media_sources.GetFirstVideoSourceByNick(it->nick);
+ it->SetSsrc(found->ssrc);
+ it->removed = true;
+ }
+ if (it->removed && found) {
+ LOG(LS_INFO) << "Remove video stream: " << found->ssrc;
+ RemoveVideoStream(session, found->ssrc);
+ media_sources.RemoveVideoSourceBySsrc(it->ssrc);
+ updates.video.push_back(*it);
+ } else if (!it->removed && !found) {
+ LOG(LS_INFO) << "Add video stream: " << it->ssrc;
+ AddVideoStream(session, it->ssrc);
+ media_sources.AddVideoSource(*it);
+ updates.video.push_back(*it);
+ }
+ }
+
+ if (!updates.audio.empty() || !updates.video.empty()) {
+ SignalMediaSourcesUpdate(this, session, updates);
+ }
+ }
+}
+
void Call::OnReceivedTerminateReason(Session *session,
const std::string &reason) {
session_client_->session_manager()->signaling_thread()->Clear(this,
diff --git a/talk/session/phone/call.h b/talk/session/phone/call.h
index 8e9bcdc..06c3981 100644
--- a/talk/session/phone/call.h
+++ b/talk/session/phone/call.h
@@ -37,6 +37,7 @@
#include "talk/p2p/client/socketmonitor.h"
#include "talk/xmpp/jid.h"
#include "talk/session/phone/audiomonitor.h"
+#include "talk/session/phone/mediamessages.h"
#include "talk/session/phone/voicechannel.h"
namespace cricket {
@@ -54,11 +55,11 @@
void RejectSession(BaseSession *session);
void TerminateSession(BaseSession *session);
void Terminate();
+ bool SendViewRequest(Session* session,
+ const ViewRequest& view_request);
void SetLocalRenderer(VideoRenderer* renderer);
void SetVideoRenderer(BaseSession *session, uint32 ssrc,
VideoRenderer* renderer);
- void AddStream(BaseSession *session, uint32 voice_ssrc, uint32 video_ssrc);
- void RemoveStream(BaseSession *session, uint32 voice_ssrc, uint32 video_ssrc);
void StartConnectionMonitor(BaseSession *session, int cms);
void StopConnectionMonitor(BaseSession *session);
void StartAudioMonitor(BaseSession *session, int cms);
@@ -96,11 +97,15 @@
sigslot::signal2<Call *, const std::vector<ConnectionInfo> &>
SignalVideoConnectionMonitor;
sigslot::signal2<Call *, const VideoMediaInfo&> SignalVideoMediaMonitor;
+ sigslot::signal3<Call *,
+ Session *,
+ const MediaSources&> SignalMediaSourcesUpdate;
private:
void OnMessage(talk_base::Message *message);
void OnSessionState(BaseSession *session, BaseSession::State state);
void OnSessionError(BaseSession *session, Session::Error error);
+ void OnSessionInfo(Session *session, const buzz::XmlElement* action_elem);
void OnReceivedTerminateReason(Session *session, const std::string &reason);
void IncomingSession(Session *session, const SessionDescription* offer);
// Returns true on success.
@@ -117,11 +122,16 @@
void OnMediaMonitor(VideoChannel *channel, const VideoMediaInfo& info);
VoiceChannel* GetVoiceChannel(BaseSession* session);
VideoChannel* GetVideoChannel(BaseSession* session);
+ void AddVoiceStream(BaseSession *session, uint32 voice_ssrc);
+ void AddVideoStream(BaseSession *session, uint32 video_ssrc);
+ void RemoveVoiceStream(BaseSession *session, uint32 voice_ssrc);
+ void RemoveVideoStream(BaseSession *session, uint32 video_ssrc);
void ContinuePlayDTMF();
uint32 id_;
MediaSessionClient *session_client_;
std::vector<Session *> sessions_;
+ MediaSources media_sources;
std::map<std::string, VoiceChannel *> voice_channel_map_;
std::map<std::string, VideoChannel *> video_channel_map_;
VideoRenderer* local_renderer_;
diff --git a/talk/session/phone/channel.cc b/talk/session/phone/channel.cc
index 92010b5..d670bf1 100644
--- a/talk/session/phone/channel.cc
+++ b/talk/session/phone/channel.cc
@@ -1018,11 +1018,16 @@
Send(MSG_SENDINTRAFRAME);
return true;
}
+
bool VideoChannel::RequestIntraFrame() {
Send(MSG_REQUESTINTRAFRAME);
return true;
}
+void VideoChannel::EnableCpuAdaptation(bool enable) {
+ Send(enable ? MSG_ENABLECPUADAPTATION : MSG_DISABLECPUADAPTATION);
+}
+
void VideoChannel::ChangeState() {
// render incoming data if we are the active call
// we receive data on the default channel and multiplexed streams
@@ -1167,6 +1172,12 @@
case MSG_REQUESTINTRAFRAME:
RequestIntraFrame_w();
break;
+ case MSG_ENABLECPUADAPTATION:
+ EnableCpuAdaptation_w(true);
+ break;
+ case MSG_DISABLECPUADAPTATION:
+ EnableCpuAdaptation_w(false);
+ break;
case MSG_CHANNEL_ERROR: {
const VideoChannelErrorMessageData* data =
static_cast<VideoChannelErrorMessageData*>(pmsg->pdata);
diff --git a/talk/session/phone/channel.h b/talk/session/phone/channel.h
index e1172dd..8b33386 100644
--- a/talk/session/phone/channel.h
+++ b/talk/session/phone/channel.h
@@ -70,7 +70,9 @@
MSG_REQUESTINTRAFRAME = 20,
MSG_RTPPACKET = 22,
MSG_RTCPPACKET = 23,
- MSG_CHANNEL_ERROR = 24
+ MSG_CHANNEL_ERROR = 24,
+ MSG_ENABLECPUADAPTATION = 25,
+ MSG_DISABLECPUADAPTATION = 26
};
// BaseChannel contains logic common to voice and video, including
@@ -402,6 +404,7 @@
bool SendIntraFrame();
bool RequestIntraFrame();
+ void EnableCpuAdaptation(bool enable);
sigslot::signal3<VideoChannel*, uint32, VideoMediaChannel::Error>
SignalMediaError;
@@ -425,6 +428,12 @@
void RequestIntraFrame_w() {
media_channel()->RequestIntraFrame();
}
+ void EnableCpuAdaptation_w(bool enable) {
+ // TODO: The following call will clear all other options, which is
+ // OK now since SetOptions is not used in video media channel. In the
+ // future, add GetOptions() method and change the options.
+ media_channel()->SetOptions(enable ? OPT_CPU_ADAPTATION : 0);
+ }
struct RenderMessageData : public talk_base::MessageData {
RenderMessageData(uint32 s, VideoRenderer* r) : ssrc(s), renderer(r) {}
diff --git a/talk/session/phone/channelmanager.h b/talk/session/phone/channelmanager.h
index 06ab92e..3b34e62 100644
--- a/talk/session/phone/channelmanager.h
+++ b/talk/session/phone/channelmanager.h
@@ -79,15 +79,6 @@
void GetSupportedAudioCodecs(std::vector<AudioCodec>* codecs) const;
void GetSupportedVideoCodecs(std::vector<VideoCodec>* codecs) const;
- // Determines if a specific audio or video codec is supported.
- // Can be called before starting the media engine.
- bool FindAudioCodec(const AudioCodec& codec) const {
- return media_engine_->FindAudioCodec(codec);
- }
- bool FindVideoCodec(const VideoCodec& video_codec) const {
- return media_engine_->FindVideoCodec(video_codec);
- }
-
// Indicates whether the media engine is started.
bool initialized() const { return initialized_; }
// Starts up the media engine.
diff --git a/talk/session/phone/filemediaengine.cc b/talk/session/phone/filemediaengine.cc
index 6e86bd2..3e7e517 100644
--- a/talk/session/phone/filemediaengine.cc
+++ b/talk/session/phone/filemediaengine.cc
@@ -58,20 +58,61 @@
}
VoiceMediaChannel* FileMediaEngine::CreateChannel() {
- if (!voice_input_filename_.empty() || !voice_output_filename_.empty()) {
- return new FileVoiceChannel(voice_input_filename_, voice_output_filename_);
- } else {
+ talk_base::FileStream* input_file_stream = NULL;
+ talk_base::FileStream* output_file_stream = NULL;
+
+ if (voice_input_filename_.empty() && voice_output_filename_.empty())
return NULL;
+ if (!voice_input_filename_.empty()) {
+ input_file_stream = talk_base::Filesystem::OpenFile(
+ talk_base::Pathname(voice_input_filename_), "rb");
+ if (!input_file_stream) {
+ LOG(LS_ERROR) << "Not able to open the input audio stream file.";
+ return NULL;
+ }
}
+
+ if (!voice_output_filename_.empty()) {
+ output_file_stream = talk_base::Filesystem::OpenFile(
+ talk_base::Pathname(voice_output_filename_), "wb");
+ if (!output_file_stream) {
+ delete input_file_stream;
+ LOG(LS_ERROR) << "Not able to open the output audio stream file.";
+ return NULL;
+ }
+ }
+
+ return new FileVoiceChannel(input_file_stream, output_file_stream);
}
VideoMediaChannel* FileMediaEngine::CreateVideoChannel(
VoiceMediaChannel* voice_ch) {
- if (!video_input_filename_.empty() || !video_output_filename_.empty()) {
- return new FileVideoChannel(video_input_filename_, video_output_filename_);
- } else {
- return NULL;
+ talk_base::FileStream* input_file_stream = NULL;
+ talk_base::FileStream* output_file_stream = NULL;
+
+ if (video_input_filename_.empty() && video_output_filename_.empty())
+ return NULL;
+
+ if (!video_input_filename_.empty()) {
+ input_file_stream = talk_base::Filesystem::OpenFile(
+ talk_base::Pathname(video_input_filename_), "rb");
+ if (!input_file_stream) {
+ LOG(LS_ERROR) << "Not able to open the input video stream file.";
+ return NULL;
+ }
}
+
+ if (!video_output_filename_.empty()) {
+ output_file_stream = talk_base::Filesystem::OpenFile(
+ talk_base::Pathname(video_output_filename_), "wb");
+ if (!output_file_stream) {
+ delete input_file_stream;
+ LOG(LS_ERROR) << "Not able to open the output video stream file.";
+ return NULL;
+ }
+ }
+
+ return new FileVideoChannel(input_file_stream, output_file_stream);
}
///////////////////////////////////////////////////////////////////////////
@@ -80,8 +121,9 @@
class RtpSenderReceiver
: public talk_base::Thread, public talk_base::MessageHandler {
public:
- RtpSenderReceiver(MediaChannel* channel, const std::string& in_file,
- const std::string& out_file);
+ RtpSenderReceiver(MediaChannel* channel,
+ talk_base::StreamInterface* input_file_stream,
+ talk_base::StreamInterface* output_file_stream);
// Called by media channel. Context: media channel thread.
bool SetSend(bool send);
@@ -117,14 +159,14 @@
///////////////////////////////////////////////////////////////////////////
// Implementation of RtpSenderReceiver.
///////////////////////////////////////////////////////////////////////////
-RtpSenderReceiver::RtpSenderReceiver(MediaChannel* channel,
- const std::string& in_file,
- const std::string& out_file)
+RtpSenderReceiver::RtpSenderReceiver(
+ MediaChannel* channel,
+ talk_base::StreamInterface* input_file_stream,
+ talk_base::StreamInterface* output_file_stream)
: media_channel_(channel),
sending_(false),
first_packet_(true) {
- input_stream_.reset(talk_base::Filesystem::OpenFile(
- talk_base::Pathname(in_file), "rb"));
+ input_stream_.reset(input_file_stream);
if (input_stream_.get()) {
rtp_dump_reader_.reset(new RtpDumpLoopReader(input_stream_.get()));
// Start the sender thread, which reads rtp dump records, waits based on
@@ -133,8 +175,7 @@
}
// Create a rtp dump writer for the output RTP dump stream.
- output_stream_.reset(talk_base::Filesystem::OpenFile(
- talk_base::Pathname(out_file), "wb"));
+ output_stream_.reset(output_file_stream);
if (output_stream_.get()) {
rtp_dump_writer_.reset(new RtpDumpWriter(output_stream_.get()));
}
@@ -207,10 +248,11 @@
///////////////////////////////////////////////////////////////////////////
// Implementation of FileVoiceChannel.
///////////////////////////////////////////////////////////////////////////
-FileVoiceChannel::FileVoiceChannel(const std::string& in_file,
- const std::string& out_file)
- : rtp_sender_receiver_(new RtpSenderReceiver(this, in_file, out_file)) {
-}
+FileVoiceChannel::FileVoiceChannel(
+ talk_base::StreamInterface* input_file_stream,
+ talk_base::StreamInterface* output_file_stream)
+ : rtp_sender_receiver_(new RtpSenderReceiver(this, input_file_stream,
+ output_file_stream)) {}
FileVoiceChannel::~FileVoiceChannel() {}
@@ -230,10 +272,11 @@
///////////////////////////////////////////////////////////////////////////
// Implementation of FileVideoChannel.
///////////////////////////////////////////////////////////////////////////
-FileVideoChannel::FileVideoChannel(const std::string& in_file,
- const std::string& out_file)
- : rtp_sender_receiver_(new RtpSenderReceiver(this, in_file, out_file)) {
-}
+FileVideoChannel::FileVideoChannel(
+ talk_base::StreamInterface* input_file_stream,
+ talk_base::StreamInterface* output_file_stream)
+ : rtp_sender_receiver_(new RtpSenderReceiver(this, input_file_stream,
+ output_file_stream)) {}
FileVideoChannel::~FileVideoChannel() {}
diff --git a/talk/session/phone/filemediaengine.h b/talk/session/phone/filemediaengine.h
index e63b415..62e28f9 100644
--- a/talk/session/phone/filemediaengine.h
+++ b/talk/session/phone/filemediaengine.h
@@ -30,6 +30,7 @@
#include <vector>
#include "talk/base/scoped_ptr.h"
+#include "talk/base/stream.h"
#include "talk/session/phone/codec.h"
#include "talk/session/phone/mediachannel.h"
#include "talk/session/phone/mediaengine.h"
@@ -125,7 +126,8 @@
class FileVoiceChannel : public VoiceMediaChannel {
public:
- FileVoiceChannel(const std::string& in_file, const std::string& out_file);
+ FileVoiceChannel(talk_base::StreamInterface* input_file_stream,
+ talk_base::StreamInterface* output_file_stream);
virtual ~FileVoiceChannel();
// Implement pure virtual methods of VoiceMediaChannel.
@@ -170,7 +172,8 @@
class FileVideoChannel : public VideoMediaChannel {
public:
- FileVideoChannel(const std::string& in_file, const std::string& out_file);
+ FileVideoChannel(talk_base::StreamInterface* input_file_stream,
+ talk_base::StreamInterface* output_file_stream);
virtual ~FileVideoChannel();
// Implement pure virtual methods of VideoMediaChannel.
diff --git a/talk/session/phone/gtkvideorenderer.cc b/talk/session/phone/gtkvideorenderer.cc
index 4e356b3..059c688 100644
--- a/talk/session/phone/gtkvideorenderer.cc
+++ b/talk/session/phone/gtkvideorenderer.cc
@@ -33,24 +33,39 @@
namespace cricket {
+GtkVideoRenderer::GtkVideoRenderer(int x, int y)
+ : window_(NULL),
+ draw_area_(NULL),
+ initial_x_(x),
+ initial_y_(y) {
+ g_thread_init(NULL);
+ gdk_threads_init();
+}
+
GtkVideoRenderer::~GtkVideoRenderer() {
if (window_) {
+ gdk_threads_enter();
gtk_widget_destroy(window_);
// Run the Gtk main loop to tear down the window.
Pump();
+ gdk_threads_leave();
}
// Don't need to destroy draw_area_ because it is not top-level, so it is
// implicitly destroyed by the above.
}
bool GtkVideoRenderer::SetSize(int width, int height, int reserved) {
+ gdk_threads_enter();
+
// For the first frame, initialize the GTK window
if (!window_ && !Initialize(width, height)) {
+ gdk_threads_leave();
return false;
}
image_.reset(new uint8[width * height * 4]);
gtk_window_resize(GTK_WINDOW(window_), width, height);
+ gdk_threads_leave();
return true;
}
@@ -70,6 +85,7 @@
frame->GetWidth() * frame->GetHeight() * 4,
frame->GetWidth() * 4);
+ gdk_threads_enter();
// draw the ABGR image
gdk_draw_rgb_32_image(draw_area_->window,
draw_area_->style->fg_gc[GTK_STATE_NORMAL],
@@ -83,6 +99,7 @@
// Run the Gtk main loop to refresh the window.
Pump();
+ gdk_threads_leave();
return true;
}
diff --git a/talk/session/phone/gtkvideorenderer.h b/talk/session/phone/gtkvideorenderer.h
index e1eb8db..6bdc606 100644
--- a/talk/session/phone/gtkvideorenderer.h
+++ b/talk/session/phone/gtkvideorenderer.h
@@ -38,12 +38,7 @@
class GtkVideoRenderer : public VideoRenderer {
public:
- GtkVideoRenderer(int x, int y)
- : window_(NULL),
- draw_area_(NULL),
- initial_x_(x),
- initial_y_(y) {
- }
+ GtkVideoRenderer(int x, int y);
virtual ~GtkVideoRenderer();
// Implementation of pure virtual methods of VideoRenderer.
diff --git a/talk/session/phone/mediachannel.h b/talk/session/phone/mediachannel.h
index 924eb5d..7c22b70 100644
--- a/talk/session/phone/mediachannel.h
+++ b/talk/session/phone/mediachannel.h
@@ -64,8 +64,10 @@
};
enum VideoMediaChannelOptions {
- OPT_INTERPOLATE = 0x10000 // Increase the output framerate by 2x by
- // interpolating frames
+ // Increase the output framerate by 2x by interpolating frames
+ OPT_INTERPOLATE = 0x10000,
+ // Enable video adaptation due to cpu load.
+ OPT_CPU_ADAPTATION = 0x20000
};
class MediaChannel : public sigslot::has_slots<> {
@@ -109,6 +111,7 @@
virtual bool SetSendBandwidth(bool autobw, int bps) = 0;
// Sets the media options to use.
virtual bool SetOptions(int options) = 0;
+ // TODO: add virtual int GetOptions() = 0;
protected:
NetworkInterface *network_interface_;
@@ -140,6 +143,9 @@
float fraction_lost;
int ext_seqnum;
int jitter_ms;
+ int jitter_buffer_ms;
+ int jitter_buffer_preferred_ms;
+ int delay_estimate_ms;
int audio_level;
};
diff --git a/talk/session/phone/mediaengine.h b/talk/session/phone/mediaengine.h
index f5dd04d..4383408 100644
--- a/talk/session/phone/mediaengine.h
+++ b/talk/session/phone/mediaengine.h
@@ -142,8 +142,6 @@
virtual const std::vector<AudioCodec>& audio_codecs() = 0;
virtual const std::vector<VideoCodec>& video_codecs() = 0;
- virtual bool FindAudioCodec(const AudioCodec &codec) = 0;
- virtual bool FindVideoCodec(const VideoCodec &codec) = 0;
// Logging control
virtual void SetVoiceLogging(int min_sev, const char* filter) = 0;
@@ -158,6 +156,7 @@
class CompositeMediaEngine : public MediaEngine {
public:
CompositeMediaEngine() {}
+ virtual ~CompositeMediaEngine() {}
virtual bool Init() {
if (!voice_.Init())
return false;
@@ -231,13 +230,6 @@
return video_.codecs();
}
- virtual bool FindAudioCodec(const AudioCodec &codec) {
- return voice_.FindCodec(codec);
- }
- virtual bool FindVideoCodec(const VideoCodec &codec) {
- return video_.FindCodec(codec);
- }
-
virtual void SetVoiceLogging(int min_sev, const char* filter) {
return voice_.SetLogging(min_sev, filter);
}
@@ -245,7 +237,7 @@
return video_.SetLogging(min_sev, filter);
}
- private:
+ protected:
VOICE voice_;
VIDEO video_;
};
@@ -273,7 +265,6 @@
int GetInputLevel() { return 0; }
bool SetLocalMonitor(bool enable) { return true; }
const std::vector<AudioCodec>& codecs() { return codecs_; }
- bool FindCodec(const AudioCodec&) { return false; }
void SetLogging(int min_sev, const char* filter) {}
private:
std::vector<AudioCodec> codecs_;
@@ -287,7 +278,8 @@
void Terminate() {}
int GetCapabilities() { return 0; }
// If you need this to return an actual channel, use FakeMediaEngine instead.
- VideoMediaChannel* CreateChannel(VoiceMediaChannel* voice_media_channel) {
+ VideoMediaChannel* CreateChannel(
+ VoiceMediaChannel* voice_media_channel) {
return NULL;
}
bool SetOptions(int opts) { return true; }
@@ -298,7 +290,6 @@
bool SetLocalRenderer(VideoRenderer* renderer) { return true; }
CaptureResult SetCapture(bool capture) { return CR_SUCCESS; }
const std::vector<VideoCodec>& codecs() { return codecs_; }
- bool FindCodec(const VideoCodec&) { return false; }
void SetLogging(int min_sev, const char* filter) {}
sigslot::signal1<CaptureResult> SignalCaptureResult;
private:
diff --git a/talk/session/phone/mediamessages.cc b/talk/session/phone/mediamessages.cc
new file mode 100644
index 0000000..b1e9b76
--- /dev/null
+++ b/talk/session/phone/mediamessages.cc
@@ -0,0 +1,242 @@
+/*
+ * libjingle
+ * Copyright 2010, 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/session/phone/mediamessages.h"
+
+#include "talk/base/stringencode.h"
+#include "talk/p2p/base/constants.h"
+#include "talk/session/phone/mediasessionclient.h"
+#include "talk/xmllite/xmlelement.h"
+
+namespace cricket {
+
+const NamedSource* GetFirstSourceByNick(const NamedSources& sources,
+ const std::string& nick) {
+ for (NamedSources::const_iterator source = sources.begin();
+ source != sources.end(); ++source) {
+ if (source->nick == nick) {
+ return &*source;
+ }
+ }
+ return NULL;
+}
+
+const NamedSource* GetSourceBySsrc(const NamedSources& sources, uint32 ssrc) {
+ for (NamedSources::const_iterator source = sources.begin();
+ source != sources.end(); ++source) {
+ if (source->ssrc == ssrc) {
+ return &*source;
+ }
+ }
+ return NULL;
+}
+
+const NamedSource* MediaSources::GetFirstAudioSourceByNick(
+ const std::string& nick) {
+ return GetFirstSourceByNick(audio, nick);
+}
+
+const NamedSource* MediaSources::GetFirstVideoSourceByNick(
+ const std::string& nick) {
+ return GetFirstSourceByNick(video, nick);
+}
+
+const NamedSource* MediaSources::GetAudioSourceBySsrc(uint32 ssrc) {
+ return GetSourceBySsrc(audio, ssrc);
+}
+
+const NamedSource* MediaSources::GetVideoSourceBySsrc(uint32 ssrc) {
+ return GetSourceBySsrc(video, ssrc);
+}
+
+// NOTE: There is no check here for duplicate sources, so check before
+// adding.
+void AddSource(NamedSources* sources, const NamedSource& source) {
+ sources->push_back(source);
+}
+
+void MediaSources::AddAudioSource(const NamedSource& source) {
+ AddSource(&audio, source);
+}
+
+void MediaSources::AddVideoSource(const NamedSource& source) {
+ AddSource(&video, source);
+}
+
+void RemoveSourceBySsrc(NamedSources* sources, uint32 ssrc) {
+ for (NamedSources::iterator source = sources->begin();
+ source != sources->end(); ) {
+ if (source->ssrc == ssrc) {
+ source = sources->erase(source);
+ } else {
+ ++source;
+ }
+ }
+}
+
+void MediaSources::RemoveAudioSourceBySsrc(uint32 ssrc) {
+ RemoveSourceBySsrc(&audio, ssrc);
+}
+
+void MediaSources::RemoveVideoSourceBySsrc(uint32 ssrc) {
+ RemoveSourceBySsrc(&video, ssrc);
+}
+
+bool ParseSsrc(const std::string& string, uint32* ssrc) {
+ return talk_base::FromString(string, ssrc);
+}
+
+bool ParseSsrc(const buzz::XmlElement* element, uint32* ssrc) {
+ if (element == NULL) {
+ return false;
+ }
+ return ParseSsrc(element->BodyText(), ssrc);
+}
+
+bool ParseNamedSource(const buzz::XmlElement* source_elem,
+ NamedSource* named_source,
+ ParseError* error) {
+ named_source->nick = source_elem->Attr(QN_JINGLE_DRAFT_SOURCE_NICK);
+ if (named_source->nick.empty()) {
+ return BadParse("Missing or invalid nick.", error);
+ }
+
+ named_source->name = source_elem->Attr(QN_JINGLE_DRAFT_SOURCE_NAME);
+ named_source->usage = source_elem->Attr(QN_JINGLE_DRAFT_SOURCE_USAGE);
+ named_source->removed =
+ (STR_JINGLE_DRAFT_SOURCE_STATE_REMOVED ==
+ source_elem->Attr(QN_JINGLE_DRAFT_SOURCE_STATE));
+
+ const buzz::XmlElement* ssrc_elem =
+ source_elem->FirstNamed(QN_JINGLE_DRAFT_SOURCE_SSRC);
+ if (ssrc_elem != NULL && !ssrc_elem->BodyText().empty()) {
+ uint32 ssrc;
+ if (!ParseSsrc(ssrc_elem->BodyText(), &ssrc)) {
+ return BadParse("Missing or invalid ssrc.", error);
+ }
+ named_source->SetSsrc(ssrc);
+ }
+
+ return true;
+}
+
+bool IsSourcesNotify(const buzz::XmlElement* action_elem) {
+ return action_elem->FirstNamed(QN_JINGLE_DRAFT_NOTIFY) != NULL;
+}
+
+bool ParseSourcesNotify(const buzz::XmlElement* action_elem,
+ const SessionDescription* session_description,
+ MediaSources* sources,
+ ParseError* error) {
+ for (const buzz::XmlElement* notify_elem
+ = action_elem->FirstNamed(QN_JINGLE_DRAFT_NOTIFY);
+ notify_elem != NULL;
+ notify_elem = notify_elem->NextNamed(QN_JINGLE_DRAFT_NOTIFY)) {
+ std::string content_name = notify_elem->Attr(QN_JINGLE_DRAFT_CONTENT_NAME);
+ for (const buzz::XmlElement* source_elem
+ = notify_elem->FirstNamed(QN_JINGLE_DRAFT_SOURCE);
+ source_elem != NULL;
+ source_elem = source_elem->NextNamed(QN_JINGLE_DRAFT_SOURCE)) {
+ NamedSource named_source;
+ if (!ParseNamedSource(source_elem, &named_source, error)) {
+ return false;
+ }
+
+ if (session_description == NULL) {
+ return BadParse("unknown content name: " + content_name, error);
+ }
+ const ContentInfo* content =
+ FindContentInfoByName(session_description->contents(), content_name);
+ if (content == NULL) {
+ return BadParse("unknown content name: " + content_name, error);
+ }
+
+ if (IsAudioContent(content)) {
+ sources->audio.push_back(named_source);
+ } else if (IsVideoContent(content)) {
+ sources->video.push_back(named_source);
+ }
+ }
+ }
+
+ return true;
+}
+
+buzz::XmlElement* CreateViewElem(const std::string& name,
+ const std::string& type) {
+ buzz::XmlElement* view_elem =
+ new buzz::XmlElement(QN_JINGLE_DRAFT_VIEW, true);
+ view_elem->AddAttr(QN_JINGLE_DRAFT_CONTENT_NAME, name);
+ view_elem->SetAttr(QN_JINGLE_DRAFT_VIEW_TYPE, type);
+ return view_elem;
+}
+
+buzz::XmlElement* CreateVideoViewElem(const std::string& content_name,
+ const std::string& type) {
+ return CreateViewElem(content_name, type);
+}
+
+buzz::XmlElement* CreateNoneVideoViewElem(const std::string& content_name) {
+ return CreateVideoViewElem(content_name, STR_JINGLE_DRAFT_VIEW_TYPE_NONE);
+}
+
+buzz::XmlElement* CreateStaticVideoViewElem(const std::string& content_name,
+ const StaticVideoView& view) {
+ buzz::XmlElement* view_elem =
+ CreateVideoViewElem(content_name, STR_JINGLE_DRAFT_VIEW_TYPE_STATIC);
+ AddXmlAttr(view_elem, QN_JINGLE_DRAFT_VIEW_SSRC, view.ssrc);
+
+ buzz::XmlElement* params_elem = new buzz::XmlElement(
+ QN_JINGLE_DRAFT_VIEW_PARAMS);
+ AddXmlAttr(params_elem, QN_JINGLE_DRAFT_VIEW_PARAMS_WIDTH, view.width);
+ AddXmlAttr(params_elem, QN_JINGLE_DRAFT_VIEW_PARAMS_HEIGHT, view.height);
+ AddXmlAttr(params_elem, QN_JINGLE_DRAFT_VIEW_PARAMS_FRAMERATE,
+ view.framerate);
+ AddXmlAttr(params_elem, QN_JINGLE_DRAFT_VIEW_PARAMS_PREFERENCE,
+ view.preference);
+ view_elem->AddElement(params_elem);
+
+ return view_elem;
+}
+
+bool WriteViewRequest(const std::string& content_name,
+ const ViewRequest& request,
+ XmlElements* elems,
+ WriteError* error) {
+ if (request.static_video_views.size() == 0) {
+ elems->push_back(CreateNoneVideoViewElem(content_name));
+ } else {
+ for (StaticVideoViews::const_iterator view =
+ request.static_video_views.begin();
+ view != request.static_video_views.end(); ++view) {
+ elems->push_back(CreateStaticVideoViewElem(content_name, *view));
+ }
+ }
+ return true;
+}
+
+} // namespace cricket
diff --git a/talk/session/phone/mediamessages.h b/talk/session/phone/mediamessages.h
new file mode 100644
index 0000000..58e8793
--- /dev/null
+++ b/talk/session/phone/mediamessages.h
@@ -0,0 +1,106 @@
+/*
+ * libjingle
+ * Copyright 2010, 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.
+ */
+
+#ifndef TALK_SESSION_PHONE_MEDIAMESSAGES_H_
+#define TALK_SESSION_PHONE_MEDIAMESSAGES_H_
+
+#include <string>
+#include <vector>
+#include "talk/base/basictypes.h"
+#include "talk/p2p/base/parsing.h"
+#include "talk/p2p/base/sessiondescription.h"
+
+namespace cricket {
+
+struct NamedSource {
+ NamedSource() : ssrc(0), ssrc_set(false), removed(false) {}
+
+ void SetSsrc(uint32 ssrc) {
+ this->ssrc = ssrc;
+ this->ssrc_set = true;
+ }
+
+ std::string nick;
+ std::string name;
+ std::string usage;
+ uint32 ssrc;
+ bool ssrc_set;
+ bool removed;
+};
+
+typedef std::vector<NamedSource> NamedSources;
+
+class MediaSources {
+ public:
+ const NamedSource* GetAudioSourceBySsrc(uint32 ssrc);
+ const NamedSource* GetVideoSourceBySsrc(uint32 ssrc);
+ // TODO: Remove once all senders use excplict remove by ssrc.
+ const NamedSource* GetFirstAudioSourceByNick(const std::string& nick);
+ const NamedSource* GetFirstVideoSourceByNick(const std::string& nick);
+ void AddAudioSource(const NamedSource& source);
+ void AddVideoSource(const NamedSource& source);
+ void RemoveAudioSourceBySsrc(uint32 ssrc);
+ void RemoveVideoSourceBySsrc(uint32 ssrc);
+ NamedSources audio;
+ NamedSources video;
+};
+
+struct StaticVideoView {
+ StaticVideoView(uint32 ssrc, int width, int height, int framerate)
+ : ssrc(ssrc),
+ width(width),
+ height(height),
+ framerate(framerate),
+ preference(0) {}
+
+ uint32 ssrc;
+ int width;
+ int height;
+ int framerate;
+ int preference;
+};
+
+typedef std::vector<StaticVideoView> StaticVideoViews;
+
+struct ViewRequest {
+ StaticVideoViews static_video_views;
+};
+
+bool WriteViewRequest(const std::string& content_name,
+ const ViewRequest& view,
+ XmlElements* elems,
+ WriteError* error);
+
+bool IsSourcesNotify(const buzz::XmlElement* action_elem);
+// The session_description is needed to map content_name => media type.
+bool ParseSourcesNotify(const buzz::XmlElement* action_elem,
+ const SessionDescription* session_description,
+ MediaSources* sources,
+ ParseError* error);
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_MEDIAMESSAGES_H_
diff --git a/talk/session/phone/mediasessionclient.cc b/talk/session/phone/mediasessionclient.cc
index 10ea1af..6112db8 100644
--- a/talk/session/phone/mediasessionclient.cc
+++ b/talk/session/phone/mediasessionclient.cc
@@ -206,6 +206,24 @@
return offer;
}
+bool IsMediaContent(const ContentInfo* content, MediaType media_type) {
+ if (content == NULL || content->type != NS_JINGLE_RTP) {
+ return false;
+ }
+
+ const MediaContentDescription* media =
+ static_cast<const MediaContentDescription*>(content->description);
+ return media->type() == media_type;
+}
+
+bool IsAudioContent(const ContentInfo* content) {
+ return IsMediaContent(content, MEDIA_TYPE_AUDIO);
+}
+
+bool IsVideoContent(const ContentInfo* content) {
+ return IsMediaContent(content, MEDIA_TYPE_VIDEO);
+}
+
const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
MediaType media_type) {
if (sdesc == NULL)
@@ -214,12 +232,8 @@
const ContentInfos& contents = sdesc->contents();
for (ContentInfos::const_iterator content = contents.begin();
content != contents.end(); content++) {
- if (content->type == NS_JINGLE_RTP) {
- const MediaContentDescription* media =
- static_cast<const MediaContentDescription*>(content->description);
- if (media->type() == media_type) {
- return &*content;
- }
+ if (IsMediaContent(&*content, media_type)) {
+ return &*content;
}
}
return NULL;
diff --git a/talk/session/phone/mediasessionclient.h b/talk/session/phone/mediasessionclient.h
index 8041678..07fc258 100644
--- a/talk/session/phone/mediasessionclient.h
+++ b/talk/session/phone/mediasessionclient.h
@@ -279,10 +279,11 @@
};
// Convenience functions.
+bool IsAudioContent(const ContentInfo* content);
+bool IsVideoContent(const ContentInfo* content);
const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc);
const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc);
-
} // namespace cricket
#endif // TALK_SESSION_PHONE_MEDIASESSIONCLIENT_H_
diff --git a/talk/session/phone/videocommon.h b/talk/session/phone/videocommon.h
index b978200..ff8e8de 100644
--- a/talk/session/phone/videocommon.h
+++ b/talk/session/phone/videocommon.h
@@ -61,6 +61,7 @@
enum FourCC {
// Canonical fourcc codes used in our code.
FOURCC_I420 = FOURCC('I', '4', '2', '0'),
+ FOURCC_YV12 = FOURCC('Y', 'V', '1', '2'),
FOURCC_YUY2 = FOURCC('Y', 'U', 'Y', '2'),
FOURCC_UYVY = FOURCC('U', 'Y', 'V', 'Y'),
FOURCC_M420 = FOURCC('M', '4', '2', '0'),
@@ -89,6 +90,8 @@
FOURCC_2VUY = FOURCC('2', 'v', 'u', 'y'), // Alias for UYVY
FOURCC_JPEG = FOURCC('J', 'P', 'E', 'G'), // Alias for MJPG
FOURCC_BA81 = FOURCC('B', 'A', '8', '1'), // Alias for BGGR
+ FOURCC_RGB3 = FOURCC('R', 'G', 'B', '3'), // Alias for RAW
+ FOURCC_BGR3 = FOURCC('B', 'G', 'R', '3'), // Alias for 24BG
// Match any fourcc.
FOURCC_ANY = 0xFFFFFFFF,
@@ -152,6 +155,19 @@
int framerate() const { return IntervalToFps(interval); }
+ // Check if both width and height are 0.
+ bool IsSize0x0() const { return 0 == width && 0 == height; }
+
+ // Check if this format is less than another one by comparing the resolution
+ // and frame rate.
+ bool IsPixelRateLess(const VideoFormat& format) const {
+ return width * height * framerate() <
+ format.width * format.height * format.framerate();
+ }
+
+ // Get a string presentation in the form of "fourcc width x height x fps"
+ std::string ToString() const;
+
int width; // in number of pixels
int height; // in number of pixels
int64 interval; // in nanoseconds
diff --git a/talk/site_scons/site_tools/talk_libjingle.py b/talk/site_scons/site_tools/talk_libjingle.py
index 3086614..33c136c 100644
--- a/talk/site_scons/site_tools/talk_libjingle.py
+++ b/talk/site_scons/site_tools/talk_libjingle.py
@@ -6,6 +6,7 @@
import subprocess
+
# We need this in libjingle because main.scons depends on it and
# libjingle depends on main.scons.
def EnableFeatureWherePackagePresent(env, bit, cpp_flag, package):
@@ -23,9 +24,10 @@
env.SetBits(bit)
env.Append(CPPDEFINES = [cpp_flag])
else:
- print ("Warning: Package \"%s\" not found. Feature \"%s\" will not be "
- "built. To build with this feature, install the package that "
- "provides the \"%s.pc\" file.") % (package, bit, package)
+ print ('Warning: Package \"%s\" not found. Feature \"%s\" will not be '
+ 'built. To build with this feature, install the package that '
+ 'provides the \"%s.pc\" file.') % (package, bit, package)
+
def _HavePackage(package):
"""Whether the given pkg-config package name is present on the build system.
@@ -33,13 +35,15 @@
Args:
package: The name of the package.
- Return:
+ Returns:
True if the package is present, else False
"""
- return subprocess.call(["pkg-config", "--exists", package]) == 0
+ return subprocess.call(['pkg-config', '--exists', package]) == 0
-def generate(env):
+
+def generate(env): # pylint: disable-msg=C6409
env.AddMethod(EnableFeatureWherePackagePresent)
-def exists(env):
+
+def exists(env): # pylint: disable-msg=C6409,W0613
return 1
diff --git a/talk/xmpp/constants.cc b/talk/xmpp/constants.cc
index 772c97d..db9095f 100644
--- a/talk/xmpp/constants.cc
+++ b/talk/xmpp/constants.cc
@@ -137,8 +137,13 @@
const std::string STR_BOTH("both");
const std::string STR_REMOVE("remove");
-const std::string STR_UNAVAILABLE("unavailable");
-
+const std::string STR_TYPE("type");
+const std::string STR_NAME("name");
+const std::string STR_ID("id");
+const std::string STR_JID("jid");
+const std::string STR_SUBSCRIPTION("subscription");
+const std::string STR_ASK("ask");
+const std::string STR_X("x");
const std::string STR_GOOGLE_COM("google.com");
const std::string STR_GMAIL_COM("gmail.com");
const std::string STR_GOOGLEMAIL_COM("googlemail.com");
@@ -146,13 +151,14 @@
const std::string STR_TALK_GOOGLE_COM("talk.google.com");
const std::string STR_TALKX_L_GOOGLE_COM("talkx.l.google.com");
-const std::string STR_X("x");
-
#ifdef FEATURE_ENABLE_VOICEMAIL
const std::string STR_VOICEMAIL("voicemail");
const std::string STR_OUTGOINGVOICEMAIL("outgoingvoicemail");
#endif
+const std::string STR_UNAVAILABLE("unavailable");
+
+
const QName QN_STREAM_STREAM(true, NS_STREAM, STR_STREAM);
const QName QN_STREAM_FEATURES(true, NS_STREAM, "features");
const QName QN_STREAM_ERROR(true, NS_STREAM, "error");
@@ -296,13 +302,6 @@
const QName QN_XML_LANG(true, NS_XML, "lang");
-const std::string STR_TYPE("type");
-const std::string STR_ID("id");
-const std::string STR_NAME("name");
-const std::string STR_JID("jid");
-const std::string STR_SUBSCRIPTION("subscription");
-const std::string STR_ASK("ask");
-
const QName QN_ENCODING(true, STR_EMPTY, STR_ENCODING);
const QName QN_VERSION(true, STR_EMPTY, STR_VERSION);
const QName QN_TO(true, STR_EMPTY, "to");
@@ -328,7 +327,6 @@
const QName QN_XMLNS_STREAM(true, NS_XMLNS, STR_STREAM);
-
// Presence
const std::string STR_SHOW_AWAY("away");
const std::string STR_SHOW_CHAT("chat");
@@ -377,12 +375,26 @@
const QName QN_DISCO_ITEMS_QUERY(true, NS_DISCO_ITEMS, "query");
const QName QN_DISCO_ITEM(true, NS_DISCO_ITEMS, "item");
+
+// JEP 0045
+const std::string NS_MUC("http://jabber.org/protocol/muc");
+const QName QN_MUC_X(true, NS_MUC, "x");
+const QName QN_MUC_ITEM(true, NS_MUC, "item");
+const QName QN_MUC_AFFILIATION(true, NS_MUC, "affiliation");
+const QName QN_MUC_ROLE(true, NS_MUC, "role");
+const std::string STR_AFFILIATION_NONE("none");
+const std::string STR_ROLE_PARTICIPANT("participant");
+
+const std::string NS_MUC_OWNER("http://jabber.org/protocol/muc#owner");
+const QName QN_MUC_OWNER_QUERY(true, NS_MUC_OWNER, "query");
+
const std::string NS_MUC_USER("http://jabber.org/protocol/muc#user");
const QName QN_MUC_USER_CONTINUE(true, NS_MUC_USER, "continue");
const QName QN_MUC_USER_X(true, NS_MUC_USER, "x");
const QName QN_MUC_USER_ITEM(true, NS_MUC_USER, "item");
const QName QN_MUC_USER_STATUS(true, NS_MUC_USER, "status");
+
// JEP 0115
const std::string NS_CAPS("http://jabber.org/protocol/caps");
const QName QN_CAPS_C(true, NS_CAPS, "c");
diff --git a/talk/xmpp/constants.h b/talk/xmpp/constants.h
index c78f7f7..8df83b9 100644
--- a/talk/xmpp/constants.h
+++ b/talk/xmpp/constants.h
@@ -91,14 +91,6 @@
extern const std::string STR_BOTH;
extern const std::string STR_REMOVE;
-extern const std::string STR_MESSAGE;
-extern const std::string STR_BODY;
-extern const std::string STR_PRESENCE;
-extern const std::string STR_STATUS;
-extern const std::string STR_SHOW;
-extern const std::string STR_PRIOIRTY;
-extern const std::string STR_IQ;
-
extern const std::string STR_TYPE;
extern const std::string STR_NAME;
extern const std::string STR_ID;
@@ -120,6 +112,7 @@
extern const std::string STR_UNAVAILABLE;
+
extern const QName QN_STREAM_STREAM;
extern const QName QN_STREAM_FEATURES;
extern const QName QN_STREAM_ERROR;
@@ -357,6 +350,10 @@
extern const QName QN_MUC_ROLE;
extern const std::string STR_AFFILIATION_NONE;
extern const std::string STR_ROLE_PARTICIPANT;
+
+extern const std::string NS_MUC_OWNER;
+extern const QName QN_MUC_OWNER_QUERY;
+
extern const std::string NS_MUC_USER;
extern const QName QN_MUC_USER_CONTINUE;
extern const QName QN_MUC_USER_X;