diff --git a/talk/app/webrtc/mediastreamhandler.cc b/talk/app/webrtc/mediastreamhandler.cc index a94eef322..ca8e1053c 100644 --- a/talk/app/webrtc/mediastreamhandler.cc +++ b/talk/app/webrtc/mediastreamhandler.cc @@ -106,6 +106,8 @@ void LocalAudioTrackHandler::Stop() { void LocalAudioTrackHandler::OnEnabledChanged() { cricket::AudioOptions options; if (audio_track_->enabled() && audio_track_->GetSource()) { + // TODO(xians): Remove this static_cast since we should be able to connect + // a remote audio track to peer connection. options = static_cast( audio_track_->GetSource())->options(); } @@ -125,10 +127,12 @@ RemoteAudioTrackHandler::RemoteAudioTrackHandler( : TrackHandler(track, ssrc), audio_track_(track), provider_(provider) { + track->GetSource()->RegisterAudioObserver(this); OnEnabledChanged(); } RemoteAudioTrackHandler::~RemoteAudioTrackHandler() { + audio_track_->GetSource()->UnregisterAudioObserver(this); } void RemoteAudioTrackHandler::Stop() { @@ -143,6 +147,14 @@ void RemoteAudioTrackHandler::OnEnabledChanged() { audio_track_->GetRenderer()); } +void RemoteAudioTrackHandler::OnSetVolume(double volume) { + // When the track is disabled, the volume of the source, which is the + // corresponding WebRtc Voice Engine channel will be 0. So we do not allow + // setting the volume to the source when the track is disabled. + if (audio_track_->enabled()) + provider_->SetAudioPlayoutVolume(ssrc(), volume); +} + LocalVideoTrackHandler::LocalVideoTrackHandler( VideoTrackInterface* track, uint32 ssrc, diff --git a/talk/app/webrtc/mediastreamhandler.h b/talk/app/webrtc/mediastreamhandler.h index 625de8501..53afd5562 100644 --- a/talk/app/webrtc/mediastreamhandler.h +++ b/talk/app/webrtc/mediastreamhandler.h @@ -118,7 +118,8 @@ class LocalAudioTrackHandler : public TrackHandler { // RemoteAudioTrackHandler listen to events on a remote AudioTrack instance // connected to a PeerConnection and orders the |provider| to executes the // requested change. -class RemoteAudioTrackHandler : public TrackHandler { +class RemoteAudioTrackHandler : public AudioSourceInterface::AudioObserver, + public TrackHandler { public: RemoteAudioTrackHandler(AudioTrackInterface* track, uint32 ssrc, @@ -131,6 +132,9 @@ class RemoteAudioTrackHandler : public TrackHandler { virtual void OnEnabledChanged() OVERRIDE; private: + // AudioSourceInterface::AudioObserver implementation. + virtual void OnSetVolume(double volume) OVERRIDE; + AudioTrackInterface* audio_track_; AudioProviderInterface* provider_; }; diff --git a/talk/app/webrtc/mediastreamhandler_unittest.cc b/talk/app/webrtc/mediastreamhandler_unittest.cc index 475258e9b..6eedb7e89 100644 --- a/talk/app/webrtc/mediastreamhandler_unittest.cc +++ b/talk/app/webrtc/mediastreamhandler_unittest.cc @@ -31,6 +31,7 @@ #include "talk/app/webrtc/audiotrack.h" #include "talk/app/webrtc/mediastream.h" +#include "talk/app/webrtc/remoteaudiosource.h" #include "talk/app/webrtc/streamcollection.h" #include "talk/app/webrtc/videosource.h" #include "talk/app/webrtc/videotrack.h" @@ -59,6 +60,7 @@ class MockAudioProvider : public AudioProviderInterface { MOCK_METHOD4(SetAudioSend, void(uint32 ssrc, bool enable, const cricket::AudioOptions& options, cricket::AudioRenderer* renderer)); + MOCK_METHOD2(SetAudioPlayoutVolume, void(uint32 ssrc, double volume)); }; // Helper class to test MediaStreamHandler. @@ -110,12 +112,11 @@ class MediaStreamHandlerTest : public testing::Test { FakeVideoSource::Create()); video_track_ = VideoTrack::Create(kVideoTrackId, source); EXPECT_TRUE(stream_->AddTrack(video_track_)); - audio_track_ = AudioTrack::Create(kAudioTrackId, - NULL); - EXPECT_TRUE(stream_->AddTrack(audio_track_)); } void AddLocalAudioTrack() { + audio_track_ = AudioTrack::Create(kAudioTrackId, NULL); + EXPECT_TRUE(stream_->AddTrack(audio_track_)); EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc, true, _, _)); handlers_.AddLocalAudioTrack(stream_, stream_->GetAudioTracks()[0], kAudioSsrc); @@ -144,6 +145,9 @@ class MediaStreamHandlerTest : public testing::Test { } void AddRemoteAudioTrack() { + audio_track_ = AudioTrack::Create(kAudioTrackId, + RemoteAudioSource::Create().get()); + EXPECT_TRUE(stream_->AddTrack(audio_track_)); EXPECT_CALL(audio_provider_, SetAudioPlayout(kAudioSsrc, true, _)); handlers_.AddRemoteAudioTrack(stream_, stream_->GetAudioTracks()[0], kAudioSsrc); @@ -292,4 +296,27 @@ TEST_F(MediaStreamHandlerTest, RemoteVideoTrackDisable) { handlers_.TearDown(); } +TEST_F(MediaStreamHandlerTest, RemoteAudioTrackSetVolume) { + AddRemoteAudioTrack(); + + double volume = 0.5; + EXPECT_CALL(audio_provider_, SetAudioPlayoutVolume(kAudioSsrc, volume)); + audio_track_->GetSource()->SetVolume(volume); + + // Disable the audio track, this should prevent setting the volume. + EXPECT_CALL(audio_provider_, SetAudioPlayout(kAudioSsrc, false, _)); + audio_track_->set_enabled(false); + audio_track_->GetSource()->SetVolume(1.0); + + EXPECT_CALL(audio_provider_, SetAudioPlayout(kAudioSsrc, true, _)); + audio_track_->set_enabled(true); + + double new_volume = 0.8; + EXPECT_CALL(audio_provider_, SetAudioPlayoutVolume(kAudioSsrc, new_volume)); + audio_track_->GetSource()->SetVolume(new_volume); + + RemoveRemoteAudioTrack(); + handlers_.TearDown(); +} + } // namespace webrtc diff --git a/talk/app/webrtc/mediastreaminterface.h b/talk/app/webrtc/mediastreaminterface.h index 96d09428a..fa0572e38 100644 --- a/talk/app/webrtc/mediastreaminterface.h +++ b/talk/app/webrtc/mediastreaminterface.h @@ -142,9 +142,24 @@ class VideoTrackInterface : public MediaStreamTrackInterface { // AudioSourceInterface is a reference counted source used for AudioTracks. // The same source can be used in multiple AudioTracks. -// TODO(perkj): Extend this class with necessary methods to allow separate -// sources for each audio track. class AudioSourceInterface : public MediaSourceInterface { + public: + class AudioObserver { + public: + virtual void OnSetVolume(double volume) = 0; + + protected: + virtual ~AudioObserver() {} + }; + + // TODO(xians): Makes all the interface pure virtual after Chrome has their + // implementations. + // Sets the volume to the source. |volume| is in the range of [0, 10]. + virtual void SetVolume(double volume) {} + + // Registers/unregisters observer to the audio source. + virtual void RegisterAudioObserver(AudioObserver* observer) {} + virtual void UnregisterAudioObserver(AudioObserver* observer) {} }; // Interface for receiving audio data from a AudioTrack. diff --git a/talk/app/webrtc/mediastreamprovider.h b/talk/app/webrtc/mediastreamprovider.h index ae00b1de7..5cf0e2716 100644 --- a/talk/app/webrtc/mediastreamprovider.h +++ b/talk/app/webrtc/mediastreamprovider.h @@ -53,6 +53,10 @@ class AudioProviderInterface { const cricket::AudioOptions& options, cricket::AudioRenderer* renderer) = 0; + // Sets the audio playout volume of a remote audio track with |ssrc|. + // |volume| is in the range of [0, 10]. + virtual void SetAudioPlayoutVolume(uint32 ssrc, double volume) = 0; + protected: virtual ~AudioProviderInterface() {} }; diff --git a/talk/app/webrtc/mediastreamsignaling.cc b/talk/app/webrtc/mediastreamsignaling.cc index 610b3f8e7..14648eee4 100644 --- a/talk/app/webrtc/mediastreamsignaling.cc +++ b/talk/app/webrtc/mediastreamsignaling.cc @@ -33,6 +33,7 @@ #include "talk/app/webrtc/mediastreamproxy.h" #include "talk/app/webrtc/mediaconstraintsinterface.h" #include "talk/app/webrtc/mediastreamtrackproxy.h" +#include "talk/app/webrtc/remoteaudiosource.h" #include "talk/app/webrtc/remotevideocapturer.h" #include "talk/app/webrtc/sctputils.h" #include "talk/app/webrtc/videosource.h" @@ -140,7 +141,7 @@ class RemoteMediaStreamFactory { AudioTrackInterface* AddAudioTrack(webrtc::MediaStreamInterface* stream, const std::string& track_id) { return AddTrack( - stream, track_id, static_cast(NULL)); + stream, track_id, RemoteAudioSource::Create().get()); } VideoTrackInterface* AddVideoTrack(webrtc::MediaStreamInterface* stream, diff --git a/talk/app/webrtc/peerconnection.cc b/talk/app/webrtc/peerconnection.cc index 40640cf86..b404ec4a9 100644 --- a/talk/app/webrtc/peerconnection.cc +++ b/talk/app/webrtc/peerconnection.cc @@ -459,13 +459,19 @@ talk_base::scoped_refptr PeerConnection::CreateDtmfSender( } bool PeerConnection::GetStats(StatsObserver* observer, - MediaStreamTrackInterface* track) { + webrtc::MediaStreamTrackInterface* track) { + return GetStats(observer, track, kStatsOutputLevelStandard); +} + +bool PeerConnection::GetStats(StatsObserver* observer, + MediaStreamTrackInterface* track, + StatsOutputLevel level) { if (!VERIFY(observer != NULL)) { LOG(LS_ERROR) << "GetStats - observer is NULL."; return false; } - stats_.UpdateStats(); + stats_.UpdateStats(level); talk_base::scoped_ptr msg(new GetStatsMsg(observer)); if (!stats_.GetStats(track, &(msg->reports))) { return false; @@ -542,7 +548,7 @@ void PeerConnection::SetLocalDescription( } // Update stats here so that we have the most recent stats for tracks and // streams that might be removed by updating the session description. - stats_.UpdateStats(); + stats_.UpdateStats(kStatsOutputLevelStandard); std::string error; if (!session_->SetLocalDescription(desc, &error)) { PostSetSessionDescriptionFailure(observer, error); @@ -565,7 +571,7 @@ void PeerConnection::SetRemoteDescription( } // Update stats here so that we have the most recent stats for tracks and // streams that might be removed by updating the session description. - stats_.UpdateStats(); + stats_.UpdateStats(kStatsOutputLevelStandard); std::string error; if (!session_->SetRemoteDescription(desc, &error)) { PostSetSessionDescriptionFailure(observer, error); @@ -606,7 +612,7 @@ const SessionDescriptionInterface* PeerConnection::remote_description() const { void PeerConnection::Close() { // Update stats here so that we have the most recent stats for tracks and // streams before the channels are closed. - stats_.UpdateStats(); + stats_.UpdateStats(kStatsOutputLevelStandard); session_->Terminate(); } diff --git a/talk/app/webrtc/peerconnection.h b/talk/app/webrtc/peerconnection.h index 9cc9f3868..70155d992 100644 --- a/talk/app/webrtc/peerconnection.h +++ b/talk/app/webrtc/peerconnection.h @@ -76,6 +76,9 @@ class PeerConnection : public PeerConnectionInterface, const DataChannelInit* config); virtual bool GetStats(StatsObserver* observer, webrtc::MediaStreamTrackInterface* track); + virtual bool GetStats(StatsObserver* observer, + webrtc::MediaStreamTrackInterface* track, + StatsOutputLevel level); virtual SignalingState signaling_state(); diff --git a/talk/app/webrtc/peerconnectioninterface.h b/talk/app/webrtc/peerconnectioninterface.h index 667774e27..2f44885c8 100644 --- a/talk/app/webrtc/peerconnectioninterface.h +++ b/talk/app/webrtc/peerconnectioninterface.h @@ -166,6 +166,15 @@ class PeerConnectionInterface : public talk_base::RefCountInterface { }; typedef std::vector IceServers; + // Used by GetStats to decide which stats to include in the stats reports. + // |kStatsOutputLevelStandard| includes the standard stats for Javascript API; + // |kStatsOutputLevelDebug| includes both the standard stats and additional + // stats for debugging purposes. + enum StatsOutputLevel { + kStatsOutputLevelStandard, + kStatsOutputLevelDebug, + }; + // Accessor methods to active local streams. virtual talk_base::scoped_refptr local_streams() = 0; @@ -190,9 +199,14 @@ class PeerConnectionInterface : public talk_base::RefCountInterface { virtual talk_base::scoped_refptr CreateDtmfSender( AudioTrackInterface* track) = 0; + // TODO(jiayl): remove the old API once all Chrome overrides are updated. virtual bool GetStats(StatsObserver* observer, MediaStreamTrackInterface* track) = 0; + virtual bool GetStats(StatsObserver* observer, + MediaStreamTrackInterface* track, + StatsOutputLevel level) = 0; + virtual talk_base::scoped_refptr CreateDataChannel( const std::string& label, const DataChannelInit* config) = 0; diff --git a/talk/app/webrtc/peerconnectionproxy.h b/talk/app/webrtc/peerconnectionproxy.h index f07416d65..57bee518c 100644 --- a/talk/app/webrtc/peerconnectionproxy.h +++ b/talk/app/webrtc/peerconnectionproxy.h @@ -45,6 +45,9 @@ BEGIN_PROXY_MAP(PeerConnection) PROXY_METHOD1(talk_base::scoped_refptr, CreateDtmfSender, AudioTrackInterface*) PROXY_METHOD2(bool, GetStats, StatsObserver*, MediaStreamTrackInterface*) + PROXY_METHOD3(bool, GetStats, StatsObserver*, + MediaStreamTrackInterface*, + StatsOutputLevel) PROXY_METHOD2(talk_base::scoped_refptr, CreateDataChannel, const std::string&, const DataChannelInit*) PROXY_CONSTMETHOD0(const SessionDescriptionInterface*, local_description) diff --git a/talk/app/webrtc/remoteaudiosource.cc b/talk/app/webrtc/remoteaudiosource.cc new file mode 100644 index 000000000..1c275c74c --- /dev/null +++ b/talk/app/webrtc/remoteaudiosource.cc @@ -0,0 +1,72 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "talk/app/webrtc/remoteaudiosource.h" + +#include +#include + +#include "talk/base/logging.h" + +namespace webrtc { + +talk_base::scoped_refptr RemoteAudioSource::Create() { + return new talk_base::RefCountedObject(); +} + +RemoteAudioSource::RemoteAudioSource() { +} + +RemoteAudioSource::~RemoteAudioSource() { + ASSERT(audio_observers_.empty()); +} + +MediaSourceInterface::SourceState RemoteAudioSource::state() const { + return MediaSourceInterface::kLive; +} + +void RemoteAudioSource::SetVolume(double volume) { + ASSERT(volume >= 0 && volume <= 10); + for (AudioObserverList::iterator it = audio_observers_.begin(); + it != audio_observers_.end(); ++it) { + (*it)->OnSetVolume(volume); + } +} + +void RemoteAudioSource::RegisterAudioObserver(AudioObserver* observer) { + ASSERT(observer != NULL); + ASSERT(std::find(audio_observers_.begin(), audio_observers_.end(), + observer) == audio_observers_.end()); + audio_observers_.push_back(observer); +} + +void RemoteAudioSource::UnregisterAudioObserver(AudioObserver* observer) { + ASSERT(observer != NULL); + audio_observers_.remove(observer); +} + +} // namespace webrtc diff --git a/talk/app/webrtc/remoteaudiosource.h b/talk/app/webrtc/remoteaudiosource.h new file mode 100644 index 000000000..ed2421449 --- /dev/null +++ b/talk/app/webrtc/remoteaudiosource.h @@ -0,0 +1,66 @@ +/* + * libjingle + * Copyright 2014, 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_APP_WEBRTC_REMOTEAUDIOSOURCE_H_ +#define TALK_APP_WEBRTC_REMOTEAUDIOSOURCE_H_ + +#include + +#include "talk/app/webrtc/mediastreaminterface.h" +#include "talk/app/webrtc/notifier.h" + +namespace webrtc { + +using webrtc::AudioSourceInterface; + +// This class implements the audio source used by the remote audio track. +class RemoteAudioSource : public Notifier { + public: + // Creates an instance of RemoteAudioSource. + static talk_base::scoped_refptr Create(); + + protected: + RemoteAudioSource(); + virtual ~RemoteAudioSource(); + + private: + typedef std::list AudioObserverList; + + // MediaSourceInterface implementation. + virtual MediaSourceInterface::SourceState state() const OVERRIDE; + + // AudioSourceInterface implementation. + virtual void SetVolume(double volume) OVERRIDE; + virtual void RegisterAudioObserver(AudioObserver* observer) OVERRIDE; + virtual void UnregisterAudioObserver(AudioObserver* observer) OVERRIDE; + + AudioObserverList audio_observers_; +}; + +} // namespace webrtc + +#endif // TALK_APP_WEBRTC_REMOTEAUDIOSOURCE_H_ diff --git a/talk/app/webrtc/statscollector.cc b/talk/app/webrtc/statscollector.cc index 2efc11b1c..a900bba37 100644 --- a/talk/app/webrtc/statscollector.cc +++ b/talk/app/webrtc/statscollector.cc @@ -78,6 +78,7 @@ const char StatsReport::kStatsValueNameEchoReturnLossEnhancement[] = const char StatsReport::kStatsValueNameEncodeUsagePercent[] = "googEncodeUsagePercent"; +const char StatsReport::kStatsValueNameExpandRate[] = "googExpandRate"; const char StatsReport::kStatsValueNameFingerprint[] = "googFingerprint"; const char StatsReport::kStatsValueNameFingerprintAlgorithm[] = "googFingerprintAlgorithm"; @@ -121,12 +122,17 @@ const char StatsReport::kStatsValueNameLocalCertificateId[] = "googLocalCertificateId"; const char StatsReport::kStatsValueNameNacksReceived[] = "googNacksReceived"; const char StatsReport::kStatsValueNameNacksSent[] = "googNacksSent"; -const char StatsReport::kStatsValueNameNetEqExpandRate[] = - "googNetEqExpandRate"; const char StatsReport::kStatsValueNamePacketsReceived[] = "packetsReceived"; const char StatsReport::kStatsValueNamePacketsSent[] = "packetsSent"; const char StatsReport::kStatsValueNamePacketsLost[] = "packetsLost"; const char StatsReport::kStatsValueNameReadable[] = "googReadable"; +const char StatsReport::kStatsValueNameRecvPacketGroupArrivalTimeDebug[] = + "googReceivedPacketGroupArrivalTimeDebug"; +const char StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaDebug[] = + "googReceivedPacketGroupPropagationDeltaDebug"; +const char +StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaSumDebug[] = + "googReceivedPacketGroupPropagationDeltaSumDebug"; const char StatsReport::kStatsValueNameRemoteAddress[] = "googRemoteAddress"; const char StatsReport::kStatsValueNameRemoteCandidateType[] = "googRemoteCandidateType"; @@ -175,6 +181,20 @@ void StatsReport::AddValue(const std::string& name, int64 value) { AddValue(name, talk_base::ToString(value)); } +template +void StatsReport::AddValue(const std::string& name, + const std::vector& value) { + std::ostringstream oss; + oss << "["; + for (size_t i = 0; i < value.size(); ++i) { + oss << talk_base::ToString(value[i]); + if (i != value.size() - 1) + oss << ", "; + } + oss << "]"; + AddValue(name, oss.str()); +} + void StatsReport::AddBoolean(const std::string& name, bool value) { AddValue(name, value ? "true" : "false"); } @@ -221,7 +241,7 @@ void ExtractStats(const cricket::VoiceReceiverInfo& info, StatsReport* report) { info.bytes_rcvd); report->AddValue(StatsReport::kStatsValueNameJitterReceived, info.jitter_ms); - report->AddValue(StatsReport::kStatsValueNameNetEqExpandRate, + report->AddValue(StatsReport::kStatsValueNameExpandRate, talk_base::ToString(info.expand_rate)); report->AddValue(StatsReport::kStatsValueNamePacketsReceived, info.packets_rcvd); @@ -334,6 +354,7 @@ void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) { void ExtractStats(const cricket::BandwidthEstimationInfo& info, double stats_gathering_started, + PeerConnectionInterface::StatsOutputLevel level, StatsReport* report) { report->id = StatsReport::kStatsReportVideoBweId; report->type = StatsReport::kStatsReportTypeBwe; @@ -358,6 +379,19 @@ void ExtractStats(const cricket::BandwidthEstimationInfo& info, info.transmit_bitrate); report->AddValue(StatsReport::kStatsValueNameBucketDelay, info.bucket_delay); + if (level >= PeerConnectionInterface::kStatsOutputLevelDebug) { + report->AddValue( + StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaSumDebug, + info.total_received_propagation_delta_ms); + if (info.recent_received_propagation_delta_ms.size() > 0) { + report->AddValue( + StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaDebug, + info.recent_received_propagation_delta_ms); + report->AddValue( + StatsReport::kStatsValueNameRecvPacketGroupArrivalTimeDebug, + info.recent_received_packet_group_arrival_time_ms); + } + } } void ExtractRemoteStats(const cricket::MediaSenderInfo& info, @@ -399,7 +433,7 @@ void ExtractStatsFromList(const std::vector& data, ExtractRemoteStats(*it, report); } } -}; +} } // namespace @@ -463,7 +497,8 @@ bool StatsCollector::GetStats(MediaStreamTrackInterface* track, return true; } -void StatsCollector::UpdateStats() { +void +StatsCollector::UpdateStats(PeerConnectionInterface::StatsOutputLevel level) { double time_now = GetTimeNow(); // Calls to UpdateStats() that occur less than kMinGatherStatsPeriod number of // ms apart will be ignored. @@ -476,7 +511,7 @@ void StatsCollector::UpdateStats() { if (session_) { ExtractSessionInfo(); ExtractVoiceInfo(); - ExtractVideoInfo(); + ExtractVideoInfo(level); } } @@ -569,6 +604,14 @@ std::string StatsCollector::AddOneCertificateReport( talk_base::scoped_ptr ssl_fingerprint( talk_base::SSLFingerprint::Create(digest_algorithm, cert)); + + // SSLFingerprint::Create can fail if the algorithm returned by + // SSLCertificate::GetSignatureDigestAlgorithm is not supported by the + // implementation of SSLCertificate::ComputeDigest. This currently happens + // with MD5- and SHA-224-signed certificates when linked to libNSS. + if (!ssl_fingerprint) + return std::string(); + std::string fingerprint = ssl_fingerprint->GetRfc4572Fingerprint(); talk_base::Buffer der_buffer; @@ -737,12 +780,17 @@ void StatsCollector::ExtractVoiceInfo() { ExtractStatsFromList(voice_info.senders, transport_id, this); } -void StatsCollector::ExtractVideoInfo() { +void StatsCollector::ExtractVideoInfo( + PeerConnectionInterface::StatsOutputLevel level) { if (!session_->video_channel()) { return; } + cricket::StatsOptions options; + options.include_received_propagation_stats = + (level >= PeerConnectionInterface::kStatsOutputLevelDebug) ? + true : false; cricket::VideoMediaInfo video_info; - if (!session_->video_channel()->GetStats(&video_info)) { + if (!session_->video_channel()->GetStats(options, &video_info)) { LOG(LS_ERROR) << "Failed to get video channel stats."; return; } @@ -760,7 +808,7 @@ void StatsCollector::ExtractVideoInfo() { } else { StatsReport* report = &reports_[StatsReport::kStatsReportVideoBweId]; ExtractStats( - video_info.bw_estimations[0], stats_gathering_started_, report); + video_info.bw_estimations[0], stats_gathering_started_, level, report); } } diff --git a/talk/app/webrtc/statscollector.h b/talk/app/webrtc/statscollector.h index 01da059b5..6256d7786 100644 --- a/talk/app/webrtc/statscollector.h +++ b/talk/app/webrtc/statscollector.h @@ -35,6 +35,7 @@ #include #include "talk/app/webrtc/mediastreaminterface.h" +#include "talk/app/webrtc/peerconnectioninterface.h" #include "talk/app/webrtc/statstypes.h" #include "talk/app/webrtc/webrtcsession.h" @@ -57,13 +58,14 @@ class StatsCollector { void AddStream(MediaStreamInterface* stream); // Gather statistics from the session and store them for future use. - void UpdateStats(); + void UpdateStats(PeerConnectionInterface::StatsOutputLevel level); // Gets a StatsReports of the last collected stats. Note that UpdateStats must // be called before this function to get the most recent stats. |selector| is // a track label or empty string. The most recent reports are stored in // |reports|. - bool GetStats(MediaStreamTrackInterface* track, StatsReports* reports); + bool GetStats(MediaStreamTrackInterface* track, + StatsReports* reports); // Prepare an SSRC report for the given ssrc. Used internally // in the ExtractStatsFromList template. @@ -87,7 +89,7 @@ class StatsCollector { void ExtractSessionInfo(); void ExtractVoiceInfo(); - void ExtractVideoInfo(); + void ExtractVideoInfo(PeerConnectionInterface::StatsOutputLevel level); double GetTimeNow(); void BuildSsrcToTransportId(); WebRtcSession* session() { return session_; } diff --git a/talk/app/webrtc/statscollector_unittest.cc b/talk/app/webrtc/statscollector_unittest.cc index 1adcb0e20..a7cda1635 100644 --- a/talk/app/webrtc/statscollector_unittest.cc +++ b/talk/app/webrtc/statscollector_unittest.cc @@ -39,11 +39,14 @@ #include "talk/session/media/channelmanager.h" #include "testing/base/public/gmock.h" +using cricket::StatsOptions; using testing::_; using testing::DoAll; +using testing::Field; using testing::Return; using testing::ReturnNull; using testing::SetArgPointee; +using webrtc::PeerConnectionInterface; namespace cricket { @@ -80,7 +83,7 @@ class MockVideoMediaChannel : public cricket::FakeVideoMediaChannel { : cricket::FakeVideoMediaChannel(NULL) { } // MOCK_METHOD0(transport_channel, cricket::TransportChannel*()); - MOCK_METHOD1(GetStats, bool(cricket::VideoMediaInfo*)); + MOCK_METHOD2(GetStats, bool(const StatsOptions&, cricket::VideoMediaInfo*)); }; bool GetValue(const webrtc::StatsReport* report, @@ -289,7 +292,7 @@ class StatsCollectorTest : public testing::Test { EXPECT_CALL(session_, video_channel()) .WillRepeatedly(ReturnNull()); - stats.UpdateStats(); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); stats.GetStats(NULL, &reports); @@ -302,16 +305,24 @@ class StatsCollectorTest : public testing::Test { webrtc::StatsReport::kStatsReportTypeComponent, reports, webrtc::StatsReport::kStatsValueNameLocalCertificateId); - EXPECT_NE(kNotFound, local_certificate_id); - CheckCertChainReports(reports, local_ders, local_certificate_id); + if (local_ders.size() > 0) { + EXPECT_NE(kNotFound, local_certificate_id); + CheckCertChainReports(reports, local_ders, local_certificate_id); + } else { + EXPECT_EQ(kNotFound, local_certificate_id); + } // Check remote certificate chain. std::string remote_certificate_id = ExtractStatsValue( webrtc::StatsReport::kStatsReportTypeComponent, reports, webrtc::StatsReport::kStatsValueNameRemoteCertificateId); - EXPECT_NE(kNotFound, remote_certificate_id); - CheckCertChainReports(reports, remote_ders, remote_certificate_id); + if (remote_ders.size() > 0) { + EXPECT_NE(kNotFound, remote_certificate_id); + CheckCertChainReports(reports, remote_ders, remote_certificate_id); + } else { + EXPECT_EQ(kNotFound, remote_certificate_id); + } } cricket::FakeMediaEngine* media_engine_; @@ -347,10 +358,10 @@ TEST_F(StatsCollectorTest, BytesCounterHandles64Bits) { EXPECT_CALL(session_, video_channel()) .WillRepeatedly(Return(&video_channel)); - EXPECT_CALL(*media_channel, GetStats(_)) - .WillOnce(DoAll(SetArgPointee<0>(stats_read), + EXPECT_CALL(*media_channel, GetStats(_, _)) + .WillOnce(DoAll(SetArgPointee<1>(stats_read), Return(true))); - stats.UpdateStats(); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); stats.GetStats(NULL, &reports); std::string result = ExtractSsrcStatsValue(reports, "bytesSent"); EXPECT_EQ(kBytesSentString, result); @@ -386,11 +397,11 @@ TEST_F(StatsCollectorTest, BandwidthEstimationInfoIsReported) { EXPECT_CALL(session_, video_channel()) .WillRepeatedly(Return(&video_channel)); - EXPECT_CALL(*media_channel, GetStats(_)) - .WillOnce(DoAll(SetArgPointee<0>(stats_read), + EXPECT_CALL(*media_channel, GetStats(_, _)) + .WillOnce(DoAll(SetArgPointee<1>(stats_read), Return(true))); - stats.UpdateStats(); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); stats.GetStats(NULL, &reports); std::string result = ExtractSsrcStatsValue(reports, "bytesSent"); EXPECT_EQ(kBytesSentString, result); @@ -406,7 +417,7 @@ TEST_F(StatsCollectorTest, SessionObjectExists) { stats.set_session(&session_); EXPECT_CALL(session_, video_channel()) .WillRepeatedly(ReturnNull()); - stats.UpdateStats(); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); stats.GetStats(NULL, &reports); const webrtc::StatsReport* session_report = FindNthReportByType( reports, webrtc::StatsReport::kStatsReportTypeSession, 1); @@ -421,8 +432,8 @@ TEST_F(StatsCollectorTest, OnlyOneSessionObjectExists) { stats.set_session(&session_); EXPECT_CALL(session_, video_channel()) .WillRepeatedly(ReturnNull()); - stats.UpdateStats(); - stats.UpdateStats(); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); stats.GetStats(NULL, &reports); const webrtc::StatsReport* session_report = FindNthReportByType( reports, webrtc::StatsReport::kStatsReportTypeSession, 1); @@ -485,11 +496,11 @@ TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) { EXPECT_CALL(session_, video_channel()) .WillRepeatedly(Return(&video_channel)); - EXPECT_CALL(*media_channel, GetStats(_)) - .WillOnce(DoAll(SetArgPointee<0>(stats_read), + EXPECT_CALL(*media_channel, GetStats(_, _)) + .WillOnce(DoAll(SetArgPointee<1>(stats_read), Return(true))); - stats.UpdateStats(); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); stats.GetStats(NULL, &reports); // |reports| should contain at least one session report, one track report, // and one ssrc report. @@ -543,8 +554,8 @@ TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) { EXPECT_CALL(session_, video_channel()) .WillRepeatedly(Return(&video_channel)); - EXPECT_CALL(*media_channel, GetStats(_)) - .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read), + EXPECT_CALL(*media_channel, GetStats(_, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(stats_read), Return(true))); InitSessionStats(kVcName); @@ -552,7 +563,7 @@ TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) { .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_), Return(true))); - stats.UpdateStats(); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); stats.GetStats(NULL, &reports); std::string transport_id = ExtractStatsValue( webrtc::StatsReport::kStatsReportTypeSsrc, @@ -581,7 +592,7 @@ TEST_F(StatsCollectorTest, RemoteSsrcInfoIsAbsent) { EXPECT_CALL(session_, video_channel()) .WillRepeatedly(ReturnNull()); - stats.UpdateStats(); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); webrtc::StatsReports reports; stats.GetStats(NULL, &reports); const webrtc::StatsReport* remote_report = FindNthReportByType(reports, @@ -624,11 +635,11 @@ TEST_F(StatsCollectorTest, RemoteSsrcInfoIsPresent) { EXPECT_CALL(session_, video_channel()) .WillRepeatedly(Return(&video_channel)); - EXPECT_CALL(*media_channel, GetStats(_)) - .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read), + EXPECT_CALL(*media_channel, GetStats(_, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(stats_read), Return(true))); - stats.UpdateStats(); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); stats.GetStats(NULL, &reports); const webrtc::StatsReport* remote_report = FindNthReportByType(reports, webrtc::StatsReport::kStatsReportTypeRemoteSsrc, 1); @@ -703,7 +714,7 @@ TEST_F(StatsCollectorTest, NoTransport) { EXPECT_CALL(session_, video_channel()) .WillRepeatedly(ReturnNull()); - stats.UpdateStats(); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); stats.GetStats(NULL, &reports); // Check that the local certificate is absent. @@ -756,7 +767,7 @@ TEST_F(StatsCollectorTest, NoCertificates) { EXPECT_CALL(session_, video_channel()) .WillRepeatedly(ReturnNull()); - stats.UpdateStats(); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); stats.GetStats(NULL, &reports); // Check that the local certificate is absent. @@ -774,4 +785,63 @@ TEST_F(StatsCollectorTest, NoCertificates) { ASSERT_EQ(kNotFound, remote_certificate_id); } +// This test verifies that a remote certificate with an unsupported digest +// algorithm is correctly ignored. +TEST_F(StatsCollectorTest, UnsupportedDigestIgnored) { + // Build a local certificate. + std::string local_der = "This is the local der."; + talk_base::FakeSSLCertificate local_cert(DerToPem(local_der)); + + // Build a remote certificate with an unsupported digest algorithm. + std::string remote_der = "This is somebody else's der."; + talk_base::FakeSSLCertificate remote_cert(DerToPem(remote_der)); + remote_cert.set_digest_algorithm("foobar"); + + TestCertificateReports(local_cert, std::vector(1, local_der), + remote_cert, std::vector()); +} + +// Verifies the correct optons are passed to the VideoMediaChannel when using +// verbose output level. +TEST_F(StatsCollectorTest, StatsOutputLevelVerbose) { + webrtc::StatsCollector stats; // Implementation under test. + MockVideoMediaChannel* media_channel = new MockVideoMediaChannel; + cricket::VideoChannel video_channel(talk_base::Thread::Current(), + media_engine_, media_channel, &session_, "", false, NULL); + stats.set_session(&session_); + + webrtc::StatsReports reports; // returned values. + cricket::VideoMediaInfo stats_read; + cricket::BandwidthEstimationInfo bwe; + bwe.total_received_propagation_delta_ms = 10; + bwe.recent_received_propagation_delta_ms.push_back(100); + bwe.recent_received_propagation_delta_ms.push_back(200); + bwe.recent_received_packet_group_arrival_time_ms.push_back(1000); + bwe.recent_received_packet_group_arrival_time_ms.push_back(2000); + stats_read.bw_estimations.push_back(bwe); + + EXPECT_CALL(session_, video_channel()) + .WillRepeatedly(Return(&video_channel)); + + StatsOptions options; + options.include_received_propagation_stats = true; + EXPECT_CALL(*media_channel, GetStats( + Field(&StatsOptions::include_received_propagation_stats, true), + _)) + .WillOnce(DoAll(SetArgPointee<1>(stats_read), + Return(true))); + + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelDebug); + stats.GetStats(NULL, &reports); + std::string result = ExtractBweStatsValue( + reports, "googReceivedPacketGroupPropagationDeltaSumDebug"); + EXPECT_EQ("10", result); + result = ExtractBweStatsValue( + reports, "googReceivedPacketGroupPropagationDeltaDebug"); + EXPECT_EQ("[100, 200]", result); + result = ExtractBweStatsValue( + reports, "googReceivedPacketGroupArrivalTimeDebug"); + EXPECT_EQ("[1000, 2000]", result); +} + } // namespace diff --git a/talk/app/webrtc/statstypes.h b/talk/app/webrtc/statstypes.h index 9110da3fb..39441e26e 100644 --- a/talk/app/webrtc/statstypes.h +++ b/talk/app/webrtc/statstypes.h @@ -53,6 +53,8 @@ class StatsReport { void AddValue(const std::string& name, const std::string& value); void AddValue(const std::string& name, int64 value); + template + void AddValue(const std::string& name, const std::vector& value); void AddBoolean(const std::string& name, bool value); double timestamp; // Time since 1970-01-01T00:00:00Z in milliseconds. @@ -141,6 +143,7 @@ class StatsReport { static const char kStatsValueNameEchoDelayStdDev[]; static const char kStatsValueNameEchoReturnLoss[]; static const char kStatsValueNameEchoReturnLossEnhancement[]; + static const char kStatsValueNameExpandRate[]; static const char kStatsValueNameFirsReceived[]; static const char kStatsValueNameFirsSent[]; static const char kStatsValueNameFrameHeightInput[]; @@ -164,7 +167,6 @@ class StatsReport { static const char kStatsValueNameJitterReceived[]; static const char kStatsValueNameNacksReceived[]; static const char kStatsValueNameNacksSent[]; - static const char kStatsValueNameNetEqExpandRate[]; static const char kStatsValueNameRtt[]; static const char kStatsValueNameAvailableSendBandwidth[]; static const char kStatsValueNameAvailableReceiveBandwidth[]; @@ -189,6 +191,9 @@ class StatsReport { static const char kStatsValueNameRemoteCertificateId[]; static const char kStatsValueNameLocalCandidateType[]; static const char kStatsValueNameRemoteCandidateType[]; + static const char kStatsValueNameRecvPacketGroupArrivalTimeDebug[]; + static const char kStatsValueNameRecvPacketGroupPropagationDeltaDebug[]; + static const char kStatsValueNameRecvPacketGroupPropagationDeltaSumDebug[]; }; typedef std::vector StatsReports; diff --git a/talk/app/webrtc/webrtc.scons b/talk/app/webrtc/webrtc.scons index 9b1af3cea..dd4bea01a 100644 --- a/talk/app/webrtc/webrtc.scons +++ b/talk/app/webrtc/webrtc.scons @@ -31,6 +31,7 @@ if env.Bit('have_webrtc_voice') and env.Bit('have_webrtc_video'): 'peerconnectionfactory.cc', 'peerconnection.cc', 'portallocatorfactory.cc', + 'remoteaudiosource.cc', 'roapmessages.cc', 'roapsession.cc', 'roapsignaling.cc', diff --git a/talk/app/webrtc/webrtcsession.cc b/talk/app/webrtc/webrtcsession.cc index 59d72709f..ef6af49e5 100644 --- a/talk/app/webrtc/webrtcsession.cc +++ b/talk/app/webrtc/webrtcsession.cc @@ -866,6 +866,18 @@ void WebRtcSession::SetAudioSend(uint32 ssrc, bool enable, voice_channel_->SetChannelOptions(options); } +void WebRtcSession::SetAudioPlayoutVolume(uint32 ssrc, double volume) { + ASSERT(signaling_thread()->IsCurrent()); + ASSERT(volume >= 0 && volume <= 10); + if (!voice_channel_) { + LOG(LS_ERROR) << "SetAudioPlayoutVolume: No audio channel exists."; + return; + } + + if (!voice_channel_->SetOutputScaling(ssrc, volume, volume)) + ASSERT(false); +} + bool WebRtcSession::SetCaptureDevice(uint32 ssrc, cricket::VideoCapturer* camera) { ASSERT(signaling_thread()->IsCurrent()); diff --git a/talk/app/webrtc/webrtcsession.h b/talk/app/webrtc/webrtcsession.h index 384ac4724..628aa1e78 100644 --- a/talk/app/webrtc/webrtcsession.h +++ b/talk/app/webrtc/webrtcsession.h @@ -165,6 +165,7 @@ class WebRtcSession : public cricket::BaseSession, virtual void SetAudioSend(uint32 ssrc, bool enable, const cricket::AudioOptions& options, cricket::AudioRenderer* renderer) OVERRIDE; + virtual void SetAudioPlayoutVolume(uint32 ssrc, double volume) OVERRIDE; // Implements VideoMediaProviderInterface. virtual bool SetCaptureDevice(uint32 ssrc, diff --git a/talk/base/asyncpacketsocket.h b/talk/base/asyncpacketsocket.h index 29ab55ffc..d9e1bff66 100644 --- a/talk/base/asyncpacketsocket.h +++ b/talk/base/asyncpacketsocket.h @@ -28,6 +28,7 @@ #ifndef TALK_BASE_ASYNCPACKETSOCKET_H_ #define TALK_BASE_ASYNCPACKETSOCKET_H_ +#include "talk/base/buffer.h" #include "talk/base/dscp.h" #include "talk/base/sigslot.h" #include "talk/base/socket.h" @@ -35,6 +36,29 @@ namespace talk_base { +// This structure holds the info needed to update the packet send time header +// extension, including the information needed to update the authentication tag +// after changing the value. +struct PacketTimeUpdateParams { + PacketTimeUpdateParams() + : rtp_sendtime_extension_id(-1), srtp_auth_tag_len(-1), + srtp_packet_index(-1) { + } + + int rtp_sendtime_extension_id; // extension header id present in packet. + Buffer srtp_auth_key; // Authentication key. + int srtp_auth_tag_len; // Authentication tag length. + int64 srtp_packet_index; // Required for Rtp Packet authentication. +}; + +// This structure holds meta information for the packet which is about to send +// over network. +struct PacketOptions { + PacketOptions() : dscp(DSCP_NO_CHANGE) {} + DiffServCodePoint dscp; + PacketTimeUpdateParams packet_time_params; +}; + // This structure will have the information about when packet is actually // received by socket. struct PacketTime { diff --git a/talk/base/fakenetwork.h b/talk/base/fakenetwork.h index 3bdc97fe7..497ff209c 100644 --- a/talk/base/fakenetwork.h +++ b/talk/base/fakenetwork.h @@ -109,10 +109,12 @@ class FakeNetworkManager : public NetworkManagerBase, prefix_length = kFakeIPv6NetworkPrefixLength; } IPAddress prefix = TruncateIP(it->ipaddr(), prefix_length); + std::string key = MakeNetworkKey(it->hostname(), prefix, prefix_length); scoped_ptr net(new Network(it->hostname(), it->hostname(), prefix, - prefix_length)); + prefix_length, + key)); net->AddIP(it->ipaddr()); networks.push_back(net.release()); } diff --git a/talk/base/fakesslidentity.h b/talk/base/fakesslidentity.h index 203bb83bf..ee0e0a2dc 100644 --- a/talk/base/fakesslidentity.h +++ b/talk/base/fakesslidentity.h @@ -38,9 +38,12 @@ namespace talk_base { class FakeSSLCertificate : public talk_base::SSLCertificate { public: - explicit FakeSSLCertificate(const std::string& data) : data_(data) {} + // SHA-1 is the default digest algorithm because it is available in all build + // configurations used for unit testing. + explicit FakeSSLCertificate(const std::string& data) + : data_(data), digest_algorithm_(DIGEST_SHA_1) {} explicit FakeSSLCertificate(const std::vector& certs) - : data_(certs.front()) { + : data_(certs.front()), digest_algorithm_(DIGEST_SHA_1) { std::vector::const_iterator it; // Skip certs[0]. for (it = certs.begin() + 1; it != certs.end(); ++it) { @@ -58,10 +61,11 @@ class FakeSSLCertificate : public talk_base::SSLCertificate { VERIFY(SSLIdentity::PemToDer(kPemTypeCertificate, data_, &der_string)); der_buffer->SetData(der_string.c_str(), der_string.size()); } + void set_digest_algorithm(const std::string& algorithm) { + digest_algorithm_ = algorithm; + } virtual bool GetSignatureDigestAlgorithm(std::string* algorithm) const { - // SHA-1 is chosen because it is available in all build configurations - // used for unit testing. - *algorithm = DIGEST_SHA_1; + *algorithm = digest_algorithm_; return true; } virtual bool ComputeDigest(const std::string &algorithm, @@ -86,6 +90,7 @@ class FakeSSLCertificate : public talk_base::SSLCertificate { } std::string data_; std::vector certs_; + std::string digest_algorithm_; }; class FakeSSLIdentity : public talk_base::SSLIdentity { diff --git a/talk/base/maccocoasocketserver.h b/talk/base/maccocoasocketserver.h index f4aeb3397..51dc749d7 100644 --- a/talk/base/maccocoasocketserver.h +++ b/talk/base/maccocoasocketserver.h @@ -54,6 +54,8 @@ class MacCocoaSocketServer : public MacBaseSocketServer { private: MacCocoaSocketServerHelper* helper_; NSTimer* timer_; // Weak. + // The count of how many times we're inside the NSApplication main loop. + int run_count_; DISALLOW_EVIL_CONSTRUCTORS(MacCocoaSocketServer); }; diff --git a/talk/base/maccocoasocketserver.mm b/talk/base/maccocoasocketserver.mm index bf308e610..8257e3866 100644 --- a/talk/base/maccocoasocketserver.mm +++ b/talk/base/maccocoasocketserver.mm @@ -53,6 +53,25 @@ - (void)timerFired:(NSTimer*)timer { socketServer_->WakeUp(); } + +- (void)breakMainloop { + [NSApp stop:self]; + // NSApp stop only exits after finishing processing of the + // current event. Since we're potentially in a timer callback + // and not an NSEvent handler, we need to trigger a dummy one + // and turn the loop over. We may be able to skip this if we're + // on the ss' thread and not inside the app loop already. + NSEvent* event = [NSEvent otherEventWithType:NSApplicationDefined + location:NSMakePoint(0,0) + modifierFlags:0 + timestamp:0 + windowNumber:0 + context:nil + subtype:0 + data1:0 + data2:0]; + [NSApp postEvent:event atStart:NO]; +} @end namespace talk_base { @@ -60,6 +79,7 @@ namespace talk_base { MacCocoaSocketServer::MacCocoaSocketServer() { helper_ = [[MacCocoaSocketServerHelper alloc] initWithSocketServer:this]; timer_ = nil; + run_count_ = 0; // Initialize the shared NSApplication [NSApplication sharedApplication]; @@ -71,12 +91,19 @@ MacCocoaSocketServer::~MacCocoaSocketServer() { [helper_ release]; } +// ::Wait is reentrant, for example when blocking on another thread while +// responding to I/O. Calls to [NSApp] MUST be made from the main thread +// only! bool MacCocoaSocketServer::Wait(int cms, bool process_io) { talk_base::ScopedAutoreleasePool pool; if (!process_io && cms == 0) { // No op. return true; } + if ([NSApp isRunning]) { + // Only allow reentrant waiting if we're in a blocking send. + ASSERT(!process_io && cms == kForever); + } if (!process_io) { // No way to listen to common modes and not get socket events, unless @@ -96,7 +123,9 @@ bool MacCocoaSocketServer::Wait(int cms, bool process_io) { } // Run until WakeUp is called, which will call stop and exit this loop. + run_count_++; [NSApp run]; + run_count_--; if (!process_io) { // Reenable them. Hopefully this won't cause spurious callbacks or @@ -107,28 +136,22 @@ bool MacCocoaSocketServer::Wait(int cms, bool process_io) { return true; } +// Can be called from any thread. Post a message back to the main thread to +// break out of the NSApp loop. void MacCocoaSocketServer::WakeUp() { - // Timer has either fired or shortcutted. - [timer_ invalidate]; - [timer_ release]; - timer_ = nil; - [NSApp stop:nil]; + if (timer_ != nil) { + [timer_ invalidate]; + [timer_ release]; + timer_ = nil; + } - // NSApp stop only exits after finishing processing of the - // current event. Since we're potentially in a timer callback - // and not an NSEvent handler, we need to trigger a dummy one - // and turn the loop over. We may be able to skip this if we're - // on the ss' thread and not inside the app loop already. - NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined - location:NSMakePoint(0,0) - modifierFlags:0 - timestamp:0 - windowNumber:0 - context:nil - subtype:1 - data1:1 - data2:1]; - [NSApp postEvent:event atStart:YES]; + // [NSApp isRunning] returns unexpected results when called from another + // thread. Maintain our own count of how many times to break the main loop. + if (run_count_ > 0) { + [helper_ performSelectorOnMainThread:@selector(breakMainloop) + withObject:nil + waitUntilDone:false]; + } } } // namespace talk_base diff --git a/talk/base/network.cc b/talk/base/network.cc index 00b04c9eb..95a2e4d81 100644 --- a/talk/base/network.cc +++ b/talk/base/network.cc @@ -79,16 +79,7 @@ const uint32 kSignalNetworksMessage = 2; // Fetch list of networks every two seconds. const int kNetworksUpdateIntervalMs = 2000; - -// Makes a string key for this network. Used in the network manager's maps. -// Network objects are keyed on interface name, network prefix and the -// length of that prefix. -std::string MakeNetworkKey(const std::string& name, const IPAddress& prefix, - int prefix_length) { - std::ostringstream ost; - ost << name << "%" << prefix.ToString() << "/" << prefix_length; - return ost.str(); -} +const int kHighestNetworkPreference = 127; bool CompareNetworks(const Network* a, const Network* b) { if (a->prefix_length() == b->prefix_length()) { @@ -99,9 +90,36 @@ bool CompareNetworks(const Network* a, const Network* b) { return a->name() < b->name(); } +bool SortNetworks(const Network* a, const Network* b) { + // Network types will be preferred above everything else while sorting + // Networks. + + // Networks are sorted first by type. + if (a->type() != b->type()) { + return a->type() < b->type(); + } + + // After type, networks are sorted by IP address precedence values + // from RFC 3484-bis + if (IPAddressPrecedence(a->ip()) != IPAddressPrecedence(b->ip())) { + return IPAddressPrecedence(a->ip()) > IPAddressPrecedence(b->ip()); + } + + // TODO(mallinath) - Add VPN and Link speed conditions while sorting. + + // Networks are sorted last by key. + return a->key() > b->key(); +} } // namespace +std::string MakeNetworkKey(const std::string& name, const IPAddress& prefix, + int prefix_length) { + std::ostringstream ost; + ost << name << "%" << prefix.ToString() << "/" << prefix_length; + return ost.str(); +} + NetworkManager::NetworkManager() { } @@ -180,6 +198,29 @@ void NetworkManagerBase::MergeNetworkList(const NetworkList& new_networks, } } networks_ = merged_list; + + // If the network lists changes, we resort it. + if (changed) { + std::sort(networks_.begin(), networks_.end(), SortNetworks); + // Now network interfaces are sorted, we should set the preference value + // for each of the interfaces we are planning to use. + // Preference order of network interfaces might have changed from previous + // sorting due to addition of higher preference network interface. + // Since we have already sorted the network interfaces based on our + // requirements, we will just assign a preference value starting with 127, + // in decreasing order. + int pref = kHighestNetworkPreference; + for (NetworkList::const_iterator iter = networks_.begin(); + iter != networks_.end(); ++iter) { + (*iter)->set_preference(pref); + if (pref > 0) { + --pref; + } else { + LOG(LS_ERROR) << "Too many network interfaces to handle!"; + break; + } + } + } } BasicNetworkManager::BasicNetworkManager() @@ -240,6 +281,7 @@ void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces, continue; } } + int prefix_length = CountIPMaskBits(mask); prefix = TruncateIP(ip, prefix_length); std::string key = MakeNetworkKey(std::string(cursor->ifa_name), @@ -249,7 +291,8 @@ void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces, scoped_ptr network(new Network(cursor->ifa_name, cursor->ifa_name, prefix, - prefix_length)); + prefix_length, + key)); network->set_scope_id(scope_id); network->AddIP(ip); bool ignored = ((cursor->ifa_flags & IFF_LOOPBACK) || @@ -386,6 +429,7 @@ bool BasicNetworkManager::CreateNetworks(bool include_ignored, continue; } } + IPAddress prefix; int prefix_length = GetPrefix(prefixlist, ip, &prefix); std::string key = MakeNetworkKey(name, prefix, prefix_length); @@ -394,7 +438,8 @@ bool BasicNetworkManager::CreateNetworks(bool include_ignored, scoped_ptr network(new Network(name, description, prefix, - prefix_length)); + prefix_length, + key)); network->set_scope_id(scope_id); network->AddIP(ip); bool ignore = ((adapter_addrs->IfType == IF_TYPE_SOFTWARE_LOOPBACK) || @@ -561,12 +606,21 @@ void BasicNetworkManager::DumpNetworks(bool include_ignored) { } } +Network::Network(const std::string& name, const std::string& desc, + const IPAddress& prefix, int prefix_length, + const std::string& key) + : name_(name), description_(desc), prefix_(prefix), + prefix_length_(prefix_length), key_(key), scope_id_(0), ignored_(false), + uniform_numerator_(0), uniform_denominator_(0), exponential_numerator_(0), + exponential_denominator_(0), type_(ADAPTER_TYPE_UNKNOWN), preference_(0) { +} + Network::Network(const std::string& name, const std::string& desc, const IPAddress& prefix, int prefix_length) : name_(name), description_(desc), prefix_(prefix), prefix_length_(prefix_length), scope_id_(0), ignored_(false), uniform_numerator_(0), uniform_denominator_(0), exponential_numerator_(0), - exponential_denominator_(0) { + exponential_denominator_(0), type_(ADAPTER_TYPE_UNKNOWN), preference_(0) { } std::string Network::ToString() const { @@ -600,4 +654,5 @@ bool Network::SetIPs(const std::vector& ips, bool changed) { ips_ = ips; return changed; } + } // namespace talk_base diff --git a/talk/base/network.h b/talk/base/network.h index 63f3e732f..75a443bca 100644 --- a/talk/base/network.h +++ b/talk/base/network.h @@ -45,9 +45,23 @@ struct ifaddrs; namespace talk_base { class Network; -class NetworkSession; class Thread; +enum AdapterType { + // This enum resembles the one in Chromium net::ConnectionType. + ADAPTER_TYPE_UNKNOWN = 0, + ADAPTER_TYPE_ETHERNET = 1, + ADAPTER_TYPE_WIFI = 2, + ADAPTER_TYPE_CELLULAR = 3, + ADAPTER_TYPE_VPN = 4 +}; + +// Makes a string key for this network. Used in the network manager's maps. +// Network objects are keyed on interface name, network prefix and the +// length of that prefix. +std::string MakeNetworkKey(const std::string& name, const IPAddress& prefix, + int prefix_length); + // Generic network manager interface. It provides list of local // networks. class NetworkManager { @@ -168,7 +182,12 @@ class BasicNetworkManager : public NetworkManagerBase, // Represents a Unix-type network interface, with a name and single address. class Network { public: - Network() : prefix_(INADDR_ANY), scope_id_(0) {} + Network() : prefix_(INADDR_ANY), scope_id_(0), + type_(ADAPTER_TYPE_UNKNOWN) {} + Network(const std::string& name, const std::string& description, + const IPAddress& prefix, int prefix_length, + const std::string& key); + Network(const std::string& name, const std::string& description, const IPAddress& prefix, int prefix_length); @@ -184,6 +203,10 @@ class Network { // Returns the length, in bits, of this network's prefix. int prefix_length() const { return prefix_length_; } + // |key_| has unique value per network interface. Used in sorting network + // interfaces. Key is derived from interface name and it's prefix. + std::string key() const { return key_; } + // Returns the Network's current idea of the 'best' IP it has. // 'Best' currently means the first one added. // TODO: We should be preferring temporary addresses. @@ -215,27 +238,32 @@ class Network { bool ignored() const { return ignored_; } void set_ignored(bool ignored) { ignored_ = ignored; } + AdapterType type() const { return type_; } + int preference() const { return preference_; } + void set_preference(int preference) { preference_ = preference; } + // Debugging description of this network std::string ToString() const; private: - typedef std::vector SessionList; - std::string name_; std::string description_; IPAddress prefix_; int prefix_length_; + std::string key_; std::vector ips_; int scope_id_; bool ignored_; - SessionList sessions_; double uniform_numerator_; double uniform_denominator_; double exponential_numerator_; double exponential_denominator_; + AdapterType type_; + int preference_; friend class NetworkManager; }; + } // namespace talk_base #endif // TALK_BASE_NETWORK_H_ diff --git a/talk/base/network_unittest.cc b/talk/base/network_unittest.cc index e11e78daa..85aa2f87a 100644 --- a/talk/base/network_unittest.cc +++ b/talk/base/network_unittest.cc @@ -527,6 +527,33 @@ TEST_F(NetworkTest, TestIPv6Toggle) { } } +TEST_F(NetworkTest, TestNetworkListSorting) { + BasicNetworkManager manager; + Network ipv4_network1("test_eth0", "Test Network Adapter 1", + IPAddress(0x12345600U), 24); + ipv4_network1.AddIP(IPAddress(0x12345600U)); + + IPAddress ip; + IPAddress prefix; + EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:abcd:efab:cdef", &ip)); + prefix = TruncateIP(ip, 64); + Network ipv6_eth1_publicnetwork1_ip1("test_eth1", "Test NetworkAdapter 2", + prefix, 64); + ipv6_eth1_publicnetwork1_ip1.AddIP(ip); + + NetworkManager::NetworkList list; + list.push_back(new Network(ipv4_network1)); + list.push_back(new Network(ipv6_eth1_publicnetwork1_ip1)); + Network* net1 = list[0]; + Network* net2 = list[1]; + + bool changed = false; + MergeNetworkList(manager, list, &changed); + ASSERT_TRUE(changed); + // After sorting IPv6 network should be higher order than IPv4 networks. + EXPECT_TRUE(net1->preference() < net2->preference()); +} + #if defined(POSIX) // Verify that we correctly handle interfaces with no address. TEST_F(NetworkTest, TestConvertIfAddrsNoAddress) { diff --git a/talk/base/openssl.h b/talk/base/openssl.h new file mode 100644 index 000000000..e2cfd2b1d --- /dev/null +++ b/talk/base/openssl.h @@ -0,0 +1,37 @@ +/* + * libjingle + * Copyright 2013, 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_BASE_OPENSSL_H_ +#define TALK_BASE_OPENSSL_H_ + +#include + +#if (OPENSSL_VERSION_NUMBER < 0x10001000L) +#error OpenSSL is older than 1.0.1, which is the minimum supported version. +#endif + +#endif // TALK_BASE_OPENSSL_H_ diff --git a/talk/base/openssladapter.cc b/talk/base/openssladapter.cc index 95d5a1a34..9e6fe72c2 100644 --- a/talk/base/openssladapter.cc +++ b/talk/base/openssladapter.cc @@ -41,7 +41,6 @@ #include #include #include -#include #include #if HAVE_CONFIG_H @@ -50,6 +49,7 @@ #include "talk/base/common.h" #include "talk/base/logging.h" +#include "talk/base/openssl.h" #include "talk/base/sslroots.h" #include "talk/base/stringutils.h" @@ -688,11 +688,7 @@ bool OpenSSLAdapter::VerifyServerName(SSL* ssl, const char* host, int extension_nid = OBJ_obj2nid(X509_EXTENSION_get_object(extension)); if (extension_nid == NID_subject_alt_name) { -#if OPENSSL_VERSION_NUMBER >= 0x10000000L const X509V3_EXT_METHOD* meth = X509V3_EXT_get(extension); -#else - X509V3_EXT_METHOD* meth = X509V3_EXT_get(extension); -#endif if (!meth) break; @@ -703,12 +699,8 @@ bool OpenSSLAdapter::VerifyServerName(SSL* ssl, const char* host, // See http://readlist.com/lists/openssl.org/openssl-users/0/4761.html. unsigned char* ext_value_data = extension->value->data; -#if OPENSSL_VERSION_NUMBER >= 0x0090800fL const unsigned char **ext_value_data_ptr = (const_cast(&ext_value_data)); -#else - unsigned char **ext_value_data_ptr = &ext_value_data; -#endif if (meth->it) { ext_str = ASN1_item_d2i(NULL, ext_value_data_ptr, diff --git a/talk/base/openssldigest.cc b/talk/base/openssldigest.cc index 3d9276de8..3d0d227e6 100644 --- a/talk/base/openssldigest.cc +++ b/talk/base/openssldigest.cc @@ -30,6 +30,7 @@ #include "talk/base/openssldigest.h" #include "talk/base/common.h" +#include "talk/base/openssl.h" namespace talk_base { @@ -78,7 +79,6 @@ bool OpenSSLDigest::GetDigestEVP(const std::string& algorithm, md = EVP_md5(); } else if (algorithm == DIGEST_SHA_1) { md = EVP_sha1(); -#if OPENSSL_VERSION_NUMBER >= 0x00908000L } else if (algorithm == DIGEST_SHA_224) { md = EVP_sha224(); } else if (algorithm == DIGEST_SHA_256) { @@ -87,7 +87,6 @@ bool OpenSSLDigest::GetDigestEVP(const std::string& algorithm, md = EVP_sha384(); } else if (algorithm == DIGEST_SHA_512) { md = EVP_sha512(); -#endif } else { return false; } @@ -108,7 +107,6 @@ bool OpenSSLDigest::GetDigestName(const EVP_MD* md, *algorithm = DIGEST_MD5; } else if (md_type == NID_sha1) { *algorithm = DIGEST_SHA_1; -#if OPENSSL_VERSION_NUMBER >= 0x00908000L } else if (md_type == NID_sha224) { *algorithm = DIGEST_SHA_224; } else if (md_type == NID_sha256) { @@ -117,7 +115,6 @@ bool OpenSSLDigest::GetDigestName(const EVP_MD* md, *algorithm = DIGEST_SHA_384; } else if (md_type == NID_sha512) { *algorithm = DIGEST_SHA_512; -#endif } else { algorithm->clear(); return false; diff --git a/talk/base/opensslidentity.cc b/talk/base/opensslidentity.cc index 33b02ddfb..bd361d10f 100644 --- a/talk/base/opensslidentity.cc +++ b/talk/base/opensslidentity.cc @@ -32,7 +32,6 @@ // Must be included first before openssl headers. #include "talk/base/win32.h" // NOLINT -#include #include #include #include @@ -43,6 +42,7 @@ #include "talk/base/checks.h" #include "talk/base/helpers.h" #include "talk/base/logging.h" +#include "talk/base/openssl.h" #include "talk/base/openssldigest.h" namespace talk_base { @@ -66,15 +66,6 @@ static const int CERTIFICATE_WINDOW = -60*60*24; static EVP_PKEY* MakeKey() { LOG(LS_INFO) << "Making key pair"; EVP_PKEY* pkey = EVP_PKEY_new(); -#if OPENSSL_VERSION_NUMBER < 0x00908000l - // Only RSA_generate_key is available. Use that. - RSA* rsa = RSA_generate_key(KEY_LENGTH, 0x10001, NULL, NULL); - if (!EVP_PKEY_assign_RSA(pkey, rsa)) { - EVP_PKEY_free(pkey); - RSA_free(rsa); - return NULL; - } -#else // RSA_generate_key is deprecated. Use _ex version. BIGNUM* exponent = BN_new(); RSA* rsa = RSA_new(); @@ -89,7 +80,6 @@ static EVP_PKEY* MakeKey() { } // ownership of rsa struct was assigned, don't free it. BN_free(exponent); -#endif LOG(LS_INFO) << "Returning key pair"; return pkey; } diff --git a/talk/base/opensslstreamadapter.cc b/talk/base/opensslstreamadapter.cc index 576b42452..cafef9266 100644 --- a/talk/base/opensslstreamadapter.cc +++ b/talk/base/opensslstreamadapter.cc @@ -37,7 +37,6 @@ #include #include #include -#include #include #include @@ -45,6 +44,7 @@ #include "talk/base/common.h" #include "talk/base/logging.h" #include "talk/base/stream.h" +#include "talk/base/openssl.h" #include "talk/base/openssladapter.h" #include "talk/base/openssldigest.h" #include "talk/base/opensslidentity.h" @@ -53,15 +53,6 @@ namespace talk_base { -#if (OPENSSL_VERSION_NUMBER >= 0x10001000L) -#define HAVE_DTLS_SRTP -#endif - -#if (OPENSSL_VERSION_NUMBER >= 0x10000000L) -#define HAVE_DTLS -#endif - -#ifdef HAVE_DTLS_SRTP // SRTP cipher suite table struct SrtpCipherMapEntry { const char* external_name; @@ -74,7 +65,6 @@ static SrtpCipherMapEntry SrtpCipherMap[] = { {"AES_CM_128_HMAC_SHA1_32", "SRTP_AES128_CM_SHA1_32"}, {NULL, NULL} }; -#endif ////////////////////////////////////////////////////////////////////// // StreamBIO @@ -248,7 +238,6 @@ bool OpenSSLStreamAdapter::ExportKeyingMaterial(const std::string& label, bool use_context, uint8* result, size_t result_len) { -#ifdef HAVE_DTLS_SRTP int i; i = SSL_export_keying_material(ssl_, result, result_len, @@ -260,9 +249,6 @@ bool OpenSSLStreamAdapter::ExportKeyingMaterial(const std::string& label, return false; return true; -#else - return false; -#endif } bool OpenSSLStreamAdapter::SetDtlsSrtpCiphers( @@ -272,7 +258,6 @@ bool OpenSSLStreamAdapter::SetDtlsSrtpCiphers( if (state_ != SSL_NONE) return false; -#ifdef HAVE_DTLS_SRTP for (std::vector::const_iterator cipher = ciphers.begin(); cipher != ciphers.end(); ++cipher) { bool found = false; @@ -298,13 +283,9 @@ bool OpenSSLStreamAdapter::SetDtlsSrtpCiphers( srtp_ciphers_ = internal_ciphers; return true; -#else - return false; -#endif } bool OpenSSLStreamAdapter::GetDtlsSrtpCipher(std::string* cipher) { -#ifdef HAVE_DTLS_SRTP ASSERT(state_ == SSL_CONNECTED); if (state_ != SSL_CONNECTED) return false; @@ -326,9 +307,6 @@ bool OpenSSLStreamAdapter::GetDtlsSrtpCipher(std::string* cipher) { ASSERT(false); // This should never happen return false; -#else - return false; -#endif } int OpenSSLStreamAdapter::StartSSLWithServer(const char* server_name) { @@ -665,14 +643,12 @@ int OpenSSLStreamAdapter::ContinueSSL() { case SSL_ERROR_WANT_READ: { LOG(LS_VERBOSE) << " -- error want read"; -#ifdef HAVE_DTLS struct timeval timeout; if (DTLSv1_get_timeout(ssl_, &timeout)) { int delay = timeout.tv_sec * 1000 + timeout.tv_usec/1000; Thread::Current()->PostDelayed(delay, this, MSG_TIMEOUT, 0); } -#endif } break; @@ -727,9 +703,7 @@ void OpenSSLStreamAdapter::OnMessage(Message* msg) { // Process our own messages and then pass others to the superclass if (MSG_TIMEOUT == msg->message_id) { LOG(LS_INFO) << "DTLS timeout expired"; -#ifdef HAVE_DTLS DTLSv1_handle_timeout(ssl_); -#endif ContinueSSL(); } else { StreamInterface::OnMessage(msg); @@ -740,19 +714,11 @@ SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() { SSL_CTX *ctx = NULL; if (role_ == SSL_CLIENT) { -#ifdef HAVE_DTLS ctx = SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ? DTLSv1_client_method() : TLSv1_client_method()); -#else - ctx = SSL_CTX_new(TLSv1_client_method()); -#endif } else { -#ifdef HAVE_DTLS ctx = SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ? DTLSv1_server_method() : TLSv1_server_method()); -#else - ctx = SSL_CTX_new(TLSv1_server_method()); -#endif } if (ctx == NULL) return NULL; @@ -771,14 +737,12 @@ SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() { SSL_CTX_set_verify_depth(ctx, 4); SSL_CTX_set_cipher_list(ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); -#ifdef HAVE_DTLS_SRTP if (!srtp_ciphers_.empty()) { if (SSL_CTX_set_tlsext_use_srtp(ctx, srtp_ciphers_.c_str())) { SSL_CTX_free(ctx); return NULL; } } -#endif return ctx; } @@ -852,27 +816,15 @@ bool OpenSSLStreamAdapter::SSLPostConnectionCheck(SSL* ssl, } bool OpenSSLStreamAdapter::HaveDtls() { -#ifdef HAVE_DTLS return true; -#else - return false; -#endif } bool OpenSSLStreamAdapter::HaveDtlsSrtp() { -#ifdef HAVE_DTLS_SRTP return true; -#else - return false; -#endif } bool OpenSSLStreamAdapter::HaveExporter() { -#ifdef HAVE_DTLS_SRTP return true; -#else - return false; -#endif } } // namespace talk_base diff --git a/talk/base/physicalsocketserver.cc b/talk/base/physicalsocketserver.cc index d4a4b1af7..07a9d4b01 100644 --- a/talk/base/physicalsocketserver.cc +++ b/talk/base/physicalsocketserver.cc @@ -541,6 +541,8 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { case OPT_DSCP: LOG(LS_WARNING) << "Socket::OPT_DSCP not supported."; return -1; + case OPT_RTP_SENDTIME_EXTN_ID: + return -1; // No logging is necessary as this not a OS socket option. default: ASSERT(false); return -1; diff --git a/talk/base/socket.h b/talk/base/socket.h index 47f55225d..590645f83 100644 --- a/talk/base/socket.h +++ b/talk/base/socket.h @@ -185,7 +185,10 @@ class Socket { OPT_SNDBUF, // send buffer size OPT_NODELAY, // whether Nagle algorithm is enabled OPT_IPV6_V6ONLY, // Whether the socket is IPv6 only. - OPT_DSCP // DSCP code + OPT_DSCP, // DSCP code + OPT_RTP_SENDTIME_EXTN_ID, // This is a non-traditional socket option param. + // This is specific to libjingle and will be used + // if SendTime option is needed at socket level. }; virtual int GetOption(Option opt, int* value) = 0; virtual int SetOption(Option opt, int value) = 0; diff --git a/talk/base/thread_unittest.cc b/talk/base/thread_unittest.cc index 3a9103fea..728e32158 100644 --- a/talk/base/thread_unittest.cc +++ b/talk/base/thread_unittest.cc @@ -339,7 +339,7 @@ class AsyncInvokeTest : public testing::Test { Thread* expected_thread_; }; -TEST_F(AsyncInvokeTest, FireAndForget) { +TEST_F(AsyncInvokeTest, DISABLED_FireAndForget) { AsyncInvoker invoker; // Create and start the thread. Thread thread; @@ -350,7 +350,7 @@ TEST_F(AsyncInvokeTest, FireAndForget) { EXPECT_TRUE_WAIT(called, kWaitTimeout); } -TEST_F(AsyncInvokeTest, WithCallback) { +TEST_F(AsyncInvokeTest, DISABLED_WithCallback) { AsyncInvoker invoker; // Create and start the thread. Thread thread; @@ -379,7 +379,7 @@ TEST_F(AsyncInvokeTest, DISABLED_CancelInvoker) { EXPECT_EQ(0, int_value_); } -TEST_F(AsyncInvokeTest, CancelCallingThread) { +TEST_F(AsyncInvokeTest, DISABLED_CancelCallingThread) { AsyncInvoker invoker; { // Create and start the thread. Thread thread; @@ -396,7 +396,7 @@ TEST_F(AsyncInvokeTest, CancelCallingThread) { EXPECT_EQ(0, int_value_); } -TEST_F(AsyncInvokeTest, KillInvokerBeforeExecute) { +TEST_F(AsyncInvokeTest, DISABLED_KillInvokerBeforeExecute) { Thread thread; thread.Start(); { @@ -413,7 +413,7 @@ TEST_F(AsyncInvokeTest, KillInvokerBeforeExecute) { EXPECT_EQ(0, int_value_); } -TEST_F(AsyncInvokeTest, Flush) { +TEST_F(AsyncInvokeTest, DISABLED_Flush) { AsyncInvoker invoker; bool flag1 = false; bool flag2 = false; @@ -431,7 +431,7 @@ TEST_F(AsyncInvokeTest, Flush) { EXPECT_TRUE(flag2); } -TEST_F(AsyncInvokeTest, FlushWithIds) { +TEST_F(AsyncInvokeTest, DISABLED_FlushWithIds) { AsyncInvoker invoker; bool flag1 = false; bool flag2 = false; diff --git a/talk/libjingle.gyp b/talk/libjingle.gyp index 0ed5c6494..73c8a048f 100755 --- a/talk/libjingle.gyp +++ b/talk/libjingle.gyp @@ -715,6 +715,7 @@ 'conditions': [ ['OS!="ios"', { 'sources': [ + 'base/openssl.h', 'base/openssladapter.cc', 'base/openssladapter.h', 'base/openssldigest.cc', @@ -1175,6 +1176,8 @@ 'app/webrtc/portallocatorfactory.cc', 'app/webrtc/portallocatorfactory.h', 'app/webrtc/proxy.h', + 'app/webrtc/remoteaudiosource.cc', + 'app/webrtc/remoteaudiosource.h', 'app/webrtc/remotevideocapturer.cc', 'app/webrtc/remotevideocapturer.h', 'app/webrtc/sctputils.cc', diff --git a/talk/media/base/fakemediaengine.h b/talk/media/base/fakemediaengine.h index 28facca0f..86a0dbf9f 100644 --- a/talk/media/base/fakemediaengine.h +++ b/talk/media/base/fakemediaengine.h @@ -560,7 +560,8 @@ class FakeVideoMediaChannel : public RtpHelper { return true; } - virtual bool GetStats(VideoMediaInfo* info) { return false; } + virtual bool GetStats(const StatsOptions& options, + VideoMediaInfo* info) { return false; } virtual bool SendIntraFrame() { sent_intra_frame_ = true; return true; diff --git a/talk/media/base/filemediaengine.h b/talk/media/base/filemediaengine.h index be196ae80..6656cdfa1 100644 --- a/talk/media/base/filemediaengine.h +++ b/talk/media/base/filemediaengine.h @@ -297,7 +297,9 @@ class FileVideoChannel : public VideoMediaChannel { virtual bool SetCapturer(uint32 ssrc, VideoCapturer* capturer) { return false; } - virtual bool GetStats(VideoMediaInfo* info) { return true; } + virtual bool GetStats(const StatsOptions& options, VideoMediaInfo* info) { + return true; + } virtual bool SendIntraFrame() { return false; } virtual bool RequestIntraFrame() { return false; } diff --git a/talk/media/base/hybridvideoengine.cc b/talk/media/base/hybridvideoengine.cc index f855417b3..8e992f0ab 100644 --- a/talk/media/base/hybridvideoengine.cc +++ b/talk/media/base/hybridvideoengine.cc @@ -273,10 +273,11 @@ bool HybridVideoMediaChannel::RequestIntraFrame() { active_channel_->RequestIntraFrame(); } -bool HybridVideoMediaChannel::GetStats(VideoMediaInfo* info) { +bool HybridVideoMediaChannel::GetStats( + const StatsOptions& options, VideoMediaInfo* info) { // TODO(juberti): Ensure that returning no stats until SetSendCodecs is OK. return active_channel_ && - active_channel_->GetStats(info); + active_channel_->GetStats(options, info); } void HybridVideoMediaChannel::OnPacketReceived( diff --git a/talk/media/base/hybridvideoengine.h b/talk/media/base/hybridvideoengine.h index 9f919205a..8cfb884f1 100644 --- a/talk/media/base/hybridvideoengine.h +++ b/talk/media/base/hybridvideoengine.h @@ -86,7 +86,7 @@ class HybridVideoMediaChannel : public VideoMediaChannel { virtual bool SendIntraFrame(); virtual bool RequestIntraFrame(); - virtual bool GetStats(VideoMediaInfo* info); + virtual bool GetStats(const StatsOptions& options, VideoMediaInfo* info); virtual void OnPacketReceived(talk_base::Buffer* packet, const talk_base::PacketTime& packet_time); diff --git a/talk/media/base/mediachannel.h b/talk/media/base/mediachannel.h index ef87c9e92..239798c2e 100644 --- a/talk/media/base/mediachannel.h +++ b/talk/media/base/mediachannel.h @@ -881,7 +881,8 @@ struct BandwidthEstimationInfo { actual_enc_bitrate(0), retransmit_bitrate(0), transmit_bitrate(0), - bucket_delay(0) { + bucket_delay(0), + total_received_propagation_delta_ms(0) { } int available_send_bandwidth; @@ -891,6 +892,11 @@ struct BandwidthEstimationInfo { int retransmit_bitrate; int transmit_bitrate; int bucket_delay; + // The following stats are only valid when + // StatsOptions::include_received_propagation_stats is true. + int total_received_propagation_delta_ms; + std::vector recent_received_propagation_delta_ms; + std::vector recent_received_packet_group_arrival_time_ms; }; struct VoiceMediaInfo { @@ -922,6 +928,12 @@ struct DataMediaInfo { std::vector receivers; }; +struct StatsOptions { + StatsOptions() : include_received_propagation_stats(false) {} + + bool include_received_propagation_stats; +}; + class VoiceMediaChannel : public MediaChannel { public: enum Error { @@ -1040,7 +1052,12 @@ class VideoMediaChannel : public MediaChannel { // |capturer|. If |ssrc| is non zero create a new stream with |ssrc| as SSRC. virtual bool SetCapturer(uint32 ssrc, VideoCapturer* capturer) = 0; // Gets quality stats for the channel. - virtual bool GetStats(VideoMediaInfo* info) = 0; + virtual bool GetStats(const StatsOptions& options, VideoMediaInfo* info) = 0; + // This is needed for MediaMonitor to use the same template for voice, video + // and data MediaChannels. + bool GetStats(VideoMediaInfo* info) { + return GetStats(StatsOptions(), info); + } // Send an intra frame to the receivers. virtual bool SendIntraFrame() = 0; diff --git a/talk/media/base/videoadapter.cc b/talk/media/base/videoadapter.cc index 29be80531..5b53d0747 100644 --- a/talk/media/base/videoadapter.cc +++ b/talk/media/base/videoadapter.cc @@ -183,7 +183,8 @@ void VideoAdapter::SetInputFormat(const VideoFormat& format) { output_format_.interval = talk_base::_max( output_format_.interval, input_format_.interval); if (old_input_interval != input_format_.interval) { - LOG(LS_INFO) << "VAdapt Input Interval: " << input_format_.interval; + LOG(LS_INFO) << "VAdapt input interval changed from " + << old_input_interval << " to " << input_format_.interval; } } @@ -218,7 +219,8 @@ void VideoAdapter::SetOutputFormat(const VideoFormat& format) { output_format_.interval = talk_base::_max( output_format_.interval, input_format_.interval); if (old_output_interval != output_format_.interval) { - LOG(LS_INFO) << "VAdapt Output Interval: " << output_format_.interval; + LOG(LS_INFO) << "VAdapt output interval changed from " + << old_output_interval << " to " << output_format_.interval; } } @@ -283,16 +285,12 @@ bool VideoAdapter::AdaptFrame(const VideoFrame* in_frame, } if (should_drop) { // Show VAdapt log every 90 frames dropped. (3 seconds) - // TODO(fbarchard): Consider GetLogSeverity() to change interval to less - // for LS_VERBOSE and more for LS_INFO. - bool show = (frames_in_ - frames_out_) % 90 == 0; - - if (show) { + if ((frames_in_ - frames_out_) % 90 == 0) { // TODO(fbarchard): Reduce to LS_VERBOSE when adapter info is not needed // in default calls. - LOG(LS_INFO) << "VAdapt Drop Frame: " << frames_scaled_ - << " / " << frames_out_ - << " / " << frames_in_ + LOG(LS_INFO) << "VAdapt Drop Frame: scaled " << frames_scaled_ + << " / out " << frames_out_ + << " / in " << frames_in_ << " Changes: " << adaption_changes_ << " Input: " << in_frame->GetWidth() << "x" << in_frame->GetHeight() @@ -344,9 +342,9 @@ bool VideoAdapter::AdaptFrame(const VideoFrame* in_frame, if (show) { // TODO(fbarchard): Reduce to LS_VERBOSE when adapter info is not needed // in default calls. - LOG(LS_INFO) << "VAdapt Frame: " << frames_scaled_ - << " / " << frames_out_ - << " / " << frames_in_ + LOG(LS_INFO) << "VAdapt Frame: scaled " << frames_scaled_ + << " / out " << frames_out_ + << " / in " << frames_in_ << " Changes: " << adaption_changes_ << " Input: " << in_frame->GetWidth() << "x" << in_frame->GetHeight() diff --git a/talk/media/base/videoengine_unittest.h b/talk/media/base/videoengine_unittest.h index e1c4a7d5c..6c782bd0e 100644 --- a/talk/media/base/videoengine_unittest.h +++ b/talk/media/base/videoengine_unittest.h @@ -781,7 +781,7 @@ class VideoMediaChannelTest : public testing::Test, void GetStats() { SendAndReceive(DefaultCodec()); cricket::VideoMediaInfo info; - EXPECT_TRUE(channel_->GetStats(&info)); + EXPECT_TRUE(channel_->GetStats(cricket::StatsOptions(), &info)); ASSERT_EQ(1U, info.senders.size()); // TODO(whyuan): bytes_sent and bytes_rcvd are different. Are both payload? @@ -839,7 +839,7 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_FRAME_ON_RENDERER_WAIT( renderer2, 1, DefaultCodec().width, DefaultCodec().height, kTimeout); cricket::VideoMediaInfo info; - EXPECT_TRUE(channel_->GetStats(&info)); + EXPECT_TRUE(channel_->GetStats(cricket::StatsOptions(), &info)); ASSERT_EQ(1U, info.senders.size()); // TODO(whyuan): bytes_sent and bytes_rcvd are different. Are both payload? @@ -912,7 +912,7 @@ class VideoMediaChannelTest : public testing::Test, // Get stats, and make sure they are correct for two senders. cricket::VideoMediaInfo info; - EXPECT_TRUE(channel_->GetStats(&info)); + EXPECT_TRUE(channel_->GetStats(cricket::StatsOptions(), &info)); ASSERT_EQ(2U, info.senders.size()); EXPECT_EQ(NumRtpPackets(), info.senders[0].packets_sent + info.senders[1].packets_sent); diff --git a/talk/media/webrtc/fakewebrtcvideoengine.h b/talk/media/webrtc/fakewebrtcvideoengine.h index 0b07925ae..315c69c7f 100644 --- a/talk/media/webrtc/fakewebrtcvideoengine.h +++ b/talk/media/webrtc/fakewebrtcvideoengine.h @@ -447,6 +447,10 @@ class FakeWebRtcVideoEngine WEBRTC_ASSERT_CHANNEL(channel); return channels_.find(channel)->second->send; } + bool GetReceive(int channel) const { + WEBRTC_ASSERT_CHANNEL(channel); + return channels_.find(channel)->second->receive_; + } int GetCaptureChannelId(int capture_id) const { WEBRTC_ASSERT_CAPTURER(capture_id); return capturers_.find(capture_id)->second->channel_id(); diff --git a/talk/media/webrtc/webrtcvideoengine.cc b/talk/media/webrtc/webrtcvideoengine.cc index d2e73eae5..9a94b882a 100644 --- a/talk/media/webrtc/webrtcvideoengine.cc +++ b/talk/media/webrtc/webrtcvideoengine.cc @@ -60,6 +60,7 @@ #include "talk/media/webrtc/webrtcvie.h" #include "talk/media/webrtc/webrtcvoe.h" #include "talk/media/webrtc/webrtcvoiceengine.h" +#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" #if !defined(LIBPEERCONNECTION_LIB) #ifndef HAVE_WEBRTC_VIDEO @@ -2253,7 +2254,8 @@ bool WebRtcVideoMediaChannel::SetRenderer(uint32 ssrc, return true; } -bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) { +bool WebRtcVideoMediaChannel::GetStats(const StatsOptions& options, + VideoMediaInfo* info) { // Get sender statistics and build VideoSenderInfo. unsigned int total_bitrate_sent = 0; unsigned int video_bitrate_sent = 0; @@ -2453,11 +2455,29 @@ bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) { LOG_RTCERR1(GetEstimatedReceiveBandwidth, channel->channel_id()); } } - // Build BandwidthEstimationInfo. // TODO(zhurunz): Add real unittest for this. BandwidthEstimationInfo bwe; + // TODO(jiayl): remove the condition when the necessary changes are available + // outside the dev branch. +#ifdef USE_WEBRTC_DEV_BRANCH + if (options.include_received_propagation_stats) { + webrtc::ReceiveBandwidthEstimatorStats additional_stats; + // Only call for the default channel because the returned stats are + // collected for all the channels using the same estimator. + if (engine_->vie()->rtp()->GetReceiveBandwidthEstimatorStats( + recv_channels_[0]->channel_id(), &additional_stats)) { + bwe.total_received_propagation_delta_ms = + additional_stats.total_propagation_time_delta_ms; + bwe.recent_received_propagation_delta_ms.swap( + additional_stats.recent_propagation_time_delta_ms); + bwe.recent_received_packet_group_arrival_time_ms.swap( + additional_stats.recent_arrival_time_ms); + } + } +#endif + // Calculations done above per send/receive stream. bwe.actual_enc_bitrate = video_bitrate_sent; bwe.transmit_bitrate = total_bitrate_sent; @@ -2632,6 +2652,15 @@ bool WebRtcVideoMediaChannel::SetSendRtpHeaderExtensions( return false; } } + + if (send_time_extension) { + // For video RTP packets, we would like to update AbsoluteSendTimeHeader + // Extension closer to the network, @ socket level before sending. + // Pushing the extension id to socket layer. + MediaChannel::SetOption(NetworkInterface::ST_RTP, + talk_base::Socket::OPT_RTP_SENDTIME_EXTN_ID, + send_time_extension->id); + } return true; } @@ -3083,6 +3112,13 @@ bool WebRtcVideoMediaChannel::ConfigureChannel(int channel_id, } } + // Start receiving for both receive and send channels so that we get incoming + // RTP (if receiving) as well as RTCP feedback (if sending). + if (engine()->vie()->base()->StartReceive(channel_id) != 0) { + LOG_RTCERR1(StartReceive, channel_id); + return false; + } + return true; } @@ -3532,14 +3568,6 @@ bool WebRtcVideoMediaChannel::SetReceiveCodecs( } } } - - // Start receiving packets if at least one receive codec has been set. - if (!receive_codecs_.empty()) { - if (engine()->vie()->base()->StartReceive(channel_id) != 0) { - LOG_RTCERR1(StartReceive, channel_id); - return false; - } - } return true; } diff --git a/talk/media/webrtc/webrtcvideoengine.h b/talk/media/webrtc/webrtcvideoengine.h index fa1b24881..cb191426b 100644 --- a/talk/media/webrtc/webrtcvideoengine.h +++ b/talk/media/webrtc/webrtcvideoengine.h @@ -258,7 +258,7 @@ class WebRtcVideoMediaChannel : public talk_base::MessageHandler, virtual bool AddRecvStream(const StreamParams& sp); virtual bool RemoveRecvStream(uint32 ssrc); virtual bool SetRenderer(uint32 ssrc, VideoRenderer* renderer); - virtual bool GetStats(VideoMediaInfo* info); + virtual bool GetStats(const StatsOptions& options, VideoMediaInfo* info); virtual bool SetCapturer(uint32 ssrc, VideoCapturer* capturer); virtual bool SendIntraFrame(); virtual bool RequestIntraFrame(); diff --git a/talk/media/webrtc/webrtcvideoengine_unittest.cc b/talk/media/webrtc/webrtcvideoengine_unittest.cc index 386ec0c52..f2af10a25 100644 --- a/talk/media/webrtc/webrtcvideoengine_unittest.cc +++ b/talk/media/webrtc/webrtcvideoengine_unittest.cc @@ -1077,6 +1077,8 @@ TEST_F(WebRtcVideoEngineTestFake, SetRender) { TEST_F(WebRtcVideoEngineTestFake, SetSend) { EXPECT_TRUE(SetupEngine()); int channel_num = vie_.GetLastChannel(); + // Verify receiving is also started. + EXPECT_TRUE(vie_.GetReceive(channel_num)); // Set send codecs on the channel. std::vector codecs; @@ -1298,12 +1300,20 @@ TEST_F(WebRtcVideoEngineTestFake, MultipleSendStreamsWithOneCapturer) { ASSERT_NE(-1, channel1); ASSERT_NE(channel0, channel1); + // Both channels should have started receiving after created. + EXPECT_TRUE(vie_.GetReceive(channel0)); + EXPECT_TRUE(vie_.GetReceive(channel1)); + // Set send codec. std::vector codecs; cricket::VideoCodec send_codec(100, "VP8", 640, 480, 30, 0); codecs.push_back(send_codec); EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_TRUE(channel_->SetSend(true)); + EXPECT_TRUE(vie_.GetSend(channel0)); + EXPECT_TRUE(vie_.GetSend(channel1)); + EXPECT_TRUE(capturer.CaptureFrame()); EXPECT_EQ(1, vie_.GetIncomingFrameNum(channel0)); EXPECT_EQ(1, vie_.GetIncomingFrameNum(channel1)); @@ -1347,7 +1357,7 @@ TEST_F(WebRtcVideoEngineTestFake, DISABLED_SendReceiveBitratesStats) { EXPECT_NE(first_receive_channel, second_receive_channel); cricket::VideoMediaInfo info; - EXPECT_TRUE(channel_->GetStats(&info)); + EXPECT_TRUE(channel_->GetStats(cricket::StatsOptions(), &info)); ASSERT_EQ(1U, info.bw_estimations.size()); ASSERT_EQ(0, info.bw_estimations[0].actual_enc_bitrate); ASSERT_EQ(0, info.bw_estimations[0].transmit_bitrate); @@ -1374,7 +1384,7 @@ TEST_F(WebRtcVideoEngineTestFake, DISABLED_SendReceiveBitratesStats) { first_channel_receive_bandwidth); info.Clear(); - EXPECT_TRUE(channel_->GetStats(&info)); + EXPECT_TRUE(channel_->GetStats(cricket::StatsOptions(), &info)); ASSERT_EQ(1U, info.bw_estimations.size()); ASSERT_EQ(send_video_bitrate, info.bw_estimations[0].actual_enc_bitrate); ASSERT_EQ(send_total_bitrate, info.bw_estimations[0].transmit_bitrate); @@ -1391,7 +1401,7 @@ TEST_F(WebRtcVideoEngineTestFake, DISABLED_SendReceiveBitratesStats) { second_channel_receive_bandwidth); info.Clear(); - EXPECT_TRUE(channel_->GetStats(&info)); + EXPECT_TRUE(channel_->GetStats(cricket::StatsOptions(), &info)); ASSERT_EQ(1U, info.bw_estimations.size()); ASSERT_EQ(send_video_bitrate, info.bw_estimations[0].actual_enc_bitrate); ASSERT_EQ(send_total_bitrate, info.bw_estimations[0].transmit_bitrate); diff --git a/talk/p2p/base/candidate.h b/talk/p2p/base/candidate.h index 19eed8cc3..0fa9f0ef8 100644 --- a/talk/p2p/base/candidate.h +++ b/talk/p2p/base/candidate.h @@ -33,6 +33,7 @@ #include #include #include + #include "talk/base/basictypes.h" #include "talk/base/socketaddress.h" #include "talk/p2p/base/constants.h" @@ -163,13 +164,30 @@ class Candidate { return ToStringInternal(true); } - uint32 GetPriority(uint32 type_preference) const { + uint32 GetPriority(uint32 type_preference, + int network_adapter_preference) const { // RFC 5245 - 4.1.2.1. // priority = (2^24)*(type preference) + // (2^8)*(local preference) + // (2^0)*(256 - component ID) + + // |local_preference| length is 2 bytes, 0-65535 inclusive. + // In our implemenation we will partion local_preference into + // 0 1 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | NIC Pref | Addr Pref | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // NIC Type - Type of the network adapter e.g. 3G/Wifi/Wired. + // Addr Pref - Address preference value as per RFC 3484. + // local preference is calculated as - NIC Type << 8 | Addr_Pref. + int addr_pref = IPAddressPrecedence(address_.ipaddr()); - return (type_preference << 24) | (addr_pref << 8) | (256 - component_); + int local_preference = (network_adapter_preference << 8) | addr_pref; + + return (type_preference << 24) | + (local_preference << 8) | + (256 - component_); } private: @@ -177,9 +195,9 @@ class Candidate { std::ostringstream ost; std::string address = sensitive ? address_.ToSensitiveString() : address_.ToString(); - ost << "Cand[" << id_ << ":" << component_ << ":" - << type_ << ":" << protocol_ << ":" - << network_name_ << ":" << address << ":" + ost << "Cand[" << foundation_ << ":" << component_ << ":" + << protocol_ << ":" << priority_ << ":" + << address << ":" << type_ << ":" << related_address_ << ":" << username_ << ":" << password_ << "]"; return ost.str(); } diff --git a/talk/p2p/base/p2ptransportchannel.cc b/talk/p2p/base/p2ptransportchannel.cc index 104b5e69d..1f53874dd 100644 --- a/talk/p2p/base/p2ptransportchannel.cc +++ b/talk/p2p/base/p2ptransportchannel.cc @@ -493,7 +493,8 @@ void P2PTransportChannel::OnUnknownAddress( port->Network()->name(), 0U, talk_base::ToString(talk_base::ComputeCrc32(id))); new_remote_candidate.set_priority( - new_remote_candidate.GetPriority(ICE_TYPE_PREFERENCE_SRFLX)); + new_remote_candidate.GetPriority(ICE_TYPE_PREFERENCE_SRFLX, + port->Network()->preference())); } if (port->IceProtocol() == ICEPROTO_RFC5245) { diff --git a/talk/p2p/base/p2ptransportchannel_unittest.cc b/talk/p2p/base/p2ptransportchannel_unittest.cc index 7fff3dad3..53a39c2a1 100644 --- a/talk/p2p/base/p2ptransportchannel_unittest.cc +++ b/talk/p2p/base/p2ptransportchannel_unittest.cc @@ -1559,8 +1559,11 @@ TEST_F(P2PTransportChannelMultihomedTest, DISABLED_TestBasic) { // Test that we can quickly switch links if an interface goes down. TEST_F(P2PTransportChannelMultihomedTest, TestFailover) { AddAddress(0, kPublicAddrs[0]); - AddAddress(1, kPublicAddrs[1]); + // Adding alternate address will make sure |kPublicAddrs| has the higher + // priority than others. This is due to FakeNetwork::AddInterface method. AddAddress(1, kAlternateAddrs[1]); + AddAddress(1, kPublicAddrs[1]); + // Use only local ports for simplicity. SetAllocatorFlags(0, kOnlyLocalPorts); SetAllocatorFlags(1, kOnlyLocalPorts); diff --git a/talk/p2p/base/port.cc b/talk/p2p/base/port.cc index b6421ad21..38031cb2f 100644 --- a/talk/p2p/base/port.cc +++ b/talk/p2p/base/port.cc @@ -258,7 +258,7 @@ void Port::AddAddress(const talk_base::SocketAddress& address, c.set_type(type); c.set_protocol(protocol); c.set_address(address); - c.set_priority(c.GetPriority(type_preference)); + c.set_priority(c.GetPriority(type_preference, network_->preference())); c.set_username(username_fragment()); c.set_password(password_); c.set_network_name(network_->name()); diff --git a/talk/p2p/client/portallocator_unittest.cc b/talk/p2p/client/portallocator_unittest.cc index 1417707e0..0ea8fb54a 100644 --- a/talk/p2p/client/portallocator_unittest.cc +++ b/talk/p2p/client/portallocator_unittest.cc @@ -53,8 +53,8 @@ using talk_base::Thread; static const SocketAddress kClientAddr("11.11.11.11", 0); static const SocketAddress kClientIPv6Addr( "2401:fa00:4:1000:be30:5bff:fee5:c3", 0); +static const SocketAddress kClientAddr2("22.22.22.22", 0); static const SocketAddress kNatAddr("77.77.77.77", talk_base::NAT_SERVER_PORT); -static const SocketAddress kRemoteClientAddr("22.22.22.22", 0); static const SocketAddress kStunAddr("99.99.99.1", cricket::STUN_SERVER_PORT); static const SocketAddress kRelayUdpIntAddr("99.99.99.2", 5000); static const SocketAddress kRelayUdpExtAddr("99.99.99.3", 5001); @@ -492,6 +492,23 @@ TEST_F(PortAllocatorTest, TestGetAllPortsNoUdpAllowed) { EXPECT_TRUE_WAIT(candidate_allocation_done_, 9000); } +TEST_F(PortAllocatorTest, TestCandidatePriorityOfMultipleInterfaces) { + AddInterface(kClientAddr); + AddInterface(kClientAddr2); + // Allocating only host UDP ports. This is done purely for testing + // convenience. + allocator().set_flags(cricket::PORTALLOCATOR_DISABLE_TCP | + cricket::PORTALLOCATOR_DISABLE_STUN | + cricket::PORTALLOCATOR_DISABLE_RELAY); + EXPECT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP)); + session_->StartGettingPorts(); + EXPECT_TRUE_WAIT(candidate_allocation_done_, kDefaultAllocationTimeout); + ASSERT_EQ(2U, candidates_.size()); + EXPECT_EQ(2U, ports_.size()); + // Candidates priorities should be different. + EXPECT_NE(candidates_[0].priority(), candidates_[1].priority()); +} + // Test to verify ICE restart process. TEST_F(PortAllocatorTest, TestGetAllPortsRestarts) { AddInterface(kClientAddr); diff --git a/talk/session/media/channel.cc b/talk/session/media/channel.cc index bc6bd0981..b177590b6 100644 --- a/talk/session/media/channel.cc +++ b/talk/session/media/channel.cc @@ -1744,9 +1744,10 @@ void VideoChannel::ChangeState() { LOG(LS_INFO) << "Changing video state, recv=" << recv << " send=" << send; } -bool VideoChannel::GetStats(VideoMediaInfo* stats) { +bool VideoChannel::GetStats( + const StatsOptions& options, VideoMediaInfo* stats) { return InvokeOnWorker(Bind(&VideoMediaChannel::GetStats, - media_channel(), stats)); + media_channel(), options, stats)); } void VideoChannel::StartMediaMonitor(int cms) { diff --git a/talk/session/media/channel.h b/talk/session/media/channel.h index f3793cbd2..5a69fed8b 100644 --- a/talk/session/media/channel.h +++ b/talk/session/media/channel.h @@ -524,7 +524,7 @@ class VideoChannel : public BaseChannel { int GetScreencastFps(uint32 ssrc); int GetScreencastMaxPixels(uint32 ssrc); // Get statistics about the current media session. - bool GetStats(VideoMediaInfo* stats); + bool GetStats(const StatsOptions& options, VideoMediaInfo* stats); sigslot::signal2&> SignalConnectionMonitor;