Update talk to 60094938.

Review URL: https://webrtc-codereview.appspot.com/7489005

git-svn-id: http://webrtc.googlecode.com/svn/trunk@5420 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
wu@webrtc.org 2014-01-23 22:12:45 +00:00
parent 754de528b7
commit a8910d2f88
29 changed files with 607 additions and 386 deletions

View File

@ -295,24 +295,24 @@ bool MediaStreamSignaling::AddLocalStream(MediaStreamInterface* local_stream) {
AudioTrackVector audio_tracks = local_stream->GetAudioTracks();
for (AudioTrackVector::const_iterator it = audio_tracks.begin();
it != audio_tracks.end(); ++it) {
TrackInfos::const_iterator track_info_it =
local_audio_tracks_.find((*it)->id());
if (track_info_it != local_audio_tracks_.end()) {
const TrackInfo& info = track_info_it->second;
OnLocalTrackSeen(info.stream_label, info.track_id, info.ssrc,
cricket::MEDIA_TYPE_AUDIO);
const TrackInfo* track_info = FindTrackInfo(local_audio_tracks_,
local_stream->label(),
(*it)->id());
if (track_info) {
OnLocalTrackSeen(track_info->stream_label, track_info->track_id,
track_info->ssrc, cricket::MEDIA_TYPE_AUDIO);
}
}
VideoTrackVector video_tracks = local_stream->GetVideoTracks();
for (VideoTrackVector::const_iterator it = video_tracks.begin();
it != video_tracks.end(); ++it) {
TrackInfos::const_iterator track_info_it =
local_video_tracks_.find((*it)->id());
if (track_info_it != local_video_tracks_.end()) {
const TrackInfo& info = track_info_it->second;
OnLocalTrackSeen(info.stream_label, info.track_id, info.ssrc,
cricket::MEDIA_TYPE_VIDEO);
const TrackInfo* track_info = FindTrackInfo(local_video_tracks_,
local_stream->label(),
(*it)->id());
if (track_info) {
OnLocalTrackSeen(track_info->stream_label, track_info->track_id,
track_info->ssrc, cricket::MEDIA_TYPE_VIDEO);
}
}
return true;
@ -321,6 +321,7 @@ bool MediaStreamSignaling::AddLocalStream(MediaStreamInterface* local_stream) {
void MediaStreamSignaling::RemoveLocalStream(
MediaStreamInterface* local_stream) {
local_streams_->RemoveStream(local_stream);
stream_observer_->OnRemoveLocalStream(local_stream);
}
@ -474,28 +475,6 @@ void MediaStreamSignaling::OnDataChannelClose() {
}
}
bool MediaStreamSignaling::GetRemoteAudioTrackSsrc(
const std::string& track_id, uint32* ssrc) const {
TrackInfos::const_iterator it = remote_audio_tracks_.find(track_id);
if (it == remote_audio_tracks_.end()) {
return false;
}
*ssrc = it->second.ssrc;
return true;
}
bool MediaStreamSignaling::GetRemoteVideoTrackSsrc(
const std::string& track_id, uint32* ssrc) const {
TrackInfos::const_iterator it = remote_video_tracks_.find(track_id);
if (it == remote_video_tracks_.end()) {
return false;
}
*ssrc = it->second.ssrc;
return true;
}
void MediaStreamSignaling::UpdateSessionOptions() {
options_.streams.clear();
if (local_streams_ != NULL) {
@ -554,12 +533,12 @@ void MediaStreamSignaling::UpdateRemoteStreamsList(
// new StreamParam.
TrackInfos::iterator track_it = current_tracks->begin();
while (track_it != current_tracks->end()) {
TrackInfo info = track_it->second;
const TrackInfo& info = *track_it;
cricket::StreamParams params;
if (!cricket::GetStreamBySsrc(streams, info.ssrc, &params) ||
params.id != info.track_id) {
OnRemoteTrackRemoved(info.stream_label, info.track_id, media_type);
current_tracks->erase(track_it++);
track_it = current_tracks->erase(track_it);
} else {
++track_it;
}
@ -583,10 +562,10 @@ void MediaStreamSignaling::UpdateRemoteStreamsList(
new_streams->AddStream(stream);
}
TrackInfos::iterator track_it = current_tracks->find(track_id);
if (track_it == current_tracks->end()) {
(*current_tracks)[track_id] =
TrackInfo(stream_label, track_id, ssrc);
const TrackInfo* track_info = FindTrackInfo(*current_tracks, stream_label,
track_id);
if (!track_info) {
current_tracks->push_back(TrackInfo(stream_label, track_id, ssrc));
OnRemoteTrackSeen(stream_label, track_id, it->first_ssrc(), media_type);
}
}
@ -642,7 +621,7 @@ void MediaStreamSignaling::RejectRemoteTracks(cricket::MediaType media_type) {
TrackInfos* current_tracks = GetRemoteTracks(media_type);
for (TrackInfos::iterator track_it = current_tracks->begin();
track_it != current_tracks->end(); ++track_it) {
TrackInfo info = track_it->second;
const TrackInfo& info = *track_it;
MediaStreamInterface* stream = remote_streams_->find(info.stream_label);
if (media_type == cricket::MEDIA_TYPE_AUDIO) {
AudioTrackInterface* track = stream->FindAudioTrack(info.track_id);
@ -695,15 +674,16 @@ void MediaStreamSignaling::MaybeCreateDefaultStream() {
}
if (remote_info_.default_audio_track_needed &&
default_remote_stream->GetAudioTracks().size() == 0) {
remote_audio_tracks_[kDefaultAudioTrackLabel] =
TrackInfo(kDefaultStreamLabel, kDefaultAudioTrackLabel, 0);
remote_audio_tracks_.push_back(TrackInfo(kDefaultStreamLabel,
kDefaultAudioTrackLabel, 0));
OnRemoteTrackSeen(kDefaultStreamLabel, kDefaultAudioTrackLabel, 0,
cricket::MEDIA_TYPE_AUDIO);
}
if (remote_info_.default_video_track_needed &&
default_remote_stream->GetVideoTracks().size() == 0) {
remote_video_tracks_[kDefaultVideoTrackLabel] =
TrackInfo(kDefaultStreamLabel, kDefaultVideoTrackLabel, 0);
remote_video_tracks_.push_back(TrackInfo(kDefaultStreamLabel,
kDefaultVideoTrackLabel, 0));
OnRemoteTrackSeen(kDefaultStreamLabel, kDefaultVideoTrackLabel, 0,
cricket::MEDIA_TYPE_VIDEO);
}
@ -736,16 +716,16 @@ void MediaStreamSignaling::UpdateLocalTracks(
cricket::MediaType media_type) {
TrackInfos* current_tracks = GetLocalTracks(media_type);
// Find removed tracks. Ie tracks where the track id or ssrc don't match the
// new StreamParam.
// Find removed tracks. Ie tracks where the track id, stream label or ssrc
// don't match the new StreamParam.
TrackInfos::iterator track_it = current_tracks->begin();
while (track_it != current_tracks->end()) {
TrackInfo info = track_it->second;
const TrackInfo& info = *track_it;
cricket::StreamParams params;
if (!cricket::GetStreamBySsrc(streams, info.ssrc, &params) ||
params.id != info.track_id) {
params.id != info.track_id || params.sync_label != info.stream_label) {
OnLocalTrackRemoved(info.stream_label, info.track_id, media_type);
current_tracks->erase(track_it++);
track_it = current_tracks->erase(track_it);
} else {
++track_it;
}
@ -759,10 +739,11 @@ void MediaStreamSignaling::UpdateLocalTracks(
const std::string& stream_label = it->sync_label;
const std::string& track_id = it->id;
uint32 ssrc = it->first_ssrc();
TrackInfos::iterator track_it = current_tracks->find(track_id);
if (track_it == current_tracks->end()) {
(*current_tracks)[track_id] =
TrackInfo(stream_label, track_id, ssrc);
const TrackInfo* track_info = FindTrackInfo(*current_tracks,
stream_label,
track_id);
if (!track_info) {
current_tracks->push_back(TrackInfo(stream_label, track_id, ssrc));
OnLocalTrackSeen(stream_label, track_id, it->first_ssrc(),
media_type);
}
@ -948,4 +929,18 @@ void MediaStreamSignaling::OnDtlsRoleReadyForSctp(talk_base::SSLRole role) {
}
}
const MediaStreamSignaling::TrackInfo*
MediaStreamSignaling::FindTrackInfo(
const MediaStreamSignaling::TrackInfos& infos,
const std::string& stream_label,
const std::string track_id) const {
for (TrackInfos::const_iterator it = infos.begin();
it != infos.end(); ++it) {
if (it->stream_label == stream_label && it->track_id == track_id)
return &*it;
}
return NULL;
}
} // namespace webrtc

View File

@ -238,10 +238,6 @@ class MediaStreamSignaling {
// Called when the data channel closes.
void OnDataChannelClose();
// Returns the SSRC for a given track.
bool GetRemoteAudioTrackSsrc(const std::string& track_id, uint32* ssrc) const;
bool GetRemoteVideoTrackSsrc(const std::string& track_id, uint32* ssrc) const;
// Returns all current known local MediaStreams.
StreamCollectionInterface* local_streams() const { return local_streams_;}
@ -287,7 +283,7 @@ class MediaStreamSignaling {
std::string track_id;
uint32 ssrc;
};
typedef std::map<std::string, TrackInfo> TrackInfos;
typedef std::vector<TrackInfo> TrackInfos;
void UpdateSessionOptions();
@ -366,6 +362,10 @@ class MediaStreamSignaling {
const std::vector<std::string>& active_channels, bool is_local_update);
void CreateRemoteDataChannel(const std::string& label, uint32 remote_ssrc);
const TrackInfo* FindTrackInfo(const TrackInfos& infos,
const std::string& stream_label,
const std::string track_id) const;
RemotePeerInfo remote_info_;
talk_base::Thread* signaling_thread_;
DataChannelFactory* data_channel_factory_;

View File

@ -26,6 +26,7 @@
*/
#include <string>
#include <vector>
#include "talk/app/webrtc/audiotrack.h"
#include "talk/app/webrtc/mediastream.h"
@ -383,31 +384,48 @@ class MockSignalingObserver : public webrtc::MediaStreamSignalingObserver {
std::string track_id;
uint32 ssrc;
};
typedef std::map<std::string, TrackInfo> TrackInfos;
typedef std::vector<TrackInfo> TrackInfos;
void AddTrack(TrackInfos* track_infos, MediaStreamInterface* stream,
MediaStreamTrackInterface* track,
uint32 ssrc) {
(*track_infos)[track->id()] = TrackInfo(stream->label(), track->id(),
ssrc);
(*track_infos).push_back(TrackInfo(stream->label(), track->id(),
ssrc));
}
void RemoveTrack(TrackInfos* track_infos, MediaStreamInterface* stream,
MediaStreamTrackInterface* track) {
TrackInfos::iterator it = track_infos->find(track->id());
ASSERT_TRUE(it != track_infos->end());
ASSERT_EQ(it->second.stream_label, stream->label());
track_infos->erase(it);
for (TrackInfos::iterator it = track_infos->begin();
it != track_infos->end(); ++it) {
if (it->stream_label == stream->label() && it->track_id == track->id()) {
track_infos->erase(it);
return;
}
}
ADD_FAILURE();
}
const TrackInfo* FindTrackInfo(const TrackInfos& infos,
const std::string& stream_label,
const std::string track_id) const {
for (TrackInfos::const_iterator it = infos.begin();
it != infos.end(); ++it) {
if (it->stream_label == stream_label && it->track_id == track_id)
return &*it;
}
return NULL;
}
void VerifyTrack(const TrackInfos& track_infos,
const std::string& stream_label,
const std::string& track_id,
uint32 ssrc) {
TrackInfos::const_iterator it = track_infos.find(track_id);
ASSERT_TRUE(it != track_infos.end());
EXPECT_EQ(stream_label, it->second.stream_label);
EXPECT_EQ(ssrc, it->second.ssrc);
const TrackInfo* track_info = FindTrackInfo(track_infos,
stream_label,
track_id);
ASSERT_TRUE(track_info != NULL);
EXPECT_EQ(ssrc, track_info->ssrc);
}
TrackInfos remote_audio_tracks_;
@ -1051,6 +1069,47 @@ TEST_F(MediaStreamSignalingTest, ChangeSsrcOnTrackInLocalSessionDescription) {
observer_->VerifyLocalVideoTrack(kStreams[0], kVideoTracks[0], 98);
}
// This test that the correct MediaStreamSignalingObserver methods are called
// if a new session description is set with the same tracks but they are now
// sent on a another MediaStream.
TEST_F(MediaStreamSignalingTest, SignalSameTracksInSeparateMediaStream) {
talk_base::scoped_ptr<SessionDescriptionInterface> desc;
CreateSessionDescriptionAndReference(1, 1, desc.use());
signaling_->AddLocalStream(reference_collection_->at(0));
signaling_->OnLocalDescriptionChanged(desc.get());
EXPECT_EQ(1u, observer_->NumberOfLocalAudioTracks());
EXPECT_EQ(1u, observer_->NumberOfLocalVideoTracks());
std::string stream_label_0 = kStreams[0];
observer_->VerifyLocalAudioTrack(stream_label_0, kAudioTracks[0], 1);
observer_->VerifyLocalVideoTrack(stream_label_0, kVideoTracks[0], 2);
// Add a new MediaStream but with the same tracks as in the first stream.
std::string stream_label_1 = kStreams[1];
talk_base::scoped_refptr<webrtc::MediaStreamInterface> stream_1(
webrtc::MediaStream::Create(kStreams[1]));
stream_1->AddTrack(reference_collection_->at(0)->GetVideoTracks()[0]);
stream_1->AddTrack(reference_collection_->at(0)->GetAudioTracks()[0]);
signaling_->AddLocalStream(stream_1);
// Replace msid in the original SDP.
std::string sdp;
desc->ToString(&sdp);
talk_base::replace_substrs(
kStreams[0], strlen(kStreams[0]), kStreams[1], strlen(kStreams[1]), &sdp);
talk_base::scoped_ptr<SessionDescriptionInterface> updated_desc(
webrtc::CreateSessionDescription(SessionDescriptionInterface::kOffer,
sdp, NULL));
signaling_->OnLocalDescriptionChanged(updated_desc.get());
observer_->VerifyLocalAudioTrack(kStreams[1], kAudioTracks[0], 1);
observer_->VerifyLocalVideoTrack(kStreams[1], kVideoTracks[0], 2);
EXPECT_EQ(1u, observer_->NumberOfLocalAudioTracks());
EXPECT_EQ(1u, observer_->NumberOfLocalVideoTracks());
}
// Verifies that an even SCTP id is allocated for SSL_CLIENT and an odd id for
// SSL_SERVER.
TEST_F(MediaStreamSignalingTest, SctpIdAllocationBasedOnRole) {

View File

@ -106,10 +106,10 @@ struct CreateVideoSourceParams : public talk_base::MessageData {
};
struct StartAecDumpParams : public talk_base::MessageData {
explicit StartAecDumpParams(FILE* aec_dump_file)
explicit StartAecDumpParams(talk_base::PlatformFile aec_dump_file)
: aec_dump_file(aec_dump_file) {
}
FILE* aec_dump_file;
talk_base::PlatformFile aec_dump_file;
bool result;
};
@ -289,7 +289,7 @@ PeerConnectionFactory::CreateVideoSource_s(
return VideoSourceProxy::Create(signaling_thread_, source);
}
bool PeerConnectionFactory::StartAecDump_s(FILE* file) {
bool PeerConnectionFactory::StartAecDump_s(talk_base::PlatformFile file) {
return channel_manager_->StartAecDump(file);
}
@ -380,7 +380,7 @@ scoped_refptr<AudioTrackInterface> PeerConnectionFactory::CreateAudioTrack(
return AudioTrackProxy::Create(signaling_thread_, track);
}
bool PeerConnectionFactory::StartAecDump(FILE* file) {
bool PeerConnectionFactory::StartAecDump(talk_base::PlatformFile file) {
StartAecDumpParams params(file);
signaling_thread_->Send(this, MSG_START_AEC_DUMP, &params);
return params.result;

View File

@ -78,7 +78,7 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface,
CreateAudioTrack(const std::string& id,
AudioSourceInterface* audio_source);
virtual bool StartAecDump(FILE* file);
virtual bool StartAecDump(talk_base::PlatformFile file);
virtual cricket::ChannelManager* channel_manager();
virtual talk_base::Thread* signaling_thread();
@ -109,7 +109,7 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface,
PortAllocatorFactoryInterface* allocator_factory,
DTLSIdentityServiceInterface* dtls_identity_service,
PeerConnectionObserver* observer);
bool StartAecDump_s(FILE* file);
bool StartAecDump_s(talk_base::PlatformFile file);
// Implements talk_base::MessageHandler.
void OnMessage(talk_base::Message* msg);

View File

@ -76,6 +76,7 @@
#include "talk/app/webrtc/jsep.h"
#include "talk/app/webrtc/mediastreaminterface.h"
#include "talk/app/webrtc/statstypes.h"
#include "talk/base/fileutils.h"
#include "talk/base/socketaddress.h"
namespace talk_base {
@ -442,9 +443,10 @@ class PeerConnectionFactoryInterface : public talk_base::RefCountInterface {
// Starts AEC dump using existing file. Takes ownership of |file| and passes
// it on to VoiceEngine (via other objects) immediately, which will take
// the ownerhip.
// the ownerhip. If the operation fails, the file will be closed.
// TODO(grunell): Remove when Chromium has started to use AEC in each source.
virtual bool StartAecDump(FILE* file) = 0;
// http://crbug.com/264611.
virtual bool StartAecDump(talk_base::PlatformFile file) = 0;
protected:
// Dtor and ctor protected as objects shouldn't be created or deleted via

View File

@ -28,6 +28,9 @@
#include <cassert>
#ifdef WIN32
// TODO(grunell): Remove io.h includes when Chromium has started
// to use AEC in each source. http://crbug.com/264611.
#include <io.h>
#include "talk/base/win32.h"
#endif
@ -294,4 +297,28 @@ bool CreateUniqueFile(Pathname& path, bool create_empty) {
return true;
}
// Taken from Chromium's base/platform_file_*.cc.
// TODO(grunell): Remove when Chromium has started to use AEC in each source.
// http://crbug.com/264611.
FILE* FdopenPlatformFileForWriting(PlatformFile file) {
#if defined(WIN32)
if (file == kInvalidPlatformFileValue)
return NULL;
int fd = _open_osfhandle(reinterpret_cast<intptr_t>(file), 0);
if (fd < 0)
return NULL;
return _fdopen(fd, "w");
#else
return fdopen(file, "w");
#endif
}
bool ClosePlatformFile(PlatformFile file) {
#if defined(WIN32)
return CloseHandle(file) != 0;
#else
return close(file);
#endif
}
} // namespace talk_base

View File

@ -452,6 +452,21 @@ class FilesystemScope{
// process).
bool CreateUniqueFile(Pathname& path, bool create_empty);
// Taken from Chromium's base/platform_file.h.
// Don't use ClosePlatformFile to close a file opened with FdopenPlatformFile.
// Use fclose instead.
// TODO(grunell): Remove when Chromium has started to use AEC in each source.
// http://crbug.com/264611.
#if defined(WIN32)
typedef HANDLE PlatformFile;
const PlatformFile kInvalidPlatformFileValue = INVALID_HANDLE_VALUE;
#elif defined(POSIX)
typedef int PlatformFile;
const PlatformFile kInvalidPlatformFileValue = -1;
#endif
FILE* FdopenPlatformFileForWriting(PlatformFile file);
bool ClosePlatformFile(PlatformFile file);
} // namespace talk_base
#endif // TALK_BASE_FILEUTILS_H_

View File

@ -509,6 +509,8 @@
'xmpp/pubsub_task.h',
'xmpp/pubsubclient.cc',
'xmpp/pubsubclient.h',
'xmpp/pubsubstateclient.cc',
'xmpp/pubsubstateclient.h',
'xmpp/pubsubtasks.cc',
'xmpp/pubsubtasks.h',
'xmpp/receivetask.cc',

View File

@ -486,7 +486,7 @@ talk.App(env, name = "relayserver",
"jingle",
],
srcs = [
"p2p/base/relayserver_main.cc",
"examples/relayserver/relayserver_main.cc",
],
)
talk.App(env, name = "stunserver",
@ -494,7 +494,7 @@ talk.App(env, name = "stunserver",
"jingle",
],
srcs = [
"p2p/base/stunserver_main.cc",
"examples/stunserver/stunserver_main.cc",
],
)
talk.App(env, name = "turnserver",
@ -503,7 +503,7 @@ talk.App(env, name = "turnserver",
"ssl",
],
srcs = [
"p2p/base/turnserver_main.cc",
"examples/turnserver/turnserver_main.cc",
],
libs = [
"jingle",

View File

@ -790,7 +790,7 @@ class FakeVoiceEngine : public FakeBaseEngine {
bool SetLocalMonitor(bool enable) { return true; }
bool StartAecDump(FILE* file) { return false; }
bool StartAecDump(talk_base::PlatformFile file) { return false; }
bool RegisterProcessor(uint32 ssrc, VoiceProcessor* voice_processor,
MediaProcessorDirection direction) {

View File

@ -133,7 +133,7 @@ class FileMediaEngine : public MediaEngineInterface {
virtual bool FindVideoCodec(const VideoCodec& codec) { return true; }
virtual void SetVoiceLogging(int min_sev, const char* filter) {}
virtual void SetVideoLogging(int min_sev, const char* filter) {}
virtual bool StartAecDump(FILE* file) { return false; }
virtual bool StartAecDump(talk_base::PlatformFile) { return false; }
virtual bool RegisterVideoProcessor(VideoProcessor* processor) {
return true;

View File

@ -36,6 +36,7 @@
#include <string>
#include <vector>
#include "talk/base/fileutils.h"
#include "talk/base/sigslotrepeater.h"
#include "talk/media/base/codec.h"
#include "talk/media/base/mediachannel.h"
@ -136,7 +137,7 @@ class MediaEngineInterface {
virtual void SetVideoLogging(int min_sev, const char* filter) = 0;
// Starts AEC dump using existing file.
virtual bool StartAecDump(FILE* file) = 0;
virtual bool StartAecDump(talk_base::PlatformFile file) = 0;
// Voice processors for effects.
virtual bool RegisterVoiceProcessor(uint32 ssrc,
@ -256,7 +257,7 @@ class CompositeMediaEngine : public MediaEngineInterface {
video_.SetLogging(min_sev, filter);
}
virtual bool StartAecDump(FILE* file) {
virtual bool StartAecDump(talk_base::PlatformFile file) {
return voice_.StartAecDump(file);
}
@ -316,7 +317,7 @@ class NullVoiceEngine {
return rtp_header_extensions_;
}
void SetLogging(int min_sev, const char* filter) {}
bool StartAecDump(FILE* file) { return false; }
bool StartAecDump(talk_base::PlatformFile file) { return false; }
bool RegisterProcessor(uint32 ssrc,
VoiceProcessor* voice_processor,
MediaProcessorDirection direction) { return true; }

View File

@ -640,9 +640,7 @@ class FakeWebRtcVideoEngine
}
WEBRTC_STUB(RegisterCpuOveruseObserver,
(int channel, webrtc::CpuOveruseObserver* observer));
#ifdef USE_WEBRTC_DEV_BRANCH
WEBRTC_STUB(CpuOveruseMeasures, (int, int*, int*, int*, int*));
#endif
WEBRTC_STUB(ConnectAudioChannel, (const int, const int));
WEBRTC_STUB(DisconnectAudioChannel, (const int));
WEBRTC_FUNC(StartSend, (const int channel)) {
@ -827,12 +825,8 @@ class FakeWebRtcVideoEngine
}
WEBRTC_STUB(RegisterSendTransport, (const int, webrtc::Transport&));
WEBRTC_STUB(DeregisterSendTransport, (const int));
#ifdef USE_WEBRTC_DEV_BRANCH
WEBRTC_STUB(ReceivedRTPPacket, (const int, const void*, const int,
const webrtc::PacketTime&));
#else
WEBRTC_STUB(ReceivedRTPPacket, (const int, const void*, const int));
#endif
WEBRTC_STUB(ReceivedRTCPPacket, (const int, const void*, const int));
// Not using WEBRTC_STUB due to bool return value
virtual bool IsIPv6Enabled(int channel) { return true; }
@ -1040,9 +1034,7 @@ class FakeWebRtcVideoEngine
channels_[channel]->rtp_absolute_send_time_receive_id_ = (enable) ? id : 0;
return 0;
}
#ifdef USE_WEBRTC_DEV_BRANCH
WEBRTC_STUB(SetRtcpXrRrtrStatus, (int, bool));
#endif
WEBRTC_FUNC(SetTransmissionSmoothingStatus, (int channel, bool enable)) {
WEBRTC_CHECK_CHANNEL(channel);
channels_[channel]->transmission_smoothing_ = enable;

View File

@ -631,13 +631,11 @@ class FakeWebRtcVoiceEngine
// webrtc::VoENetEqStats
WEBRTC_STUB(GetNetworkStatistics, (int, webrtc::NetworkStatistics&));
#ifdef USE_WEBRTC_DEV_BRANCH
WEBRTC_FUNC_CONST(GetDecodingCallStatistics, (int channel,
webrtc::AudioDecodingCallStats*)) {
WEBRTC_CHECK_CHANNEL(channel);
return 0;
}
#endif
// webrtc::VoENetwork
WEBRTC_FUNC(RegisterExternalTransport, (int channel,
@ -923,9 +921,7 @@ class FakeWebRtcVoiceEngine
WEBRTC_STUB(GetEcDelayMetrics, (int& delay_median, int& delay_std));
WEBRTC_STUB(StartDebugRecording, (const char* fileNameUTF8));
#ifdef USE_WEBRTC_DEV_BRANCH
WEBRTC_STUB(StartDebugRecording, (FILE* handle));
#endif
WEBRTC_STUB(StopDebugRecording, ());
WEBRTC_FUNC(SetTypingDetectionStatus, (bool enable)) {

View File

@ -145,7 +145,7 @@ class WebRtcMediaEngine : public cricket::MediaEngineInterface {
virtual void SetVideoLogging(int min_sev, const char* filter) OVERRIDE {
delegate_->SetVideoLogging(min_sev, filter);
}
virtual bool StartAecDump(FILE* file) OVERRIDE {
virtual bool StartAecDump(talk_base::PlatformFile file) OVERRIDE {
return delegate_->StartAecDump(file);
}
virtual bool RegisterVoiceProcessor(

View File

@ -2300,7 +2300,6 @@ bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) {
sinfo.encode_usage_percent = -1;
sinfo.capture_queue_delay_ms_per_s = -1;
#ifdef USE_WEBRTC_DEV_BRANCH
int capture_jitter_ms = 0;
int avg_encode_time_ms = 0;
int encode_usage_percent = 0;
@ -2316,7 +2315,6 @@ bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) {
sinfo.encode_usage_percent = encode_usage_percent;
sinfo.capture_queue_delay_ms_per_s = capture_queue_delay_ms_per_s;
}
#endif
// Get received RTCP statistics for the sender (reported by the remote
// client in a RTCP packet), if available.
@ -2465,9 +2463,7 @@ bool WebRtcVideoMediaChannel::SetCapturer(uint32 ssrc,
MaybeDisconnectCapturer(old_capturer);
send_channel->set_video_capturer(capturer);
capturer->SignalVideoFrame.connect(
this,
&WebRtcVideoMediaChannel::SendFrame);
MaybeConnectCapturer(capturer);
if (!capturer->IsScreencast() && ratio_w_ != 0 && ratio_h_ != 0) {
capturer->UpdateAspectRatio(ratio_w_, ratio_h_);
}
@ -2500,12 +2496,8 @@ void WebRtcVideoMediaChannel::OnPacketReceived(
engine()->vie()->network()->ReceivedRTPPacket(
which_channel,
packet->data(),
#ifdef USE_WEBRTC_DEV_BRANCH
static_cast<int>(packet->length()),
webrtc::PacketTime(packet_time.timestamp, packet_time.not_before));
#else
static_cast<int>(packet->length()));
#endif
}
void WebRtcVideoMediaChannel::OnRtcpReceived(

View File

@ -1274,11 +1274,7 @@ TEST_F(WebRtcVideoEngineTestFake, SetOptionsWithDenoising) {
EXPECT_FALSE(vie_.GetCaptureDenoising(capture_id));
}
// Test disabled because it drops frames when adapt-before-effects is turned
// off (turned off because it was exposing a crash - see bug 12250150). This is
// safe for now because this test exercises an unused feature.
// TODO(tpsiaki) reenable once adapt-before-effects is turned back on.
TEST_F(WebRtcVideoEngineTestFake, DISABLED_MultipleSendStreamsWithOneCapturer) {
TEST_F(WebRtcVideoEngineTestFake, MultipleSendStreamsWithOneCapturer) {
EXPECT_TRUE(SetupEngine());
// Start the capturer

View File

@ -1433,20 +1433,23 @@ bool WebRtcVoiceEngine::SetAudioDeviceModule(webrtc::AudioDeviceModule* adm,
return true;
}
bool WebRtcVoiceEngine::StartAecDump(FILE* file) {
#ifdef USE_WEBRTC_DEV_BRANCH
bool WebRtcVoiceEngine::StartAecDump(talk_base::PlatformFile file) {
FILE* aec_dump_file_stream = talk_base::FdopenPlatformFileForWriting(file);
if (!aec_dump_file_stream) {
LOG(LS_ERROR) << "Could not open AEC dump file stream.";
if (!talk_base::ClosePlatformFile(file))
LOG(LS_WARNING) << "Could not close file.";
return false;
}
StopAecDump();
if (voe_wrapper_->processing()->StartDebugRecording(file) !=
if (voe_wrapper_->processing()->StartDebugRecording(aec_dump_file_stream) !=
webrtc::AudioProcessing::kNoError) {
LOG_RTCERR1(StartDebugRecording, "FILE*");
fclose(file);
LOG_RTCERR0(StartDebugRecording);
fclose(aec_dump_file_stream);
return false;
}
is_dumping_aec_ = true;
return true;
#else
return false;
#endif
}
bool WebRtcVoiceEngine::RegisterProcessor(

View File

@ -175,7 +175,7 @@ class WebRtcVoiceEngine
webrtc::AudioDeviceModule* adm_sc);
// Starts AEC dump using existing file.
bool StartAecDump(FILE* file);
bool StartAecDump(talk_base::PlatformFile file);
// Check whether the supplied trace should be ignored.
bool ShouldIgnoreTrace(const std::string& trace);

View File

@ -24,6 +24,9 @@
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#if defined(POSIX)
#include <dirent.h>
#endif
#include "talk/base/asynctcpsocket.h"
#include "talk/base/buffer.h"
@ -71,7 +74,7 @@ static const char kIcePwd1[] = "TESTICEPWD00000000000001";
static const char kIcePwd2[] = "TESTICEPWD00000000000002";
static const char kTurnUsername[] = "test";
static const char kTurnPassword[] = "test";
static const int kTimeout = 1000;
static const unsigned int kTimeout = 1000;
static const cricket::ProtocolAddress kTurnUdpProtoAddr(
kTurnUdpIntAddr, cricket::PROTO_UDP);
@ -80,8 +83,26 @@ static const cricket::ProtocolAddress kTurnTcpProtoAddr(
static const cricket::ProtocolAddress kTurnUdpIPv6ProtoAddr(
kTurnUdpIPv6IntAddr, cricket::PROTO_UDP);
static const unsigned int MSG_TESTFINISH = 0;
#if defined(LINUX)
static int GetFDCount() {
struct dirent *dp;
int fd_count = 0;
DIR *dir = opendir("/proc/self/fd/");
while ((dp = readdir(dir)) != NULL) {
if (dp->d_name[0] == '.')
continue;
++fd_count;
}
closedir(dir);
return fd_count;
}
#endif
class TurnPortTest : public testing::Test,
public sigslot::has_slots<> {
public sigslot::has_slots<>,
public talk_base::MessageHandler {
public:
TurnPortTest()
: main_(talk_base::Thread::Current()),
@ -95,10 +116,17 @@ class TurnPortTest : public testing::Test,
turn_error_(false),
turn_unknown_address_(false),
turn_create_permission_success_(false),
udp_ready_(false) {
udp_ready_(false),
test_finish_(false) {
network_.AddIP(talk_base::IPAddress(INADDR_ANY));
}
virtual void OnMessage(talk_base::Message* msg) {
ASSERT(msg->message_id == MSG_TESTFINISH);
if (msg->message_id == MSG_TESTFINISH)
test_finish_ = true;
}
void OnTurnPortComplete(Port* port) {
turn_ready_ = true;
}
@ -264,6 +292,7 @@ class TurnPortTest : public testing::Test,
bool turn_unknown_address_;
bool turn_create_permission_success_;
bool udp_ready_;
bool test_finish_;
std::vector<talk_base::Buffer> turn_packets_;
std::vector<talk_base::Buffer> udp_packets_;
};
@ -378,3 +407,24 @@ TEST_F(TurnPortTest, TestTurnLocalIPv6AddressServerIPv6ExtenalIPv4) {
EXPECT_NE(0, turn_port_->Candidates()[0].address().port());
}
// This test verifies any FD's are not leaked after TurnPort is destroyed.
// https://code.google.com/p/webrtc/issues/detail?id=2651
#if defined(LINUX)
TEST_F(TurnPortTest, TestResolverShutdown) {
turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, cricket::PROTO_UDP);
int last_fd_count = GetFDCount();
// Need to supply unresolved address to kick off resolver.
CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword,
cricket::ProtocolAddress(talk_base::SocketAddress(
"stun.l.google.com", 3478), cricket::PROTO_UDP));
turn_port_->PrepareAddress();
ASSERT_TRUE_WAIT(turn_error_, kTimeout);
EXPECT_TRUE(turn_port_->Candidates().empty());
turn_port_.reset();
talk_base::Thread::Current()->Post(this, MSG_TESTFINISH);
// Waiting for above message to be processed.
ASSERT_TRUE_WAIT(test_finish_, kTimeout);
EXPECT_EQ(last_fd_count, GetFDCount());
}
#endif

View File

@ -947,7 +947,7 @@ bool ChannelManager::SetAudioOptions(const AudioOptions& options) {
return true;
}
bool ChannelManager::StartAecDump(FILE* file) {
bool ChannelManager::StartAecDump(talk_base::PlatformFile file) {
return worker_thread_->Invoke<bool>(
Bind(&MediaEngineInterface::StartAecDump, media_engine_.get(), file));
}

View File

@ -32,6 +32,7 @@
#include <vector>
#include "talk/base/criticalsection.h"
#include "talk/base/fileutils.h"
#include "talk/base/sigslotrepeater.h"
#include "talk/base/thread.h"
#include "talk/media/base/capturemanager.h"
@ -215,7 +216,7 @@ class ChannelManager : public talk_base::MessageHandler,
const VideoFormat& max_format);
// Starts AEC dump using existing file.
bool StartAecDump(FILE* file);
bool StartAecDump(talk_base::PlatformFile file);
sigslot::repeater0<> SignalDevicesChange;
sigslot::signal2<VideoCapturer*, CaptureState> SignalVideoCaptureStateChange;

View File

@ -44,54 +44,8 @@ const char kPresenting[] = "s";
const char kNotPresenting[] = "o";
const char kEmpty[] = "";
const std::string GetPublisherNickFromPubSubItem(const XmlElement* item_elem) {
if (item_elem == NULL) {
return "";
}
return Jid(item_elem->Attr(QN_ATTR_PUBLISHER)).resource();
}
} // namespace
// Knows how to handle specific states and XML.
template <typename C>
class PubSubStateSerializer {
public:
virtual ~PubSubStateSerializer() {}
virtual XmlElement* Write(const QName& state_name, const C& state) = 0;
virtual C Parse(const XmlElement* state_elem) = 0;
};
// Knows how to create "keys" for states, which determines their
// uniqueness. Most states are per-nick, but block is
// per-blocker-and-blockee. This is independent of itemid, especially
// in the case of presenter state.
class PubSubStateKeySerializer {
public:
virtual ~PubSubStateKeySerializer() {}
virtual std::string GetKey(const std::string& publisher_nick,
const std::string& published_nick) = 0;
};
class PublishedNickKeySerializer : public PubSubStateKeySerializer {
public:
virtual std::string GetKey(const std::string& publisher_nick,
const std::string& published_nick) {
return published_nick;
}
};
class PublisherAndPublishedNicksKeySerializer
: public PubSubStateKeySerializer {
public:
virtual std::string GetKey(const std::string& publisher_nick,
const std::string& published_nick) {
return publisher_nick + ":" + published_nick;
}
};
// A simple serialiazer where presence of item => true, lack of item
// => false.
class BoolStateSerializer : public PubSubStateSerializer<bool> {
@ -103,195 +57,11 @@ class BoolStateSerializer : public PubSubStateSerializer<bool> {
return new XmlElement(state_name, true);
}
virtual bool Parse(const XmlElement* state_elem) {
return state_elem != NULL;
virtual void Parse(const XmlElement* state_elem, bool *state_out) {
*state_out = state_elem != NULL;
}
};
// Adapts PubSubClient to be specifically suited for pub sub call
// states. Signals state changes and keeps track of keys, which are
// normally nicks.
// TODO: Expose this as a generally useful class, not just
// private to hangouts.
template <typename C>
class PubSubStateClient : public sigslot::has_slots<> {
public:
// Gets ownership of the serializers, but not the client.
PubSubStateClient(const std::string& publisher_nick,
PubSubClient* client,
const QName& state_name,
C default_state,
PubSubStateKeySerializer* key_serializer,
PubSubStateSerializer<C>* state_serializer)
: publisher_nick_(publisher_nick),
client_(client),
state_name_(state_name),
default_state_(default_state) {
key_serializer_.reset(key_serializer);
state_serializer_.reset(state_serializer);
client_->SignalItems.connect(
this, &PubSubStateClient<C>::OnItems);
client_->SignalPublishResult.connect(
this, &PubSubStateClient<C>::OnPublishResult);
client_->SignalPublishError.connect(
this, &PubSubStateClient<C>::OnPublishError);
client_->SignalRetractResult.connect(
this, &PubSubStateClient<C>::OnRetractResult);
client_->SignalRetractError.connect(
this, &PubSubStateClient<C>::OnRetractError);
}
virtual ~PubSubStateClient() {}
virtual void Publish(const std::string& published_nick,
const C& state,
std::string* task_id_out) {
std::string key = key_serializer_->GetKey(publisher_nick_, published_nick);
std::string itemid = state_name_.LocalPart() + ":" + key;
if (StatesEqual(state, default_state_)) {
client_->RetractItem(itemid, task_id_out);
} else {
XmlElement* state_elem = state_serializer_->Write(state_name_, state);
state_elem->AddAttr(QN_NICK, published_nick);
client_->PublishItem(itemid, state_elem, task_id_out);
}
};
sigslot::signal1<const PubSubStateChange<C>&> SignalStateChange;
// Signal (task_id, item). item is NULL for retract.
sigslot::signal2<const std::string&,
const XmlElement*> SignalPublishResult;
// Signal (task_id, item, error stanza). item is NULL for retract.
sigslot::signal3<const std::string&,
const XmlElement*,
const XmlElement*> SignalPublishError;
protected:
// return false if retracted item (no info or state given)
virtual bool ParseStateItem(const PubSubItem& item,
StateItemInfo* info_out,
bool* state_out) {
const XmlElement* state_elem = item.elem->FirstNamed(state_name_);
if (state_elem == NULL) {
return false;
}
info_out->publisher_nick = GetPublisherNickFromPubSubItem(item.elem);
info_out->published_nick = state_elem->Attr(QN_NICK);
*state_out = state_serializer_->Parse(state_elem);
return true;
};
virtual bool StatesEqual(C state1, C state2) {
return state1 == state2;
}
PubSubClient* client() { return client_; }
private:
void OnItems(PubSubClient* pub_sub_client,
const std::vector<PubSubItem>& items) {
for (std::vector<PubSubItem>::const_iterator item = items.begin();
item != items.end(); ++item) {
OnItem(*item);
}
}
void OnItem(const PubSubItem& item) {
const std::string& itemid = item.itemid;
StateItemInfo info;
C new_state;
bool retracted = !ParseStateItem(item, &info, &new_state);
if (retracted) {
bool known_itemid =
(info_by_itemid_.find(itemid) != info_by_itemid_.end());
if (!known_itemid) {
// Nothing to retract, and nothing to publish.
// Probably a different state type.
return;
} else {
info = info_by_itemid_[itemid];
info_by_itemid_.erase(itemid);
new_state = default_state_;
}
} else {
// TODO: Assert new key matches the known key. It
// shouldn't change!
info_by_itemid_[itemid] = info;
}
std::string key = key_serializer_->GetKey(
info.publisher_nick, info.published_nick);
bool has_old_state = (state_by_key_.find(key) != state_by_key_.end());
C old_state = has_old_state ? state_by_key_[key] : default_state_;
if ((retracted && !has_old_state) || StatesEqual(new_state, old_state)) {
// Nothing change, so don't bother signalling.
return;
}
if (retracted || StatesEqual(new_state, default_state_)) {
// We treat a default state similar to a retract.
state_by_key_.erase(key);
} else {
state_by_key_[key] = new_state;
}
PubSubStateChange<C> change;
if (!retracted) {
// Retracts do not have publisher information.
change.publisher_nick = info.publisher_nick;
}
change.published_nick = info.published_nick;
change.old_state = old_state;
change.new_state = new_state;
SignalStateChange(change);
}
void OnPublishResult(PubSubClient* pub_sub_client,
const std::string& task_id,
const XmlElement* item) {
SignalPublishResult(task_id, item);
}
void OnPublishError(PubSubClient* pub_sub_client,
const std::string& task_id,
const buzz::XmlElement* item,
const buzz::XmlElement* stanza) {
SignalPublishError(task_id, item, stanza);
}
void OnRetractResult(PubSubClient* pub_sub_client,
const std::string& task_id) {
// There's no point in differentiating between publish and retract
// errors, so we simplify by making them both signal a publish
// result.
const XmlElement* item = NULL;
SignalPublishResult(task_id, item);
}
void OnRetractError(PubSubClient* pub_sub_client,
const std::string& task_id,
const buzz::XmlElement* stanza) {
// There's no point in differentiating between publish and retract
// errors, so we simplify by making them both signal a publish
// error.
const XmlElement* item = NULL;
SignalPublishError(task_id, item, stanza);
}
std::string publisher_nick_;
PubSubClient* client_;
const QName state_name_;
C default_state_;
talk_base::scoped_ptr<PubSubStateKeySerializer> key_serializer_;
talk_base::scoped_ptr<PubSubStateSerializer<C> > state_serializer_;
// key => state
std::map<std::string, C> state_by_key_;
// itemid => StateItemInfo
std::map<std::string, StateItemInfo> info_by_itemid_;
};
class PresenterStateClient : public PubSubStateClient<bool> {
public:
PresenterStateClient(const std::string& publisher_nick,
@ -336,15 +106,17 @@ class PresenterStateClient : public PubSubStateClient<bool> {
return false;
}
info_out->publisher_nick = GetPublisherNickFromPubSubItem(item.elem);
info_out->publisher_nick =
client()->GetPublisherNickFromPubSubItem(item.elem);
info_out->published_nick = presenter_elem->Attr(QN_NICK);
*state_out = (presentation_item_elem->Attr(
QN_PRESENTER_PRESENTATION_TYPE) != kNotPresenting);
return true;
}
virtual bool StatesEqual(bool state1, bool state2) {
return false; // Make every item trigger an event, even if state doesn't change.
virtual bool StatesEqual(const bool& state1, const bool& state2) {
// Make every item trigger an event, even if state doesn't change.
return false;
}
};

View File

@ -37,6 +37,7 @@
#include "talk/base/sigslotrepeater.h"
#include "talk/xmpp/jid.h"
#include "talk/xmpp/pubsubclient.h"
#include "talk/xmpp/pubsubstateclient.h"
// Gives a high-level API for MUC call PubSub needs such as
// presenter state, recording state, mute state, and remote mute.
@ -47,30 +48,6 @@ class Jid;
class XmlElement;
class XmppTaskParentInterface;
// To handle retracts correctly, we need to remember certain details
// about an item. We could just cache the entire XML element, but
// that would take more memory and require re-parsing.
struct StateItemInfo {
std::string published_nick;
std::string publisher_nick;
};
// Represents a PubSub state change. Usually, the key is the nick,
// but not always. It's a per-state-type thing. Currently documented
// at https://docs.google.com/a/google.com/document/d/
// 1QyHu_ufyVdf0VICdfc_DtJbrOdrdIUm4eM73RZqnivI/edit?hl=en_US
template <typename C>
struct PubSubStateChange {
// The nick of the user changing the state.
std::string publisher_nick;
// The nick of the user whose state is changing.
std::string published_nick;
C old_state;
C new_state;
};
template <typename C> class PubSubStateClient;
// A client tied to a specific MUC jid and local nick. Provides ways
// to get updates and publish state and events. Must call
// RequestAll() to start getting updates.

View File

@ -134,4 +134,13 @@ void PubSubClient::OnRetractError(IqTask* task,
SignalRetractError(this, retract_task->task_id(), stanza);
}
const std::string PubSubClient::GetPublisherNickFromPubSubItem(
const XmlElement* item_elem) {
if (item_elem == NULL) {
return "";
}
return Jid(item_elem->Attr(QN_ATTR_PUBLISHER)).resource();
}
} // namespace buzz

View File

@ -101,6 +101,9 @@ class PubSubClient : public sigslot::has_slots<> {
void RetractItem(const std::string& itemid,
std::string* task_id_out);
// Get the publisher nick if it exists from the pubsub item.
const std::string GetPublisherNickFromPubSubItem(const XmlElement* item_elem);
private:
void OnRequestError(IqTask* task,
const XmlElement* stanza);

View File

@ -0,0 +1,42 @@
/*
* libjingle
* Copyright 2011, Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "talk/xmpp/pubsubstateclient.h"
namespace buzz {
std::string PublishedNickKeySerializer::GetKey(
const std::string& publisher_nick, const std::string& published_nick) {
return published_nick;
}
std::string PublisherAndPublishedNicksKeySerializer::GetKey(
const std::string& publisher_nick, const std::string& published_nick) {
return publisher_nick + ":" + published_nick;
}
} // namespace buzz

View File

@ -0,0 +1,287 @@
/*
* libjingle
* Copyright 2011, Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TALK_XMPP_PUBSUBSTATECLIENT_H_
#define TALK_XMPP_PUBSUBSTATECLIENT_H_
#include <map>
#include <string>
#include <vector>
#include "talk/base/scoped_ptr.h"
#include "talk/base/sigslot.h"
#include "talk/base/sigslotrepeater.h"
#include "talk/xmpp/constants.h"
#include "talk/xmpp/jid.h"
#include "talk/xmpp/pubsubclient.h"
#include "talk/xmllite/qname.h"
#include "talk/xmllite/xmlelement.h"
namespace buzz {
// To handle retracts correctly, we need to remember certain details
// about an item. We could just cache the entire XML element, but
// that would take more memory and require re-parsing.
struct StateItemInfo {
std::string published_nick;
std::string publisher_nick;
};
// Represents a PubSub state change. Usually, the key is the nick,
// but not always. It's a per-state-type thing. Look below on how keys are
// computed.
template <typename C>
struct PubSubStateChange {
// The nick of the user changing the state.
std::string publisher_nick;
// The nick of the user whose state is changing.
std::string published_nick;
C old_state;
C new_state;
};
// Knows how to handle specific states and XML.
template <typename C>
class PubSubStateSerializer {
public:
virtual ~PubSubStateSerializer() {}
virtual XmlElement* Write(const QName& state_name, const C& state) = 0;
virtual void Parse(const XmlElement* state_elem, C* state_out) = 0;
};
// Knows how to create "keys" for states, which determines their
// uniqueness. Most states are per-nick, but block is
// per-blocker-and-blockee. This is independent of itemid, especially
// in the case of presenter state.
class PubSubStateKeySerializer {
public:
virtual ~PubSubStateKeySerializer() {}
virtual std::string GetKey(const std::string& publisher_nick,
const std::string& published_nick) = 0;
};
class PublishedNickKeySerializer : public PubSubStateKeySerializer {
public:
virtual std::string GetKey(const std::string& publisher_nick,
const std::string& published_nick);
};
class PublisherAndPublishedNicksKeySerializer
: public PubSubStateKeySerializer {
public:
virtual std::string GetKey(const std::string& publisher_nick,
const std::string& published_nick);
};
// Adapts PubSubClient to be specifically suited for pub sub call
// states. Signals state changes and keeps track of keys, which are
// normally nicks.
template <typename C>
class PubSubStateClient : public sigslot::has_slots<> {
public:
// Gets ownership of the serializers, but not the client.
PubSubStateClient(const std::string& publisher_nick,
PubSubClient* client,
const QName& state_name,
C default_state,
PubSubStateKeySerializer* key_serializer,
PubSubStateSerializer<C>* state_serializer)
: publisher_nick_(publisher_nick),
client_(client),
state_name_(state_name),
default_state_(default_state) {
key_serializer_.reset(key_serializer);
state_serializer_.reset(state_serializer);
client_->SignalItems.connect(
this, &PubSubStateClient<C>::OnItems);
client_->SignalPublishResult.connect(
this, &PubSubStateClient<C>::OnPublishResult);
client_->SignalPublishError.connect(
this, &PubSubStateClient<C>::OnPublishError);
client_->SignalRetractResult.connect(
this, &PubSubStateClient<C>::OnRetractResult);
client_->SignalRetractError.connect(
this, &PubSubStateClient<C>::OnRetractError);
}
virtual ~PubSubStateClient() {}
virtual void Publish(const std::string& published_nick,
const C& state,
std::string* task_id_out) {
std::string key = key_serializer_->GetKey(publisher_nick_, published_nick);
std::string itemid = state_name_.LocalPart() + ":" + key;
if (StatesEqual(state, default_state_)) {
client_->RetractItem(itemid, task_id_out);
} else {
XmlElement* state_elem = state_serializer_->Write(state_name_, state);
state_elem->AddAttr(QN_NICK, published_nick);
client_->PublishItem(itemid, state_elem, task_id_out);
}
}
sigslot::signal1<const PubSubStateChange<C>&> SignalStateChange;
// Signal (task_id, item). item is NULL for retract.
sigslot::signal2<const std::string&,
const XmlElement*> SignalPublishResult;
// Signal (task_id, item, error stanza). item is NULL for retract.
sigslot::signal3<const std::string&,
const XmlElement*,
const XmlElement*> SignalPublishError;
protected:
// return false if retracted item (no info or state given)
virtual bool ParseStateItem(const PubSubItem& item,
StateItemInfo* info_out,
C* state_out) {
const XmlElement* state_elem = item.elem->FirstNamed(state_name_);
if (state_elem == NULL) {
return false;
}
info_out->publisher_nick =
client_->GetPublisherNickFromPubSubItem(item.elem);
info_out->published_nick = state_elem->Attr(QN_NICK);
state_serializer_->Parse(state_elem, state_out);
return true;
}
virtual bool StatesEqual(const C& state1, const C& state2) {
return state1 == state2;
}
PubSubClient* client() { return client_; }
const QName& state_name() { return state_name_; }
private:
void OnItems(PubSubClient* pub_sub_client,
const std::vector<PubSubItem>& items) {
for (std::vector<PubSubItem>::const_iterator item = items.begin();
item != items.end(); ++item) {
OnItem(*item);
}
}
void OnItem(const PubSubItem& item) {
const std::string& itemid = item.itemid;
StateItemInfo info;
C new_state;
bool retracted = !ParseStateItem(item, &info, &new_state);
if (retracted) {
bool known_itemid =
(info_by_itemid_.find(itemid) != info_by_itemid_.end());
if (!known_itemid) {
// Nothing to retract, and nothing to publish.
// Probably a different state type.
return;
} else {
info = info_by_itemid_[itemid];
info_by_itemid_.erase(itemid);
new_state = default_state_;
}
} else {
// TODO: Assert new key matches the known key. It
// shouldn't change!
info_by_itemid_[itemid] = info;
}
std::string key = key_serializer_->GetKey(
info.publisher_nick, info.published_nick);
bool has_old_state = (state_by_key_.find(key) != state_by_key_.end());
C old_state = has_old_state ? state_by_key_[key] : default_state_;
if ((retracted && !has_old_state) || StatesEqual(new_state, old_state)) {
// Nothing change, so don't bother signalling.
return;
}
if (retracted || StatesEqual(new_state, default_state_)) {
// We treat a default state similar to a retract.
state_by_key_.erase(key);
} else {
state_by_key_[key] = new_state;
}
PubSubStateChange<C> change;
if (!retracted) {
// Retracts do not have publisher information.
change.publisher_nick = info.publisher_nick;
}
change.published_nick = info.published_nick;
change.old_state = old_state;
change.new_state = new_state;
SignalStateChange(change);
}
void OnPublishResult(PubSubClient* pub_sub_client,
const std::string& task_id,
const XmlElement* item) {
SignalPublishResult(task_id, item);
}
void OnPublishError(PubSubClient* pub_sub_client,
const std::string& task_id,
const buzz::XmlElement* item,
const buzz::XmlElement* stanza) {
SignalPublishError(task_id, item, stanza);
}
void OnRetractResult(PubSubClient* pub_sub_client,
const std::string& task_id) {
// There's no point in differentiating between publish and retract
// errors, so we simplify by making them both signal a publish
// result.
const XmlElement* item = NULL;
SignalPublishResult(task_id, item);
}
void OnRetractError(PubSubClient* pub_sub_client,
const std::string& task_id,
const buzz::XmlElement* stanza) {
// There's no point in differentiating between publish and retract
// errors, so we simplify by making them both signal a publish
// error.
const XmlElement* item = NULL;
SignalPublishError(task_id, item, stanza);
}
std::string publisher_nick_;
PubSubClient* client_;
const QName state_name_;
C default_state_;
talk_base::scoped_ptr<PubSubStateKeySerializer> key_serializer_;
talk_base::scoped_ptr<PubSubStateSerializer<C> > state_serializer_;
// key => state
std::map<std::string, C> state_by_key_;
// itemid => StateItemInfo
std::map<std::string, StateItemInfo> info_by_itemid_;
DISALLOW_COPY_AND_ASSIGN(PubSubStateClient);
};
} // namespace buzz
#endif // TALK_XMPP_PUBSUBSTATECLIENT_H_