diff --git a/DEPS b/DEPS index 6bf74cbe8d..205ba4e8ca 100644 --- a/DEPS +++ b/DEPS @@ -72,7 +72,7 @@ deps = { Var("chromium_trunk") + "/deps/third_party/libvpx@225010", "third_party/libyuv": - (Var("googlecode_url") % "libyuv") + "/trunk@723", + (Var("googlecode_url") % "libyuv") + "/trunk@826", "third_party/opus": Var("chromium_trunk") + "/src/third_party/opus@185405", diff --git a/talk/app/webrtc/localaudiosource.cc b/talk/app/webrtc/localaudiosource.cc index 3663aace52..2cd472a5a0 100644 --- a/talk/app/webrtc/localaudiosource.cc +++ b/talk/app/webrtc/localaudiosource.cc @@ -53,7 +53,9 @@ const char MediaConstraintsInterface::kHighpassFilter[] = "googHighpassFilter"; const char MediaConstraintsInterface::kTypingNoiseDetection[] = "googTypingNoiseDetection"; -const char MediaConstraintsInterface::kInternalAecDump[] = "internalAecDump"; +const char MediaConstraintsInterface::kAudioMirroring[] = "googAudioMirroring"; +// TODO(perkj): Remove kInternalAecDump once its not used by Chrome. +const char MediaConstraintsInterface::kInternalAecDump[] = "deprecatedAecDump"; namespace { @@ -90,10 +92,10 @@ bool FromConstraints(const MediaConstraintsInterface::Constraints& constraints, options->noise_suppression.Set(value); else if (iter->key == MediaConstraintsInterface::kHighpassFilter) options->highpass_filter.Set(value); - else if (iter->key == MediaConstraintsInterface::kInternalAecDump) - options->aec_dump.Set(value); else if (iter->key == MediaConstraintsInterface::kTypingNoiseDetection) options->typing_detection.Set(value); + else if (iter->key == MediaConstraintsInterface::kAudioMirroring) + options->stereo_swapping.Set(value); else success = false; } @@ -103,14 +105,16 @@ bool FromConstraints(const MediaConstraintsInterface::Constraints& constraints, } // namespace talk_base::scoped_refptr LocalAudioSource::Create( + const PeerConnectionFactoryInterface::Options& options, const MediaConstraintsInterface* constraints) { talk_base::scoped_refptr source( new talk_base::RefCountedObject()); - source->Initialize(constraints); + source->Initialize(options, constraints); return source; } void LocalAudioSource::Initialize( + const PeerConnectionFactoryInterface::Options& options, const MediaConstraintsInterface* constraints) { if (!constraints) return; @@ -119,12 +123,14 @@ void LocalAudioSource::Initialize( // constraints. FromConstraints(constraints->GetOptional(), &options_); - cricket::AudioOptions options; - if (!FromConstraints(constraints->GetMandatory(), &options)) { + cricket::AudioOptions audio_options; + if (!FromConstraints(constraints->GetMandatory(), &audio_options)) { source_state_ = kEnded; return; } - options_.SetAll(options); + options_.SetAll(audio_options); + if (options.enable_aec_dump) + options_.aec_dump.Set(true); source_state_ = kLive; } diff --git a/talk/app/webrtc/localaudiosource.h b/talk/app/webrtc/localaudiosource.h index e0fda03d7f..fb769ed621 100644 --- a/talk/app/webrtc/localaudiosource.h +++ b/talk/app/webrtc/localaudiosource.h @@ -30,6 +30,7 @@ #include "talk/app/webrtc/mediastreaminterface.h" #include "talk/app/webrtc/notifier.h" +#include "talk/app/webrtc/peerconnectioninterface.h" #include "talk/base/scoped_ptr.h" #include "talk/media/base/mediachannel.h" @@ -44,6 +45,7 @@ class LocalAudioSource : public Notifier { public: // Creates an instance of LocalAudioSource. static talk_base::scoped_refptr Create( + const PeerConnectionFactoryInterface::Options& options, const MediaConstraintsInterface* constraints); virtual SourceState state() const { return source_state_; } @@ -58,7 +60,8 @@ class LocalAudioSource : public Notifier { } private: - void Initialize(const MediaConstraintsInterface* constraints); + void Initialize(const PeerConnectionFactoryInterface::Options& options, + const MediaConstraintsInterface* constraints); cricket::AudioOptions options_; SourceState source_state_; diff --git a/talk/app/webrtc/localaudiosource_unittest.cc b/talk/app/webrtc/localaudiosource_unittest.cc index ae077878d4..f8880e0f69 100644 --- a/talk/app/webrtc/localaudiosource_unittest.cc +++ b/talk/app/webrtc/localaudiosource_unittest.cc @@ -39,6 +39,7 @@ using webrtc::LocalAudioSource; using webrtc::MediaConstraintsInterface; using webrtc::MediaSourceInterface; +using webrtc::PeerConnectionFactoryInterface; TEST(LocalAudioSourceTest, SetValidOptions) { webrtc::FakeConstraints constraints; @@ -52,7 +53,8 @@ TEST(LocalAudioSourceTest, SetValidOptions) { constraints.AddOptional(MediaConstraintsInterface::kHighpassFilter, true); talk_base::scoped_refptr source = - LocalAudioSource::Create(&constraints); + LocalAudioSource::Create(PeerConnectionFactoryInterface::Options(), + &constraints); bool value; EXPECT_TRUE(source->options().echo_cancellation.Get(&value)); @@ -72,7 +74,8 @@ TEST(LocalAudioSourceTest, SetValidOptions) { TEST(LocalAudioSourceTest, OptionNotSet) { webrtc::FakeConstraints constraints; talk_base::scoped_refptr source = - LocalAudioSource::Create(&constraints); + LocalAudioSource::Create(PeerConnectionFactoryInterface::Options(), + &constraints); bool value; EXPECT_FALSE(source->options().highpass_filter.Get(&value)); } @@ -83,7 +86,8 @@ TEST(LocalAudioSourceTest, MandatoryOverridesOptional) { constraints.AddOptional(MediaConstraintsInterface::kEchoCancellation, true); talk_base::scoped_refptr source = - LocalAudioSource::Create(&constraints); + LocalAudioSource::Create(PeerConnectionFactoryInterface::Options(), + &constraints); bool value; EXPECT_TRUE(source->options().echo_cancellation.Get(&value)); @@ -96,7 +100,8 @@ TEST(LocalAudioSourceTest, InvalidOptional) { constraints.AddOptional("invalidKey", false); talk_base::scoped_refptr source = - LocalAudioSource::Create(&constraints); + LocalAudioSource::Create(PeerConnectionFactoryInterface::Options(), + &constraints); EXPECT_EQ(MediaSourceInterface::kLive, source->state()); bool value; @@ -110,7 +115,8 @@ TEST(LocalAudioSourceTest, InvalidMandatory) { constraints.AddMandatory("invalidKey", false); talk_base::scoped_refptr source = - LocalAudioSource::Create(&constraints); + LocalAudioSource::Create(PeerConnectionFactoryInterface::Options(), + &constraints); EXPECT_EQ(MediaSourceInterface::kEnded, source->state()); bool value; diff --git a/talk/app/webrtc/mediaconstraintsinterface.h b/talk/app/webrtc/mediaconstraintsinterface.h index 48022dd89d..97d5cf63f5 100644 --- a/talk/app/webrtc/mediaconstraintsinterface.h +++ b/talk/app/webrtc/mediaconstraintsinterface.h @@ -81,6 +81,7 @@ class MediaConstraintsInterface { static const char kNoiseSuppression[]; // googNoiseSuppression static const char kHighpassFilter[]; // googHighpassFilter static const char kTypingNoiseDetection[]; // googTypingNoiseDetection + static const char kAudioMirroring[]; // googAudioMirroring // Google-specific constraint keys for a local video source static const char kNoiseReduction[]; // googNoiseReduction @@ -106,6 +107,8 @@ class MediaConstraintsInterface { static const char kEnableDtlsSrtp[]; // Enable DTLS-SRTP // Temporary pseudo-constraints used to enable DataChannels static const char kEnableRtpDataChannels[]; // Enable RTP DataChannels + // TODO(perkj): Remove kEnableSctpDataChannels once Chrome use + // PeerConnectionFactory::SetOptions. static const char kEnableSctpDataChannels[]; // Enable SCTP DataChannels // The prefix of internal-only constraints whose JS set values should be @@ -116,9 +119,8 @@ class MediaConstraintsInterface { // line flags. So they are prefixed with "internal" so JS values will be // removed. // Used by a local audio source. + // TODO(perkj): Remove once Chrome use PeerConnectionFactory::SetOptions. static const char kInternalAecDump[]; // internalAecDump - // Used for disabling security and use plain RTP. - static const char kInternalDisableEncryption[]; // internalDisableEncryption protected: // Dtor protected as objects shouldn't be deleted via this interface diff --git a/talk/app/webrtc/mediastreamsignaling.cc b/talk/app/webrtc/mediastreamsignaling.cc index 771a4e800f..ef9f3e0204 100644 --- a/talk/app/webrtc/mediastreamsignaling.cc +++ b/talk/app/webrtc/mediastreamsignaling.cc @@ -267,6 +267,10 @@ bool MediaStreamSignaling::AddDataChannelFromOpenMessage( } scoped_refptr channel( data_channel_factory_->CreateDataChannel(label, &config)); + if (!channel.get()) { + LOG(LS_ERROR) << "Failed to create DataChannel from the OPEN message."; + return false; + } data_channels_[label] = channel; stream_observer_->OnAddDataChannel(channel); // It's immediately ready to use. diff --git a/talk/app/webrtc/peerconnection.cc b/talk/app/webrtc/peerconnection.cc index 7fafad7114..de88f88629 100644 --- a/talk/app/webrtc/peerconnection.cc +++ b/talk/app/webrtc/peerconnection.cc @@ -317,7 +317,8 @@ bool PeerConnection::DoInitialize( stats_.set_session(session_.get()); // Initialize the WebRtcSession. It creates transport channels etc. - if (!session_->Initialize(constraints, dtls_identity_service)) + if (!session_->Initialize(factory_->options(), constraints, + dtls_identity_service)) return false; // Register PeerConnection as receiver of local ice candidates. diff --git a/talk/app/webrtc/peerconnection_unittest.cc b/talk/app/webrtc/peerconnection_unittest.cc index 9de6ec94a7..76d9cd73cf 100644 --- a/talk/app/webrtc/peerconnection_unittest.cc +++ b/talk/app/webrtc/peerconnection_unittest.cc @@ -157,8 +157,8 @@ class PeerConnectionTestClientBase // Disable highpass filter so that we can get all the test audio frames. constraints.AddMandatory( MediaConstraintsInterface::kHighpassFilter, false); - talk_base::scoped_refptr source = - webrtc::LocalAudioSource::Create(&constraints); + talk_base::scoped_refptr source = + peer_connection_factory_->CreateAudioSource(&constraints); // TODO(perkj): Test audio source when it is implemented. Currently audio // always use the default input. talk_base::scoped_refptr audio_track( diff --git a/talk/app/webrtc/peerconnectionfactory.cc b/talk/app/webrtc/peerconnectionfactory.cc index 7d30fab8ec..e8b8f63169 100644 --- a/talk/app/webrtc/peerconnectionfactory.cc +++ b/talk/app/webrtc/peerconnectionfactory.cc @@ -261,7 +261,7 @@ talk_base::scoped_refptr PeerConnectionFactory::CreateAudioSource_s( const MediaConstraintsInterface* constraints) { talk_base::scoped_refptr source( - LocalAudioSource::Create(constraints)); + LocalAudioSource::Create(options_, constraints)); return source; } diff --git a/talk/app/webrtc/peerconnectionfactory.h b/talk/app/webrtc/peerconnectionfactory.h index 7faf609424..dff885dfe9 100644 --- a/talk/app/webrtc/peerconnectionfactory.h +++ b/talk/app/webrtc/peerconnectionfactory.h @@ -40,6 +40,10 @@ namespace webrtc { class PeerConnectionFactory : public PeerConnectionFactoryInterface, public talk_base::MessageHandler { public: + virtual void SetOptions(const Options& options) { + options_ = options; + } + virtual talk_base::scoped_refptr CreatePeerConnection( const PeerConnectionInterface::IceServers& configuration, @@ -77,6 +81,7 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface, virtual cricket::ChannelManager* channel_manager(); virtual talk_base::Thread* signaling_thread(); virtual talk_base::Thread* worker_thread(); + const Options& options() const { return options_; } protected: PeerConnectionFactory(); @@ -109,6 +114,7 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface, bool owns_ptrs_; talk_base::Thread* signaling_thread_; talk_base::Thread* worker_thread_; + Options options_; talk_base::scoped_refptr allocator_factory_; // External Audio device used for audio playback. talk_base::scoped_refptr default_adm_; diff --git a/talk/app/webrtc/peerconnectioninterface.h b/talk/app/webrtc/peerconnectioninterface.h index bd86f3ed2f..a127dad3a9 100644 --- a/talk/app/webrtc/peerconnectioninterface.h +++ b/talk/app/webrtc/peerconnectioninterface.h @@ -390,6 +390,19 @@ class DTLSIdentityServiceInterface { // argument. class PeerConnectionFactoryInterface : public talk_base::RefCountInterface { public: + class Options { + public: + Options() : + enable_aec_dump(false), + disable_encryption(false), + disable_sctp_data_channels(false) { + } + bool enable_aec_dump; + bool disable_encryption; + bool disable_sctp_data_channels; + }; + + virtual void SetOptions(const Options& options) = 0; virtual talk_base::scoped_refptr CreatePeerConnection( const PeerConnectionInterface::IceServers& configuration, diff --git a/talk/app/webrtc/statscollector.cc b/talk/app/webrtc/statscollector.cc index 42ae662c1c..db7cac433d 100644 --- a/talk/app/webrtc/statscollector.cc +++ b/talk/app/webrtc/statscollector.cc @@ -82,6 +82,15 @@ const char StatsReport::kStatsValueNameFrameRateDecoded[] = "googFrameRateDecoded"; const char StatsReport::kStatsValueNameFrameRateOutput[] = "googFrameRateOutput"; +const char StatsReport::kStatsValueNameDecodeMs[] = "googDecodeMs"; +const char StatsReport::kStatsValueNameMaxDecodeMs[] = "googMaxDecodeMs"; +const char StatsReport::kStatsValueNameCurrentDelayMs[] = "googCurrentDelayMs"; +const char StatsReport::kStatsValueNameTargetDelayMs[] = "googTargetDelayMs"; +const char StatsReport::kStatsValueNameJitterBufferMs[] = "googJitterBufferMs"; +const char StatsReport::kStatsValueNameMinPlayoutDelayMs[] = + "googMinPlayoutDelayMs"; +const char StatsReport::kStatsValueNameRenderDelayMs[] = "googRenderDelayMs"; + const char StatsReport::kStatsValueNameFrameRateInput[] = "googFrameRateInput"; const char StatsReport::kStatsValueNameFrameRateSent[] = "googFrameRateSent"; const char StatsReport::kStatsValueNameFrameWidthReceived[] = @@ -119,6 +128,7 @@ const char StatsReport::kStatsValueNameWritable[] = "googWritable"; const char StatsReport::kStatsReportTypeSession[] = "googLibjingleSession"; const char StatsReport::kStatsReportTypeBwe[] = "VideoBwe"; +const char StatsReport::kStatsReportTypeRemoteSsrc[] = "remoteSsrc"; const char StatsReport::kStatsReportTypeSsrc[] = "ssrc"; const char StatsReport::kStatsReportTypeTrack[] = "googTrack"; const char StatsReport::kStatsReportTypeIceCandidate[] = "iceCandidate"; @@ -241,6 +251,21 @@ void ExtractStats(const cricket::VideoReceiverInfo& info, StatsReport* report) { info.framerate_decoded); report->AddValue(StatsReport::kStatsValueNameFrameRateOutput, info.framerate_output); + + report->AddValue(StatsReport::kStatsValueNameDecodeMs, + info.decode_ms); + report->AddValue(StatsReport::kStatsValueNameMaxDecodeMs, + info.max_decode_ms); + report->AddValue(StatsReport::kStatsValueNameCurrentDelayMs, + info.current_delay_ms); + report->AddValue(StatsReport::kStatsValueNameTargetDelayMs, + info.target_delay_ms); + report->AddValue(StatsReport::kStatsValueNameJitterBufferMs, + info.jitter_buffer_ms); + report->AddValue(StatsReport::kStatsValueNameMinPlayoutDelayMs, + info.min_playout_delay_ms); + report->AddValue(StatsReport::kStatsValueNameRenderDelayMs, + info.render_delay_ms); } void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) { @@ -293,6 +318,18 @@ void ExtractStats(const cricket::BandwidthEstimationInfo& info, info.bucket_delay); } +void ExtractRemoteStats(const cricket::MediaSenderInfo& info, + StatsReport* report) { + report->timestamp = info.remote_stats[0].timestamp; + // TODO(hta): Extract some stats here. +} + +void ExtractRemoteStats(const cricket::MediaReceiverInfo& info, + StatsReport* report) { + report->timestamp = info.remote_stats[0].timestamp; + // TODO(hta): Extract some stats here. +} + uint32 ExtractSsrc(const cricket::VoiceReceiverInfo& info) { return info.ssrc; } @@ -319,11 +356,20 @@ void ExtractStatsFromList(const std::vector& data, for (; it != data.end(); ++it) { std::string id; uint32 ssrc = ExtractSsrc(*it); - StatsReport* report = collector->PrepareReport(ssrc, transport_id); + // Each object can result in 2 objects, a local and a remote object. + // TODO(hta): Handle the case of multiple SSRCs per object. + StatsReport* report = collector->PrepareLocalReport(ssrc, transport_id); if (!report) { continue; } ExtractStats(*it, report); + if (it->remote_stats.size() > 0) { + report = collector->PrepareRemoteReport(ssrc, transport_id); + if (!report) { + continue; + } + ExtractRemoteStats(*it, report); + } } }; @@ -406,8 +452,9 @@ void StatsCollector::UpdateStats() { } } -StatsReport* StatsCollector::PrepareReport(uint32 ssrc, - const std::string& transport_id) { +StatsReport* StatsCollector::PrepareLocalReport( + uint32 ssrc, + const std::string& transport_id) { std::string ssrc_id = talk_base::ToString(ssrc); StatsMap::iterator it = reports_.find(StatsId( StatsReport::kStatsReportTypeSsrc, ssrc_id)); @@ -427,10 +474,8 @@ StatsReport* StatsCollector::PrepareReport(uint32 ssrc, &track_id); } - StatsReport* report = &reports_[ - StatsId(StatsReport::kStatsReportTypeSsrc, ssrc_id)]; - report->id = StatsId(StatsReport::kStatsReportTypeSsrc, ssrc_id); - report->type = StatsReport::kStatsReportTypeSsrc; + StatsReport* report = GetOrCreateReport(StatsReport::kStatsReportTypeSsrc, + ssrc_id); // Clear out stats from previous GatherStats calls if any. if (report->timestamp != stats_gathering_started_) { @@ -446,6 +491,44 @@ StatsReport* StatsCollector::PrepareReport(uint32 ssrc, return report; } +StatsReport* StatsCollector::PrepareRemoteReport( + uint32 ssrc, + const std::string& transport_id) { + std::string ssrc_id = talk_base::ToString(ssrc); + StatsMap::iterator it = reports_.find(StatsId( + StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id)); + + std::string track_id; + if (it == reports_.end()) { + if (!session()->GetTrackIdBySsrc(ssrc, &track_id)) { + LOG(LS_WARNING) << "The SSRC " << ssrc + << " is not associated with a track"; + return NULL; + } + } else { + // Keeps the old track id since we want to report the stats for inactive + // tracks. + ExtractValueFromReport(it->second, + StatsReport::kStatsValueNameTrackId, + &track_id); + } + + StatsReport* report = GetOrCreateReport( + StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id); + + // Clear out stats from previous GatherStats calls if any. + // The timestamp will be added later. Zero it for debugging. + report->values.clear(); + report->timestamp = 0; + + report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id); + report->AddValue(StatsReport::kStatsValueNameTrackId, track_id); + // Add the mapping of SSRC to transport. + report->AddValue(StatsReport::kStatsValueNameTransportId, + transport_id); + return report; +} + std::string StatsCollector::AddOneCertificateReport( const talk_base::SSLCertificate* cert, const std::string& issuer_id) { // TODO(bemasc): Move this computation to a helper class that caches these @@ -592,6 +675,9 @@ void StatsCollector::ExtractSessionInfo() { info.local_candidate.address().ToString()); report.AddValue(StatsReport::kStatsValueNameRemoteAddress, info.remote_candidate.address().ToString()); + report.AddValue(StatsReport::kStatsValueNameRtt, info.rtt); + report.AddValue(StatsReport::kStatsValueNameTransportType, + info.local_candidate.protocol()); reports_[report.id] = report; } } @@ -668,4 +754,19 @@ bool StatsCollector::GetTransportIdFromProxy(const std::string& proxy, return true; } +StatsReport* StatsCollector::GetOrCreateReport(const std::string& type, + const std::string& id) { + std::string statsid = StatsId(type, id); + StatsReport* report = NULL; + std::map::iterator it = reports_.find(statsid); + if (it == reports_.end()) { + report = &reports_[statsid]; // Create new element. + report->id = statsid; + report->type = type; + } else { + report = &reports_[statsid]; + } + return report; +} + } // namespace webrtc diff --git a/talk/app/webrtc/statscollector.h b/talk/app/webrtc/statscollector.h index c34b5a0b6f..01da059b51 100644 --- a/talk/app/webrtc/statscollector.h +++ b/talk/app/webrtc/statscollector.h @@ -65,9 +65,11 @@ class StatsCollector { // |reports|. bool GetStats(MediaStreamTrackInterface* track, StatsReports* reports); - WebRtcSession* session() { return session_; } - // Prepare an SSRC report for the given ssrc. Used internally. - StatsReport* PrepareReport(uint32 ssrc, const std::string& transport); + // Prepare an SSRC report for the given ssrc. Used internally + // in the ExtractStatsFromList template. + StatsReport* PrepareLocalReport(uint32 ssrc, const std::string& transport); + // Prepare an SSRC report for the given remote ssrc. Used internally. + StatsReport* PrepareRemoteReport(uint32 ssrc, const std::string& transport); // Extracts the ID of a Transport belonging to an SSRC. Used internally. bool GetTransportIdFromProxy(const std::string& proxy, std::string* transport_id); @@ -88,9 +90,12 @@ class StatsCollector { void ExtractVideoInfo(); double GetTimeNow(); void BuildSsrcToTransportId(); + WebRtcSession* session() { return session_; } + webrtc::StatsReport* GetOrCreateReport(const std::string& type, + const std::string& id); // A map from the report id to the report. - std::map reports_; + std::map reports_; // Raw pointer to the session the statistics are gathered from. WebRtcSession* session_; double stats_gathering_started_; diff --git a/talk/app/webrtc/statscollector_unittest.cc b/talk/app/webrtc/statscollector_unittest.cc index d9cde5d83a..66a5ee0c47 100644 --- a/talk/app/webrtc/statscollector_unittest.cc +++ b/talk/app/webrtc/statscollector_unittest.cc @@ -58,6 +58,10 @@ namespace { const char kNotFound[] = "NOT FOUND"; const char kNoReports[] = "NO REPORTS"; +// Constant names for track identification. +const char kTrackId[] = "somename"; +const uint32 kSsrcOfTrack = 1234; + class MockWebRtcSession : public webrtc::WebRtcSession { public: explicit MockWebRtcSession(cricket::ChannelManager* channel_manager) @@ -207,11 +211,37 @@ class StatsCollectorTest : public testing::Test { new cricket::ChannelManager(media_engine_, new cricket::FakeDeviceManager(), talk_base::Thread::Current())), - session_(channel_manager_.get()) { + session_(channel_manager_.get()), + track_id_(kTrackId) { // By default, we ignore session GetStats calls. EXPECT_CALL(session_, GetStats(_)).WillRepeatedly(Return(false)); } + // This creates a standard setup with a transport called "trspname" + // having one transport channel + // and the specified virtual connection name. + void InitSessionStats(const std::string vc_name) { + const std::string kTransportName("trspname"); + cricket::TransportStats transport_stats; + cricket::TransportChannelStats channel_stats; + channel_stats.component = 1; + transport_stats.content_name = kTransportName; + transport_stats.channel_stats.push_back(channel_stats); + + session_stats_.transport_stats[kTransportName] = transport_stats; + session_stats_.proxy_to_transport[vc_name] = kTransportName; + } + + // Adds a track with a given SSRC into the stats. + void AddVideoTrackStats() { + stream_ = webrtc::MediaStream::Create("streamlabel"); + track_= webrtc::VideoTrack::Create(kTrackId, NULL); + stream_->AddTrack(track_); + EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(track_id_), + Return(true))); + } + void TestCertificateReports(const talk_base::FakeSSLCertificate& local_cert, const std::vector& local_ders, const talk_base::FakeSSLCertificate& remote_cert, @@ -283,9 +313,14 @@ class StatsCollectorTest : public testing::Test { EXPECT_NE(kNotFound, remote_certificate_id); CheckCertChainReports(reports, remote_ders, remote_certificate_id); } + cricket::FakeMediaEngine* media_engine_; talk_base::scoped_ptr channel_manager_; MockWebRtcSession session_; + cricket::SessionStats session_stats_; + talk_base::scoped_refptr stream_; + talk_base::scoped_refptr track_; + std::string track_id_; }; // This test verifies that 64-bit counters are passed successfully. @@ -297,17 +332,13 @@ TEST_F(StatsCollectorTest, BytesCounterHandles64Bits) { webrtc::StatsReports reports; // returned values. cricket::VideoSenderInfo video_sender_info; cricket::VideoMediaInfo stats_read; - const uint32 kSsrcOfTrack = 1234; - const std::string kNameOfTrack("somename"); // The number of bytes must be larger than 0xFFFFFFFF for this test. const int64 kBytesSent = 12345678901234LL; const std::string kBytesSentString("12345678901234"); stats.set_session(&session_); - talk_base::scoped_refptr stream( - webrtc::MediaStream::Create("streamlabel")); - stream->AddTrack(webrtc::VideoTrack::Create(kNameOfTrack, NULL)); - stats.AddStream(stream); + AddVideoTrackStats(); + stats.AddStream(stream_); // Construct a stats value to read. video_sender_info.ssrcs.push_back(1234); @@ -319,9 +350,6 @@ TEST_F(StatsCollectorTest, BytesCounterHandles64Bits) { EXPECT_CALL(*media_channel, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(stats_read), Return(true))); - EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _)) - .WillOnce(DoAll(SetArgPointee<1>(kNameOfTrack), - Return(true))); stats.UpdateStats(); stats.GetStats(NULL, &reports); std::string result = ExtractSsrcStatsValue(reports, "bytesSent"); @@ -339,16 +367,12 @@ TEST_F(StatsCollectorTest, BandwidthEstimationInfoIsReported) { cricket::VideoMediaInfo stats_read; // Set up an SSRC just to test that we get both kinds of stats back: SSRC and // BWE. - const uint32 kSsrcOfTrack = 1234; - const std::string kNameOfTrack("somename"); const int64 kBytesSent = 12345678901234LL; const std::string kBytesSentString("12345678901234"); stats.set_session(&session_); - talk_base::scoped_refptr stream( - webrtc::MediaStream::Create("streamlabel")); - stream->AddTrack(webrtc::VideoTrack::Create(kNameOfTrack, NULL)); - stats.AddStream(stream); + AddVideoTrackStats(); + stats.AddStream(stream_); // Construct a stats value to read. video_sender_info.ssrcs.push_back(1234); @@ -365,9 +389,7 @@ TEST_F(StatsCollectorTest, BandwidthEstimationInfoIsReported) { EXPECT_CALL(*media_channel, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(stats_read), Return(true))); - EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _)) - .WillOnce(DoAll(SetArgPointee<1>(kNameOfTrack), - Return(true))); + stats.UpdateStats(); stats.GetStats(NULL, &reports); std::string result = ExtractSsrcStatsValue(reports, "bytesSent"); @@ -417,13 +439,8 @@ TEST_F(StatsCollectorTest, TrackObjectExistsWithoutUpdateStats) { MockVideoMediaChannel* media_channel = new MockVideoMediaChannel; cricket::VideoChannel video_channel(talk_base::Thread::Current(), media_engine_, media_channel, &session_, "", false, NULL); - const std::string kTrackId("somename"); - talk_base::scoped_refptr stream( - webrtc::MediaStream::Create("streamlabel")); - talk_base::scoped_refptr track = - webrtc::VideoTrack::Create(kTrackId, NULL); - stream->AddTrack(track); - stats.AddStream(stream); + AddVideoTrackStats(); + stats.AddStream(stream_); stats.set_session(&session_); @@ -449,13 +466,8 @@ TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) { MockVideoMediaChannel* media_channel = new MockVideoMediaChannel; cricket::VideoChannel video_channel(talk_base::Thread::Current(), media_engine_, media_channel, &session_, "", false, NULL); - const std::string kTrackId("somename"); - talk_base::scoped_refptr stream( - webrtc::MediaStream::Create("streamlabel")); - talk_base::scoped_refptr track = - webrtc::VideoTrack::Create(kTrackId, NULL); - stream->AddTrack(track); - stats.AddStream(stream); + AddVideoTrackStats(); + stats.AddStream(stream_); stats.set_session(&session_); @@ -464,7 +476,6 @@ TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) { // Constructs an ssrc stats update. cricket::VideoSenderInfo video_sender_info; cricket::VideoMediaInfo stats_read; - const uint32 kSsrcOfTrack = 1234; const int64 kBytesSent = 12345678901234LL; // Construct a stats value to read. @@ -477,23 +488,20 @@ TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) { EXPECT_CALL(*media_channel, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(stats_read), Return(true))); - EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _)) - .WillOnce(DoAll(SetArgPointee<1>(kTrackId), - Return(true))); stats.UpdateStats(); stats.GetStats(NULL, &reports); - // |reports| should contain one session report, one track report, and one ssrc - // report. - EXPECT_EQ((size_t)3, reports.size()); + // |reports| should contain at least one session report, one track report, + // and one ssrc report. + EXPECT_LE((size_t)3, reports.size()); const webrtc::StatsReport* track_report = FindNthReportByType( reports, webrtc::StatsReport::kStatsReportTypeTrack, 1); EXPECT_FALSE(track_report == NULL); - stats.GetStats(track, &reports); - // |reports| should contain one session report, one track report, and one ssrc - // report. - EXPECT_EQ((size_t)3, reports.size()); + stats.GetStats(track_, &reports); + // |reports| should contain at least one session report, one track report, + // and one ssrc report. + EXPECT_LE((size_t)3, reports.size()); track_report = FindNthReportByType( reports, webrtc::StatsReport::kStatsReportTypeTrack, 1); EXPECT_FALSE(track_report == NULL); @@ -516,13 +524,8 @@ TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) { const std::string kVcName("vcname"); cricket::VideoChannel video_channel(talk_base::Thread::Current(), media_engine_, media_channel, &session_, kVcName, false, NULL); - const std::string kTrackId("somename"); - talk_base::scoped_refptr stream( - webrtc::MediaStream::Create("streamlabel")); - talk_base::scoped_refptr track = - webrtc::VideoTrack::Create(kTrackId, NULL); - stream->AddTrack(track); - stats.AddStream(stream); + AddVideoTrackStats(); + stats.AddStream(stream_); stats.set_session(&session_); @@ -531,7 +534,6 @@ TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) { // Constructs an ssrc stats update. cricket::VideoSenderInfo video_sender_info; cricket::VideoMediaInfo stats_read; - const uint32 kSsrcOfTrack = 1234; const int64 kBytesSent = 12345678901234LL; // Construct a stats value to read. @@ -544,23 +546,10 @@ TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) { EXPECT_CALL(*media_channel, GetStats(_)) .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read), Return(true))); - EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _)) - .WillOnce(DoAll(SetArgPointee<1>(kTrackId), - Return(true))); - // Instruct the session to return stats containing the transport channel. - const std::string kTransportName("trspname"); - cricket::SessionStats session_stats; - cricket::TransportStats transport_stats; - cricket::TransportChannelStats channel_stats; - channel_stats.component = 1; - transport_stats.content_name = kTransportName; - transport_stats.channel_stats.push_back(channel_stats); - - session_stats.transport_stats[kTransportName] = transport_stats; - session_stats.proxy_to_transport[kVcName] = kTransportName; + InitSessionStats(kVcName); EXPECT_CALL(session_, GetStats(_)) - .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats), + .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_), Return(true))); stats.UpdateStats(); @@ -575,6 +564,78 @@ TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) { ASSERT_FALSE(transport_report == NULL); } +// This test verifies that a remote stats object will not be created for +// an outgoing SSRC where remote stats are not returned. +TEST_F(StatsCollectorTest, RemoteSsrcInfoIsAbsent) { + webrtc::StatsCollector stats; // Implementation under test. + MockVideoMediaChannel* media_channel = new MockVideoMediaChannel; + // The content_name known by the video channel. + const std::string kVcName("vcname"); + cricket::VideoChannel video_channel(talk_base::Thread::Current(), + media_engine_, media_channel, &session_, kVcName, false, NULL); + AddVideoTrackStats(); + stats.AddStream(stream_); + + stats.set_session(&session_); + + EXPECT_CALL(session_, video_channel()) + .WillRepeatedly(ReturnNull()); + + stats.UpdateStats(); + webrtc::StatsReports reports; + stats.GetStats(NULL, &reports); + const webrtc::StatsReport* remote_report = FindNthReportByType(reports, + webrtc::StatsReport::kStatsReportTypeRemoteSsrc, 1); + EXPECT_TRUE(remote_report == NULL); +} + +// This test verifies that a remote stats object will be created for +// an outgoing SSRC where stats are returned. +TEST_F(StatsCollectorTest, RemoteSsrcInfoIsPresent) { + webrtc::StatsCollector stats; // Implementation under test. + MockVideoMediaChannel* media_channel = new MockVideoMediaChannel; + // The content_name known by the video channel. + const std::string kVcName("vcname"); + cricket::VideoChannel video_channel(talk_base::Thread::Current(), + media_engine_, media_channel, &session_, kVcName, false, NULL); + AddVideoTrackStats(); + stats.AddStream(stream_); + + stats.set_session(&session_); + + webrtc::StatsReports reports; + + // Instruct the session to return stats containing the transport channel. + InitSessionStats(kVcName); + EXPECT_CALL(session_, GetStats(_)) + .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_), + Return(true))); + + // Constructs an ssrc stats update. + cricket::VideoMediaInfo stats_read; + + cricket::SsrcReceiverInfo remote_ssrc_stats; + remote_ssrc_stats.timestamp = 12345.678; + remote_ssrc_stats.ssrc = kSsrcOfTrack; + cricket::VideoSenderInfo video_sender_info; + video_sender_info.ssrcs.push_back(kSsrcOfTrack); + video_sender_info.remote_stats.push_back(remote_ssrc_stats); + stats_read.senders.push_back(video_sender_info); + + EXPECT_CALL(session_, video_channel()) + .WillRepeatedly(Return(&video_channel)); + EXPECT_CALL(*media_channel, GetStats(_)) + .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read), + Return(true))); + + stats.UpdateStats(); + stats.GetStats(NULL, &reports); + const webrtc::StatsReport* remote_report = FindNthReportByType(reports, + webrtc::StatsReport::kStatsReportTypeRemoteSsrc, 1); + EXPECT_FALSE(remote_report == NULL); + EXPECT_NE(0, remote_report->timestamp); +} + // This test verifies that all chained certificates are correctly // reported TEST_F(StatsCollectorTest, ChainedCertificateReportsCreated) { @@ -638,6 +699,7 @@ TEST_F(StatsCollectorTest, NoTransport) { EXPECT_CALL(session_, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(session_stats), Return(true))); + EXPECT_CALL(session_, video_channel()) .WillRepeatedly(ReturnNull()); @@ -712,5 +774,4 @@ TEST_F(StatsCollectorTest, NoCertificates) { ASSERT_EQ(kNotFound, remote_certificate_id); } - } // namespace diff --git a/talk/app/webrtc/statstypes.h b/talk/app/webrtc/statstypes.h index fb8c15b8ef..e76aa86e60 100644 --- a/talk/app/webrtc/statstypes.h +++ b/talk/app/webrtc/statstypes.h @@ -88,6 +88,10 @@ class StatsReport { // The |id| field is the SSRC in decimal form of the rtp stream. static const char kStatsReportTypeSsrc[]; + // StatsReport of |type| = "remoteSsrc" is statistics for a specific + // rtp stream, generated by the remote end of the connection. + static const char kStatsReportTypeRemoteSsrc[]; + // StatsReport of |type| = "googTrack" is statistics for a specific media // track. The |id| field is the track id. static const char kStatsReportTypeTrack[]; @@ -137,6 +141,13 @@ class StatsReport { static const char kStatsValueNameFrameRateReceived[]; static const char kStatsValueNameFrameRateDecoded[]; static const char kStatsValueNameFrameRateOutput[]; + static const char kStatsValueNameDecodeMs[]; + static const char kStatsValueNameMaxDecodeMs[]; + static const char kStatsValueNameCurrentDelayMs[]; + static const char kStatsValueNameTargetDelayMs[]; + static const char kStatsValueNameJitterBufferMs[]; + static const char kStatsValueNameMinPlayoutDelayMs[]; + static const char kStatsValueNameRenderDelayMs[]; static const char kStatsValueNameFrameRateInput[]; static const char kStatsValueNameFrameRateSent[]; static const char kStatsValueNameFrameWidthReceived[]; diff --git a/talk/app/webrtc/test/fakeconstraints.h b/talk/app/webrtc/test/fakeconstraints.h index 927432da8b..b23007eafc 100644 --- a/talk/app/webrtc/test/fakeconstraints.h +++ b/talk/app/webrtc/test/fakeconstraints.h @@ -119,7 +119,6 @@ class FakeConstraints : public webrtc::MediaConstraintsInterface { } void SetAllowDtlsSctpDataChannels() { - SetMandatory(MediaConstraintsInterface::kEnableSctpDataChannels, true); SetMandatory(MediaConstraintsInterface::kEnableDtlsSrtp, true); } diff --git a/talk/app/webrtc/webrtcsdp_unittest.cc b/talk/app/webrtc/webrtcsdp_unittest.cc index 6f2e388357..777a707b6b 100644 --- a/talk/app/webrtc/webrtcsdp_unittest.cc +++ b/talk/app/webrtc/webrtcsdp_unittest.cc @@ -282,7 +282,7 @@ static const char kSdpSctpDataChannelString[] = "a=ice-ufrag:ufrag_data\r\n" "a=ice-pwd:pwd_data\r\n" "a=mid:data_content_name\r\n" - "a=sctpmap:5000 webrtc-datachannel 65536\r\n"; + "a=sctpmap:5000 webrtc-datachannel 1024\r\n"; static const char kSdpSctpDataChannelWithCandidatesString[] = "m=application 2345 DTLS/SCTP 5000\r\n" @@ -297,7 +297,7 @@ static const char kSdpSctpDataChannelWithCandidatesString[] = "a=ice-ufrag:ufrag_data\r\n" "a=ice-pwd:pwd_data\r\n" "a=mid:data_content_name\r\n" - "a=sctpmap:5000 webrtc-datachannel 65536\r\n"; + "a=sctpmap:5000 webrtc-datachannel 1024\r\n"; // One candidate reference string as per W3c spec. diff --git a/talk/app/webrtc/webrtcsession.cc b/talk/app/webrtc/webrtcsession.cc index 74d1488e7b..69a47c4688 100644 --- a/talk/app/webrtc/webrtcsession.cc +++ b/talk/app/webrtc/webrtcsession.cc @@ -68,10 +68,7 @@ const char MediaConstraintsInterface::kEnableRtpDataChannels[] = // line flag. So it is prefixed with kInternalConstraintPrefix so JS values // will be removed. const char MediaConstraintsInterface::kEnableSctpDataChannels[] = - "internalSctpDataChannels"; - -const char MediaConstraintsInterface::kInternalDisableEncryption[] = - "internalDisableEncryption"; + "deprecatedSctpDataChannels"; // Error messages const char kSetLocalSdpFailed[] = "SetLocalDescription failed: "; @@ -458,6 +455,7 @@ WebRtcSession::~WebRtcSession() { } bool WebRtcSession::Initialize( + const PeerConnectionFactoryInterface::Options& options, const MediaConstraintsInterface* constraints, DTLSIdentityServiceInterface* dtls_identity_service) { // TODO(perkj): Take |constraints| into consideration. Return false if not all @@ -476,19 +474,16 @@ bool WebRtcSession::Initialize( } // Enable creation of RTP data channels if the kEnableRtpDataChannels is set. - // It takes precendence over the kEnableSctpDataChannels constraint. + // It takes precendence over the disable_sctp_data_channels + // PeerConnectionFactoryInterface::Options. if (FindConstraint( constraints, MediaConstraintsInterface::kEnableRtpDataChannels, &value, NULL) && value) { LOG(LS_INFO) << "Allowing RTP data engine."; data_channel_type_ = cricket::DCT_RTP; } else { - bool sctp_enabled = FindConstraint( - constraints, - MediaConstraintsInterface::kEnableSctpDataChannels, - &value, NULL) && value; // DTLS has to be enabled to use SCTP. - if (sctp_enabled && dtls_enabled_) { + if (!options.disable_sctp_data_channels && dtls_enabled_) { LOG(LS_INFO) << "Allowing SCTP data engine."; data_channel_type_ = cricket::DCT_SCTP; } @@ -520,11 +515,7 @@ bool WebRtcSession::Initialize( webrtc_session_desc_factory_->SignalIdentityReady.connect( this, &WebRtcSession::OnIdentityReady); - // Disable encryption if kDisableEncryption is set. - if (FindConstraint( - constraints, - MediaConstraintsInterface::kInternalDisableEncryption, - &value, NULL) && value) { + if (options.disable_encryption) { webrtc_session_desc_factory_->set_secure(cricket::SEC_DISABLED); } diff --git a/talk/app/webrtc/webrtcsession.h b/talk/app/webrtc/webrtcsession.h index 12e2291c79..dde33cae2c 100644 --- a/talk/app/webrtc/webrtcsession.h +++ b/talk/app/webrtc/webrtcsession.h @@ -110,7 +110,8 @@ class WebRtcSession : public cricket::BaseSession, MediaStreamSignaling* mediastream_signaling); virtual ~WebRtcSession(); - bool Initialize(const MediaConstraintsInterface* constraints, + bool Initialize(const PeerConnectionFactoryInterface::Options& options, + const MediaConstraintsInterface* constraints, DTLSIdentityServiceInterface* dtls_identity_service); // Deletes the voice, video and data channel and changes the session state // to STATE_RECEIVEDTERMINATE. diff --git a/talk/app/webrtc/webrtcsession_unittest.cc b/talk/app/webrtc/webrtcsession_unittest.cc index 493294fcb1..ecbba1817c 100644 --- a/talk/app/webrtc/webrtcsession_unittest.cc +++ b/talk/app/webrtc/webrtcsession_unittest.cc @@ -81,6 +81,7 @@ using webrtc::FakeConstraints; using webrtc::IceCandidateCollection; using webrtc::JsepIceCandidate; using webrtc::JsepSessionDescription; +using webrtc::PeerConnectionFactoryInterface; using webrtc::PeerConnectionInterface; using webrtc::SessionDescriptionInterface; using webrtc::StreamCollection; @@ -317,7 +318,8 @@ class WebRtcSessionTest : public testing::Test { EXPECT_EQ(PeerConnectionInterface::kIceGatheringNew, observer_.ice_gathering_state_); - EXPECT_TRUE(session_->Initialize(constraints_.get(), identity_service)); + EXPECT_TRUE(session_->Initialize(options_, constraints_.get(), + identity_service)); } void InitWithDtmfCodec() { @@ -919,6 +921,7 @@ class WebRtcSessionTest : public testing::Test { cricket::TestStunServer stun_server_; talk_base::FakeNetworkManager network_manager_; cricket::BasicPortAllocator allocator_; + PeerConnectionFactoryInterface::Options options_; talk_base::scoped_ptr constraints_; FakeMediaStreamSignaling mediastream_signaling_; talk_base::scoped_ptr session_; @@ -1932,9 +1935,7 @@ TEST_F(WebRtcSessionTest, VerifyCryptoParamsInSDP) { } TEST_F(WebRtcSessionTest, VerifyNoCryptoParamsInSDP) { - constraints_.reset(new FakeConstraints()); - constraints_->AddOptional( - webrtc::MediaConstraintsInterface::kInternalDisableEncryption, true); + options_.disable_encryption = true; Init(NULL); mediastream_signaling_.SendAudioVideoStream1(); scoped_ptr offer( @@ -2424,9 +2425,7 @@ TEST_F(WebRtcSessionTest, TestCryptoAfterSetLocalDescription) { // This test verifies the crypto parameter when security is disabled. TEST_F(WebRtcSessionTest, TestCryptoAfterSetLocalDescriptionWithDisabled) { - constraints_.reset(new FakeConstraints()); - constraints_->AddOptional( - webrtc::MediaConstraintsInterface::kInternalDisableEncryption, true); + options_.disable_encryption = true; Init(NULL); mediastream_signaling_.SendAudioVideoStream1(); talk_base::scoped_ptr offer( @@ -2567,8 +2566,8 @@ TEST_F(WebRtcSessionTest, TestRtpDataChannelConstraintTakesPrecedence) { constraints_.reset(new FakeConstraints()); constraints_->AddOptional( webrtc::MediaConstraintsInterface::kEnableRtpDataChannels, true); - constraints_->AddOptional( - webrtc::MediaConstraintsInterface::kEnableSctpDataChannels, true); + options_.disable_sctp_data_channels = false; + InitWithDtls(false); SetLocalDescriptionWithDataChannel(); @@ -2578,9 +2577,6 @@ TEST_F(WebRtcSessionTest, TestRtpDataChannelConstraintTakesPrecedence) { TEST_F(WebRtcSessionTest, TestCreateOfferWithSctpEnabledWithoutStreams) { MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); - constraints_.reset(new FakeConstraints()); - constraints_->AddOptional( - webrtc::MediaConstraintsInterface::kEnableSctpDataChannels, true); InitWithDtls(false); talk_base::scoped_ptr offer(CreateOffer(NULL)); @@ -2591,9 +2587,6 @@ TEST_F(WebRtcSessionTest, TestCreateOfferWithSctpEnabledWithoutStreams) { TEST_F(WebRtcSessionTest, TestCreateAnswerWithSctpInOfferAndNoStreams) { MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); SetFactoryDtlsSrtp(); - constraints_.reset(new FakeConstraints()); - constraints_->AddOptional( - webrtc::MediaConstraintsInterface::kEnableSctpDataChannels, true); InitWithDtls(false); // Create remote offer with SCTP. @@ -2612,8 +2605,6 @@ TEST_F(WebRtcSessionTest, TestCreateAnswerWithSctpInOfferAndNoStreams) { TEST_F(WebRtcSessionTest, TestSctpDataChannelWithoutDtls) { constraints_.reset(new FakeConstraints()); - constraints_->AddOptional( - webrtc::MediaConstraintsInterface::kEnableSctpDataChannels, true); constraints_->AddOptional( webrtc::MediaConstraintsInterface::kEnableDtlsSrtp, false); InitWithDtls(false); @@ -2625,24 +2616,26 @@ TEST_F(WebRtcSessionTest, TestSctpDataChannelWithoutDtls) { TEST_F(WebRtcSessionTest, TestSctpDataChannelWithDtls) { MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); - constraints_.reset(new FakeConstraints()); - constraints_->AddOptional( - webrtc::MediaConstraintsInterface::kEnableSctpDataChannels, true); InitWithDtls(false); SetLocalDescriptionWithDataChannel(); EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type()); } +TEST_F(WebRtcSessionTest, TestDisableSctpDataChannels) { + MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); + options_.disable_sctp_data_channels = true; + InitWithDtls(false); + + SetLocalDescriptionWithDataChannel(); + EXPECT_EQ(cricket::DCT_NONE, data_engine_->last_channel_type()); +} + TEST_F(WebRtcSessionTest, TestSctpDataChannelSendPortParsing) { MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); const int new_send_port = 9998; const int new_recv_port = 7775; - constraints_.reset(new FakeConstraints()); - constraints_->AddOptional( - webrtc::MediaConstraintsInterface::kEnableSctpDataChannels, true); - InitWithDtls(false); SetFactoryDtlsSrtp(); diff --git a/talk/base/atomicops.h b/talk/base/atomicops.h index 94ade69730..068cd5f3dc 100644 --- a/talk/base/atomicops.h +++ b/talk/base/atomicops.h @@ -79,7 +79,7 @@ class FixedSizeLockFreeQueue { FixedSizeLockFreeQueue() : pushed_count_(0), popped_count_(0), capacity_(0), - data_(NULL) {} + data_() {} // Constructs an empty queue with the given capacity. FixedSizeLockFreeQueue(size_t capacity) : pushed_count_(0), popped_count_(0), @@ -157,7 +157,7 @@ class FixedSizeLockFreeQueue { volatile Atomic32 pushed_count_; volatile Atomic32 popped_count_; size_t capacity_; - talk_base::scoped_array data_; + talk_base::scoped_ptr data_; DISALLOW_COPY_AND_ASSIGN(FixedSizeLockFreeQueue); }; diff --git a/talk/base/buffer.h b/talk/base/buffer.h index 311cfad726..47096332c5 100644 --- a/talk/base/buffer.h +++ b/talk/base/buffer.h @@ -88,7 +88,7 @@ class Buffer { } void SetCapacity(size_t capacity) { if (capacity > capacity_) { - talk_base::scoped_array data(new char[capacity]); + talk_base::scoped_ptr data(new char[capacity]); memcpy(data.get(), data_.get(), length_); data_.swap(data); capacity_ = capacity; @@ -109,7 +109,7 @@ class Buffer { SetData(data, length); } - scoped_array data_; + scoped_ptr data_; size_t length_; size_t capacity_; }; diff --git a/talk/base/common.h b/talk/base/common.h index b9aeca4f78..6350baf1e4 100644 --- a/talk/base/common.h +++ b/talk/base/common.h @@ -25,7 +25,7 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TALK_BASE_COMMON_H_ +#ifndef TALK_BASE_COMMON_H_ // NOLINT #define TALK_BASE_COMMON_H_ #include "talk/base/basictypes.h" @@ -64,7 +64,7 @@ inline void Unused(const void*) {} #define strnicmp(x, y, n) strncasecmp(x, y, n) #define stricmp(x, y) strcasecmp(x, y) -// TODO: Remove this. std::max should be used everywhere in the code. +// TODO(fbarchard): Remove this. std::max should be used everywhere in the code. // NOMINMAX must be defined where we include . #define stdmax(x, y) std::max(x, y) #else @@ -181,9 +181,28 @@ inline bool ImplicitCastToBool(bool result) { return result; } #if defined(WIN32) #define OVERRIDE override #elif defined(__clang__) +// Clang defaults to C++03 and warns about using override. Squelch that. +// Intentionally no push/pop here so all users of OVERRIDE ignore the warning +// too. This is like passing -Wno-c++11-extensions, except that GCC won't die +// (because it won't see this pragma). +#pragma clang diagnostic ignored "-Wc++11-extensions" #define OVERRIDE override #else #define OVERRIDE #endif -#endif // TALK_BASE_COMMON_H_ +// Annotate a function indicating the caller must examine the return value. +// Use like: +// int foo() WARN_UNUSED_RESULT; +// To explicitly ignore a result, see |ignore_result()| in . +// TODO(ajm): Hack to avoid multiple definitions until the base/ of webrtc and +// libjingle are merged. +#if !defined(WARN_UNUSED_RESULT) +#if defined(COMPILER_GCC) +#define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +#define WARN_UNUSED_RESULT +#endif +#endif // WARN_UNUSED_RESULT + +#endif // TALK_BASE_COMMON_H_ // NOLINT diff --git a/talk/base/compile_assert.h b/talk/base/compile_assert.h new file mode 100644 index 0000000000..7252d08004 --- /dev/null +++ b/talk/base/compile_assert.h @@ -0,0 +1,99 @@ +/* + * 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. + */ + +// COMPILE_ASSERT macro, borrowed from google3/base/macros.h. +#ifndef TALK_BASE_COMPILE_ASSERT_H_ +#define TALK_BASE_COMPILE_ASSERT_H_ + +// The COMPILE_ASSERT macro can be used to verify that a compile time +// expression is true. For example, you could use it to verify the +// size of a static array: +// +// COMPILE_ASSERT(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES, +// content_type_names_incorrect_size); +// +// or to make sure a struct is smaller than a certain size: +// +// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large); +// +// The second argument to the macro is the name of the variable. If +// the expression is false, most compilers will issue a warning/error +// containing the name of the variable. + +// TODO(ajm): Hack to avoid multiple definitions until the base/ of webrtc and +// libjingle are merged. +#if !defined(COMPILE_ASSERT) +template +struct CompileAssert { +}; + +#define COMPILE_ASSERT(expr, msg) \ + typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] // NOLINT +#endif // COMPILE_ASSERT + +// Implementation details of COMPILE_ASSERT: +// +// - COMPILE_ASSERT works by defining an array type that has -1 +// elements (and thus is invalid) when the expression is false. +// +// - The simpler definition +// +// #define COMPILE_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1] +// +// does not work, as gcc supports variable-length arrays whose sizes +// are determined at run-time (this is gcc's extension and not part +// of the C++ standard). As a result, gcc fails to reject the +// following code with the simple definition: +// +// int foo; +// COMPILE_ASSERT(foo, msg); // not supposed to compile as foo is +// // not a compile-time constant. +// +// - By using the type CompileAssert<(bool(expr))>, we ensures that +// expr is a compile-time constant. (Template arguments must be +// determined at compile-time.) +// +// - The outer parentheses in CompileAssert<(bool(expr))> are necessary +// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written +// +// CompileAssert +// +// instead, these compilers will refuse to compile +// +// COMPILE_ASSERT(5 > 0, some_message); +// +// (They seem to think the ">" in "5 > 0" marks the end of the +// template argument list.) +// +// - The array size is (bool(expr) ? 1 : -1), instead of simply +// +// ((expr) ? 1 : -1). +// +// This is to avoid running into a bug in MS VC 7.1, which +// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1. + +#endif // TALK_BASE_COMPILE_ASSERT_H_ diff --git a/talk/base/constructormagic.h b/talk/base/constructormagic.h index 8b1f7ffacb..3023044e95 100644 --- a/talk/base/constructormagic.h +++ b/talk/base/constructormagic.h @@ -2,26 +2,26 @@ * libjingle * Copyright 2004--2005, Google Inc. * - * Redistribution and use in source and binary forms, with or without + * 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, + * 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 + * 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 + * 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, + * 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 + * 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. */ @@ -32,7 +32,10 @@ void operator=(const TypeName&) // A macro to disallow the evil copy constructor and operator= functions -// This should be used in the private: declarations for a class +// This should be used in the private: declarations for a class. +// Undefine this, just in case. Some third-party includes have their own +// version. +#undef DISALLOW_COPY_AND_ASSIGN #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName&); \ DISALLOW_ASSIGN(TypeName) diff --git a/talk/base/cpumonitor.cc b/talk/base/cpumonitor.cc index 0a70887402..e9b481fdbd 100644 --- a/talk/base/cpumonitor.cc +++ b/talk/base/cpumonitor.cc @@ -210,7 +210,7 @@ float CpuSampler::GetSystemLoad() { } else { if (nt_query_system_information) { ULONG returned_length = 0; - scoped_array processor_info( + scoped_ptr processor_info( new SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION[cpus_]); nt_query_system_information( ::SystemProcessorPerformanceInformation, @@ -281,6 +281,13 @@ float CpuSampler::GetSystemLoad() { const uint64 cpu_times = nice + system + user; const uint64 total_times = cpu_times + idle; #endif // defined(LINUX) || defined(ANDROID) + +#if defined(__native_client__) + // TODO(ryanpetrie): Implement this via PPAPI when it's available. + const uint64 cpu_times = 0; + const uint64 total_times = 0; +#endif // defined(__native_client__) + system_.prev_load_time_ = timenow; system_.prev_load_ = UpdateCpuLoad(total_times, cpu_times * cpus_, @@ -359,6 +366,12 @@ float CpuSampler::GetProcessLoad() { (usage.ru_utime.tv_sec + usage.ru_stime.tv_sec) * kNumMicrosecsPerSec + usage.ru_utime.tv_usec + usage.ru_stime.tv_usec; #endif // defined(LINUX) || defined(ANDROID) + +#if defined(__native_client__) + // TODO(ryanpetrie): Implement this via PPAPI when it's available. + const uint64 cpu_times = 0; +#endif // defined(__native_client__) + process_.prev_load_time_ = timenow; process_.prev_load_ = UpdateCpuLoad(total_times, cpu_times, diff --git a/talk/base/helpers.cc b/talk/base/helpers.cc index bda940b87e..b10a3f742a 100644 --- a/talk/base/helpers.cc +++ b/talk/base/helpers.cc @@ -239,7 +239,7 @@ bool CreateRandomString(size_t len, const char* table, int table_size, std::string* str) { str->clear(); - scoped_array bytes(new uint8[len]); + scoped_ptr bytes(new uint8[len]); if (!Rng().Generate(bytes.get(), len)) { LOG(LS_ERROR) << "Failed to generate random string!"; return false; diff --git a/talk/base/macutils.cc b/talk/base/macutils.cc index c73b0fa6f9..28f96e2ef2 100644 --- a/talk/base/macutils.cc +++ b/talk/base/macutils.cc @@ -43,7 +43,7 @@ bool ToUtf8(const CFStringRef str16, std::string* str8) { size_t maxlen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str16), kCFStringEncodingUTF8) + 1; - scoped_array buffer(new char[maxlen]); + scoped_ptr buffer(new char[maxlen]); if (!buffer || !CFStringGetCString(str16, buffer.get(), maxlen, kCFStringEncodingUTF8)) return false; diff --git a/talk/base/messagedigest.cc b/talk/base/messagedigest.cc index 6136ae28b3..d91d0674b5 100644 --- a/talk/base/messagedigest.cc +++ b/talk/base/messagedigest.cc @@ -85,7 +85,7 @@ size_t ComputeDigest(const std::string& alg, const void* input, size_t in_len, } std::string ComputeDigest(MessageDigest* digest, const std::string& input) { - scoped_array output(new char[digest->Size()]); + scoped_ptr output(new char[digest->Size()]); ComputeDigest(digest, input.data(), input.size(), output.get(), digest->Size()); return hex_encode(output.get(), digest->Size()); @@ -120,7 +120,7 @@ size_t ComputeHmac(MessageDigest* digest, } // Copy the key to a block-sized buffer to simplify padding. // If the key is longer than a block, hash it and use the result instead. - scoped_array new_key(new uint8[block_len]); + scoped_ptr new_key(new uint8[block_len]); if (key_len > block_len) { ComputeDigest(digest, key, key_len, new_key.get(), block_len); memset(new_key.get() + digest->Size(), 0, block_len - digest->Size()); @@ -129,13 +129,13 @@ size_t ComputeHmac(MessageDigest* digest, memset(new_key.get() + key_len, 0, block_len - key_len); } // Set up the padding from the key, salting appropriately for each padding. - scoped_array o_pad(new uint8[block_len]), i_pad(new uint8[block_len]); + scoped_ptr o_pad(new uint8[block_len]), i_pad(new uint8[block_len]); for (size_t i = 0; i < block_len; ++i) { o_pad[i] = 0x5c ^ new_key[i]; i_pad[i] = 0x36 ^ new_key[i]; } // Inner hash; hash the inner padding, and then the input buffer. - scoped_array inner(new uint8[digest->Size()]); + scoped_ptr inner(new uint8[digest->Size()]); digest->Update(i_pad.get(), block_len); digest->Update(input, in_len); digest->Finish(inner.get(), digest->Size()); @@ -158,7 +158,7 @@ size_t ComputeHmac(const std::string& alg, const void* key, size_t key_len, std::string ComputeHmac(MessageDigest* digest, const std::string& key, const std::string& input) { - scoped_array output(new char[digest->Size()]); + scoped_ptr output(new char[digest->Size()]); ComputeHmac(digest, key.data(), key.size(), input.data(), input.size(), output.get(), digest->Size()); return hex_encode(output.get(), digest->Size()); diff --git a/talk/base/move.h b/talk/base/move.h new file mode 100644 index 0000000000..0cd1dcfae1 --- /dev/null +++ b/talk/base/move.h @@ -0,0 +1,207 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_WEBRTC_FILES_TALK_BASE_MOVE_H_ +#define THIRD_PARTY_WEBRTC_FILES_TALK_BASE_MOVE_H_ + +// Macro with the boilerplate that makes a type move-only in C++03. +// +// USAGE +// +// This macro should be used instead of DISALLOW_COPY_AND_ASSIGN to create +// a "move-only" type. Unlike DISALLOW_COPY_AND_ASSIGN, this macro should be +// the first line in a class declaration. +// +// A class using this macro must call .Pass() (or somehow be an r-value already) +// before it can be: +// +// * Passed as a function argument +// * Used as the right-hand side of an assignment +// * Returned from a function +// +// Each class will still need to define their own "move constructor" and "move +// operator=" to make this useful. Here's an example of the macro, the move +// constructor, and the move operator= from the scoped_ptr class: +// +// template +// class scoped_ptr { +// MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue) +// public: +// scoped_ptr(RValue& other) : ptr_(other.release()) { } +// scoped_ptr& operator=(RValue& other) { +// swap(other); +// return *this; +// } +// }; +// +// Note that the constructor must NOT be marked explicit. +// +// For consistency, the second parameter to the macro should always be RValue +// unless you have a strong reason to do otherwise. It is only exposed as a +// macro parameter so that the move constructor and move operator= don't look +// like they're using a phantom type. +// +// +// HOW THIS WORKS +// +// For a thorough explanation of this technique, see: +// +// http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Move_Constructor +// +// The summary is that we take advantage of 2 properties: +// +// 1) non-const references will not bind to r-values. +// 2) C++ can apply one user-defined conversion when initializing a +// variable. +// +// The first lets us disable the copy constructor and assignment operator +// by declaring private version of them with a non-const reference parameter. +// +// For l-values, direct initialization still fails like in +// DISALLOW_COPY_AND_ASSIGN because the copy constructor and assignment +// operators are private. +// +// For r-values, the situation is different. The copy constructor and +// assignment operator are not viable due to (1), so we are trying to call +// a non-existent constructor and non-existing operator= rather than a private +// one. Since we have not committed an error quite yet, we can provide an +// alternate conversion sequence and a constructor. We add +// +// * a private struct named "RValue" +// * a user-defined conversion "operator RValue()" +// * a "move constructor" and "move operator=" that take the RValue& as +// their sole parameter. +// +// Only r-values will trigger this sequence and execute our "move constructor" +// or "move operator=." L-values will match the private copy constructor and +// operator= first giving a "private in this context" error. This combination +// gives us a move-only type. +// +// For signaling a destructive transfer of data from an l-value, we provide a +// method named Pass() which creates an r-value for the current instance +// triggering the move constructor or move operator=. +// +// Other ways to get r-values is to use the result of an expression like a +// function call. +// +// Here's an example with comments explaining what gets triggered where: +// +// class Foo { +// MOVE_ONLY_TYPE_FOR_CPP_03(Foo, RValue); +// +// public: +// ... API ... +// Foo(RValue other); // Move constructor. +// Foo& operator=(RValue rhs); // Move operator= +// }; +// +// Foo MakeFoo(); // Function that returns a Foo. +// +// Foo f; +// Foo f_copy(f); // ERROR: Foo(Foo&) is private in this context. +// Foo f_assign; +// f_assign = f; // ERROR: operator=(Foo&) is private in this context. +// +// +// Foo f(MakeFoo()); // R-value so alternate conversion executed. +// Foo f_copy(f.Pass()); // R-value so alternate conversion executed. +// f = f_copy.Pass(); // R-value so alternate conversion executed. +// +// +// IMPLEMENTATION SUBTLETIES WITH RValue +// +// The RValue struct is just a container for a pointer back to the original +// object. It should only ever be created as a temporary, and no external +// class should ever declare it or use it in a parameter. +// +// It is tempting to want to use the RValue type in function parameters, but +// excluding the limited usage here for the move constructor and move +// operator=, doing so would mean that the function could take both r-values +// and l-values equially which is unexpected. See COMPARED To Boost.Move for +// more details. +// +// An alternate, and incorrect, implementation of the RValue class used by +// Boost.Move makes RValue a fieldless child of the move-only type. RValue& +// is then used in place of RValue in the various operators. The RValue& is +// "created" by doing *reinterpret_cast(this). This has the appeal +// of never creating a temporary RValue struct even with optimizations +// disabled. Also, by virtue of inheritance you can treat the RValue +// reference as if it were the move-only type itself. Unfortunately, +// using the result of this reinterpret_cast<> is actually undefined behavior +// due to C++98 5.2.10.7. In certain compilers (e.g., NaCl) the optimizer +// will generate non-working code. +// +// In optimized builds, both implementations generate the same assembly so we +// choose the one that adheres to the standard. +// +// +// COMPARED TO C++11 +// +// In C++11, you would implement this functionality using an r-value reference +// and our .Pass() method would be replaced with a call to std::move(). +// +// This emulation also has a deficiency where it uses up the single +// user-defined conversion allowed by C++ during initialization. This can +// cause problems in some API edge cases. For instance, in scoped_ptr, it is +// impossible to make a function "void Foo(scoped_ptr p)" accept a +// value of type scoped_ptr even if you add a constructor to +// scoped_ptr<> that would make it look like it should work. C++11 does not +// have this deficiency. +// +// +// COMPARED TO Boost.Move +// +// Our implementation similar to Boost.Move, but we keep the RValue struct +// private to the move-only type, and we don't use the reinterpret_cast<> hack. +// +// In Boost.Move, RValue is the boost::rv<> template. This type can be used +// when writing APIs like: +// +// void MyFunc(boost::rv& f) +// +// that can take advantage of rv<> to avoid extra copies of a type. However you +// would still be able to call this version of MyFunc with an l-value: +// +// Foo f; +// MyFunc(f); // Uh oh, we probably just destroyed |f| w/o calling Pass(). +// +// unless someone is very careful to also declare a parallel override like: +// +// void MyFunc(const Foo& f) +// +// that would catch the l-values first. This was declared unsafe in C++11 and +// a C++11 compiler will explicitly fail MyFunc(f). Unfortunately, we cannot +// ensure this in C++03. +// +// Since we have no need for writing such APIs yet, our implementation keeps +// RValue private and uses a .Pass() method to do the conversion instead of +// trying to write a version of "std::move()." Writing an API like std::move() +// would require the RValue struct to be public. +// +// +// CAVEATS +// +// If you include a move-only type as a field inside a class that does not +// explicitly declare a copy constructor, the containing class's implicit +// copy constructor will change from Containing(const Containing&) to +// Containing(Containing&). This can cause some unexpected errors. +// +// http://llvm.org/bugs/show_bug.cgi?id=11528 +// +// The workaround is to explicitly declare your copy constructor. +// +#define MOVE_ONLY_TYPE_FOR_CPP_03(type, rvalue_type) \ + private: \ + struct rvalue_type { \ + explicit rvalue_type(type* object) : object(object) {} \ + type* object; \ + }; \ + type(type&); \ + void operator=(type&); \ + public: \ + operator rvalue_type() { return rvalue_type(this); } \ + type Pass() { return type(rvalue_type(this)); } \ + private: + +#endif // THIRD_PARTY_WEBRTC_FILES_TALK_BASE_MOVE_H_ diff --git a/talk/base/natserver.cc b/talk/base/natserver.cc index ef85f743b7..3ad378ca51 100644 --- a/talk/base/natserver.cc +++ b/talk/base/natserver.cc @@ -149,7 +149,7 @@ void NATServer::OnExternalPacket( // Forward this packet to the internal address. // First prepend the address in a quasi-STUN format. - scoped_array real_buf(new char[size + kNATEncodedIPv6AddressSize]); + scoped_ptr real_buf(new char[size + kNATEncodedIPv6AddressSize]); size_t addrlength = PackAddressForNAT(real_buf.get(), size + kNATEncodedIPv6AddressSize, remote_addr); diff --git a/talk/base/natsocketfactory.cc b/talk/base/natsocketfactory.cc index 1686f206e8..395069e665 100644 --- a/talk/base/natsocketfactory.cc +++ b/talk/base/natsocketfactory.cc @@ -154,7 +154,7 @@ class NATSocket : public AsyncSocket, public sigslot::has_slots<> { return socket_->SendTo(data, size, addr); } // This array will be too large for IPv4 packets, but only by 12 bytes. - scoped_array buf(new char[size + kNATEncodedIPv6AddressSize]); + scoped_ptr buf(new char[size + kNATEncodedIPv6AddressSize]); size_t addrlength = PackAddressForNAT(buf.get(), size + kNATEncodedIPv6AddressSize, addr); diff --git a/talk/base/nethelpers.cc b/talk/base/nethelpers.cc index eebc6cfa7e..05e02c9f47 100644 --- a/talk/base/nethelpers.cc +++ b/talk/base/nethelpers.cc @@ -109,7 +109,7 @@ bool HasIPv6Enabled() { return false; } DWORD protbuff_size = 4096; - scoped_array protocols; + scoped_ptr protocols; LPWSAPROTOCOL_INFOW protocol_infos = NULL; int requested_protocols[2] = {AF_INET6, 0}; diff --git a/talk/base/network.cc b/talk/base/network.cc index cbfcb5fcba..d4dda13813 100644 --- a/talk/base/network.cc +++ b/talk/base/network.cc @@ -316,7 +316,7 @@ bool BasicNetworkManager::CreateNetworks(bool include_ignored, NetworkMap current_networks; // MSDN recommends a 15KB buffer for the first try at GetAdaptersAddresses. size_t buffer_size = 16384; - scoped_array adapter_info(new char[buffer_size]); + scoped_ptr adapter_info(new char[buffer_size]); PIP_ADAPTER_ADDRESSES adapter_addrs = reinterpret_cast(adapter_info.get()); int adapter_flags = (GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_ANYCAST | diff --git a/talk/base/network_unittest.cc b/talk/base/network_unittest.cc index ce19120fe1..e11e78daa4 100644 --- a/talk/base/network_unittest.cc +++ b/talk/base/network_unittest.cc @@ -120,7 +120,8 @@ TEST_F(NetworkTest, TestIgnoreList) { EXPECT_FALSE(IsIgnoredNetwork(network_manager, include_me)); } -TEST_F(NetworkTest, TestCreateNetworks) { +// Test is failing on Windows opt: b/11288214 +TEST_F(NetworkTest, DISABLED_TestCreateNetworks) { BasicNetworkManager manager; NetworkManager::NetworkList result = GetNetworks(manager, true); // We should be able to bind to any addresses we find. diff --git a/talk/base/physicalsocketserver.cc b/talk/base/physicalsocketserver.cc index 21d6eabaa6..891330a54d 100644 --- a/talk/base/physicalsocketserver.cc +++ b/talk/base/physicalsocketserver.cc @@ -534,6 +534,9 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { *slevel = IPPROTO_TCP; *sopt = TCP_NODELAY; break; + case OPT_DSCP: + LOG(LS_WARNING) << "Socket::OPT_DSCP not supported."; + return -1; default: ASSERT(false); return -1; diff --git a/talk/base/scoped_ptr.h b/talk/base/scoped_ptr.h index 5a8364e10b..c12948b4cc 100644 --- a/talk/base/scoped_ptr.h +++ b/talk/base/scoped_ptr.h @@ -1,34 +1,102 @@ -// (C) Copyright Greg Colvin and Beman Dawes 1998, 1999. -// Copyright (c) 2001, 2002 Peter Dimov -// -// Permission to copy, use, modify, sell and distribute this software -// is granted provided this copyright notice appears in all copies. -// This software is provided "as is" without express or implied -// warranty, and with no claim as to its suitability for any purpose. -// -// See http://www.boost.org/libs/smart_ptr/scoped_ptr.htm for documentation. -// +// Borrowed from chromium. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. -// scoped_ptr mimics a built-in pointer except that it guarantees deletion -// of the object pointed to, either on destruction of the scoped_ptr or via -// an explicit reset(). scoped_ptr is a simple solution for simple needs; -// use shared_ptr or std::auto_ptr if your needs are more complex. +// Scopers help you manage ownership of a pointer, helping you easily manage the +// a pointer within a scope, and automatically destroying the pointer at the +// end of a scope. There are two main classes you will use, which correspond +// to the operators new/delete and new[]/delete[]. +// +// Example usage (scoped_ptr): +// { +// scoped_ptr foo(new Foo("wee")); +// } // foo goes out of scope, releasing the pointer with it. +// +// { +// scoped_ptr foo; // No pointer managed. +// foo.reset(new Foo("wee")); // Now a pointer is managed. +// foo.reset(new Foo("wee2")); // Foo("wee") was destroyed. +// foo.reset(new Foo("wee3")); // Foo("wee2") was destroyed. +// foo->Method(); // Foo::Method() called. +// foo.get()->Method(); // Foo::Method() called. +// SomeFunc(foo.release()); // SomeFunc takes ownership, foo no longer +// // manages a pointer. +// foo.reset(new Foo("wee4")); // foo manages a pointer again. +// foo.reset(); // Foo("wee4") destroyed, foo no longer +// // manages a pointer. +// } // foo wasn't managing a pointer, so nothing was destroyed. +// +// Example usage (scoped_ptr): +// { +// scoped_ptr foo(new Foo[100]); +// foo.get()->Method(); // Foo::Method on the 0th element. +// foo[10].Method(); // Foo::Method on the 10th element. +// } +// +// These scopers also implement part of the functionality of C++11 unique_ptr +// in that they are "movable but not copyable." You can use the scopers in +// the parameter and return types of functions to signify ownership transfer +// in to and out of a function. When calling a function that has a scoper +// as the argument type, it must be called with the result of an analogous +// scoper's Pass() function or another function that generates a temporary; +// passing by copy will NOT work. Here is an example using scoped_ptr: +// +// void TakesOwnership(scoped_ptr arg) { +// // Do something with arg +// } +// scoped_ptr CreateFoo() { +// // No need for calling Pass() because we are constructing a temporary +// // for the return value. +// return scoped_ptr(new Foo("new")); +// } +// scoped_ptr PassThru(scoped_ptr arg) { +// return arg.Pass(); +// } +// +// { +// scoped_ptr ptr(new Foo("yay")); // ptr manages Foo("yay"). +// TakesOwnership(ptr.Pass()); // ptr no longer owns Foo("yay"). +// scoped_ptr ptr2 = CreateFoo(); // ptr2 owns the return Foo. +// scoped_ptr ptr3 = // ptr3 now owns what was in ptr2. +// PassThru(ptr2.Pass()); // ptr2 is correspondingly NULL. +// } +// +// Notice that if you do not call Pass() when returning from PassThru(), or +// when invoking TakesOwnership(), the code will not compile because scopers +// are not copyable; they only implement move semantics which require calling +// the Pass() function to signify a destructive transfer of state. CreateFoo() +// is different though because we are constructing a temporary on the return +// line and thus can avoid needing to call Pass(). +// +// Pass() properly handles upcast in initialization, i.e. you can use a +// scoped_ptr to initialize a scoped_ptr: +// +// scoped_ptr foo(new Foo()); +// scoped_ptr parent(foo.Pass()); +// +// PassAs<>() should be used to upcast return value in return statement: +// +// scoped_ptr CreateFoo() { +// scoped_ptr result(new FooChild()); +// return result.PassAs(); +// } +// +// Note that PassAs<>() is implemented only for scoped_ptr, but not for +// scoped_ptr. This is because casting array pointers may not be safe. -// scoped_ptr_malloc added in by Google. When one of -// these goes out of scope, instead of doing a delete or delete[], it -// calls free(). scoped_ptr_malloc is likely to see much more -// use than any other specializations. - -// release() added in by Google. Use this to conditionally -// transfer ownership of a heap-allocated object to the caller, usually on -// method success. #ifndef TALK_BASE_SCOPED_PTR_H__ #define TALK_BASE_SCOPED_PTR_H__ #include // for std::ptrdiff_t #include // for free() decl +#include // For std::swap(). + #include "talk/base/common.h" // for ASSERT +#include "talk/base/compile_assert.h" // for COMPILE_ASSERT +#include "talk/base/move.h" // for MOVE_ONLY_TYPE_FOR_CPP_03 +#include "talk/base/template_util.h" // for is_convertible, is_array #ifdef _WIN32 namespace std { using ::ptrdiff_t; }; @@ -36,242 +104,495 @@ namespace std { using ::ptrdiff_t; }; namespace talk_base { -template -class scoped_ptr { - private: - - T* ptr; - - scoped_ptr(scoped_ptr const &); - scoped_ptr & operator=(scoped_ptr const &); - - public: - - typedef T element_type; - - explicit scoped_ptr(T* p = NULL): ptr(p) {} - - ~scoped_ptr() { - typedef char type_must_be_complete[sizeof(T)]; +// Function object which deletes its parameter, which must be a pointer. +// If C is an array type, invokes 'delete[]' on the parameter; otherwise, +// invokes 'delete'. The default deleter for scoped_ptr. +template +struct DefaultDeleter { + DefaultDeleter() {} + template DefaultDeleter(const DefaultDeleter& other) { + // IMPLEMENTATION NOTE: C++11 20.7.1.1.2p2 only provides this constructor + // if U* is implicitly convertible to T* and U is not an array type. + // + // Correct implementation should use SFINAE to disable this + // constructor. However, since there are no other 1-argument constructors, + // using a COMPILE_ASSERT() based on is_convertible<> and requiring + // complete types is simpler and will cause compile failures for equivalent + // misuses. + // + // Note, the is_convertible check also ensures that U is not an + // array. T is guaranteed to be a non-array, so any U* where U is an array + // cannot convert to T*. + enum { T_must_be_complete = sizeof(T) }; + enum { U_must_be_complete = sizeof(U) }; + COMPILE_ASSERT((talk_base::is_convertible::value), + U_ptr_must_implicitly_convert_to_T_ptr); + } + inline void operator()(T* ptr) const { + enum { type_must_be_complete = sizeof(T) }; delete ptr; } - - void reset(T* p = NULL) { - typedef char type_must_be_complete[sizeof(T)]; - - if (ptr != p) { - T* obj = ptr; - ptr = p; - // Delete last, in case obj destructor indirectly results in ~scoped_ptr - delete obj; - } - } - - T& operator*() const { - ASSERT(ptr != NULL); - return *ptr; - } - - T* operator->() const { - ASSERT(ptr != NULL); - return ptr; - } - - T* get() const { - return ptr; - } - - void swap(scoped_ptr & b) { - T* tmp = b.ptr; - b.ptr = ptr; - ptr = tmp; - } - - T* release() { - T* tmp = ptr; - ptr = NULL; - return tmp; - } - - T** accept() { - if (ptr) { - delete ptr; - ptr = NULL; - } - return &ptr; - } - - T** use() { - return &ptr; - } - - // Allow scoped_ptr to be used in boolean expressions, but not - // implicitly convertible to a real bool (which is dangerous). - // Borrowed from chromium's scoped_ptr implementation. - typedef T* scoped_ptr::*Testable; - operator Testable() const { return ptr ? &scoped_ptr::ptr : NULL; } - }; -template inline -void swap(scoped_ptr& a, scoped_ptr& b) { - a.swap(b); -} - - - - -// scoped_array extends scoped_ptr to arrays. Deletion of the array pointed to -// is guaranteed, either on destruction of the scoped_array or via an explicit -// reset(). Use shared_array or std::vector if your needs are more complex. - -template -class scoped_array { - private: - - T* ptr; - - scoped_array(scoped_array const &); - scoped_array & operator=(scoped_array const &); - - public: - - typedef T element_type; - - explicit scoped_array(T* p = NULL) : ptr(p) {} - - ~scoped_array() { - typedef char type_must_be_complete[sizeof(T)]; +// Specialization of DefaultDeleter for array types. +template +struct DefaultDeleter { + inline void operator()(T* ptr) const { + enum { type_must_be_complete = sizeof(T) }; delete[] ptr; } - void reset(T* p = NULL) { - typedef char type_must_be_complete[sizeof(T)]; + private: + // Disable this operator for any U != T because it is undefined to execute + // an array delete when the static type of the array mismatches the dynamic + // type. + // + // References: + // C++98 [expr.delete]p3 + // http://cplusplus.github.com/LWG/lwg-defects.html#938 + template void operator()(U* array) const; +}; - if (ptr != p) { - T* arr = ptr; - ptr = p; - // Delete last, in case arr destructor indirectly results in ~scoped_array - delete [] arr; +template +struct DefaultDeleter { + // Never allow someone to declare something like scoped_ptr. + COMPILE_ASSERT(sizeof(T) == -1, do_not_use_array_with_size_as_type); +}; + +// Function object which invokes 'free' on its parameter, which must be +// a pointer. Can be used to store malloc-allocated pointers in scoped_ptr: +// +// scoped_ptr foo_ptr( +// static_cast(malloc(sizeof(int)))); +struct FreeDeleter { + inline void operator()(void* ptr) const { + free(ptr); + } +}; + +namespace internal { + +// Minimal implementation of the core logic of scoped_ptr, suitable for +// reuse in both scoped_ptr and its specializations. +template +class scoped_ptr_impl { + public: + explicit scoped_ptr_impl(T* p) : data_(p) { } + + // Initializer for deleters that have data parameters. + scoped_ptr_impl(T* p, const D& d) : data_(p, d) {} + + // Templated constructor that destructively takes the value from another + // scoped_ptr_impl. + template + scoped_ptr_impl(scoped_ptr_impl* other) + : data_(other->release(), other->get_deleter()) { + // We do not support move-only deleters. We could modify our move + // emulation to have talk_base::subtle::move() and + // talk_base::subtle::forward() + // functions that are imperfect emulations of their C++11 equivalents, + // but until there's a requirement, just assume deleters are copyable. + } + + template + void TakeState(scoped_ptr_impl* other) { + // See comment in templated constructor above regarding lack of support + // for move-only deleters. + reset(other->release()); + get_deleter() = other->get_deleter(); + } + + ~scoped_ptr_impl() { + if (data_.ptr != NULL) { + // Not using get_deleter() saves one function call in non-optimized + // builds. + static_cast(data_)(data_.ptr); } } - T& operator[](std::ptrdiff_t i) const { - ASSERT(ptr != NULL); - ASSERT(i >= 0); - return ptr[i]; + void reset(T* p) { + // This is a self-reset, which is no longer allowed: http://crbug.com/162971 + if (p != NULL && p == data_.ptr) + abort(); + + // Note that running data_.ptr = p can lead to undefined behavior if + // get_deleter()(get()) deletes this. In order to pevent this, reset() + // should update the stored pointer before deleting its old value. + // + // However, changing reset() to use that behavior may cause current code to + // break in unexpected ways. If the destruction of the owned object + // dereferences the scoped_ptr when it is destroyed by a call to reset(), + // then it will incorrectly dispatch calls to |p| rather than the original + // value of |data_.ptr|. + // + // During the transition period, set the stored pointer to NULL while + // deleting the object. Eventually, this safety check will be removed to + // prevent the scenario initially described from occuring and + // http://crbug.com/176091 can be closed. + T* old = data_.ptr; + data_.ptr = NULL; + if (old != NULL) + static_cast(data_)(old); + data_.ptr = p; } - T* get() const { - return ptr; - } + T* get() const { return data_.ptr; } - void swap(scoped_array & b) { - T* tmp = b.ptr; - b.ptr = ptr; - ptr = tmp; + D& get_deleter() { return data_; } + const D& get_deleter() const { return data_; } + + void swap(scoped_ptr_impl& p2) { + // Standard swap idiom: 'using std::swap' ensures that std::swap is + // present in the overload set, but we call swap unqualified so that + // any more-specific overloads can be used, if available. + using std::swap; + swap(static_cast(data_), static_cast(p2.data_)); + swap(data_.ptr, p2.data_.ptr); } T* release() { - T* tmp = ptr; - ptr = NULL; - return tmp; + T* old_ptr = data_.ptr; + data_.ptr = NULL; + return old_ptr; } T** accept() { - if (ptr) { - delete [] ptr; - ptr = NULL; - } - return &ptr; + reset(NULL); + return &(data_.ptr); } - // Allow scoped_array to be used in boolean expressions, but not - // implicitly convertible to a real bool (which is dangerous). - // Borrowed from chromium's scoped_array implementation. - typedef T* scoped_array::*Testable; - operator Testable() const { return ptr ? &scoped_array::ptr : NULL; } + T** use() { + return &(data_.ptr); + } + + private: + // Needed to allow type-converting constructor. + template friend class scoped_ptr_impl; + + // Use the empty base class optimization to allow us to have a D + // member, while avoiding any space overhead for it when D is an + // empty class. See e.g. http://www.cantrip.org/emptyopt.html for a good + // discussion of this technique. + struct Data : public D { + explicit Data(T* ptr_in) : ptr(ptr_in) {} + Data(T* ptr_in, const D& other) : D(other), ptr(ptr_in) {} + T* ptr; + }; + + Data data_; + + DISALLOW_COPY_AND_ASSIGN(scoped_ptr_impl); }; -template inline -void swap(scoped_array& a, scoped_array& b) { - a.swap(b); -} +} // namespace internal -// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a -// second template argument, the function used to free the object. - -template class scoped_ptr_malloc { - private: - - T* ptr; - - scoped_ptr_malloc(scoped_ptr_malloc const &); - scoped_ptr_malloc & operator=(scoped_ptr_malloc const &); +// A scoped_ptr is like a T*, except that the destructor of scoped_ptr +// automatically deletes the pointer it holds (if any). +// That is, scoped_ptr owns the T object that it points to. +// Like a T*, a scoped_ptr may hold either NULL or a pointer to a T object. +// Also like T*, scoped_ptr is thread-compatible, and once you +// dereference it, you get the thread safety guarantees of T. +// +// The size of scoped_ptr is small. On most compilers, when using the +// DefaultDeleter, sizeof(scoped_ptr) == sizeof(T*). Custom deleters will +// increase the size proportional to whatever state they need to have. See +// comments inside scoped_ptr_impl<> for details. +// +// Current implementation targets having a strict subset of C++11's +// unique_ptr<> features. Known deficiencies include not supporting move-only +// deleteres, function pointers as deleters, and deleters with reference +// types. +template > +class scoped_ptr { + MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue) public: - + // The element and deleter types. typedef T element_type; + typedef D deleter_type; - explicit scoped_ptr_malloc(T* p = 0): ptr(p) {} + // Constructor. Defaults to initializing with NULL. + scoped_ptr() : impl_(NULL) { } - ~scoped_ptr_malloc() { - FF(ptr); + // Constructor. Takes ownership of p. + explicit scoped_ptr(element_type* p) : impl_(p) { } + + // Constructor. Allows initialization of a stateful deleter. + scoped_ptr(element_type* p, const D& d) : impl_(p, d) { } + + // Constructor. Allows construction from a scoped_ptr rvalue for a + // convertible type and deleter. + // + // IMPLEMENTATION NOTE: C++11 unique_ptr<> keeps this constructor distinct + // from the normal move constructor. By C++11 20.7.1.2.1.21, this constructor + // has different post-conditions if D is a reference type. Since this + // implementation does not support deleters with reference type, + // we do not need a separate move constructor allowing us to avoid one + // use of SFINAE. You only need to care about this if you modify the + // implementation of scoped_ptr. + template + scoped_ptr(scoped_ptr other) : impl_(&other.impl_) { + COMPILE_ASSERT(!talk_base::is_array::value, U_cannot_be_an_array); } - void reset(T* p = 0) { - if (ptr != p) { - FF(ptr); - ptr = p; - } + // Constructor. Move constructor for C++03 move emulation of this type. + scoped_ptr(RValue rvalue) : impl_(&rvalue.object->impl_) { } + + // operator=. Allows assignment from a scoped_ptr rvalue for a convertible + // type and deleter. + // + // IMPLEMENTATION NOTE: C++11 unique_ptr<> keeps this operator= distinct from + // the normal move assignment operator. By C++11 20.7.1.2.3.4, this templated + // form has different requirements on for move-only Deleters. Since this + // implementation does not support move-only Deleters, we do not need a + // separate move assignment operator allowing us to avoid one use of SFINAE. + // You only need to care about this if you modify the implementation of + // scoped_ptr. + template + scoped_ptr& operator=(scoped_ptr rhs) { + COMPILE_ASSERT(!talk_base::is_array::value, U_cannot_be_an_array); + impl_.TakeState(&rhs.impl_); + return *this; } - T& operator*() const { - ASSERT(ptr != 0); - return *ptr; - } + // Reset. Deletes the currently owned object, if any. + // Then takes ownership of a new object, if given. + void reset(element_type* p = NULL) { impl_.reset(p); } - T* operator->() const { - ASSERT(ptr != 0); - return ptr; + // Accessors to get the owned object. + // operator* and operator-> will assert() if there is no current object. + element_type& operator*() const { + ASSERT(impl_.get() != NULL); + return *impl_.get(); } - - T* get() const { - return ptr; + element_type* operator->() const { + ASSERT(impl_.get() != NULL); + return impl_.get(); } + element_type* get() const { return impl_.get(); } - void swap(scoped_ptr_malloc & b) { - T* tmp = b.ptr; - b.ptr = ptr; - ptr = tmp; - } + // Access to the deleter. + deleter_type& get_deleter() { return impl_.get_deleter(); } + const deleter_type& get_deleter() const { return impl_.get_deleter(); } - T* release() { - T* tmp = ptr; - ptr = 0; - return tmp; - } - - T** accept() { - if (ptr) { - FF(ptr); - ptr = 0; - } - return &ptr; - } - - // Allow scoped_ptr_malloc to be used in boolean expressions, but not + // Allow scoped_ptr to be used in boolean expressions, but not // implicitly convertible to a real bool (which is dangerous). - // Borrowed from chromium's scoped_ptr_malloc implementation. - typedef T* scoped_ptr_malloc::*Testable; - operator Testable() const { return ptr ? &scoped_ptr_malloc::ptr : NULL; } + // + // Note that this trick is only safe when the == and != operators + // are declared explicitly, as otherwise "scoped_ptr1 == + // scoped_ptr2" will compile but do the wrong thing (i.e., convert + // to Testable and then do the comparison). + private: + typedef talk_base::internal::scoped_ptr_impl + scoped_ptr::*Testable; + + public: + operator Testable() const { return impl_.get() ? &scoped_ptr::impl_ : NULL; } + + // Comparison operators. + // These return whether two scoped_ptr refer to the same object, not just to + // two different but equal objects. + bool operator==(const element_type* p) const { return impl_.get() == p; } + bool operator!=(const element_type* p) const { return impl_.get() != p; } + + // Swap two scoped pointers. + void swap(scoped_ptr& p2) { + impl_.swap(p2.impl_); + } + + // Release a pointer. + // The return value is the current pointer held by this object. + // If this object holds a NULL pointer, the return value is NULL. + // After this operation, this object will hold a NULL pointer, + // and will not own the object any more. + element_type* release() WARN_UNUSED_RESULT { + return impl_.release(); + } + + // Delete the currently held pointer and return a pointer + // to allow overwriting of the current pointer address. + element_type** accept() WARN_UNUSED_RESULT { + return impl_.accept(); + } + + // Return a pointer to the current pointer address. + element_type** use() WARN_UNUSED_RESULT { + return impl_.use(); + } + + // C++98 doesn't support functions templates with default parameters which + // makes it hard to write a PassAs() that understands converting the deleter + // while preserving simple calling semantics. + // + // Until there is a use case for PassAs() with custom deleters, just ignore + // the custom deleter. + template + scoped_ptr PassAs() { + return scoped_ptr(Pass()); + } + + private: + // Needed to reach into |impl_| in the constructor. + template friend class scoped_ptr; + talk_base::internal::scoped_ptr_impl impl_; + + // Forbidden for API compatibility with std::unique_ptr. + explicit scoped_ptr(int disallow_construction_from_null); + + // Forbid comparison of scoped_ptr types. If U != T, it totally + // doesn't make sense, and if U == T, it still doesn't make sense + // because you should never have the same object owned by two different + // scoped_ptrs. + template bool operator==(scoped_ptr const& p2) const; + template bool operator!=(scoped_ptr const& p2) const; }; -template inline -void swap(scoped_ptr_malloc& a, scoped_ptr_malloc& b) { - a.swap(b); +template +class scoped_ptr { + MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue) + + public: + // The element and deleter types. + typedef T element_type; + typedef D deleter_type; + + // Constructor. Defaults to initializing with NULL. + scoped_ptr() : impl_(NULL) { } + + // Constructor. Stores the given array. Note that the argument's type + // must exactly match T*. In particular: + // - it cannot be a pointer to a type derived from T, because it is + // inherently unsafe in the general case to access an array through a + // pointer whose dynamic type does not match its static type (eg., if + // T and the derived types had different sizes access would be + // incorrectly calculated). Deletion is also always undefined + // (C++98 [expr.delete]p3). If you're doing this, fix your code. + // - it cannot be NULL, because NULL is an integral expression, not a + // pointer to T. Use the no-argument version instead of explicitly + // passing NULL. + // - it cannot be const-qualified differently from T per unique_ptr spec + // (http://cplusplus.github.com/LWG/lwg-active.html#2118). Users wanting + // to work around this may use implicit_cast(). + // However, because of the first bullet in this comment, users MUST + // NOT use implicit_cast() to upcast the static type of the array. + explicit scoped_ptr(element_type* array) : impl_(array) { } + + // Constructor. Move constructor for C++03 move emulation of this type. + scoped_ptr(RValue rvalue) : impl_(&rvalue.object->impl_) { } + + // operator=. Move operator= for C++03 move emulation of this type. + scoped_ptr& operator=(RValue rhs) { + impl_.TakeState(&rhs.object->impl_); + return *this; + } + + // Reset. Deletes the currently owned array, if any. + // Then takes ownership of a new object, if given. + void reset(element_type* array = NULL) { impl_.reset(array); } + + // Accessors to get the owned array. + element_type& operator[](size_t i) const { + ASSERT(impl_.get() != NULL); + return impl_.get()[i]; + } + element_type* get() const { return impl_.get(); } + + // Access to the deleter. + deleter_type& get_deleter() { return impl_.get_deleter(); } + const deleter_type& get_deleter() const { return impl_.get_deleter(); } + + // Allow scoped_ptr to be used in boolean expressions, but not + // implicitly convertible to a real bool (which is dangerous). + private: + typedef talk_base::internal::scoped_ptr_impl + scoped_ptr::*Testable; + + public: + operator Testable() const { return impl_.get() ? &scoped_ptr::impl_ : NULL; } + + // Comparison operators. + // These return whether two scoped_ptr refer to the same object, not just to + // two different but equal objects. + bool operator==(element_type* array) const { return impl_.get() == array; } + bool operator!=(element_type* array) const { return impl_.get() != array; } + + // Swap two scoped pointers. + void swap(scoped_ptr& p2) { + impl_.swap(p2.impl_); + } + + // Release a pointer. + // The return value is the current pointer held by this object. + // If this object holds a NULL pointer, the return value is NULL. + // After this operation, this object will hold a NULL pointer, + // and will not own the object any more. + element_type* release() WARN_UNUSED_RESULT { + return impl_.release(); + } + + // Delete the currently held pointer and return a pointer + // to allow overwriting of the current pointer address. + element_type** accept() WARN_UNUSED_RESULT { + return impl_.accept(); + } + + // Return a pointer to the current pointer address. + element_type** use() WARN_UNUSED_RESULT { + return impl_.use(); + } + + private: + // Force element_type to be a complete type. + enum { type_must_be_complete = sizeof(element_type) }; + + // Actually hold the data. + talk_base::internal::scoped_ptr_impl impl_; + + // Disable initialization from any type other than element_type*, by + // providing a constructor that matches such an initialization, but is + // private and has no definition. This is disabled because it is not safe to + // call delete[] on an array whose static type does not match its dynamic + // type. + template explicit scoped_ptr(U* array); + explicit scoped_ptr(int disallow_construction_from_null); + + // Disable reset() from any type other than element_type*, for the same + // reasons as the constructor above. + template void reset(U* array); + void reset(int disallow_reset_from_null); + + // Forbid comparison of scoped_ptr types. If U != T, it totally + // doesn't make sense, and if U == T, it still doesn't make sense + // because you should never have the same object owned by two different + // scoped_ptrs. + template bool operator==(scoped_ptr const& p2) const; + template bool operator!=(scoped_ptr const& p2) const; +}; + +} // namespace talk_base + +// Free functions +template +void swap(talk_base::scoped_ptr& p1, talk_base::scoped_ptr& p2) { + p1.swap(p2); } -} // namespace talk_base +template +bool operator==(T* p1, const talk_base::scoped_ptr& p2) { + return p1 == p2.get(); +} + +template +bool operator!=(T* p1, const talk_base::scoped_ptr& p2) { + return p1 != p2.get(); +} + +// A function to convert T* into talk_base::scoped_ptr +// Doing e.g. make_scoped_ptr(new FooBarBaz(arg)) is a shorter notation +// for talk_base::scoped_ptr >(new FooBarBaz(arg)) +template +talk_base::scoped_ptr make_scoped_ptr(T* ptr) { + return talk_base::scoped_ptr(ptr); +} #endif // #ifndef TALK_BASE_SCOPED_PTR_H__ diff --git a/talk/base/socket_unittest.cc b/talk/base/socket_unittest.cc index eafab81f40..a9c4dbb0de 100644 --- a/talk/base/socket_unittest.cc +++ b/talk/base/socket_unittest.cc @@ -693,8 +693,8 @@ void SocketTest::TcpInternal(const IPAddress& loopback) { // Create test data. const size_t kDataSize = 1024 * 1024; - scoped_array send_buffer(new char[kDataSize]); - scoped_array recv_buffer(new char[kDataSize]); + scoped_ptr send_buffer(new char[kDataSize]); + scoped_ptr recv_buffer(new char[kDataSize]); size_t send_pos = 0, recv_pos = 0; for (size_t i = 0; i < kDataSize; ++i) { send_buffer[i] = static_cast(i % 256); @@ -934,7 +934,7 @@ void SocketTest::UdpReadyToSend(const IPAddress& loopback) { scoped_ptr client( new TestClient(AsyncUDPSocket::Create(ss_, empty))); int test_packet_size = 1200; - talk_base::scoped_array test_packet(new char[test_packet_size]); + talk_base::scoped_ptr test_packet(new char[test_packet_size]); // Init the test packet just to avoid memcheck warning. memset(test_packet.get(), 0, test_packet_size); // Set the send buffer size to the same size as the test packet to have a diff --git a/talk/base/sslidentity_unittest.cc b/talk/base/sslidentity_unittest.cc index e425df2274..b63b8b9d43 100644 --- a/talk/base/sslidentity_unittest.cc +++ b/talk/base/sslidentity_unittest.cc @@ -57,7 +57,7 @@ const unsigned char kTestCertSha1[] = {0xA6, 0xC8, 0x59, 0xEA, class SSLIdentityTest : public testing::Test { public: SSLIdentityTest() : - identity1_(NULL), identity2_(NULL) { + identity1_(), identity2_() { } ~SSLIdentityTest() { diff --git a/talk/base/stream.cc b/talk/base/stream.cc index 996908d587..c1cf90743a 100644 --- a/talk/base/stream.cc +++ b/talk/base/stream.cc @@ -166,7 +166,7 @@ StreamAdapterInterface::~StreamAdapterInterface() { /////////////////////////////////////////////////////////////////////////////// StreamTap::StreamTap(StreamInterface* stream, StreamInterface* tap) - : StreamAdapterInterface(stream), tap_(NULL), tap_result_(SR_SUCCESS), + : StreamAdapterInterface(stream), tap_(), tap_result_(SR_SUCCESS), tap_error_(0) { AttachTap(tap); } diff --git a/talk/base/stream.h b/talk/base/stream.h index 457396cdf8..4571def9bb 100644 --- a/talk/base/stream.h +++ b/talk/base/stream.h @@ -691,7 +691,7 @@ class FifoBuffer : public StreamInterface { size_t offset, size_t* bytes_written); StreamState state_; // keeps the opened/closed state of the stream - scoped_array buffer_; // the allocated buffer + scoped_ptr buffer_; // the allocated buffer size_t buffer_length_; // size of the allocated buffer size_t data_length_; // amount of readable data in the buffer size_t read_position_; // offset to the readable data diff --git a/talk/base/systeminfo.cc b/talk/base/systeminfo.cc index ec1865889e..ff331ac71a 100644 --- a/talk/base/systeminfo.cc +++ b/talk/base/systeminfo.cc @@ -74,7 +74,7 @@ static void GetProcessorInformation(int* physical_cpus, int* cache_size) { // Determine buffer size, allocate and get processor information. // Size can change between calls (unlikely), so a loop is done. DWORD return_length = 0; - scoped_array infos; + scoped_ptr infos; while (!glpi(infos.get(), &return_length)) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { infos.reset(new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[ @@ -183,6 +183,8 @@ SystemInfo::SystemInfo() if (!sysctlbyname("machdep.cpu.stepping", &sysctl_value, &length, NULL, 0)) { cpu_stepping_ = static_cast(sysctl_value); } +#elif defined(__native_client__) + // TODO(ryanpetrie): Implement this via PPAPI when it's available. #else // LINUX || ANDROID ProcCpuInfo proc_info; if (proc_info.LoadFromSystem()) { diff --git a/talk/base/template_util.h b/talk/base/template_util.h new file mode 100644 index 0000000000..5cc4ba430a --- /dev/null +++ b/talk/base/template_util.h @@ -0,0 +1,106 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TALK_BASE_TEMPLATE_UTIL_H_ +#define TALK_BASE_TEMPLATE_UTIL_H_ + +#include // For size_t. + +namespace talk_base { + +// template definitions from tr1 + +template +struct integral_constant { + static const T value = v; + typedef T value_type; + typedef integral_constant type; +}; + +template const T integral_constant::value; + +typedef integral_constant true_type; +typedef integral_constant false_type; + +template struct is_pointer : false_type {}; +template struct is_pointer : true_type {}; + +template struct is_same : public false_type {}; +template struct is_same : true_type {}; + +template struct is_array : public false_type {}; +template struct is_array : public true_type {}; +template struct is_array : public true_type {}; + +template struct is_non_const_reference : false_type {}; +template struct is_non_const_reference : true_type {}; +template struct is_non_const_reference : false_type {}; + +template struct is_void : false_type {}; +template <> struct is_void : true_type {}; + +namespace internal { + +// Types YesType and NoType are guaranteed such that sizeof(YesType) < +// sizeof(NoType). +typedef char YesType; + +struct NoType { + YesType dummy[2]; +}; + +// This class is an implementation detail for is_convertible, and you +// don't need to know how it works to use is_convertible. For those +// who care: we declare two different functions, one whose argument is +// of type To and one with a variadic argument list. We give them +// return types of different size, so we can use sizeof to trick the +// compiler into telling us which function it would have chosen if we +// had called it with an argument of type From. See Alexandrescu's +// _Modern C++ Design_ for more details on this sort of trick. + +struct ConvertHelper { + template + static YesType Test(To); + + template + static NoType Test(...); + + template + static From& Create(); +}; + +// Used to determine if a type is a struct/union/class. Inspired by Boost's +// is_class type_trait implementation. +struct IsClassHelper { + template + static YesType Test(void(C::*)(void)); + + template + static NoType Test(...); +}; + +} // namespace internal + +// Inherits from true_type if From is convertible to To, false_type otherwise. +// +// Note that if the type is convertible, this will be a true_type REGARDLESS +// of whether or not the conversion would emit a warning. +template +struct is_convertible + : integral_constant( + internal::ConvertHelper::Create())) == + sizeof(internal::YesType)> { +}; + +template +struct is_class + : integral_constant(0)) == + sizeof(internal::YesType)> { +}; + +} // namespace talk_base + +#endif // TALK_BASE_TEMPLATE_UTIL_H_ diff --git a/talk/base/win32filesystem.cc b/talk/base/win32filesystem.cc index 42c0388558..5caeac2a32 100644 --- a/talk/base/win32filesystem.cc +++ b/talk/base/win32filesystem.cc @@ -111,7 +111,7 @@ bool Win32Filesystem::CreatePrivateFile(const Pathname &filename) { &token_user_size); // Get the TOKEN_USER structure. - scoped_array token_user_bytes(new char[token_user_size]); + scoped_ptr token_user_bytes(new char[token_user_size]); PTOKEN_USER token_user = reinterpret_cast( token_user_bytes.get()); memset(token_user, 0, token_user_size); @@ -137,7 +137,7 @@ bool Win32Filesystem::CreatePrivateFile(const Pathname &filename) { GetLengthSid(token_user->User.Sid); // Allocate it. - scoped_array acl_bytes(new char[acl_size]); + scoped_ptr acl_bytes(new char[acl_size]); PACL acl = reinterpret_cast(acl_bytes.get()); memset(acl, 0, acl_size); if (!::InitializeAcl(acl, acl_size, ACL_REVISION)) { @@ -440,7 +440,7 @@ bool Win32Filesystem::GetDiskFreeSpace(const Pathname& path, int64 *freebytes) { Pathname Win32Filesystem::GetCurrentDirectory() { Pathname cwd; int path_len = 0; - scoped_array path; + scoped_ptr path; do { int needed = ::GetCurrentDirectory(path_len, path.get()); if (needed == 0) { diff --git a/talk/base/win32regkey.cc b/talk/base/win32regkey.cc index cb98cafade..403fdc014c 100644 --- a/talk/base/win32regkey.cc +++ b/talk/base/win32regkey.cc @@ -160,7 +160,7 @@ HRESULT RegKey::GetValue(const wchar_t* full_key_name, ASSERT(full_key_name != NULL); DWORD byte_count = 0; - scoped_array buffer; + scoped_ptr buffer; HRESULT hr = GetValueStaticHelper(full_key_name, value_name, REG_BINARY, buffer.accept(), &byte_count); if (SUCCEEDED(hr)) { @@ -179,7 +179,7 @@ HRESULT RegKey::GetValue(const wchar_t* full_key_name, ASSERT(full_key_name != NULL); DWORD byte_count = 0; - scoped_array buffer; + scoped_ptr buffer; HRESULT hr = GetValueStaticHelper(full_key_name, value_name, REG_BINARY, buffer.accept(), &byte_count); if (SUCCEEDED(hr)) { @@ -206,7 +206,7 @@ HRESULT RegKey::GetValue(const wchar_t* full_key_name, ASSERT(full_key_name != NULL); ASSERT(value != NULL); - scoped_array buffer; + scoped_ptr buffer; HRESULT hr = RegKey::GetValue(full_key_name, value_name, buffer.accept()); if (SUCCEEDED(hr)) { value->assign(buffer.get()); diff --git a/talk/base/win32socketserver.cc b/talk/base/win32socketserver.cc index 55128e7a25..9566669733 100644 --- a/talk/base/win32socketserver.cc +++ b/talk/base/win32socketserver.cc @@ -620,6 +620,9 @@ int Win32Socket::TranslateOption(Option opt, int* slevel, int* sopt) { *slevel = IPPROTO_TCP; *sopt = TCP_NODELAY; break; + case OPT_DSCP: + LOG(LS_WARNING) << "Socket::OPT_DSCP not supported."; + return -1; default: ASSERT(false); return -1; diff --git a/talk/examples/call/callclient.cc b/talk/examples/call/callclient.cc index 66c4b6fa69..afbdd2968f 100644 --- a/talk/examples/call/callclient.cc +++ b/talk/examples/call/callclient.cc @@ -396,7 +396,7 @@ CallClient::CallClient(buzz::XmppClient* xmpp_client, transport_protocol_(cricket::ICEPROTO_HYBRID), sdes_policy_(cricket::SEC_DISABLED), dtls_policy_(cricket::SEC_DISABLED), - ssl_identity_(NULL), + ssl_identity_(), show_roster_messages_(false) { xmpp_client_->SignalStateChange.connect(this, &CallClient::OnStateChange); my_status_.set_caps_node(caps_node); diff --git a/talk/examples/chat/chatapp.cc b/talk/examples/chat/chatapp.cc index 1b59910b0b..59b1c69fb9 100644 --- a/talk/examples/chat/chatapp.cc +++ b/talk/examples/chat/chatapp.cc @@ -38,10 +38,10 @@ ChatApp::ChatApp(buzz::XmppClient* xmpp_client, talk_base::Thread* main_thread) : xmpp_client_(xmpp_client), - presence_out_task_(NULL), - presence_receive_task_(NULL), - message_send_task_(NULL), - message_received_task_(NULL), + presence_out_task_(), + presence_receive_task_(), + message_send_task_(), + message_received_task_(), console_task_(new buzz::ConsoleTask(main_thread)), ui_state_(STATE_BASE) { xmpp_client_->SignalStateChange.connect(this, &ChatApp::OnStateChange); diff --git a/talk/examples/peerconnection/client/linux/main_wnd.h b/talk/examples/peerconnection/client/linux/main_wnd.h index c23c1164be..5a44640e1f 100644 --- a/talk/examples/peerconnection/client/linux/main_wnd.h +++ b/talk/examples/peerconnection/client/linux/main_wnd.h @@ -110,7 +110,7 @@ class GtkMainWnd : public MainWindow { } protected: - talk_base::scoped_array image_; + talk_base::scoped_ptr image_; int width_; int height_; GtkMainWnd* main_wnd_; diff --git a/talk/examples/peerconnection/client/main_wnd.h b/talk/examples/peerconnection/client/main_wnd.h index 0ed0f1422b..6d2bf3eeee 100644 --- a/talk/examples/peerconnection/client/main_wnd.h +++ b/talk/examples/peerconnection/client/main_wnd.h @@ -147,7 +147,7 @@ class MainWnd : public MainWindow { HWND wnd_; BITMAPINFO bmi_; - talk_base::scoped_array image_; + talk_base::scoped_ptr image_; CRITICAL_SECTION buffer_lock_; talk_base::scoped_refptr rendered_track_; }; diff --git a/talk/media/base/cpuid.cc b/talk/media/base/cpuid.cc index 9fd7865889..bd87d2e3cb 100644 --- a/talk/media/base/cpuid.cc +++ b/talk/media/base/cpuid.cc @@ -51,8 +51,8 @@ void CpuInfo::MaskCpuFlagsForTest(int enable_flags) { bool IsCoreIOrBetter() { #if !defined(DISABLE_YUV) && (defined(__i386__) || defined(__x86_64__) || \ defined(_M_IX86) || defined(_M_X64)) - int cpu_info[4]; - libyuv::CpuId(cpu_info, 0); // Function 0: Vendor ID + uint32 cpu_info[4]; + libyuv::CpuId(0, 0, &cpu_info[0]); // Function 0: Vendor ID if (cpu_info[1] == 0x756e6547 && cpu_info[3] == 0x49656e69 && cpu_info[2] == 0x6c65746e) { // GenuineIntel // Detect CPU Family and Model @@ -62,7 +62,7 @@ bool IsCoreIOrBetter() { // 13:12 - Processor Type // 19:16 - Extended Model // 27:20 - Extended Family - libyuv::CpuId(cpu_info, 1); // Function 1: Family and Model + libyuv::CpuId(1, 0, &cpu_info[0]); // Function 1: Family and Model int family = ((cpu_info[0] >> 8) & 0x0f) | ((cpu_info[0] >> 16) & 0xff0); int model = ((cpu_info[0] >> 4) & 0x0f) | ((cpu_info[0] >> 12) & 0xf0); // CpuFamily | CpuModel | Name diff --git a/talk/media/base/fakevideocapturer.h b/talk/media/base/fakevideocapturer.h index 5a33265b3a..8dc69c3454 100644 --- a/talk/media/base/fakevideocapturer.h +++ b/talk/media/base/fakevideocapturer.h @@ -101,7 +101,7 @@ class FakeVideoCapturer : public cricket::VideoCapturer { frame.time_stamp = initial_unix_timestamp_ + next_timestamp_; next_timestamp_ += 33333333; // 30 fps - talk_base::scoped_array data(new char[size]); + talk_base::scoped_ptr data(new char[size]); frame.data = data.get(); // Copy something non-zero into the buffer so Validate wont complain that // the frame is all duplicate. diff --git a/talk/media/base/mediachannel.h b/talk/media/base/mediachannel.h index f90265c40b..3dc9c56354 100644 --- a/talk/media/base/mediachannel.h +++ b/talk/media/base/mediachannel.h @@ -162,29 +162,51 @@ struct AudioOptions { void SetAll(const AudioOptions& change) { echo_cancellation.SetFrom(change.echo_cancellation); auto_gain_control.SetFrom(change.auto_gain_control); + rx_auto_gain_control.SetFrom(change.rx_auto_gain_control); noise_suppression.SetFrom(change.noise_suppression); highpass_filter.SetFrom(change.highpass_filter); stereo_swapping.SetFrom(change.stereo_swapping); typing_detection.SetFrom(change.typing_detection); + aecm_generate_comfort_noise.SetFrom(change.aecm_generate_comfort_noise); conference_mode.SetFrom(change.conference_mode); adjust_agc_delta.SetFrom(change.adjust_agc_delta); experimental_agc.SetFrom(change.experimental_agc); experimental_aec.SetFrom(change.experimental_aec); aec_dump.SetFrom(change.aec_dump); + tx_agc_target_dbov.SetFrom(change.tx_agc_target_dbov); + tx_agc_digital_compression_gain.SetFrom( + change.tx_agc_digital_compression_gain); + tx_agc_limiter.SetFrom(change.tx_agc_limiter); + rx_agc_target_dbov.SetFrom(change.rx_agc_target_dbov); + rx_agc_digital_compression_gain.SetFrom( + change.rx_agc_digital_compression_gain); + rx_agc_limiter.SetFrom(change.rx_agc_limiter); + recording_sample_rate.SetFrom(change.recording_sample_rate); + playout_sample_rate.SetFrom(change.playout_sample_rate); } bool operator==(const AudioOptions& o) const { return echo_cancellation == o.echo_cancellation && auto_gain_control == o.auto_gain_control && + rx_auto_gain_control == o.rx_auto_gain_control && noise_suppression == o.noise_suppression && highpass_filter == o.highpass_filter && stereo_swapping == o.stereo_swapping && typing_detection == o.typing_detection && + aecm_generate_comfort_noise == o.aecm_generate_comfort_noise && conference_mode == o.conference_mode && experimental_agc == o.experimental_agc && experimental_aec == o.experimental_aec && adjust_agc_delta == o.adjust_agc_delta && - aec_dump == o.aec_dump; + aec_dump == o.aec_dump && + tx_agc_target_dbov == o.tx_agc_target_dbov && + tx_agc_digital_compression_gain == o.tx_agc_digital_compression_gain && + tx_agc_limiter == o.tx_agc_limiter && + rx_agc_target_dbov == o.rx_agc_target_dbov && + rx_agc_digital_compression_gain == o.rx_agc_digital_compression_gain && + rx_agc_limiter == o.rx_agc_limiter && + recording_sample_rate == o.recording_sample_rate && + playout_sample_rate == o.playout_sample_rate; } std::string ToString() const { @@ -192,15 +214,27 @@ struct AudioOptions { ost << "AudioOptions {"; ost << ToStringIfSet("aec", echo_cancellation); ost << ToStringIfSet("agc", auto_gain_control); + ost << ToStringIfSet("rx_agc", rx_auto_gain_control); ost << ToStringIfSet("ns", noise_suppression); ost << ToStringIfSet("hf", highpass_filter); ost << ToStringIfSet("swap", stereo_swapping); ost << ToStringIfSet("typing", typing_detection); + ost << ToStringIfSet("comfort_noise", aecm_generate_comfort_noise); ost << ToStringIfSet("conference", conference_mode); ost << ToStringIfSet("agc_delta", adjust_agc_delta); ost << ToStringIfSet("experimental_agc", experimental_agc); ost << ToStringIfSet("experimental_aec", experimental_aec); ost << ToStringIfSet("aec_dump", aec_dump); + ost << ToStringIfSet("tx_agc_target_dbov", tx_agc_target_dbov); + ost << ToStringIfSet("tx_agc_digital_compression_gain", + tx_agc_digital_compression_gain); + ost << ToStringIfSet("tx_agc_limiter", tx_agc_limiter); + ost << ToStringIfSet("rx_agc_target_dbov", rx_agc_target_dbov); + ost << ToStringIfSet("rx_agc_digital_compression_gain", + rx_agc_digital_compression_gain); + ost << ToStringIfSet("rx_agc_limiter", rx_agc_limiter); + ost << ToStringIfSet("recording_sample_rate", recording_sample_rate); + ost << ToStringIfSet("playout_sample_rate", playout_sample_rate); ost << "}"; return ost.str(); } @@ -210,6 +244,8 @@ struct AudioOptions { Settable echo_cancellation; // Audio processing to adjust the sensitivity of the local mic dynamically. Settable auto_gain_control; + // Audio processing to apply gain to the remote audio. + Settable rx_auto_gain_control; // Audio processing to filter out background noise. Settable noise_suppression; // Audio processing to remove background noise of lower frequencies. @@ -218,11 +254,21 @@ struct AudioOptions { Settable stereo_swapping; // Audio processing to detect typing. Settable typing_detection; + Settable aecm_generate_comfort_noise; Settable conference_mode; Settable adjust_agc_delta; Settable experimental_agc; Settable experimental_aec; Settable aec_dump; + // Note that tx_agc_* only applies to non-experimental AGC. + Settable tx_agc_target_dbov; + Settable tx_agc_digital_compression_gain; + Settable tx_agc_limiter; + Settable rx_agc_target_dbov; + Settable rx_agc_digital_compression_gain; + Settable rx_agc_limiter; + Settable recording_sample_rate; + Settable playout_sample_rate; }; // Options that can be applied to a VideoMediaChannel or a VideoMediaEngine. @@ -244,12 +290,13 @@ struct VideoOptions { video_adapt_third.SetFrom(change.video_adapt_third); video_noise_reduction.SetFrom(change.video_noise_reduction); video_three_layers.SetFrom(change.video_three_layers); - video_enable_camera_list.SetFrom(change.video_enable_camera_list); video_one_layer_screencast.SetFrom(change.video_one_layer_screencast); video_high_bitrate.SetFrom(change.video_high_bitrate); video_watermark.SetFrom(change.video_watermark); video_temporal_layer_screencast.SetFrom( change.video_temporal_layer_screencast); + video_temporal_layer_realtime.SetFrom( + change.video_temporal_layer_realtime); video_leaky_bucket.SetFrom(change.video_leaky_bucket); cpu_overuse_detection.SetFrom(change.cpu_overuse_detection); conference_mode.SetFrom(change.conference_mode); @@ -269,11 +316,11 @@ struct VideoOptions { video_adapt_third == o.video_adapt_third && video_noise_reduction == o.video_noise_reduction && video_three_layers == o.video_three_layers && - video_enable_camera_list == o.video_enable_camera_list && video_one_layer_screencast == o.video_one_layer_screencast && video_high_bitrate == o.video_high_bitrate && video_watermark == o.video_watermark && video_temporal_layer_screencast == o.video_temporal_layer_screencast && + video_temporal_layer_realtime == o.video_temporal_layer_realtime && video_leaky_bucket == o.video_leaky_bucket && cpu_overuse_detection == o.cpu_overuse_detection && conference_mode == o.conference_mode && @@ -295,12 +342,13 @@ struct VideoOptions { ost << ToStringIfSet("video adapt third", video_adapt_third); ost << ToStringIfSet("noise reduction", video_noise_reduction); ost << ToStringIfSet("3 layers", video_three_layers); - ost << ToStringIfSet("camera list", video_enable_camera_list); ost << ToStringIfSet("1 layer screencast", video_one_layer_screencast); ost << ToStringIfSet("high bitrate", video_high_bitrate); ost << ToStringIfSet("watermark", video_watermark); ost << ToStringIfSet("video temporal layer screencast", video_temporal_layer_screencast); + ost << ToStringIfSet("video temporal layer realtime", + video_temporal_layer_realtime); ost << ToStringIfSet("leaky bucket", video_leaky_bucket); ost << ToStringIfSet("cpu overuse detection", cpu_overuse_detection); ost << ToStringIfSet("conference mode", conference_mode); @@ -326,8 +374,6 @@ struct VideoOptions { Settable video_noise_reduction; // Experimental: Enable multi layer? Settable video_three_layers; - // Experimental: Enable camera list? - Settable video_enable_camera_list; // Experimental: Enable one layer screencast? Settable video_one_layer_screencast; // Experimental: Enable WebRtc higher bitrate? @@ -336,6 +382,8 @@ struct VideoOptions { Settable video_watermark; // Experimental: Enable WebRTC layered screencast. Settable video_temporal_layer_screencast; + // Experimental: Enable WebRTC temporal layer strategy for realtime video. + Settable video_temporal_layer_realtime; // Enable WebRTC leaky bucket when sending media packets. Settable video_leaky_bucket; // Enable WebRTC Cpu Overuse Detection, which is a new version of the CPU @@ -513,15 +561,68 @@ enum SendFlags { SEND_MICROPHONE }; -struct VoiceSenderInfo { - VoiceSenderInfo() +// The stats information is structured as follows: +// Media are represented by either MediaSenderInfo or MediaReceiverInfo. +// Media contains a vector of SSRC infos that are exclusively used by this +// media. (SSRCs shared between media streams can't be represented.) + +// Information about an SSRC. +// This data may be locally recorded, or received in an RTCP SR or RR. +struct SsrcSenderInfo { + SsrcSenderInfo() : ssrc(0), - bytes_sent(0), + timestamp(0) { + } + uint32 ssrc; + double timestamp; // NTP timestamp, represented as seconds since epoch. +}; + +struct SsrcReceiverInfo { + SsrcReceiverInfo() + : ssrc(0), + timestamp(0) { + } + uint32 ssrc; + double timestamp; +}; + +struct MediaSenderInfo { + MediaSenderInfo() + : bytes_sent(0), packets_sent(0), packets_lost(0), fraction_lost(0.0), + rtt_ms(0) { + } + int64 bytes_sent; + int packets_sent; + int packets_lost; + float fraction_lost; + int rtt_ms; + std::string codec_name; + std::vector local_stats; + std::vector remote_stats; +}; + +struct MediaReceiverInfo { + MediaReceiverInfo() + : bytes_rcvd(0), + packets_rcvd(0), + packets_lost(0), + fraction_lost(0.0) { + } + int64 bytes_rcvd; + int packets_rcvd; + int packets_lost; + float fraction_lost; + std::vector local_stats; + std::vector remote_stats; +}; + +struct VoiceSenderInfo : public MediaSenderInfo { + VoiceSenderInfo() + : ssrc(0), ext_seqnum(0), - rtt_ms(0), jitter_ms(0), audio_level(0), aec_quality_min(0.0), @@ -533,13 +634,7 @@ struct VoiceSenderInfo { } uint32 ssrc; - std::string codec_name; - int64 bytes_sent; - int packets_sent; - int packets_lost; - float fraction_lost; int ext_seqnum; - int rtt_ms; int jitter_ms; int audio_level; float aec_quality_min; @@ -550,13 +645,9 @@ struct VoiceSenderInfo { bool typing_noise_detected; }; -struct VoiceReceiverInfo { +struct VoiceReceiverInfo : public MediaReceiverInfo { VoiceReceiverInfo() : ssrc(0), - bytes_rcvd(0), - packets_rcvd(0), - packets_lost(0), - fraction_lost(0.0), ext_seqnum(0), jitter_ms(0), jitter_buffer_ms(0), @@ -567,10 +658,6 @@ struct VoiceReceiverInfo { } uint32 ssrc; - int64 bytes_rcvd; - int packets_rcvd; - int packets_lost; - float fraction_lost; int ext_seqnum; int jitter_ms; int jitter_buffer_ms; @@ -581,16 +668,11 @@ struct VoiceReceiverInfo { float expand_rate; }; -struct VideoSenderInfo { +struct VideoSenderInfo : public MediaSenderInfo { VideoSenderInfo() - : bytes_sent(0), - packets_sent(0), - packets_cached(0), - packets_lost(0), - fraction_lost(0.0), + : packets_cached(0), firs_rcvd(0), nacks_rcvd(0), - rtt_ms(0), frame_width(0), frame_height(0), framerate_input(0), @@ -602,15 +684,9 @@ struct VideoSenderInfo { std::vector ssrcs; std::vector ssrc_groups; - std::string codec_name; - int64 bytes_sent; - int packets_sent; int packets_cached; - int packets_lost; - float fraction_lost; int firs_rcvd; int nacks_rcvd; - int rtt_ms; int frame_width; int frame_height; int framerate_input; @@ -620,13 +696,9 @@ struct VideoSenderInfo { int adapt_reason; }; -struct VideoReceiverInfo { +struct VideoReceiverInfo : public MediaReceiverInfo { VideoReceiverInfo() - : bytes_rcvd(0), - packets_rcvd(0), - packets_lost(0), - packets_concealed(0), - fraction_lost(0.0), + : packets_concealed(0), firs_sent(0), nacks_sent(0), frame_width(0), @@ -635,17 +707,19 @@ struct VideoReceiverInfo { framerate_decoded(0), framerate_output(0), framerate_render_input(0), - framerate_render_output(0) { + framerate_render_output(0), + decode_ms(0), + max_decode_ms(0), + jitter_buffer_ms(0), + min_playout_delay_ms(0), + render_delay_ms(0), + target_delay_ms(0), + current_delay_ms(0) { } std::vector ssrcs; std::vector ssrc_groups; - int64 bytes_rcvd; - // vector layer_bytes_rcvd; - int packets_rcvd; - int packets_lost; int packets_concealed; - float fraction_lost; int firs_sent; int nacks_sent; int frame_width; @@ -657,31 +731,42 @@ struct VideoReceiverInfo { int framerate_render_input; // Framerate that the renderer reports. int framerate_render_output; + + // All stats below are gathered per-VideoReceiver, but some will be correlated + // across MediaStreamTracks. NOTE(hta): when sinking stats into per-SSRC + // structures, reflect this in the new layout. + + // Current frame decode latency. + int decode_ms; + // Maximum observed frame decode latency. + int max_decode_ms; + // Jitter (network-related) latency. + int jitter_buffer_ms; + // Requested minimum playout latency. + int min_playout_delay_ms; + // Requested latency to account for rendering delay. + int render_delay_ms; + // Target overall delay: network+decode+render, accounting for + // min_playout_delay_ms. + int target_delay_ms; + // Current overall delay, possibly ramping towards target_delay_ms. + int current_delay_ms; }; -struct DataSenderInfo { +struct DataSenderInfo : public MediaSenderInfo { DataSenderInfo() - : ssrc(0), - bytes_sent(0), - packets_sent(0) { + : ssrc(0) { } uint32 ssrc; - std::string codec_name; - int64 bytes_sent; - int packets_sent; }; -struct DataReceiverInfo { +struct DataReceiverInfo : public MediaReceiverInfo { DataReceiverInfo() - : ssrc(0), - bytes_rcvd(0), - packets_rcvd(0) { + : ssrc(0) { } uint32 ssrc; - int64 bytes_rcvd; - int packets_rcvd; }; struct BandwidthEstimationInfo { diff --git a/talk/media/base/videocapturer.cc b/talk/media/base/videocapturer.cc index acab19d661..900a6f1a09 100644 --- a/talk/media/base/videocapturer.cc +++ b/talk/media/base/videocapturer.cc @@ -373,7 +373,7 @@ void VideoCapturer::OnFrameCaptured(VideoCapturer*, // TODO(fbarchard): Avoid scale and convert if muted. // Temporary buffer is scoped here so it will persist until i420_frame.Init() // makes a copy of the frame, converting to I420. - talk_base::scoped_array temp_buffer; + talk_base::scoped_ptr temp_buffer; // YUY2 can be scaled vertically using an ARGB scaler. Aspect ratio is only // a problem on OSX. OSX always converts webcams to YUY2 or UYVY. bool can_scale = diff --git a/talk/media/base/videocommon.cc b/talk/media/base/videocommon.cc index 5dd45d7307..b051d526a3 100644 --- a/talk/media/base/videocommon.cc +++ b/talk/media/base/videocommon.cc @@ -153,12 +153,15 @@ void ComputeScale(int frame_width, int frame_height, int fps, // Compute size to crop video frame to. // If cropped_format_* is 0, return the frame_* size as is. -void ComputeCrop(int cropped_format_width, - int cropped_format_height, +void ComputeCrop(int cropped_format_width, int cropped_format_height, int frame_width, int frame_height, int pixel_width, int pixel_height, int rotation, int* cropped_width, int* cropped_height) { + // Transform screen crop to camera space if rotated. + if (rotation == 90 || rotation == 270) { + std::swap(cropped_format_width, cropped_format_height); + } ASSERT(cropped_format_width >= 0); ASSERT(cropped_format_height >= 0); ASSERT(frame_width > 0); @@ -182,39 +185,26 @@ void ComputeCrop(int cropped_format_width, static_cast(frame_height * pixel_height); float crop_aspect = static_cast(cropped_format_width) / static_cast(cropped_format_height); - int new_frame_width = frame_width; - int new_frame_height = frame_height; - if (rotation == 90 || rotation == 270) { - frame_aspect = 1.0f / frame_aspect; - new_frame_width = frame_height; - new_frame_height = frame_width; - } - // kAspectThresh is the maximum aspect ratio difference that we'll accept - // for cropping. The value 1.33 is based on 4:3 being cropped to 16:9. + // for cropping. The value 1.34 allows cropping from 4:3 to 16:9. // Set to zero to disable cropping entirely. // TODO(fbarchard): crop to multiple of 16 width for better performance. - const float kAspectThresh = 16.f / 9.f / (4.f / 3.f) + 0.01f; // 1.33 + const float kAspectThresh = 1.34f; // Wide aspect - crop horizontally if (frame_aspect > crop_aspect && frame_aspect < crop_aspect * kAspectThresh) { // Round width down to multiple of 4 to avoid odd chroma width. // Width a multiple of 4 allows a half size image to have chroma channel - // that avoids rounding errors. lmi and webrtc have odd width limitations. - new_frame_width = static_cast((crop_aspect * frame_height * + // that avoids rounding errors. + frame_width = static_cast((crop_aspect * frame_height * pixel_height) / pixel_width + 0.5f) & ~3; - } else if (crop_aspect > frame_aspect && - crop_aspect < frame_aspect * kAspectThresh) { - new_frame_height = static_cast((frame_width * pixel_width) / + } else if (frame_aspect < crop_aspect && + frame_aspect > crop_aspect / kAspectThresh) { + frame_height = static_cast((frame_width * pixel_width) / (crop_aspect * pixel_height) + 0.5f) & ~1; } - - *cropped_width = new_frame_width; - *cropped_height = new_frame_height; - if (rotation == 90 || rotation == 270) { - *cropped_width = new_frame_height; - *cropped_height = new_frame_width; - } + *cropped_width = frame_width; + *cropped_height = frame_height; } // Compute the frame size that makes pixels square pixel aspect ratio. diff --git a/talk/media/base/videocommon_unittest.cc b/talk/media/base/videocommon_unittest.cc index 9122843735..455a47b79a 100644 --- a/talk/media/base/videocommon_unittest.cc +++ b/talk/media/base/videocommon_unittest.cc @@ -276,6 +276,15 @@ TEST(VideoCommonTest, TestComputeCrop) { EXPECT_EQ(640, cropped_width); EXPECT_EQ(480, cropped_height); + // Request 9:16 from VGA rotated (portrait). Expect crop. + ComputeCrop(360, 640, // Crop size 9:16 + 640, 480, // Frame is 3:4 portrait + 1, 1, // Normal 1:1 pixels + 90, + &cropped_width, &cropped_height); + EXPECT_EQ(640, cropped_width); + EXPECT_EQ(360, cropped_height); + // Cropped size 0x0. Expect no cropping. // This is used when adding multiple capturers ComputeCrop(0, 0, // Crop size 0x0 diff --git a/talk/media/base/videoframe_unittest.h b/talk/media/base/videoframe_unittest.h index f70e5675b0..361c195c2c 100644 --- a/talk/media/base/videoframe_unittest.h +++ b/talk/media/base/videoframe_unittest.h @@ -157,7 +157,7 @@ class VideoFrameTest : public testing::Test { prefix.c_str(), frame.GetWidth(), frame.GetHeight()); size_t out_size = cricket::VideoFrame::SizeOf(frame.GetWidth(), frame.GetHeight()); - talk_base::scoped_array out(new uint8[out_size]); + talk_base::scoped_ptr out(new uint8[out_size]); frame.CopyToBuffer(out.get(), out_size); return DumpSample(filename, out.get(), out_size); } @@ -514,7 +514,7 @@ class VideoFrameTest : public testing::Test { T frame1, frame2; ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); size_t buf_size = kWidth * kHeight * 2; - talk_base::scoped_array buf(new uint8[buf_size + kAlignment]); + talk_base::scoped_ptr buf(new uint8[buf_size + kAlignment]); uint8* y = ALIGNP(buf.get(), kAlignment); uint8* u = y + kWidth * kHeight; uint8* v = u + (kWidth / 2) * kHeight; @@ -535,7 +535,7 @@ class VideoFrameTest : public testing::Test { T frame1, frame2; ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); size_t buf_size = kWidth * kHeight * 2; - talk_base::scoped_array buf(new uint8[buf_size + kAlignment]); + talk_base::scoped_ptr buf(new uint8[buf_size + kAlignment]); uint8* yuy2 = ALIGNP(buf.get(), kAlignment); EXPECT_EQ(0, libyuv::I420ToYUY2(frame1.GetYPlane(), frame1.GetYPitch(), frame1.GetUPlane(), frame1.GetUPitch(), @@ -552,7 +552,7 @@ class VideoFrameTest : public testing::Test { T frame1, frame2; ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); size_t buf_size = kWidth * kHeight * 2; - talk_base::scoped_array buf(new uint8[buf_size + kAlignment + 1]); + talk_base::scoped_ptr buf(new uint8[buf_size + kAlignment + 1]); uint8* yuy2 = ALIGNP(buf.get(), kAlignment) + 1; EXPECT_EQ(0, libyuv::I420ToYUY2(frame1.GetYPlane(), frame1.GetYPitch(), frame1.GetUPlane(), frame1.GetUPitch(), @@ -718,7 +718,7 @@ class VideoFrameTest : public testing::Test { void ConstructRGB565() { T frame1, frame2; size_t out_size = kWidth * kHeight * 2; - talk_base::scoped_array outbuf(new uint8[out_size + kAlignment]); + talk_base::scoped_ptr outbuf(new uint8[out_size + kAlignment]); uint8 *out = ALIGNP(outbuf.get(), kAlignment); T frame; ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); @@ -734,7 +734,7 @@ class VideoFrameTest : public testing::Test { void ConstructARGB1555() { T frame1, frame2; size_t out_size = kWidth * kHeight * 2; - talk_base::scoped_array outbuf(new uint8[out_size + kAlignment]); + talk_base::scoped_ptr outbuf(new uint8[out_size + kAlignment]); uint8 *out = ALIGNP(outbuf.get(), kAlignment); T frame; ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); @@ -750,7 +750,7 @@ class VideoFrameTest : public testing::Test { void ConstructARGB4444() { T frame1, frame2; size_t out_size = kWidth * kHeight * 2; - talk_base::scoped_array outbuf(new uint8[out_size + kAlignment]); + talk_base::scoped_ptr outbuf(new uint8[out_size + kAlignment]); uint8 *out = ALIGNP(outbuf.get(), kAlignment); T frame; ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); @@ -769,7 +769,7 @@ class VideoFrameTest : public testing::Test { #define TEST_BYR(NAME, BAYER) \ void NAME() { \ size_t bayer_size = kWidth * kHeight; \ - talk_base::scoped_array bayerbuf(new uint8[ \ + talk_base::scoped_ptr bayerbuf(new uint8[ \ bayer_size + kAlignment]); \ uint8 *bayer = ALIGNP(bayerbuf.get(), kAlignment); \ T frame1, frame2; \ @@ -994,7 +994,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ } // Convert back to ARGB. size_t out_size = 4; - talk_base::scoped_array outbuf(new uint8[out_size + kAlignment]); + talk_base::scoped_ptr outbuf(new uint8[out_size + kAlignment]); uint8 *out = ALIGNP(outbuf.get(), kAlignment); EXPECT_EQ(out_size, frame.ConvertToRgbBuffer(cricket::FOURCC_ARGB, @@ -1031,7 +1031,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ } // Convert back to ARGB size_t out_size = 10 * 4; - talk_base::scoped_array outbuf(new uint8[out_size + kAlignment]); + talk_base::scoped_ptr outbuf(new uint8[out_size + kAlignment]); uint8 *out = ALIGNP(outbuf.get(), kAlignment); EXPECT_EQ(out_size, frame.ConvertToRgbBuffer(cricket::FOURCC_ARGB, @@ -1162,7 +1162,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ // Allocate a buffer with end page aligned. const int kPadToHeapSized = 16 * 1024 * 1024; - talk_base::scoped_array page_buffer( + talk_base::scoped_ptr page_buffer( new uint8[((data_size + kPadToHeapSized + 4095) & ~4095)]); uint8* data_ptr = page_buffer.get(); if (!data_ptr) { @@ -1427,7 +1427,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ int astride = kWidth * bpp + rowpad; size_t out_size = astride * kHeight; - talk_base::scoped_array outbuf(new uint8[out_size + kAlignment + 1]); + talk_base::scoped_ptr outbuf(new uint8[out_size + kAlignment + 1]); memset(outbuf.get(), 0, out_size + kAlignment + 1); uint8 *outtop = ALIGNP(outbuf.get(), kAlignment); uint8 *out = outtop; @@ -1841,7 +1841,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ void ConvertToI422Buffer() { T frame1, frame2; size_t out_size = kWidth * kHeight * 2; - talk_base::scoped_array buf(new uint8[out_size + kAlignment]); + talk_base::scoped_ptr buf(new uint8[out_size + kAlignment]); uint8* y = ALIGNP(buf.get(), kAlignment); uint8* u = y + kWidth * kHeight; uint8* v = u + (kWidth / 2) * kHeight; @@ -1865,7 +1865,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ #define TEST_TOBYR(NAME, BAYER) \ void NAME() { \ size_t bayer_size = kWidth * kHeight; \ - talk_base::scoped_array bayerbuf(new uint8[ \ + talk_base::scoped_ptr bayerbuf(new uint8[ \ bayer_size + kAlignment]); \ uint8 *bayer = ALIGNP(bayerbuf.get(), kAlignment); \ T frame; \ @@ -1894,7 +1894,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ } \ void NAME##Unaligned() { \ size_t bayer_size = kWidth * kHeight; \ - talk_base::scoped_array bayerbuf(new uint8[ \ + talk_base::scoped_ptr bayerbuf(new uint8[ \ bayer_size + 1 + kAlignment]); \ uint8 *bayer = ALIGNP(bayerbuf.get(), kAlignment) + 1; \ T frame; \ @@ -1931,7 +1931,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ #define TEST_BYRTORGB(NAME, BAYER) \ void NAME() { \ size_t bayer_size = kWidth * kHeight; \ - talk_base::scoped_array bayerbuf(new uint8[ \ + talk_base::scoped_ptr bayerbuf(new uint8[ \ bayer_size + kAlignment]); \ uint8 *bayer1 = ALIGNP(bayerbuf.get(), kAlignment); \ for (int i = 0; i < kWidth * kHeight; ++i) { \ @@ -1947,7 +1947,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ kWidth * 4, \ kWidth, kHeight); \ } \ - talk_base::scoped_array bayer2buf(new uint8[ \ + talk_base::scoped_ptr bayer2buf(new uint8[ \ bayer_size + kAlignment]); \ uint8 *bayer2 = ALIGNP(bayer2buf.get(), kAlignment); \ libyuv::ARGBToBayer##BAYER(reinterpret_cast(ms->GetBuffer()), \ @@ -2010,7 +2010,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ ASSERT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_I420, kWidth, kHeight, &frame)); size_t out_size = kWidth * kHeight * 3 / 2; - talk_base::scoped_array out(new uint8[out_size]); + talk_base::scoped_ptr out(new uint8[out_size]); for (int i = 0; i < repeat_; ++i) { EXPECT_EQ(out_size, frame.CopyToBuffer(out.get(), out_size)); } @@ -2056,7 +2056,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ void CopyToBuffer1Pixel() { size_t out_size = 3; - talk_base::scoped_array out(new uint8[out_size + 1]); + talk_base::scoped_ptr out(new uint8[out_size + 1]); memset(out.get(), 0xfb, out_size + 1); // Fill buffer uint8 pixel[3] = { 1, 2, 3 }; T frame; diff --git a/talk/media/devices/carbonvideorenderer.h b/talk/media/devices/carbonvideorenderer.h index e09118613b..6c52fcfc69 100644 --- a/talk/media/devices/carbonvideorenderer.h +++ b/talk/media/devices/carbonvideorenderer.h @@ -57,7 +57,7 @@ class CarbonVideoRenderer : public VideoRenderer { static OSStatus DrawEventHandler(EventHandlerCallRef handler, EventRef event, void* data); - talk_base::scoped_array image_; + talk_base::scoped_ptr image_; talk_base::CriticalSection image_crit_; int image_width_; int image_height_; diff --git a/talk/media/devices/gdivideorenderer.cc b/talk/media/devices/gdivideorenderer.cc index c8024b75a9..9633eb6d8e 100755 --- a/talk/media/devices/gdivideorenderer.cc +++ b/talk/media/devices/gdivideorenderer.cc @@ -98,7 +98,7 @@ class GdiVideoRenderer::VideoWindow : public talk_base::Win32Window { void OnRenderFrame(const VideoFrame* frame); BITMAPINFO bmi_; - talk_base::scoped_array image_; + talk_base::scoped_ptr image_; talk_base::scoped_ptr window_thread_; // The initial position of the window. int initial_x_; diff --git a/talk/media/devices/gtkvideorenderer.h b/talk/media/devices/gtkvideorenderer.h index 6276b51c80..744c19f490 100755 --- a/talk/media/devices/gtkvideorenderer.h +++ b/talk/media/devices/gtkvideorenderer.h @@ -56,7 +56,7 @@ class GtkVideoRenderer : public VideoRenderer { // Check if the window has been closed. bool IsClosed() const; - talk_base::scoped_array image_; + talk_base::scoped_ptr image_; GtkWidget* window_; GtkWidget* draw_area_; // The initial position of the window. diff --git a/talk/media/devices/macdevicemanager.cc b/talk/media/devices/macdevicemanager.cc index 10d85a0f73..e92408e416 100644 --- a/talk/media/devices/macdevicemanager.cc +++ b/talk/media/devices/macdevicemanager.cc @@ -120,7 +120,7 @@ static bool GetAudioDeviceIDs(bool input, } size_t num_devices = propsize / sizeof(AudioDeviceID); - talk_base::scoped_array device_ids( + talk_base::scoped_ptr device_ids( new AudioDeviceID[num_devices]); err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, diff --git a/talk/media/sctp/sctpdataengine.h b/talk/media/sctp/sctpdataengine.h index cadf78c205..d09b152b47 100644 --- a/talk/media/sctp/sctpdataengine.h +++ b/talk/media/sctp/sctpdataengine.h @@ -56,7 +56,7 @@ struct socket; namespace cricket { // The highest stream ID (Sid) that SCTP allows, and the number of streams we // tell SCTP we're going to use. -const uint32 kMaxSctpSid = USHRT_MAX; +const uint32 kMaxSctpSid = 1023; // A DataEngine that interacts with usrsctp. // diff --git a/talk/media/sctp/sctputils.cc b/talk/media/sctp/sctputils.cc index c33c64e06f..4073905de7 100644 --- a/talk/media/sctp/sctputils.cc +++ b/talk/media/sctp/sctputils.cc @@ -35,7 +35,7 @@ namespace cricket { // Format defined at -// http://tools.ietf.org/html/draft-jesup-rtcweb-data-protocol-04 +// http://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-00#section-6.1 static const uint8 DATA_CHANNEL_OPEN_MESSAGE_TYPE = 0x03; @@ -73,16 +73,17 @@ bool ParseDataChannelOpenMessage( LOG(LS_WARNING) << "Could not read OPEN message channel type."; return false; } - uint16 reliability_param; - if (!buffer.ReadUInt16(&reliability_param)) { - LOG(LS_WARNING) << "Could not read OPEN message reliabilility param."; - return false; - } + uint16 priority; if (!buffer.ReadUInt16(&priority)) { LOG(LS_WARNING) << "Could not read OPEN message reliabilility prioirty."; return false; } + uint32 reliability_param; + if (!buffer.ReadUInt32(&reliability_param)) { + LOG(LS_WARNING) << "Could not read OPEN message reliabilility param."; + return false; + } uint16 label_length; if (!buffer.ReadUInt16(&label_length)) { LOG(LS_WARNING) << "Could not read OPEN message label length."; @@ -116,10 +117,11 @@ bool ParseDataChannelOpenMessage( case DCOMCT_ORDERED_PARTIAL_RTXS: case DCOMCT_UNORDERED_PARTIAL_RTXS: config->maxRetransmits = reliability_param; - + break; case DCOMCT_ORDERED_PARTIAL_TIME: case DCOMCT_UNORDERED_PARTIAL_TIME: config->maxRetransmitTime = reliability_param; + break; } return true; @@ -130,11 +132,9 @@ bool WriteDataChannelOpenMessage( const webrtc::DataChannelInit& config, talk_base::Buffer* payload) { // Format defined at - // http://tools.ietf.org/html/draft-jesup-rtcweb-data-protocol-04 - // TODO(pthatcher) - + // http://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-00#section-6.1 uint8 channel_type = 0; - uint16 reliability_param = 0; + uint32 reliability_param = 0; uint16 priority = 0; if (config.ordered) { if (config.maxRetransmits > -1) { @@ -163,8 +163,8 @@ bool WriteDataChannelOpenMessage( talk_base::ByteBuffer::ORDER_NETWORK); buffer.WriteUInt8(DATA_CHANNEL_OPEN_MESSAGE_TYPE); buffer.WriteUInt8(channel_type); - buffer.WriteUInt16(reliability_param); buffer.WriteUInt16(priority); + buffer.WriteUInt32(reliability_param); buffer.WriteUInt16(static_cast(label.length())); buffer.WriteUInt16(static_cast(config.protocol.length())); buffer.WriteString(label); diff --git a/talk/media/sctp/sctputils_unittest.cc b/talk/media/sctp/sctputils_unittest.cc index 1cc9a709f9..70f67b8b58 100644 --- a/talk/media/sctp/sctputils_unittest.cc +++ b/talk/media/sctp/sctputils_unittest.cc @@ -37,7 +37,7 @@ class SctpUtilsTest : public testing::Test { const webrtc::DataChannelInit& config) { uint8 message_type; uint8 channel_type; - uint16 reliability; + uint32 reliability; uint16 priority; uint16 label_length; uint16 protocol_length; @@ -57,15 +57,15 @@ class SctpUtilsTest : public testing::Test { channel_type); } - ASSERT_TRUE(buffer.ReadUInt16(&reliability)); + ASSERT_TRUE(buffer.ReadUInt16(&priority)); + + ASSERT_TRUE(buffer.ReadUInt32(&reliability)); if (config.maxRetransmits > -1 || config.maxRetransmitTime > -1) { EXPECT_EQ(config.maxRetransmits > -1 ? config.maxRetransmits : config.maxRetransmitTime, - reliability); + static_cast(reliability)); } - ASSERT_TRUE(buffer.ReadUInt16(&priority)); - ASSERT_TRUE(buffer.ReadUInt16(&label_length)); ASSERT_TRUE(buffer.ReadUInt16(&protocol_length)); EXPECT_EQ(label.size(), label_length); @@ -86,13 +86,14 @@ TEST_F(SctpUtilsTest, WriteParseMessageWithOrderedReliable) { config.protocol = "y"; talk_base::Buffer packet; - ASSERT(cricket::WriteDataChannelOpenMessage(input_label, config, &packet)); + ASSERT_TRUE( + cricket::WriteDataChannelOpenMessage(input_label, config, &packet)); VerifyOpenMessageFormat(packet, input_label, config); std::string output_label; webrtc::DataChannelInit output_config; - ASSERT(cricket::ParseDataChannelOpenMessage( + ASSERT_TRUE(cricket::ParseDataChannelOpenMessage( packet, &output_label, &output_config)); EXPECT_EQ(input_label, output_label); @@ -110,19 +111,21 @@ TEST_F(SctpUtilsTest, WriteParseOpenMessageWithMaxRetransmitTime) { config.protocol = "y"; talk_base::Buffer packet; - ASSERT(cricket::WriteDataChannelOpenMessage(input_label, config, &packet)); + ASSERT_TRUE( + cricket::WriteDataChannelOpenMessage(input_label, config, &packet)); VerifyOpenMessageFormat(packet, input_label, config); std::string output_label; webrtc::DataChannelInit output_config; - ASSERT(cricket::ParseDataChannelOpenMessage( + ASSERT_TRUE(cricket::ParseDataChannelOpenMessage( packet, &output_label, &output_config)); EXPECT_EQ(input_label, output_label); EXPECT_EQ(config.protocol, output_config.protocol); EXPECT_EQ(config.ordered, output_config.ordered); EXPECT_EQ(config.maxRetransmitTime, output_config.maxRetransmitTime); + EXPECT_EQ(-1, output_config.maxRetransmits); } TEST_F(SctpUtilsTest, WriteParseOpenMessageWithMaxRetransmits) { @@ -132,17 +135,19 @@ TEST_F(SctpUtilsTest, WriteParseOpenMessageWithMaxRetransmits) { config.protocol = "y"; talk_base::Buffer packet; - ASSERT(cricket::WriteDataChannelOpenMessage(input_label, config, &packet)); + ASSERT_TRUE( + cricket::WriteDataChannelOpenMessage(input_label, config, &packet)); VerifyOpenMessageFormat(packet, input_label, config); std::string output_label; webrtc::DataChannelInit output_config; - ASSERT(cricket::ParseDataChannelOpenMessage( + ASSERT_TRUE(cricket::ParseDataChannelOpenMessage( packet, &output_label, &output_config)); EXPECT_EQ(input_label, output_label); EXPECT_EQ(config.protocol, output_config.protocol); EXPECT_EQ(config.ordered, output_config.ordered); EXPECT_EQ(config.maxRetransmits, output_config.maxRetransmits); + EXPECT_EQ(-1, output_config.maxRetransmitTime); } diff --git a/talk/media/webrtc/fakewebrtcvideoengine.h b/talk/media/webrtc/fakewebrtcvideoengine.h index 31de172a21..0b687287de 100644 --- a/talk/media/webrtc/fakewebrtcvideoengine.h +++ b/talk/media/webrtc/fakewebrtcvideoengine.h @@ -1046,7 +1046,14 @@ class FakeWebRtcVideoEngine return 0; } WEBRTC_STUB(EnableColorEnhancement, (const int, const bool)); - +#ifdef USE_WEBRTC_DEV_BRANCH + WEBRTC_VOID_STUB(RegisterPreEncodeCallback, + (int, webrtc::I420FrameCallback*)); + WEBRTC_VOID_STUB(DeRegisterPreEncodeCallback, (int)); + WEBRTC_VOID_STUB(RegisterPreRenderCallback, + (int, webrtc::I420FrameCallback*)); + WEBRTC_VOID_STUB(DeRegisterPreRenderCallback, (int)); +#endif // webrtc::ViEExternalCodec WEBRTC_FUNC(RegisterExternalSendCodec, (const int channel, const unsigned char pl_type, webrtc::VideoEncoder*, diff --git a/talk/media/webrtc/fakewebrtcvoiceengine.h b/talk/media/webrtc/fakewebrtcvoiceengine.h index c3cd786009..9696518bcf 100644 --- a/talk/media/webrtc/fakewebrtcvoiceengine.h +++ b/talk/media/webrtc/fakewebrtcvoiceengine.h @@ -87,6 +87,8 @@ class FakeWebRtcVoiceEngine fec(false), nack(false), media_processor_registered(false), + rx_agc_enabled(false), + rx_agc_mode(webrtc::kAgcDefault), cn8_type(13), cn16_type(105), dtmf_type(106), @@ -95,6 +97,7 @@ class FakeWebRtcVoiceEngine send_ssrc(0), level_header_ext_(-1) { memset(&send_codec, 0, sizeof(send_codec)); + memset(&rx_agc_config, 0, sizeof(rx_agc_config)); } bool external_transport; bool send; @@ -107,6 +110,9 @@ class FakeWebRtcVoiceEngine bool fec; bool nack; bool media_processor_registered; + bool rx_agc_enabled; + webrtc::AgcModes rx_agc_mode; + webrtc::AgcConfig rx_agc_config; int cn8_type; int cn16_type; int dtmf_type; @@ -144,6 +150,8 @@ class FakeWebRtcVoiceEngine send_fail_channel_(-1), fail_start_recording_microphone_(false), recording_microphone_(false), + recording_sample_rate_(-1), + playout_sample_rate_(-1), media_processor_(NULL) { memset(&agc_config_, 0, sizeof(agc_config_)); } @@ -584,10 +592,22 @@ class FakeWebRtcVoiceEngine WEBRTC_STUB(AudioDeviceControl, (unsigned int, unsigned int, unsigned int)); WEBRTC_STUB(SetLoudspeakerStatus, (bool enable)); WEBRTC_STUB(GetLoudspeakerStatus, (bool& enabled)); - WEBRTC_STUB(SetRecordingSampleRate, (unsigned int samples_per_sec)); - WEBRTC_STUB_CONST(RecordingSampleRate, (unsigned int* samples_per_sec)); - WEBRTC_STUB(SetPlayoutSampleRate, (unsigned int samples_per_sec)); - WEBRTC_STUB_CONST(PlayoutSampleRate, (unsigned int* samples_per_sec)); + WEBRTC_FUNC(SetRecordingSampleRate, (unsigned int samples_per_sec)) { + recording_sample_rate_ = samples_per_sec; + return 0; + } + WEBRTC_FUNC_CONST(RecordingSampleRate, (unsigned int* samples_per_sec)) { + *samples_per_sec = recording_sample_rate_; + return 0; + } + WEBRTC_FUNC(SetPlayoutSampleRate, (unsigned int samples_per_sec)) { + playout_sample_rate_ = samples_per_sec; + return 0; + } + WEBRTC_FUNC_CONST(PlayoutSampleRate, (unsigned int* samples_per_sec)) { + *samples_per_sec = playout_sample_rate_; + return 0; + } WEBRTC_STUB(EnableBuiltInAEC, (bool enable)); virtual bool BuiltInAECIsEnabled() const { return true; } @@ -841,12 +861,27 @@ class FakeWebRtcVoiceEngine WEBRTC_STUB(SetRxNsStatus, (int channel, bool enable, webrtc::NsModes mode)); WEBRTC_STUB(GetRxNsStatus, (int channel, bool& enabled, webrtc::NsModes& mode)); - WEBRTC_STUB(SetRxAgcStatus, (int channel, bool enable, - webrtc::AgcModes mode)); - WEBRTC_STUB(GetRxAgcStatus, (int channel, bool& enabled, - webrtc::AgcModes& mode)); - WEBRTC_STUB(SetRxAgcConfig, (int channel, webrtc::AgcConfig config)); - WEBRTC_STUB(GetRxAgcConfig, (int channel, webrtc::AgcConfig& config)); + WEBRTC_FUNC(SetRxAgcStatus, (int channel, bool enable, + webrtc::AgcModes mode)) { + channels_[channel]->rx_agc_enabled = enable; + channels_[channel]->rx_agc_mode = mode; + return 0; + } + WEBRTC_FUNC(GetRxAgcStatus, (int channel, bool& enabled, + webrtc::AgcModes& mode)) { + enabled = channels_[channel]->rx_agc_enabled; + mode = channels_[channel]->rx_agc_mode; + return 0; + } + + WEBRTC_FUNC(SetRxAgcConfig, (int channel, webrtc::AgcConfig config)) { + channels_[channel]->rx_agc_config = config; + return 0; + } + WEBRTC_FUNC(GetRxAgcConfig, (int channel, webrtc::AgcConfig& config)) { + config = channels_[channel]->rx_agc_config; + return 0; + } WEBRTC_STUB(RegisterRxVadObserver, (int, webrtc::VoERxVadCallback&)); WEBRTC_STUB(DeRegisterRxVadObserver, (int channel)); @@ -996,6 +1031,8 @@ class FakeWebRtcVoiceEngine int send_fail_channel_; bool fail_start_recording_microphone_; bool recording_microphone_; + int recording_sample_rate_; + int playout_sample_rate_; DtmfInfo dtmf_info_; webrtc::VoEMediaProcess* media_processor_; }; diff --git a/talk/media/webrtc/webrtcvideoengine.cc b/talk/media/webrtc/webrtcvideoengine.cc index 3f4667dce4..05f8b2b3c0 100644 --- a/talk/media/webrtc/webrtcvideoengine.cc +++ b/talk/media/webrtc/webrtcvideoengine.cc @@ -309,6 +309,13 @@ class WebRtcDecoderObserver : public webrtc::ViEDecoderObserver { : video_channel_(video_channel), framerate_(0), bitrate_(0), + decode_ms_(0), + max_decode_ms_(0), + current_delay_ms_(0), + target_delay_ms_(0), + jitter_buffer_ms_(0), + min_playout_delay_ms_(0), + render_delay_ms_(0), firs_requested_(0) { } @@ -323,23 +330,42 @@ class WebRtcDecoderObserver : public webrtc::ViEDecoderObserver { framerate_ = framerate; bitrate_ = bitrate; } + + virtual void DecoderTiming(int decode_ms, + int max_decode_ms, + int current_delay_ms, + int target_delay_ms, + int jitter_buffer_ms, + int min_playout_delay_ms, + int render_delay_ms) { + talk_base::CritScope cs(&crit_); + decode_ms_ = decode_ms; + max_decode_ms_ = max_decode_ms; + current_delay_ms_ = current_delay_ms; + target_delay_ms_ = target_delay_ms; + jitter_buffer_ms_ = jitter_buffer_ms; + min_playout_delay_ms_ = min_playout_delay_ms; + render_delay_ms_ = render_delay_ms; + } + virtual void RequestNewKeyFrame(const int videoChannel) { talk_base::CritScope cs(&crit_); ASSERT(video_channel_ == videoChannel); ++firs_requested_; } - int framerate() const { + // Populate |rinfo| based on previously-set data in |*this|. + void ExportTo(VideoReceiverInfo* rinfo) { talk_base::CritScope cs(&crit_); - return framerate_; - } - int bitrate() const { - talk_base::CritScope cs(&crit_); - return bitrate_; - } - int firs_requested() const { - talk_base::CritScope cs(&crit_); - return firs_requested_; + rinfo->firs_sent = firs_requested_; + rinfo->framerate_rcvd = framerate_; + rinfo->decode_ms = decode_ms_; + rinfo->max_decode_ms = max_decode_ms_; + rinfo->current_delay_ms = current_delay_ms_; + rinfo->target_delay_ms = target_delay_ms_; + rinfo->jitter_buffer_ms = jitter_buffer_ms_; + rinfo->min_playout_delay_ms = min_playout_delay_ms_; + rinfo->render_delay_ms = render_delay_ms_; } private: @@ -347,6 +373,13 @@ class WebRtcDecoderObserver : public webrtc::ViEDecoderObserver { int video_channel_; int framerate_; int bitrate_; + int decode_ms_; + int max_decode_ms_; + int current_delay_ms_; + int target_delay_ms_; + int jitter_buffer_ms_; + int min_playout_delay_ms_; + int render_delay_ms_; int firs_requested_; }; @@ -2303,14 +2336,13 @@ bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) { rinfo.packets_lost = -1; rinfo.packets_concealed = -1; rinfo.fraction_lost = -1; // from SentRTCP - rinfo.firs_sent = channel->decoder_observer()->firs_requested(); rinfo.nacks_sent = -1; rinfo.frame_width = channel->render_adapter()->width(); rinfo.frame_height = channel->render_adapter()->height(); - rinfo.framerate_rcvd = channel->decoder_observer()->framerate(); int fps = channel->render_adapter()->framerate(); rinfo.framerate_decoded = fps; rinfo.framerate_output = fps; + channel->decoder_observer()->ExportTo(&rinfo); // Get sent RTCP statistics. uint16 s_fraction_lost; diff --git a/talk/media/webrtc/webrtcvideoengine_unittest.cc b/talk/media/webrtc/webrtcvideoengine_unittest.cc index 9537673974..9fbbbe4e38 100644 --- a/talk/media/webrtc/webrtcvideoengine_unittest.cc +++ b/talk/media/webrtc/webrtcvideoengine_unittest.cc @@ -118,7 +118,7 @@ class WebRtcVideoEngineTestFake : public testing::Test, } cricket::WebRtcVideoFrame frame; size_t size = width * height * 3 / 2; // I420 - talk_base::scoped_array pixel(new uint8[size]); + talk_base::scoped_ptr pixel(new uint8[size]); if (!frame.Init(cricket::FOURCC_I420, width, height, width, height, pixel.get(), size, 1, 1, 0, 0, 0)) { @@ -138,7 +138,7 @@ class WebRtcVideoEngineTestFake : public testing::Test, } cricket::WebRtcVideoFrame frame; size_t size = width * height * 3 / 2; // I420 - talk_base::scoped_array pixel(new uint8[size]); + talk_base::scoped_ptr pixel(new uint8[size]); if (!frame.Init(cricket::FOURCC_I420, width, height, width, height, pixel.get(), size, 1, 1, 0, timestamp, 0)) { @@ -1161,7 +1161,8 @@ TEST_F(WebRtcVideoEngineTestFake, SetOptionsWithDenoising) { } -TEST_F(WebRtcVideoEngineTestFake, SendReceiveBitratesStats) { +// Disabled since its flaky: b/11288120 +TEST_F(WebRtcVideoEngineTestFake, DISABLED_SendReceiveBitratesStats) { EXPECT_TRUE(SetupEngine()); cricket::VideoOptions options; options.conference_mode.Set(true); diff --git a/talk/media/webrtc/webrtcvideoframe.h b/talk/media/webrtc/webrtcvideoframe.h index 18475a6977..e02323404c 100644 --- a/talk/media/webrtc/webrtcvideoframe.h +++ b/talk/media/webrtc/webrtcvideoframe.h @@ -55,7 +55,7 @@ class FrameBuffer { const webrtc::VideoFrame* frame() const; private: - talk_base::scoped_array data_; + talk_base::scoped_ptr data_; size_t length_; webrtc::VideoFrame video_frame_; }; diff --git a/talk/media/webrtc/webrtcvideoframe_unittest.cc b/talk/media/webrtc/webrtcvideoframe_unittest.cc index 2f0decb289..ebc345e65c 100644 --- a/talk/media/webrtc/webrtcvideoframe_unittest.cc +++ b/talk/media/webrtc/webrtcvideoframe_unittest.cc @@ -53,7 +53,7 @@ class WebRtcVideoFrameTest : public VideoFrameTest { captured_frame.height = frame_height; captured_frame.data_size = (frame_width * frame_height) + ((frame_width + 1) / 2) * ((frame_height + 1) / 2) * 2; - talk_base::scoped_array captured_frame_buffer( + talk_base::scoped_ptr captured_frame_buffer( new uint8[captured_frame.data_size]); captured_frame.data = captured_frame_buffer.get(); diff --git a/talk/media/webrtc/webrtcvie.h b/talk/media/webrtc/webrtcvie.h index 9550962e5d..50cc5d70ed 100644 --- a/talk/media/webrtc/webrtcvie.h +++ b/talk/media/webrtc/webrtcvie.h @@ -45,6 +45,7 @@ #include "webrtc/video_engine/include/vie_network.h" #include "webrtc/video_engine/include/vie_render.h" #include "webrtc/video_engine/include/vie_rtp_rtcp.h" +#include "webrtc/video_engine/new_include/frame_callback.h" namespace cricket { diff --git a/talk/media/webrtc/webrtcvoiceengine.cc b/talk/media/webrtc/webrtcvoiceengine.cc index 83cbdaf573..121dd46247 100644 --- a/talk/media/webrtc/webrtcvoiceengine.cc +++ b/talk/media/webrtc/webrtcvoiceengine.cc @@ -528,7 +528,7 @@ bool WebRtcVoiceEngine::InitInternal() { // Save the default AGC configuration settings. This must happen before // calling SetOptions or the default will be overwritten. if (voe_wrapper_->processing()->GetAgcConfig(default_agc_config_) == -1) { - LOG_RTCERR0(GetAGCConfig); + LOG_RTCERR0(GetAgcConfig); return false; } @@ -686,6 +686,10 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { webrtc::AgcModes agc_mode = webrtc::kAgcAdaptiveAnalog; webrtc::NsModes ns_mode = webrtc::kNsHighSuppression; bool aecm_comfort_noise = false; + if (options.aecm_generate_comfort_noise.Get(&aecm_comfort_noise)) { + LOG(LS_VERBOSE) << "Comfort noise explicitly set to " + << aecm_comfort_noise << " (default is false)."; + } #if defined(IOS) // On iOS, VPIO provides built-in EC and AGC. @@ -713,6 +717,9 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { if (voep->SetEcStatus(echo_cancellation, ec_mode) == -1) { LOG_RTCERR2(SetEcStatus, echo_cancellation, ec_mode); return false; + } else { + LOG(LS_VERBOSE) << "Echo control set to " << echo_cancellation + << " with mode " << ec_mode; } #if !defined(ANDROID) // TODO(ajm): Remove the error return on Android from webrtc. @@ -734,6 +741,38 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { if (voep->SetAgcStatus(auto_gain_control, agc_mode) == -1) { LOG_RTCERR2(SetAgcStatus, auto_gain_control, agc_mode); return false; + } else { + LOG(LS_VERBOSE) << "Auto gain set to " << auto_gain_control + << " with mode " << agc_mode; + } + } + + if (options.tx_agc_target_dbov.IsSet() || + options.tx_agc_digital_compression_gain.IsSet() || + options.tx_agc_limiter.IsSet()) { + // Override default_agc_config_. Generally, an unset option means "leave + // the VoE bits alone" in this function, so we want whatever is set to be + // stored as the new "default". If we didn't, then setting e.g. + // tx_agc_target_dbov would reset digital compression gain and limiter + // settings. + // Also, if we don't update default_agc_config_, then adjust_agc_delta + // would be an offset from the original values, and not whatever was set + // explicitly. + default_agc_config_.targetLeveldBOv = + options.tx_agc_target_dbov.GetWithDefaultIfUnset( + default_agc_config_.targetLeveldBOv); + default_agc_config_.digitalCompressionGaindB = + options.tx_agc_digital_compression_gain.GetWithDefaultIfUnset( + default_agc_config_.digitalCompressionGaindB); + default_agc_config_.limiterEnable = + options.tx_agc_limiter.GetWithDefaultIfUnset( + default_agc_config_.limiterEnable); + if (voe_wrapper_->processing()->SetAgcConfig(default_agc_config_) == -1) { + LOG_RTCERR3(SetAgcConfig, + default_agc_config_.targetLeveldBOv, + default_agc_config_.digitalCompressionGaindB, + default_agc_config_.limiterEnable); + return false; } } @@ -742,6 +781,9 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { if (voep->SetNsStatus(noise_suppression, ns_mode) == -1) { LOG_RTCERR2(SetNsStatus, noise_suppression, ns_mode); return false; + } else { + LOG(LS_VERBOSE) << "Noise suppression set to " << noise_suppression + << " with mode " << ns_mode; } } @@ -799,6 +841,20 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { } } + uint32 recording_sample_rate; + if (options.recording_sample_rate.Get(&recording_sample_rate)) { + if (voe_wrapper_->hw()->SetRecordingSampleRate(recording_sample_rate)) { + LOG_RTCERR1(SetRecordingSampleRate, recording_sample_rate); + } + } + + uint32 playout_sample_rate; + if (options.playout_sample_rate.Get(&playout_sample_rate)) { + if (voe_wrapper_->hw()->SetPlayoutSampleRate(playout_sample_rate)) { + LOG_RTCERR1(SetPlayoutSampleRate, playout_sample_rate); + } + } + return true; } @@ -1136,6 +1192,18 @@ void WebRtcVoiceEngine::SetTraceOptions(const std::string& options) { } } + // Allow trace options to override the trace filter. We default + // it to log_filter_ (as a translation of libjingle log levels) + // elsewhere, but this allows clients to explicitly set webrtc + // log levels. + std::vector::iterator tracefilter = + std::find(opts.begin(), opts.end(), "tracefilter"); + if (tracefilter != opts.end() && ++tracefilter != opts.end()) { + if (!tracing_->SetTraceFilter(talk_base::FromString(*tracefilter))) { + LOG_RTCERR1(SetTraceFilter, *tracefilter); + } + } + // Set AEC dump file std::vector::iterator recordEC = std::find(opts.begin(), opts.end(), "recordEC"); @@ -1587,6 +1655,56 @@ bool WebRtcVoiceMediaChannel::SetOptions(const AudioOptions& options) { // Will be interpreted when appropriate. } + // Receiver-side auto gain control happens per channel, so set it here from + // options. Note that, like conference mode, setting it on the engine won't + // have the desired effect, since voice channels don't inherit options from + // the media engine when those options are applied per-channel. + bool rx_auto_gain_control; + if (options.rx_auto_gain_control.Get(&rx_auto_gain_control)) { + if (engine()->voe()->processing()->SetRxAgcStatus( + voe_channel(), rx_auto_gain_control, + webrtc::kAgcFixedDigital) == -1) { + LOG_RTCERR1(SetRxAgcStatus, rx_auto_gain_control); + return false; + } else { + LOG(LS_VERBOSE) << "Rx auto gain set to " << rx_auto_gain_control + << " with mode " << webrtc::kAgcFixedDigital; + } + } + if (options.rx_agc_target_dbov.IsSet() || + options.rx_agc_digital_compression_gain.IsSet() || + options.rx_agc_limiter.IsSet()) { + webrtc::AgcConfig config; + // If only some of the options are being overridden, get the current + // settings for the channel and bail if they aren't available. + if (!options.rx_agc_target_dbov.IsSet() || + !options.rx_agc_digital_compression_gain.IsSet() || + !options.rx_agc_limiter.IsSet()) { + if (engine()->voe()->processing()->GetRxAgcConfig( + voe_channel(), config) != 0) { + LOG(LS_ERROR) << "Failed to get default rx agc configuration for " + << "channel " << voe_channel() << ". Since not all rx " + << "agc options are specified, unable to safely set rx " + << "agc options."; + return false; + } + } + config.targetLeveldBOv = + options.rx_agc_target_dbov.GetWithDefaultIfUnset( + config.targetLeveldBOv); + config.digitalCompressionGaindB = + options.rx_agc_digital_compression_gain.GetWithDefaultIfUnset( + config.digitalCompressionGaindB); + config.limiterEnable = options.rx_agc_limiter.GetWithDefaultIfUnset( + config.limiterEnable); + if (engine()->voe()->processing()->SetRxAgcConfig( + voe_channel(), config) == -1) { + LOG_RTCERR4(SetRxAgcConfig, voe_channel(), config.targetLeveldBOv, + config.digitalCompressionGaindB, config.limiterEnable); + return false; + } + } + LOG(LS_INFO) << "Set voice channel options. Current options: " << options_.ToString(); return true; diff --git a/talk/media/webrtc/webrtcvoiceengine_unittest.cc b/talk/media/webrtc/webrtcvoiceengine_unittest.cc index acefc38cdc..2e52c8f03b 100644 --- a/talk/media/webrtc/webrtcvoiceengine_unittest.cc +++ b/talk/media/webrtc/webrtcvoiceengine_unittest.cc @@ -55,9 +55,10 @@ class FakeVoEWrapper : public cricket::VoEWrapper { } }; -class NullVoETraceWrapper : public cricket::VoETraceWrapper { +class FakeVoETraceWrapper : public cricket::VoETraceWrapper { public: virtual int SetTraceFilter(const unsigned int filter) { + filter_ = filter; return 0; } virtual int SetTraceFile(const char* fileNameUTF8) { @@ -66,6 +67,7 @@ class NullVoETraceWrapper : public cricket::VoETraceWrapper { virtual int SetTraceCallback(webrtc::TraceCallback* callback) { return 0; } + unsigned int filter_; }; class WebRtcVoiceEngineTestFake : public testing::Test { @@ -102,9 +104,10 @@ class WebRtcVoiceEngineTestFake : public testing::Test { WebRtcVoiceEngineTestFake() : voe_(kAudioCodecs, ARRAY_SIZE(kAudioCodecs)), voe_sc_(kAudioCodecs, ARRAY_SIZE(kAudioCodecs)), + trace_wrapper_(new FakeVoETraceWrapper()), engine_(new FakeVoEWrapper(&voe_), new FakeVoEWrapper(&voe_sc_), - new NullVoETraceWrapper()), + trace_wrapper_), channel_(NULL), soundclip_(NULL) { options_conference_.conference_mode.Set(true); options_adjust_agc_.adjust_agc_delta.Set(-10); @@ -277,6 +280,7 @@ class WebRtcVoiceEngineTestFake : public testing::Test { protected: cricket::FakeWebRtcVoiceEngine voe_; cricket::FakeWebRtcVoiceEngine voe_sc_; + FakeVoETraceWrapper* trace_wrapper_; cricket::WebRtcVoiceEngine engine_; cricket::VoiceMediaChannel* channel_; cricket::SoundclipMedia* soundclip_; @@ -1873,6 +1877,84 @@ TEST_F(WebRtcVoiceEngineTestFake, CodianSendAndPlayout) { EXPECT_FALSE(voe_.GetPlayout(channel_num)); } +TEST_F(WebRtcVoiceEngineTestFake, TxAgcConfigViaOptions) { + EXPECT_TRUE(SetupEngine()); + webrtc::AgcConfig agc_config; + EXPECT_EQ(0, voe_.GetAgcConfig(agc_config)); + EXPECT_EQ(0, agc_config.targetLeveldBOv); + + cricket::AudioOptions options; + options.tx_agc_target_dbov.Set(3); + options.tx_agc_digital_compression_gain.Set(9); + options.tx_agc_limiter.Set(true); + options.auto_gain_control.Set(true); + EXPECT_TRUE(engine_.SetOptions(options)); + + EXPECT_EQ(0, voe_.GetAgcConfig(agc_config)); + EXPECT_EQ(3, agc_config.targetLeveldBOv); + EXPECT_EQ(9, agc_config.digitalCompressionGaindB); + EXPECT_TRUE(agc_config.limiterEnable); + + // Check interaction with adjust_agc_delta. Both should be respected, for + // backwards compatibility. + options.adjust_agc_delta.Set(-10); + EXPECT_TRUE(engine_.SetOptions(options)); + + EXPECT_EQ(0, voe_.GetAgcConfig(agc_config)); + EXPECT_EQ(13, agc_config.targetLeveldBOv); +} + +TEST_F(WebRtcVoiceEngineTestFake, RxAgcConfigViaOptions) { + EXPECT_TRUE(SetupEngine()); + int channel_num = voe_.GetLastChannel(); + cricket::AudioOptions options; + options.rx_agc_target_dbov.Set(6); + options.rx_agc_digital_compression_gain.Set(0); + options.rx_agc_limiter.Set(true); + options.rx_auto_gain_control.Set(true); + EXPECT_TRUE(channel_->SetOptions(options)); + + webrtc::AgcConfig agc_config; + EXPECT_EQ(0, engine_.voe()->processing()->GetRxAgcConfig( + channel_num, agc_config)); + EXPECT_EQ(6, agc_config.targetLeveldBOv); + EXPECT_EQ(0, agc_config.digitalCompressionGaindB); + EXPECT_TRUE(agc_config.limiterEnable); +} + +TEST_F(WebRtcVoiceEngineTestFake, SampleRatesViaOptions) { + EXPECT_TRUE(SetupEngine()); + cricket::AudioOptions options; + options.recording_sample_rate.Set(48000u); + options.playout_sample_rate.Set(44100u); + EXPECT_TRUE(engine_.SetOptions(options)); + + unsigned int recording_sample_rate, playout_sample_rate; + EXPECT_EQ(0, voe_.RecordingSampleRate(&recording_sample_rate)); + EXPECT_EQ(0, voe_.PlayoutSampleRate(&playout_sample_rate)); + EXPECT_EQ(48000u, recording_sample_rate); + EXPECT_EQ(44100u, playout_sample_rate); +} + +TEST_F(WebRtcVoiceEngineTestFake, TraceFilterViaTraceOptions) { + EXPECT_TRUE(SetupEngine()); + engine_.SetLogging(talk_base::LS_INFO, ""); + EXPECT_EQ( + // Info: + webrtc::kTraceStateInfo | webrtc::kTraceInfo | + // Warning: + webrtc::kTraceTerseInfo | webrtc::kTraceWarning | + // Error: + webrtc::kTraceError | webrtc::kTraceCritical, + static_cast(trace_wrapper_->filter_)); + // Now set it explicitly + std::string filter = + "tracefilter " + talk_base::ToString(webrtc::kTraceDefault); + engine_.SetLogging(talk_base::LS_VERBOSE, filter.c_str()); + EXPECT_EQ(static_cast(webrtc::kTraceDefault), + trace_wrapper_->filter_); +} + // Test that we can set the outgoing SSRC properly. // SSRC is set in SetupEngine by calling AddSendStream. TEST_F(WebRtcVoiceEngineTestFake, SetSendSsrc) { diff --git a/talk/p2p/base/dtlstransportchannel_unittest.cc b/talk/p2p/base/dtlstransportchannel_unittest.cc index 6a4e5ade90..c6e2804aed 100644 --- a/talk/p2p/base/dtlstransportchannel_unittest.cc +++ b/talk/p2p/base/dtlstransportchannel_unittest.cc @@ -233,7 +233,7 @@ class DtlsTestClient : public sigslot::has_slots<> { void SendPackets(size_t channel, size_t size, size_t count, bool srtp) { ASSERT(channel < channels_.size()); - talk_base::scoped_array packet(new char[size]); + talk_base::scoped_ptr packet(new char[size]); size_t sent = 0; do { // Fill the packet with a known value and a sequence number to check diff --git a/talk/p2p/base/p2ptransportchannel_unittest.cc b/talk/p2p/base/p2ptransportchannel_unittest.cc index e3cddc0e22..07cfeaafc8 100644 --- a/talk/p2p/base/p2ptransportchannel_unittest.cc +++ b/talk/p2p/base/p2ptransportchannel_unittest.cc @@ -57,6 +57,11 @@ static const int kOnlyLocalPorts = cricket::PORTALLOCATOR_DISABLE_STUN | // Addresses on the public internet. static const SocketAddress kPublicAddrs[2] = { SocketAddress("11.11.11.11", 0), SocketAddress("22.22.22.22", 0) }; +// IPv6 Addresses on the public internet. +static const SocketAddress kIPv6PublicAddrs[2] = { + SocketAddress("2400:4030:1:2c00:be30:abcd:efab:cdef", 0), + SocketAddress("2620:0:1000:1b03:2e41:38ff:fea6:f2a4", 0) +}; // For configuring multihomed clients. static const SocketAddress kAlternateAddrs[2] = { SocketAddress("11.11.11.101", 0), SocketAddress("22.22.22.202", 0) }; @@ -1413,6 +1418,34 @@ TEST_F(P2PTransportChannelTest, TestDefaultDscpValue) { GetEndpoint(1)->cd1_.ch_->DefaultDscpValue()); } +// Verify IPv6 connection is preferred over IPv4. +TEST_F(P2PTransportChannelTest, TestIPv6Connections) { + AddAddress(0, kIPv6PublicAddrs[0]); + AddAddress(0, kPublicAddrs[0]); + AddAddress(1, kIPv6PublicAddrs[1]); + AddAddress(1, kPublicAddrs[1]); + + SetAllocationStepDelay(0, kMinimumStepDelay); + SetAllocationStepDelay(1, kMinimumStepDelay); + + // Enable IPv6 + SetAllocatorFlags(0, cricket::PORTALLOCATOR_ENABLE_IPV6); + SetAllocatorFlags(1, cricket::PORTALLOCATOR_ENABLE_IPV6); + + CreateChannels(1); + + EXPECT_TRUE_WAIT(ep1_ch1()->readable() && ep1_ch1()->writable() && + ep2_ch1()->readable() && ep2_ch1()->writable(), + 1000); + EXPECT_TRUE( + ep1_ch1()->best_connection() && ep2_ch1()->best_connection() && + LocalCandidate(ep1_ch1())->address().EqualIPs(kIPv6PublicAddrs[0]) && + RemoteCandidate(ep1_ch1())->address().EqualIPs(kIPv6PublicAddrs[1])); + + TestSendRecv(1); + DestroyChannels(); +} + // Test what happens when we have 2 users behind the same NAT. This can lead // to interesting behavior because the STUN server will only give out the // address of the outermost NAT. diff --git a/talk/p2p/base/port_unittest.cc b/talk/p2p/base/port_unittest.cc index d3e02ac9f5..1cc3049116 100644 --- a/talk/p2p/base/port_unittest.cc +++ b/talk/p2p/base/port_unittest.cc @@ -215,7 +215,7 @@ class TestChannel : public sigslot::has_slots<> { public: TestChannel(Port* p1, Port* p2) : ice_mode_(ICEMODE_FULL), src_(p1), dst_(p2), complete_count_(0), - conn_(NULL), remote_request_(NULL), nominated_(false) { + conn_(NULL), remote_request_(), nominated_(false) { src_->SignalPortComplete.connect( this, &TestChannel::OnPortComplete); src_->SignalUnknownAddress.connect(this, &TestChannel::OnUnknownAddress); diff --git a/talk/p2p/base/pseudotcp.cc b/talk/p2p/base/pseudotcp.cc index b647fbf3de..56aa5b019a 100644 --- a/talk/p2p/base/pseudotcp.cc +++ b/talk/p2p/base/pseudotcp.cc @@ -539,7 +539,7 @@ IPseudoTcpNotify::WriteResult PseudoTcp::packet(uint32 seq, uint8 flags, uint32 now = Now(); - talk_base::scoped_array buffer(new uint8[MAX_PACKET]); + talk_base::scoped_ptr buffer(new uint8[MAX_PACKET]); long_to_bytes(m_conv, buffer.get()); long_to_bytes(seq, buffer.get() + 4); long_to_bytes(m_rcv_nxt, buffer.get() + 8); diff --git a/talk/p2p/base/stun.cc b/talk/p2p/base/stun.cc index 2a0f6d9783..38fc96ee1a 100644 --- a/talk/p2p/base/stun.cc +++ b/talk/p2p/base/stun.cc @@ -188,7 +188,7 @@ bool StunMessage::ValidateMessageIntegrity(const char* data, size_t size, // Getting length of the message to calculate Message Integrity. size_t mi_pos = current_pos; - talk_base::scoped_array temp_data(new char[current_pos]); + talk_base::scoped_ptr temp_data(new char[current_pos]); memcpy(temp_data.get(), data, current_pos); if (size > mi_pos + kStunAttributeHeaderSize + kStunMessageIntegritySize) { // Stun message has other attributes after message integrity. diff --git a/talk/p2p/base/stunport.cc b/talk/p2p/base/stunport.cc index 5e0e500297..b440bf4f69 100644 --- a/talk/p2p/base/stunport.cc +++ b/talk/p2p/base/stunport.cc @@ -287,8 +287,13 @@ void UDPPort::SendStunBindingRequest() { if (server_addr_.IsUnresolved()) { ResolveStunAddress(); } else if (socket_->GetState() == talk_base::AsyncPacketSocket::STATE_BOUND) { - if (server_addr_.family() == ip().family()) { + // Check if |server_addr_| is compatible with the port's ip. + if (IsCompatibleAddress(server_addr_)) { requests_.Send(new StunBindingRequest(this, true, server_addr_)); + } else { + // Since we can't send stun messages to the server, we should mark this + // port ready. + OnStunBindingOrResolveRequestFailed(); } } } diff --git a/talk/p2p/base/testturnserver.h b/talk/p2p/base/testturnserver.h index 32251c82c3..7a3c83f3fa 100644 --- a/talk/p2p/base/testturnserver.h +++ b/talk/p2p/base/testturnserver.h @@ -47,8 +47,7 @@ class TestTurnServer : public TurnAuthInterface { const talk_base::SocketAddress& udp_int_addr, const talk_base::SocketAddress& udp_ext_addr) : server_(thread) { - server_.AddInternalSocket(talk_base::AsyncUDPSocket::Create( - thread->socketserver(), udp_int_addr), PROTO_UDP); + AddInternalSocket(udp_int_addr, cricket::PROTO_UDP); server_.SetExternalSocketFactory(new talk_base::BasicPacketSocketFactory(), udp_ext_addr); server_.set_realm(kTestRealm); @@ -62,6 +61,23 @@ class TestTurnServer : public TurnAuthInterface { TurnServer* server() { return &server_; } + void AddInternalSocket(const talk_base::SocketAddress& int_addr, + ProtocolType proto) { + talk_base::Thread* thread = talk_base::Thread::Current(); + if (proto == cricket::PROTO_UDP) { + server_.AddInternalSocket(talk_base::AsyncUDPSocket::Create( + thread->socketserver(), int_addr), proto); + } else if (proto == cricket::PROTO_TCP) { + // For TCP we need to create a server socket which can listen for incoming + // new connections. + talk_base::AsyncSocket* socket = + thread->socketserver()->CreateAsyncSocket(SOCK_STREAM); + socket->Bind(int_addr); + socket->Listen(5); + server_.AddInternalServerSocket(socket, proto); + } + } + private: // For this test server, succeed if the password is the same as the username. // Obviously, do not use this in a production environment. diff --git a/talk/p2p/base/turnport.cc b/talk/p2p/base/turnport.cc index 35e51fc2da..880af83976 100644 --- a/talk/p2p/base/turnport.cc +++ b/talk/p2p/base/turnport.cc @@ -206,6 +206,14 @@ void TurnPort::PrepareAddress() { return; } + // If protocol family of server address doesn't match with local, return. + if (!IsCompatibleAddress(server_address_.address)) { + LOG(LS_ERROR) << "Server IP address family does not match with " + << "local host address family type"; + OnAllocateError(); + return; + } + if (!server_address_.address.port()) { // We will set default TURN port, if no port is set in the address. server_address_.address.SetPort(TURN_DEFAULT_PORT); diff --git a/talk/p2p/base/turnport_unittest.cc b/talk/p2p/base/turnport_unittest.cc index 726175c5c2..11e2213586 100644 --- a/talk/p2p/base/turnport_unittest.cc +++ b/talk/p2p/base/turnport_unittest.cc @@ -53,11 +53,17 @@ using cricket::UDPPort; static const SocketAddress kLocalAddr1("11.11.11.11", 0); static const SocketAddress kLocalAddr2("22.22.22.22", 0); +static const SocketAddress kLocalIPv6Addr( + "2401:fa00:4:1000:be30:5bff:fee5:c3", 0); static const SocketAddress kTurnUdpIntAddr("99.99.99.3", cricket::TURN_SERVER_PORT); static const SocketAddress kTurnTcpIntAddr("99.99.99.4", cricket::TURN_SERVER_PORT); static const SocketAddress kTurnUdpExtAddr("99.99.99.5", 0); +static const SocketAddress kTurnUdpIPv6IntAddr( + "2400:4030:1:2c00:be30:abcd:efab:cdef", cricket::TURN_SERVER_PORT); +static const SocketAddress kTurnUdpIPv6ExtAddr( + "2620:0:1000:1b03:2e41:38ff:fea6:f2a4", 0); static const char kIceUfrag1[] = "TESTICEUFRAG0001"; static const char kIceUfrag2[] = "TESTICEUFRAG0002"; @@ -71,6 +77,8 @@ static const cricket::ProtocolAddress kTurnUdpProtoAddr( kTurnUdpIntAddr, cricket::PROTO_UDP); static const cricket::ProtocolAddress kTurnTcpProtoAddr( kTurnTcpIntAddr, cricket::PROTO_TCP); +static const cricket::ProtocolAddress kTurnUdpIPv6ProtoAddr( + kTurnUdpIPv6IntAddr, cricket::PROTO_UDP); class TurnPortTest : public testing::Test, public sigslot::has_slots<> { @@ -130,9 +138,15 @@ class TurnPortTest : public testing::Test, void CreateTurnPort(const std::string& username, const std::string& password, const cricket::ProtocolAddress& server_address) { + CreateTurnPort(kLocalAddr1, username, password, server_address); + } + void CreateTurnPort(const talk_base::SocketAddress& local_address, + const std::string& username, + const std::string& password, + const cricket::ProtocolAddress& server_address) { cricket::RelayCredentials credentials(username, password); turn_port_.reset(TurnPort::Create(main_, &socket_factory_, &network_, - kLocalAddr1.ipaddr(), 0, 0, + local_address.ipaddr(), 0, 0, kIceUfrag1, kIcePwd1, server_address, credentials)); turn_port_->SignalPortComplete.connect(this, @@ -265,10 +279,7 @@ TEST_F(TurnPortTest, TestTurnAllocate) { } TEST_F(TurnPortTest, TestTurnTcpAllocate) { - talk_base::AsyncSocket* tcp_server_socket = - CreateServerSocket(kTurnTcpIntAddr); - turn_server_.server()->AddInternalServerSocket( - tcp_server_socket, cricket::PROTO_TCP); + turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP); CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); EXPECT_EQ(0, turn_port_->SetOption(talk_base::Socket::OPT_SNDBUF, 10*1024)); turn_port_->PrepareAddress(); @@ -298,10 +309,7 @@ TEST_F(TurnPortTest, TestTurnConnection) { // Test that we can establish a TCP connection with TURN server. TEST_F(TurnPortTest, TestTurnTcpConnection) { - talk_base::AsyncSocket* tcp_server_socket = - CreateServerSocket(kTurnTcpIntAddr); - turn_server_.server()->AddInternalServerSocket( - tcp_server_socket, cricket::PROTO_TCP); + turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP); CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); TestTurnConnection(); } @@ -327,19 +335,44 @@ TEST_F(TurnPortTest, TestTurnConnectionUsingOTUNonce) { TestTurnConnection(); } -// Do a TURN allocation, establish a connection, and send some data. +// Do a TURN allocation, establish a UDP connection, and send some data. TEST_F(TurnPortTest, TestTurnSendDataTurnUdpToUdp) { // Create ports and prepare addresses. CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); TestTurnSendData(); } +// Do a TURN allocation, establish a TCP connection, and send some data. TEST_F(TurnPortTest, TestTurnSendDataTurnTcpToUdp) { - talk_base::AsyncSocket* tcp_server_socket = - CreateServerSocket(kTurnTcpIntAddr); - turn_server_.server()->AddInternalServerSocket( - tcp_server_socket, cricket::PROTO_TCP); + turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP); // Create ports and prepare addresses. CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); TestTurnSendData(); } + +// Test TURN fails to make a connection from IPv6 address to a server which has +// IPv4 address. +TEST_F(TurnPortTest, TestTurnLocalIPv6AddressServerIPv4) { + turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, cricket::PROTO_UDP); + CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword, + kTurnUdpProtoAddr); + turn_port_->PrepareAddress(); + ASSERT_TRUE_WAIT(turn_error_, kTimeout); + EXPECT_TRUE(turn_port_->Candidates().empty()); +} + +// Test TURN make a connection from IPv6 address to a server which has +// IPv6 intenal address. But in this test external address is a IPv4 address, +// hence allocated address will be a IPv4 address. +TEST_F(TurnPortTest, TestTurnLocalIPv6AddressServerIPv6ExtenalIPv4) { + turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, cricket::PROTO_UDP); + CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword, + kTurnUdpIPv6ProtoAddr); + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_ready_, kTimeout); + ASSERT_EQ(1U, turn_port_->Candidates().size()); + EXPECT_EQ(kTurnUdpExtAddr.ipaddr(), + turn_port_->Candidates()[0].address().ipaddr()); + EXPECT_NE(0, turn_port_->Candidates()[0].address().port()); +} + diff --git a/talk/p2p/client/basicportallocator.cc b/talk/p2p/client/basicportallocator.cc index a728d989b1..9f10d3394a 100644 --- a/talk/p2p/client/basicportallocator.cc +++ b/talk/p2p/client/basicportallocator.cc @@ -708,7 +708,7 @@ AllocationSequence::AllocationSequence(BasicPortAllocatorSession* session, config_(config), state_(kInit), flags_(flags), - udp_socket_(NULL), + udp_socket_(), phase_(0) { } diff --git a/talk/p2p/client/fakeportallocator.h b/talk/p2p/client/fakeportallocator.h index 2368948b07..5375e50156 100644 --- a/talk/p2p/client/fakeportallocator.h +++ b/talk/p2p/client/fakeportallocator.h @@ -32,7 +32,7 @@ class FakePortAllocatorSession : public PortAllocatorSession { factory_(factory), network_("network", "unittest", talk_base::IPAddress(INADDR_LOOPBACK), 8), - port_(NULL), running_(false), + port_(), running_(false), port_config_count_(0) { network_.AddIP(talk_base::IPAddress(INADDR_LOOPBACK)); } diff --git a/talk/p2p/client/portallocator_unittest.cc b/talk/p2p/client/portallocator_unittest.cc index 1ee97f1111..6966e44d04 100644 --- a/talk/p2p/client/portallocator_unittest.cc +++ b/talk/p2p/client/portallocator_unittest.cc @@ -50,6 +50,8 @@ using talk_base::SocketAddress; 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 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); @@ -740,6 +742,36 @@ TEST_F(PortAllocatorTest, TestEnableSharedSocketNoUdpAllowed) { EXPECT_EQ(1U, candidates_.size()); } +// This test verifies allocator can use IPv6 addresses along with IPv4. +TEST_F(PortAllocatorTest, TestEnableIPv6Addresses) { + allocator().set_flags(allocator().flags() | + cricket::PORTALLOCATOR_DISABLE_RELAY | + cricket::PORTALLOCATOR_ENABLE_IPV6 | + cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG | + cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET); + AddInterface(kClientIPv6Addr); + AddInterface(kClientAddr); + allocator_->set_step_delay(cricket::kMinimumStepDelay); + EXPECT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP)); + session_->StartGettingPorts(); + ASSERT_EQ_WAIT(4U, ports_.size(), kDefaultAllocationTimeout); + EXPECT_EQ(4U, candidates_.size()); + EXPECT_TRUE_WAIT(candidate_allocation_done_, kDefaultAllocationTimeout); + EXPECT_PRED5(CheckCandidate, candidates_[0], + cricket::ICE_CANDIDATE_COMPONENT_RTP, "local", "udp", + kClientIPv6Addr); + EXPECT_PRED5(CheckCandidate, candidates_[1], + cricket::ICE_CANDIDATE_COMPONENT_RTP, "local", "udp", + kClientAddr); + EXPECT_PRED5(CheckCandidate, candidates_[2], + cricket::ICE_CANDIDATE_COMPONENT_RTP, "local", "tcp", + kClientIPv6Addr); + EXPECT_PRED5(CheckCandidate, candidates_[3], + cricket::ICE_CANDIDATE_COMPONENT_RTP, "local", "tcp", + kClientAddr); + EXPECT_EQ(4U, candidates_.size()); +} + // Test that the httpportallocator correctly maintains its lists of stun and // relay servers, by never allowing an empty list. TEST(HttpPortAllocatorTest, TestHttpPortAllocatorHostLists) { diff --git a/talk/session/media/channel.cc b/talk/session/media/channel.cc index db169bea40..9c9847fb2b 100644 --- a/talk/session/media/channel.cc +++ b/talk/session/media/channel.cc @@ -399,7 +399,6 @@ BaseChannel::BaseChannel(talk_base::Thread* thread, writable_(false), rtp_ready_to_send_(false), rtcp_ready_to_send_(false), - optimistic_data_send_(false), was_ever_writable_(false), local_content_direction_(MD_INACTIVE), remote_content_direction_(MD_INACTIVE), @@ -670,7 +669,7 @@ bool BaseChannel::SendPacket(bool rtcp, talk_base::Buffer* packet, // transport. TransportChannel* channel = (!rtcp || rtcp_mux_filter_.IsActive()) ? transport_channel_ : rtcp_transport_channel_; - if (!channel || (!optimistic_data_send_ && !channel->writable())) { + if (!channel || !channel->writable()) { return false; } diff --git a/talk/session/media/channel.h b/talk/session/media/channel.h index 3a627ed02a..51fb575dd9 100644 --- a/talk/session/media/channel.h +++ b/talk/session/media/channel.h @@ -97,10 +97,6 @@ class BaseChannel return rtcp_transport_channel_; } bool enabled() const { return enabled_; } - // Set to true to have the channel optimistically allow data to be sent even - // when the channel isn't fully writable. - void set_optimistic_data_send(bool value) { optimistic_data_send_ = value; } - bool optimistic_data_send() const { return optimistic_data_send_; } // This function returns true if we are using SRTP. bool secure() const { return srtp_filter_.IsActive(); } @@ -362,7 +358,6 @@ class BaseChannel bool writable_; bool rtp_ready_to_send_; bool rtcp_ready_to_send_; - bool optimistic_data_send_; bool was_ever_writable_; MediaContentDirection local_content_direction_; MediaContentDirection remote_content_direction_; diff --git a/talk/session/media/channel_unittest.cc b/talk/session/media/channel_unittest.cc index 24ece06962..02739079d7 100644 --- a/talk/session/media/channel_unittest.cc +++ b/talk/session/media/channel_unittest.cc @@ -496,11 +496,6 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { // overridden in specialized classes } - void SetOptimisticDataSend(bool optimistic_data_send) { - channel1_->set_optimistic_data_send(optimistic_data_send); - channel2_->set_optimistic_data_send(optimistic_data_send); - } - // Creates a cricket::SessionDescription with one MediaContent and one stream. // kPcmuCodec is used as audio codec and kH264Codec is used as video codec. cricket::SessionDescription* CreateSessionDescriptionWithStream(uint32 ssrc) { @@ -1394,19 +1389,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { EXPECT_TRUE(CheckNoRtp1()); EXPECT_TRUE(CheckNoRtp2()); - // Lose writability, with optimistic send - SetOptimisticDataSend(true); + // Lose writability, which should fail. GetTransport1()->SetWritable(false); - EXPECT_TRUE(media_channel1_->sending()); - EXPECT_TRUE(SendRtp1()); - EXPECT_TRUE(SendRtp2()); - EXPECT_TRUE(CheckRtp1()); - EXPECT_TRUE(CheckRtp2()); - EXPECT_TRUE(CheckNoRtp1()); - EXPECT_TRUE(CheckNoRtp2()); - - // Check again with optimistic send off, which should fail. - SetOptimisticDataSend(false); EXPECT_FALSE(SendRtp1()); EXPECT_TRUE(SendRtp2()); EXPECT_TRUE(CheckRtp1()); @@ -1426,13 +1410,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { GetTransport1()->SetDestination(NULL); EXPECT_TRUE(media_channel1_->sending()); - // Should fail regardless of optimistic send at this point. - SetOptimisticDataSend(true); - EXPECT_FALSE(SendRtp1()); - EXPECT_TRUE(SendRtp2()); - EXPECT_TRUE(CheckRtp1()); - EXPECT_TRUE(CheckNoRtp2()); - SetOptimisticDataSend(false); + // Should fail also. EXPECT_FALSE(SendRtp1()); EXPECT_TRUE(SendRtp2()); EXPECT_TRUE(CheckRtp1()); diff --git a/talk/session/media/mediasession.cc b/talk/session/media/mediasession.cc index ea23591f32..ae3fb577b6 100644 --- a/talk/session/media/mediasession.cc +++ b/talk/session/media/mediasession.cc @@ -46,7 +46,7 @@ #ifdef HAVE_SCTP #include "talk/media/sctp/sctpdataengine.h" #else -static const uint32 kMaxSctpSid = USHRT_MAX; +static const uint32 kMaxSctpSid = 1023; #endif namespace { diff --git a/talk/session/media/mediasession_unittest.cc b/talk/session/media/mediasession_unittest.cc index 850d67f21b..ceb2bcd090 100644 --- a/talk/session/media/mediasession_unittest.cc +++ b/talk/session/media/mediasession_unittest.cc @@ -882,8 +882,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswerRtcpMux) { answer_opts.data_channel_type = cricket::DCT_RTP; offer_opts.data_channel_type = cricket::DCT_RTP; - talk_base::scoped_ptr offer(NULL); - talk_base::scoped_ptr answer(NULL); + talk_base::scoped_ptr offer; + talk_base::scoped_ptr answer; offer_opts.rtcp_mux_enabled = true; answer_opts.rtcp_mux_enabled = true; diff --git a/talk/sound/alsasoundsystem.cc b/talk/sound/alsasoundsystem.cc index de9e2d67fc..7a8857cdf1 100644 --- a/talk/sound/alsasoundsystem.cc +++ b/talk/sound/alsasoundsystem.cc @@ -342,7 +342,7 @@ class AlsaInputStream : } AlsaStream stream_; - talk_base::scoped_array buffer_; + talk_base::scoped_ptr buffer_; size_t buffer_size_; DISALLOW_COPY_AND_ASSIGN(AlsaInputStream); diff --git a/talk/xmllite/xmlbuilder.cc b/talk/xmllite/xmlbuilder.cc index 486b6d54ea..f71e542d67 100644 --- a/talk/xmllite/xmlbuilder.cc +++ b/talk/xmllite/xmlbuilder.cc @@ -37,7 +37,7 @@ namespace buzz { XmlBuilder::XmlBuilder() : pelCurrent_(NULL), - pelRoot_(NULL), + pelRoot_(), pvParents_(new std::vector()) { } diff --git a/talk/xmpp/xmppclient.cc b/talk/xmpp/xmppclient.cc index 9c49a9cae9..8927dad4e4 100644 --- a/talk/xmpp/xmppclient.cc +++ b/talk/xmpp/xmppclient.cc @@ -46,8 +46,8 @@ public: explicit Private(XmppClient* client) : client_(client), - socket_(NULL), - engine_(NULL), + socket_(), + engine_(), proxy_port_(0), pre_engine_error_(XmppEngine::ERROR_NONE), pre_engine_subcode_(0), diff --git a/talk/xmpp/xmppengineimpl.cc b/talk/xmpp/xmppengineimpl.cc index 8bcea029a3..d4c9c7d5f6 100644 --- a/talk/xmpp/xmppengineimpl.cc +++ b/talk/xmpp/xmppengineimpl.cc @@ -58,12 +58,12 @@ XmppEngineImpl::XmppEngineImpl() encrypted_(false), error_code_(ERROR_NONE), subcode_(0), - stream_error_(NULL), + stream_error_(), raised_reset_(false), output_handler_(NULL), session_handler_(NULL), iq_entries_(new IqEntryVector()), - sasl_handler_(NULL), + sasl_handler_(), output_(new std::stringstream()) { for (int i = 0; i < HL_COUNT; i+= 1) { stanza_handlers_[i].reset(new StanzaHandlerVector()); diff --git a/talk/xmpp/xmpplogintask.cc b/talk/xmpp/xmpplogintask.cc index eec943bec6..b3a2047cfe 100644 --- a/talk/xmpp/xmpplogintask.cc +++ b/talk/xmpp/xmpplogintask.cc @@ -66,11 +66,11 @@ XmppLoginTask::XmppLoginTask(XmppEngineImpl * pctx) : pelStanza_(NULL), isStart_(false), iqId_(STR_EMPTY), - pelFeatures_(NULL), + pelFeatures_(), fullJid_(STR_EMPTY), streamId_(STR_EMPTY), pvecQueuedStanzas_(new std::vector()), - sasl_mech_(NULL) { + sasl_mech_() { } XmppLoginTask::~XmppLoginTask() {