diff --git a/talk/app/webrtc/javatests/src/org/webrtc/PeerConnectionTest.java b/talk/app/webrtc/javatests/src/org/webrtc/PeerConnectionTest.java index 2a06ff417..3bf128e16 100644 --- a/talk/app/webrtc/javatests/src/org/webrtc/PeerConnectionTest.java +++ b/talk/app/webrtc/javatests/src/org/webrtc/PeerConnectionTest.java @@ -668,7 +668,7 @@ public class PeerConnectionTest extends TestCase { offeringExpectations.expectMessage(expectedBinaryMessage, true); assertTrue(answeringExpectations.dataChannel.send( new DataChannel.Buffer( - ByteBuffer.wrap(new byte[] { 1, 2, 3, 4, 5 } ), true))); + ByteBuffer.wrap(new byte[] { 1, 2, 3, 4, 5 }), true))); offeringExpectations.waitForAllExpectationsToBeSatisfied(); offeringExpectations.expectStateChange(DataChannel.State.CLOSING); diff --git a/talk/app/webrtc/localaudiosource.cc b/talk/app/webrtc/localaudiosource.cc index 7ac59fc4c..ab9ae4fa9 100644 --- a/talk/app/webrtc/localaudiosource.cc +++ b/talk/app/webrtc/localaudiosource.cc @@ -70,6 +70,9 @@ bool FromConstraints(const MediaConstraintsInterface::Constraints& constraints, options->experimental_agc.Set(value); else if (iter->key == MediaConstraintsInterface::kNoiseSuppression) options->noise_suppression.Set(value); + else if (iter->key == + MediaConstraintsInterface::kExperimentalNoiseSuppression) + options->experimental_ns.Set(value); else if (iter->key == MediaConstraintsInterface::kHighpassFilter) options->highpass_filter.Set(value); else if (iter->key == MediaConstraintsInterface::kTypingNoiseDetection) diff --git a/talk/app/webrtc/mediaconstraintsinterface.cc b/talk/app/webrtc/mediaconstraintsinterface.cc index 251293201..63d2be009 100644 --- a/talk/app/webrtc/mediaconstraintsinterface.cc +++ b/talk/app/webrtc/mediaconstraintsinterface.cc @@ -56,6 +56,8 @@ const char MediaConstraintsInterface::kExperimentalAutoGainControl[] = "googAutoGainControl2"; const char MediaConstraintsInterface::kNoiseSuppression[] = "googNoiseSuppression"; +const char MediaConstraintsInterface::kExperimentalNoiseSuppression[] = + "googNoiseSuppression2"; const char MediaConstraintsInterface::kHighpassFilter[] = "googHighpassFilter"; const char MediaConstraintsInterface::kTypingNoiseDetection[] = diff --git a/talk/app/webrtc/mediaconstraintsinterface.h b/talk/app/webrtc/mediaconstraintsinterface.h index 7a207e400..fae065cbc 100644 --- a/talk/app/webrtc/mediaconstraintsinterface.h +++ b/talk/app/webrtc/mediaconstraintsinterface.h @@ -79,6 +79,7 @@ class MediaConstraintsInterface { static const char kAutoGainControl[]; // googAutoGainControl static const char kExperimentalAutoGainControl[]; // googAutoGainControl2 static const char kNoiseSuppression[]; // googNoiseSuppression + static const char kExperimentalNoiseSuppression[]; // googNoiseSuppression2 static const char kHighpassFilter[]; // googHighpassFilter static const char kTypingNoiseDetection[]; // googTypingNoiseDetection static const char kAudioMirroring[]; // googAudioMirroring diff --git a/talk/app/webrtc/mediastreamhandler.cc b/talk/app/webrtc/mediastreamhandler.cc index 932d55ee7..a94eef322 100644 --- a/talk/app/webrtc/mediastreamhandler.cc +++ b/talk/app/webrtc/mediastreamhandler.cc @@ -114,7 +114,7 @@ void LocalAudioTrackHandler::OnEnabledChanged() { // adapter owned by this class. cricket::AudioRenderer* renderer = audio_track_->GetRenderer() ? audio_track_->GetRenderer() : sink_adapter_.get(); - ASSERT(renderer); + ASSERT(renderer != NULL); provider_->SetAudioSend(ssrc(), audio_track_->enabled(), options, renderer); } diff --git a/talk/app/webrtc/peerconnection_unittest.cc b/talk/app/webrtc/peerconnection_unittest.cc index edf79f5a7..1b6d73d03 100644 --- a/talk/app/webrtc/peerconnection_unittest.cc +++ b/talk/app/webrtc/peerconnection_unittest.cc @@ -1269,7 +1269,12 @@ TEST_F(JsepPeerConnectionP2PTestClient, GetBytesSentStats) { } // This test sets up a call between two parties with audio, video and data. +// TODO(jiayl): fix the flakiness on Windows and reenable. Issue 2891. +#if defined(WIN32) +TEST_F(JsepPeerConnectionP2PTestClient, DISABLED_LocalP2PTestDataChannel) { +#else TEST_F(JsepPeerConnectionP2PTestClient, LocalP2PTestDataChannel) { +#endif FakeConstraints setup_constraints; setup_constraints.SetAllowRtpDataChannels(); ASSERT_TRUE(CreateTestClients(&setup_constraints, &setup_constraints)); diff --git a/talk/app/webrtc/statscollector.cc b/talk/app/webrtc/statscollector.cc index 7e1e7eec6..2efc11b1c 100644 --- a/talk/app/webrtc/statscollector.cc +++ b/talk/app/webrtc/statscollector.cc @@ -83,6 +83,8 @@ const char StatsReport::kStatsValueNameFingerprintAlgorithm[] = "googFingerprintAlgorithm"; const char StatsReport::kStatsValueNameFirsReceived[] = "googFirsReceived"; const char StatsReport::kStatsValueNameFirsSent[] = "googFirsSent"; +const char StatsReport::kStatsValueNameFrameHeightInput[] = + "googFrameHeightInput"; const char StatsReport::kStatsValueNameFrameHeightReceived[] = "googFrameHeightReceived"; const char StatsReport::kStatsValueNameFrameHeightSent[] = @@ -104,6 +106,8 @@ const char StatsReport::kStatsValueNameRenderDelayMs[] = "googRenderDelayMs"; const char StatsReport::kStatsValueNameFrameRateInput[] = "googFrameRateInput"; const char StatsReport::kStatsValueNameFrameRateSent[] = "googFrameRateSent"; +const char StatsReport::kStatsValueNameFrameWidthInput[] = + "googFrameWidthInput"; const char StatsReport::kStatsValueNameFrameWidthReceived[] = "googFrameWidthReceived"; const char StatsReport::kStatsValueNameFrameWidthSent[] = "googFrameWidthSent"; @@ -117,6 +121,8 @@ const char StatsReport::kStatsValueNameLocalCertificateId[] = "googLocalCertificateId"; const char StatsReport::kStatsValueNameNacksReceived[] = "googNacksReceived"; const char StatsReport::kStatsValueNameNacksSent[] = "googNacksSent"; +const char StatsReport::kStatsValueNameNetEqExpandRate[] = + "googNetEqExpandRate"; const char StatsReport::kStatsValueNamePacketsReceived[] = "packetsReceived"; const char StatsReport::kStatsValueNamePacketsSent[] = "packetsSent"; const char StatsReport::kStatsValueNamePacketsLost[] = "packetsLost"; @@ -215,6 +221,8 @@ void ExtractStats(const cricket::VoiceReceiverInfo& info, StatsReport* report) { info.bytes_rcvd); report->AddValue(StatsReport::kStatsValueNameJitterReceived, info.jitter_ms); + report->AddValue(StatsReport::kStatsValueNameNetEqExpandRate, + talk_base::ToString(info.expand_rate)); report->AddValue(StatsReport::kStatsValueNamePacketsReceived, info.packets_rcvd); report->AddValue(StatsReport::kStatsValueNamePacketsLost, @@ -295,10 +303,14 @@ void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) { info.firs_rcvd); report->AddValue(StatsReport::kStatsValueNameNacksReceived, info.nacks_rcvd); + report->AddValue(StatsReport::kStatsValueNameFrameWidthInput, + info.input_frame_width); + report->AddValue(StatsReport::kStatsValueNameFrameHeightInput, + info.input_frame_height); report->AddValue(StatsReport::kStatsValueNameFrameWidthSent, - info.frame_width); + info.send_frame_width); report->AddValue(StatsReport::kStatsValueNameFrameHeightSent, - info.frame_height); + info.send_frame_height); report->AddValue(StatsReport::kStatsValueNameFrameRateInput, info.framerate_input); report->AddValue(StatsReport::kStatsValueNameFrameRateSent, diff --git a/talk/app/webrtc/statstypes.h b/talk/app/webrtc/statstypes.h index 6890f9e01..9110da3fb 100644 --- a/talk/app/webrtc/statstypes.h +++ b/talk/app/webrtc/statstypes.h @@ -143,6 +143,7 @@ class StatsReport { static const char kStatsValueNameEchoReturnLossEnhancement[]; static const char kStatsValueNameFirsReceived[]; static const char kStatsValueNameFirsSent[]; + static const char kStatsValueNameFrameHeightInput[]; static const char kStatsValueNameFrameHeightReceived[]; static const char kStatsValueNameFrameHeightSent[]; static const char kStatsValueNameFrameRateReceived[]; @@ -157,11 +158,13 @@ class StatsReport { static const char kStatsValueNameRenderDelayMs[]; static const char kStatsValueNameFrameRateInput[]; static const char kStatsValueNameFrameRateSent[]; + static const char kStatsValueNameFrameWidthInput[]; static const char kStatsValueNameFrameWidthReceived[]; static const char kStatsValueNameFrameWidthSent[]; static const char kStatsValueNameJitterReceived[]; static const char kStatsValueNameNacksReceived[]; static const char kStatsValueNameNacksSent[]; + static const char kStatsValueNameNetEqExpandRate[]; static const char kStatsValueNameRtt[]; static const char kStatsValueNameAvailableSendBandwidth[]; static const char kStatsValueNameAvailableReceiveBandwidth[]; diff --git a/talk/base/asyncinvoker-inl.h b/talk/base/asyncinvoker-inl.h new file mode 100644 index 000000000..c4afd3a19 --- /dev/null +++ b/talk/base/asyncinvoker-inl.h @@ -0,0 +1,101 @@ +/* + * libjingle + * Copyright 2014 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_BASE_ASYNCINVOKER_INL_H_ +#define TALK_BASE_ASYNCINVOKER_INL_H_ + +#include "talk/base/criticalsection.h" +#include "talk/base/messagehandler.h" +#include "talk/base/sigslot.h" +#include "talk/base/thread.h" + +namespace talk_base { + +// Helper class for AsyncInvoker. Runs a functor on a message queue or thread +// and doesn't execute the callback when finished if the calling thread ends. +template +class AsyncFunctorMessageHandler + : public FunctorMessageHandler, + public sigslot::has_slots<> { + typedef AsyncFunctorMessageHandler ThisT; + public: + explicit AsyncFunctorMessageHandler(const FunctorT& functor) + : FunctorMessageHandler(functor), + thread_(Thread::Current()), + shutting_down_(false) { + thread_->SignalQueueDestroyed.connect(this, &ThisT::OnThreadDestroyed); + } + + virtual ~AsyncFunctorMessageHandler() { + CritScope cs(&running_crit_); + shutting_down_ = true; + } + + virtual void OnMessage(Message* msg) { + CritScope cs(&running_crit_); + if (!shutting_down_) { + FunctorMessageHandler::OnMessage(msg); + } + } + + // Returns the thread that initiated the async call. + Thread* thread() const { return thread_; } + + // Wraps a callback so that it won't execute if |thread_| goes away. + void WrapCallback(Callback0 cb) { + this->SetCallback( + Callback0(Bind(&ThisT::MaybeRunCallback, this, cb))); + } + + private: + void OnThreadDestroyed() { + CritScope cs(&thread_crit_); + thread_ = NULL; + this->SetCallback(Callback0()); // Clear out the callback. + } + + void MaybeRunCallback(Callback0 cb) { +#ifdef _DEBUG + ASSERT(running_crit_.CurrentThreadIsOwner()); +#endif + CritScope cs(&thread_crit_); + if (thread_ && !shutting_down_) { + cb(); + } + } + + FunctorT functor_; + Thread* thread_; + CriticalSection thread_crit_; + CriticalSection running_crit_; + bool shutting_down_; +}; + +} // namespace talk_base + + +#endif // TALK_BASE_ASYNCINVOKER_INL_H_ diff --git a/talk/base/asyncinvoker.cc b/talk/base/asyncinvoker.cc new file mode 100644 index 000000000..63171be40 --- /dev/null +++ b/talk/base/asyncinvoker.cc @@ -0,0 +1,78 @@ +/* + * libjingle + * Copyright 2014 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "talk/base/asyncinvoker.h" + +namespace talk_base { + +// Synchronously execute all outstanding calls we own pending +// on |thread|. Optionally filter by message id. +void AsyncInvoker::Flush(Thread* thread, uint32 id /*= MQID_ANY*/) { + // Run this on |thread| to reduce the number of context switches. + if (Thread::Current() != thread) { + thread->Invoke(Bind(&AsyncInvoker::Flush, this, thread, id)); + return; + } + + // Make a copy of handlers_, since it'll be modified by + // callbacks to RemoveHandler when each is done executing. + crit_.Enter(); + std::vector handlers(handlers_.collection()); + crit_.Leave(); + MessageList removed; + for (size_t i = 0; i < handlers.size(); ++i) { + removed.clear(); + thread->Clear(handlers[i], id, &removed); + if (!removed.empty()) { + // Since each message gets its own handler with AsyncInvoker, + // we expect a maximum of one removed. + ASSERT(removed.size() == 1); + // This handler was pending on this thread, so run it now. + const Message& msg = removed.front(); + thread->Send(msg.phandler, + msg.message_id, + msg.pdata); + } + } +} + +void AsyncInvoker::InvokeHandler(Thread* thread, MessageHandler* handler, + uint32 id) { + { + CritScope cs(&crit_); + handlers_.PushBack(handler); + } + thread->Post(handler, id); +} + +void AsyncInvoker::RemoveHandler(MessageHandler* handler) { + CritScope cs(&crit_); + handlers_.Remove(handler); + delete handler; +} + +} // namespace talk_base diff --git a/talk/base/asyncinvoker.h b/talk/base/asyncinvoker.h new file mode 100644 index 000000000..fabdbdcbd --- /dev/null +++ b/talk/base/asyncinvoker.h @@ -0,0 +1,167 @@ +/* + * libjingle + * Copyright 2014 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_BASE_ASYNCINVOKER_H_ +#define TALK_BASE_ASYNCINVOKER_H_ + +#include "talk/base/asyncinvoker-inl.h" +#include "talk/base/bind.h" +#include "talk/base/scopedptrcollection.h" +#include "talk/base/thread.h" + +namespace talk_base { + +// Invokes function objects (aka functors) asynchronously on a Thread, and +// owns the lifetime of calls (ie, when this object is destroyed, calls in +// flight are cancelled). AsyncInvoker can optionally execute a user-specified +// function when the asynchronous call is complete, or operates in +// fire-and-forget mode otherwise. +// +// AsyncInvoker does not own the thread it calls functors on. +// +// A note about async calls and object lifetimes: users should +// be mindful of object lifetimes when calling functions asynchronously and +// ensure objects used by the function _cannot_ be deleted between the +// invocation and execution of the functor. AsyncInvoker is designed to +// help: any calls in flight will be cancelled when the AsyncInvoker used to +// make the call is destructed, and any calls executing will be allowed to +// complete before AsyncInvoker destructs. +// +// The easiest way to ensure lifetimes are handled correctly is to create a +// class that owns the Thread and AsyncInvoker objects, and then call its +// methods asynchronously as needed. +// +// Example: +// class MyClass { +// public: +// void FireAsyncTaskWithResult(Thread* thread, int x) { +// // Specify a callback to get the result upon completion. +// invoker_.AsyncInvoke( +// thread, Bind(&MyClass::AsyncTaskWithResult, this, x), +// &MyClass::OnTaskComplete, this); +// } +// void FireAnotherAsyncTask(Thread* thread) { +// // No callback specified means fire-and-forget. +// invoker_.AsyncInvoke( +// thread, Bind(&MyClass::AnotherAsyncTask, this)); +// +// private: +// int AsyncTaskWithResult(int x) { +// // Some long running process... +// return x * x; +// } +// void AnotherAsyncTask() { +// // Some other long running process... +// } +// void OnTaskComplete(int result) { result_ = result; } +// +// AsyncInvoker invoker_; +// int result_; +// }; +class AsyncInvoker { + public: + // Call |functor| asynchronously on |thread|, with no callback upon + // completion. Returns immediately. + template + void AsyncInvoke(Thread* thread, + const FunctorT& functor, + uint32 id = 0) { + FunctorMessageHandler* handler = + new FunctorMessageHandler(functor); + handler->SetCallback(Bind(&AsyncInvoker::RemoveHandler, this, handler)); + InvokeHandler(thread, handler, id); + } + + // Call |functor| asynchronously on |thread|, calling |callback| when done. + template + void AsyncInvoke(Thread* thread, + const FunctorT& functor, + void (HostT::*callback)(ReturnT), + HostT* callback_host, + uint32 id = 0) { + AsyncFunctorMessageHandler* handler = + new AsyncFunctorMessageHandler(functor); + handler->WrapCallback( + Bind(&AsyncInvoker::OnAsyncCallCompleted, + this, handler, callback, callback_host)); + InvokeHandler(thread, handler, id); + } + + // Call |functor| asynchronously on |thread|, calling |callback| when done. + // Overloaded for void return. + template + void AsyncInvoke(Thread* thread, + const FunctorT& functor, + void (HostT::*callback)(), + HostT* callback_host, + uint32 id = 0) { + AsyncFunctorMessageHandler* handler = + new AsyncFunctorMessageHandler(functor); + handler->WrapCallback( + Bind(&AsyncInvoker::OnAsyncVoidCallCompleted, + this, handler, callback, callback_host)); + InvokeHandler(thread, handler, id); + } + + // Synchronously execute on |thread| all outstanding calls we own + // that are pending on |thread|, and wait for calls to complete + // before returning. Optionally filter by message id. + // The destructor will not wait for outstanding calls, so if that + // behavior is desired, call Flush() before destroying this object. + void Flush(Thread* thread, uint32 id = MQID_ANY); + + private: + void InvokeHandler(Thread* thread, MessageHandler* handler, uint32 id); + void RemoveHandler(MessageHandler* handler); + + template + void OnAsyncCallCompleted( + AsyncFunctorMessageHandler* handler, + void (HostT::*callback)(ReturnT), + HostT* callback_host) { + AsyncInvoke(handler->thread(), + Bind(callback, callback_host, handler->result())); + RemoveHandler(handler); + } + + template + void OnAsyncVoidCallCompleted( + AsyncFunctorMessageHandler* handler, + void (HostT::*callback)(), + HostT* callback_host) { + AsyncInvoke(handler->thread(), Bind(callback, callback_host)); + RemoveHandler(handler); + } + + CriticalSection crit_; + ScopedPtrCollection handlers_; +}; + +} // namespace talk_base + + +#endif // TALK_BASE_ASYNCINVOKER_H_ diff --git a/talk/base/bind.h b/talk/base/bind.h index 622cc679d..5b4eaac94 100644 --- a/talk/base/bind.h +++ b/talk/base/bind.h @@ -84,6 +84,18 @@ class MethodFunctor0 { ObjectT* object_; }; +template +class Functor0 { + public: + explicit Functor0(const FunctorT& functor) + : functor_(functor) {} + R operator()() const { + return functor_(); } + private: + FunctorT functor_; +}; + + #define FP_T(x) R (ObjectT::*x)() template @@ -103,6 +115,16 @@ Bind(FP_T(method), const ObjectT* object) { method, object); } +#undef FP_T +#define FP_T(x) R (*x)() + +template +Functor0 +Bind(FP_T(function)) { + return Functor0( + function); +} + #undef FP_T template +class Functor1 { + public: + Functor1(const FunctorT& functor, P1 p1) + : functor_(functor), + p1_(p1) {} + R operator()() const { + return functor_(p1_); } + private: + FunctorT functor_; + P1 p1_; +}; + + #define FP_T(x) R (ObjectT::*x)(P1) template +Functor1 +Bind(FP_T(function), + typename detail::identity::type p1) { + return Functor1( + function, p1); +} + #undef FP_T template +class Functor2 { + public: + Functor2(const FunctorT& functor, P1 p1, P2 p2) + : functor_(functor), + p1_(p1), + p2_(p2) {} + R operator()() const { + return functor_(p1_, p2_); } + private: + FunctorT functor_; + P1 p1_; + P2 p2_; +}; + + #define FP_T(x) R (ObjectT::*x)(P1, P2) template +Functor2 +Bind(FP_T(function), + typename detail::identity::type p1, + typename detail::identity::type p2) { + return Functor2( + function, p1, p2); +} + #undef FP_T template +class Functor3 { + public: + Functor3(const FunctorT& functor, P1 p1, P2 p2, P3 p3) + : functor_(functor), + p1_(p1), + p2_(p2), + p3_(p3) {} + R operator()() const { + return functor_(p1_, p2_, p3_); } + private: + FunctorT functor_; + P1 p1_; + P2 p2_; + P3 p3_; +}; + + #define FP_T(x) R (ObjectT::*x)(P1, P2, P3) template +Functor3 +Bind(FP_T(function), + typename detail::identity::type p1, + typename detail::identity::type p2, + typename detail::identity::type p3) { + return Functor3( + function, p1, p2, p3); +} + #undef FP_T template +class Functor4 { + public: + Functor4(const FunctorT& functor, P1 p1, P2 p2, P3 p3, P4 p4) + : functor_(functor), + p1_(p1), + p2_(p2), + p3_(p3), + p4_(p4) {} + R operator()() const { + return functor_(p1_, p2_, p3_, p4_); } + private: + FunctorT functor_; + P1 p1_; + P2 p2_; + P3 p3_; + P4 p4_; +}; + + #define FP_T(x) R (ObjectT::*x)(P1, P2, P3, P4) template +Functor4 +Bind(FP_T(function), + typename detail::identity::type p1, + typename detail::identity::type p2, + typename detail::identity::type p3, + typename detail::identity::type p4) { + return Functor4( + function, p1, p2, p3, p4); +} + #undef FP_T template +class Functor5 { + public: + Functor5(const FunctorT& functor, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : functor_(functor), + p1_(p1), + p2_(p2), + p3_(p3), + p4_(p4), + p5_(p5) {} + R operator()() const { + return functor_(p1_, p2_, p3_, p4_, p5_); } + private: + FunctorT functor_; + P1 p1_; + P2 p2_; + P3 p3_; + P4 p4_; + P5 p5_; +}; + + #define FP_T(x) R (ObjectT::*x)(P1, P2, P3, P4, P5) template +Functor5 +Bind(FP_T(function), + typename detail::identity::type p1, + typename detail::identity::type p2, + typename detail::identity::type p3, + typename detail::identity::type p4, + typename detail::identity::type p5) { + return Functor5( + function, p1, p2, p3, p4, p5); +} + #undef FP_T } // namespace talk_base diff --git a/talk/base/bind.h.pump b/talk/base/bind.h.pump index 7f4c39e63..2ebb89551 100644 --- a/talk/base/bind.h.pump +++ b/talk/base/bind.h.pump @@ -91,6 +91,24 @@ class MethodFunctor$i { }; +template +class Functor$i { + public: + $if i == 0 [[explicit ]] +Functor$i(const FunctorT& functor$for j [[, P$j p$j]]) + : functor_(functor)$for j [[, + p$(j)_(p$j)]] {} + R operator()() const { + return functor_($for j , [[p$(j)_]]); } + private: + FunctorT functor_;$for j [[ + + P$j p$(j)_;]] + +}; + + #define FP_T(x) R (ObjectT::*x)($for j , [[P$j]]) template +Functor$i +Bind(FP_T(function)$for j [[, + typename detail::identity::type p$j]]) { + return Functor$i( + function$for j [[, p$j]]); +} + #undef FP_T ]] diff --git a/talk/base/bind_unittest.cc b/talk/base/bind_unittest.cc index 81bbddd6b..78ac27837 100644 --- a/talk/base/bind_unittest.cc +++ b/talk/base/bind_unittest.cc @@ -43,6 +43,10 @@ struct MethodBindTester { mutable int call_count; }; +int Return42() { return 42; } +int Negate(int a) { return -a; } +int Multiply(int a, int b) { return a * b; } + } // namespace TEST(BindTest, BindToMethod) { @@ -71,4 +75,10 @@ TEST(BindTest, BindToMethod) { EXPECT_EQ(8, object.call_count); } +TEST(BindTest, BindToFunction) { + EXPECT_EQ(42, Bind(&Return42)()); + EXPECT_EQ(3, Bind(&Negate, -3)()); + EXPECT_EQ(56, Bind(&Multiply, 8, 7)()); +} + } // namespace talk_base diff --git a/talk/base/callback.h b/talk/base/callback.h new file mode 100644 index 000000000..e77276875 --- /dev/null +++ b/talk/base/callback.h @@ -0,0 +1,291 @@ +// This file was GENERATED by command: +// pump.py callback.h.pump +// DO NOT EDIT BY HAND!!! + +/* + * libjingle + * Copyright 2012 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. + */ + +// To generate callback.h from callback.h.pump, execute: +// /home/build/google3/third_party/gtest/scripts/pump.py callback.h.pump + +// Callbacks are callable object containers. They can hold a function pointer +// or a function object and behave like a value type. Internally, data is +// reference-counted, making copies and pass-by-value inexpensive. +// +// Callbacks are typed using template arguments. The format is: +// CallbackN +// where N is the number of arguments supplied to the callable object. +// Callbacks are invoked using operator(), just like a function or a function +// object. Default-constructed callbacks are "empty," and executing an empty +// callback does nothing. A callback can be made empty by assigning it from +// a default-constructed callback. +// +// Callbacks are similar in purpose to std::function (which isn't available on +// all platforms we support) and a lightweight alternative to sigslots. Since +// they effectively hide the type of the object they call, they're useful in +// breaking dependencies between objects that need to interact with one another. +// Notably, they can hold the results of Bind(), std::bind*, etc, without +// needing +// to know the resulting object type of those calls. +// +// Sigslots, on the other hand, provide a fuller feature set, such as multiple +// subscriptions to a signal, optional thread-safety, and lifetime tracking of +// slots. When these features are needed, choose sigslots. +// +// Example: +// int sqr(int x) { return x * x; } +// struct AddK { +// int k; +// int operator()(int x) const { return x + k; } +// } add_k = {5}; +// +// Callback1 my_callback; +// cout << my_callback.empty() << endl; // true +// +// my_callback = Callback1(&sqr); +// cout << my_callback.empty() << endl; // false +// cout << my_callback(3) << endl; // 9 +// +// my_callback = Callback1(add_k); +// cout << my_callback(10) << endl; // 15 +// +// my_callback = Callback1(); +// cout << my_callback.empty() << endl; // true + +#ifndef TALK_BASE_CALLBACK_H_ +#define TALK_BASE_CALLBACK_H_ + +#include "talk/base/logging.h" +#include "talk/base/refcount.h" +#include "talk/base/scoped_ref_ptr.h" + +namespace talk_base { + +template +class Callback0 { + public: + // Default copy operations are appropriate for this class. + Callback0() {} + template Callback0(const T& functor) + : helper_(new RefCountedObject< HelperImpl >(functor)) {} + R operator()() { + if (empty()) { + LOG_F(LS_WARNING) << "Tried to execute an empty callback."; + return R(); + } + return helper_->Run(); + } + bool empty() const { return !helper_; } + + private: + struct Helper : RefCountInterface { + virtual ~Helper() {} + virtual R Run() = 0; + }; + template struct HelperImpl : Helper { + explicit HelperImpl(const T& functor) : functor_(functor) {} + virtual R Run() { + return functor_(); + } + T functor_; + }; + scoped_refptr helper_; +}; + +template +class Callback1 { + public: + // Default copy operations are appropriate for this class. + Callback1() {} + template Callback1(const T& functor) + : helper_(new RefCountedObject< HelperImpl >(functor)) {} + R operator()(P1 p1) { + if (empty()) { + LOG_F(LS_WARNING) << "Tried to execute an empty callback."; + return R(); + } + return helper_->Run(p1); + } + bool empty() const { return !helper_; } + + private: + struct Helper : RefCountInterface { + virtual ~Helper() {} + virtual R Run(P1 p1) = 0; + }; + template struct HelperImpl : Helper { + explicit HelperImpl(const T& functor) : functor_(functor) {} + virtual R Run(P1 p1) { + return functor_(p1); + } + T functor_; + }; + scoped_refptr helper_; +}; + +template +class Callback2 { + public: + // Default copy operations are appropriate for this class. + Callback2() {} + template Callback2(const T& functor) + : helper_(new RefCountedObject< HelperImpl >(functor)) {} + R operator()(P1 p1, P2 p2) { + if (empty()) { + LOG_F(LS_WARNING) << "Tried to execute an empty callback."; + return R(); + } + return helper_->Run(p1, p2); + } + bool empty() const { return !helper_; } + + private: + struct Helper : RefCountInterface { + virtual ~Helper() {} + virtual R Run(P1 p1, P2 p2) = 0; + }; + template struct HelperImpl : Helper { + explicit HelperImpl(const T& functor) : functor_(functor) {} + virtual R Run(P1 p1, P2 p2) { + return functor_(p1, p2); + } + T functor_; + }; + scoped_refptr helper_; +}; + +template +class Callback3 { + public: + // Default copy operations are appropriate for this class. + Callback3() {} + template Callback3(const T& functor) + : helper_(new RefCountedObject< HelperImpl >(functor)) {} + R operator()(P1 p1, P2 p2, P3 p3) { + if (empty()) { + LOG_F(LS_WARNING) << "Tried to execute an empty callback."; + return R(); + } + return helper_->Run(p1, p2, p3); + } + bool empty() const { return !helper_; } + + private: + struct Helper : RefCountInterface { + virtual ~Helper() {} + virtual R Run(P1 p1, P2 p2, P3 p3) = 0; + }; + template struct HelperImpl : Helper { + explicit HelperImpl(const T& functor) : functor_(functor) {} + virtual R Run(P1 p1, P2 p2, P3 p3) { + return functor_(p1, p2, p3); + } + T functor_; + }; + scoped_refptr helper_; +}; + +template +class Callback4 { + public: + // Default copy operations are appropriate for this class. + Callback4() {} + template Callback4(const T& functor) + : helper_(new RefCountedObject< HelperImpl >(functor)) {} + R operator()(P1 p1, P2 p2, P3 p3, P4 p4) { + if (empty()) { + LOG_F(LS_WARNING) << "Tried to execute an empty callback."; + return R(); + } + return helper_->Run(p1, p2, p3, p4); + } + bool empty() const { return !helper_; } + + private: + struct Helper : RefCountInterface { + virtual ~Helper() {} + virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4) = 0; + }; + template struct HelperImpl : Helper { + explicit HelperImpl(const T& functor) : functor_(functor) {} + virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4) { + return functor_(p1, p2, p3, p4); + } + T functor_; + }; + scoped_refptr helper_; +}; + +template +class Callback5 { + public: + // Default copy operations are appropriate for this class. + Callback5() {} + template Callback5(const T& functor) + : helper_(new RefCountedObject< HelperImpl >(functor)) {} + R operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) { + if (empty()) { + LOG_F(LS_WARNING) << "Tried to execute an empty callback."; + return R(); + } + return helper_->Run(p1, p2, p3, p4, p5); + } + bool empty() const { return !helper_; } + + private: + struct Helper : RefCountInterface { + virtual ~Helper() {} + virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) = 0; + }; + template struct HelperImpl : Helper { + explicit HelperImpl(const T& functor) : functor_(functor) {} + virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) { + return functor_(p1, p2, p3, p4, p5); + } + T functor_; + }; + scoped_refptr helper_; +}; +} // namespace talk_base + + +#endif // TALK_BASE_CALLBACK_H_ diff --git a/talk/base/callback.h.pump b/talk/base/callback.h.pump new file mode 100644 index 000000000..ae9ab87de --- /dev/null +++ b/talk/base/callback.h.pump @@ -0,0 +1,123 @@ +/* + * libjingle + * Copyright 2012 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. + */ + +// To generate callback.h from callback.h.pump, execute: +// /home/build/google3/third_party/gtest/scripts/pump.py callback.h.pump + +// Callbacks are callable object containers. They can hold a function pointer +// or a function object and behave like a value type. Internally, data is +// reference-counted, making copies and pass-by-value inexpensive. +// +// Callbacks are typed using template arguments. The format is: +// CallbackN +// where N is the number of arguments supplied to the callable object. +// Callbacks are invoked using operator(), just like a function or a function +// object. Default-constructed callbacks are "empty," and executing an empty +// callback does nothing. A callback can be made empty by assigning it from +// a default-constructed callback. +// +// Callbacks are similar in purpose to std::function (which isn't available on +// all platforms we support) and a lightweight alternative to sigslots. Since +// they effectively hide the type of the object they call, they're useful in +// breaking dependencies between objects that need to interact with one another. +// Notably, they can hold the results of Bind(), std::bind*, etc, without needing +// to know the resulting object type of those calls. +// +// Sigslots, on the other hand, provide a fuller feature set, such as multiple +// subscriptions to a signal, optional thread-safety, and lifetime tracking of +// slots. When these features are needed, choose sigslots. +// +// Example: +// int sqr(int x) { return x * x; } +// struct AddK { +// int k; +// int operator()(int x) const { return x + k; } +// } add_k = {5}; +// +// Callback1 my_callback; +// cout << my_callback.empty() << endl; // true +// +// my_callback = Callback1(&sqr); +// cout << my_callback.empty() << endl; // false +// cout << my_callback(3) << endl; // 9 +// +// my_callback = Callback1(add_k); +// cout << my_callback(10) << endl; // 15 +// +// my_callback = Callback1(); +// cout << my_callback.empty() << endl; // true + +#ifndef TALK_BASE_CALLBACK_H_ +#define TALK_BASE_CALLBACK_H_ + +#include "talk/base/logging.h" +#include "talk/base/refcount.h" +#include "talk/base/scoped_ref_ptr.h" + +namespace talk_base { + +$var n = 5 +$range i 0..n +$for i [[ +$range j 1..i + +template +class Callback$i { + public: + // Default copy operations are appropriate for this class. + Callback$i() {} + template Callback$i(const T& functor) + : helper_(new RefCountedObject< HelperImpl >(functor)) {} + R operator()($for j , [[P$j p$j]]) { + if (empty()) { + LOG_F(LS_WARNING) << "Tried to execute an empty callback."; + return R(); + } + return helper_->Run($for j , [[p$j]]); + } + bool empty() const { return !helper_; } + + private: + struct Helper : RefCountInterface { + virtual ~Helper() {} + virtual R Run($for j , [[P$j p$j]]) = 0; + }; + template struct HelperImpl : Helper { + explicit HelperImpl(const T& functor) : functor_(functor) {} + virtual R Run($for j , [[P$j p$j]]) { + return functor_($for j , [[p$j]]); + } + T functor_; + }; + scoped_refptr helper_; +}; + +]] +} // namespace talk_base + +#endif // TALK_BASE_CALLBACK_H_ \ No newline at end of file diff --git a/talk/base/callback_unittest.cc b/talk/base/callback_unittest.cc new file mode 100644 index 000000000..c7ca00f0f --- /dev/null +++ b/talk/base/callback_unittest.cc @@ -0,0 +1,98 @@ +/* + * libjingle + * Copyright 2004--2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "talk/base/bind.h" +#include "talk/base/callback.h" +#include "talk/base/gunit.h" + +namespace talk_base { + +namespace { + +void f() {} +int g() { return 42; } +int h(int x) { return x * x; } +void i(int& x) { x *= x; } // NOLINT: Testing refs + +struct BindTester { + int a() { return 24; } + int b(int x) const { return x * x; } +}; + +} // namespace + +TEST(CallbackTest, VoidReturn) { + Callback0 cb; + EXPECT_TRUE(cb.empty()); + cb(); // Executing an empty callback should not crash. + cb = Callback0(&f); + EXPECT_FALSE(cb.empty()); + cb(); +} + +TEST(CallbackTest, IntReturn) { + Callback0 cb; + EXPECT_TRUE(cb.empty()); + cb = Callback0(&g); + EXPECT_FALSE(cb.empty()); + EXPECT_EQ(42, cb()); + EXPECT_EQ(42, cb()); +} + +TEST(CallbackTest, OneParam) { + Callback1 cb1(&h); + EXPECT_FALSE(cb1.empty()); + EXPECT_EQ(9, cb1(-3)); + EXPECT_EQ(100, cb1(10)); + + // Try clearing a callback. + cb1 = Callback1(); + EXPECT_TRUE(cb1.empty()); + + // Try a callback with a ref parameter. + Callback1 cb2(&i); + int x = 3; + cb2(x); + EXPECT_EQ(9, x); + cb2(x); + EXPECT_EQ(81, x); +} + +TEST(CallbackTest, WithBind) { + BindTester t; + Callback0 cb1 = Bind(&BindTester::a, &t); + EXPECT_EQ(24, cb1()); + EXPECT_EQ(24, cb1()); + cb1 = Bind(&BindTester::b, &t, 10); + EXPECT_EQ(100, cb1()); + EXPECT_EQ(100, cb1()); + cb1 = Bind(&BindTester::b, &t, 5); + EXPECT_EQ(25, cb1()); + EXPECT_EQ(25, cb1()); +} + +} // namespace talk_base diff --git a/talk/base/messagehandler.h b/talk/base/messagehandler.h index 913edf8ce..8b9e5f6d1 100644 --- a/talk/base/messagehandler.h +++ b/talk/base/messagehandler.h @@ -28,6 +28,7 @@ #ifndef TALK_BASE_MESSAGEHANDLER_H_ #define TALK_BASE_MESSAGEHANDLER_H_ +#include "talk/base/callback.h" #include "talk/base/constructormagic.h" namespace talk_base { @@ -38,16 +39,52 @@ struct Message; class MessageHandler { public: + virtual ~MessageHandler(); virtual void OnMessage(Message* msg) = 0; protected: MessageHandler() {} - virtual ~MessageHandler(); private: DISALLOW_COPY_AND_ASSIGN(MessageHandler); }; +// Helper class to facilitate executing a functor on a thread. +template +class FunctorMessageHandler : public MessageHandler { + public: + explicit FunctorMessageHandler(const FunctorT& functor) + : functor_(functor) {} + virtual void OnMessage(Message* msg) { + result_ = functor_(); + if (!callback_.empty()) callback_(); + } + const ReturnT& result() const { return result_; } + void SetCallback(const Callback0& callback) { callback_ = callback; } + private: + FunctorT functor_; + ReturnT result_; + Callback0 callback_; +}; + +// Specialization for ReturnT of void. +template +class FunctorMessageHandler : public MessageHandler { + public: + explicit FunctorMessageHandler(const FunctorT& functor) + : functor_(functor) {} + virtual void OnMessage(Message* msg) { + functor_(); + if (!callback_.empty()) callback_(); + } + void result() const {} + void SetCallback(const Callback0& callback) { callback_ = callback; } + private: + FunctorT functor_; + Callback0 callback_; +}; + + } // namespace talk_base #endif // TALK_BASE_MESSAGEHANDLER_H_ diff --git a/talk/base/scopedptrcollection.h b/talk/base/scopedptrcollection.h new file mode 100644 index 000000000..ec2726e27 --- /dev/null +++ b/talk/base/scopedptrcollection.h @@ -0,0 +1,77 @@ +/* + * libjingle + * Copyright 2014 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Stores a collection of pointers that are deleted when the container is +// destructed. + +#ifndef TALK_BASE_SCOPEDPTRCOLLECTION_H_ +#define TALK_BASE_SCOPEDPTRCOLLECTION_H_ + +#include +#include + +#include "talk/base/basictypes.h" +#include "talk/base/constructormagic.h" + +namespace talk_base { + +template +class ScopedPtrCollection { + public: + typedef std::vector VectorT; + + ScopedPtrCollection() { } + ~ScopedPtrCollection() { + for (typename VectorT::iterator it = collection_.begin(); + it != collection_.end(); ++it) { + delete *it; + } + } + + const VectorT& collection() const { return collection_; } + void Reserve(size_t size) { + collection_.reserve(size); + } + void PushBack(T* t) { + collection_.push_back(t); + } + + // Remove |t| from the collection without deleting it. + void Remove(T* t) { + collection_.erase(std::remove(collection_.begin(), collection_.end(), t), + collection_.end()); + } + + private: + VectorT collection_; + + DISALLOW_COPY_AND_ASSIGN(ScopedPtrCollection); +}; + +} // namespace talk_base + +#endif // TALK_BASE_SCOPEDPTRCOLLECTION_H_ diff --git a/talk/base/scopedptrcollection_unittest.cc b/talk/base/scopedptrcollection_unittest.cc new file mode 100644 index 000000000..dd9002e42 --- /dev/null +++ b/talk/base/scopedptrcollection_unittest.cc @@ -0,0 +1,90 @@ +/* + * libjingle + * Copyright 2014 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "talk/base/scopedptrcollection.h" +#include "talk/base/gunit.h" + +namespace talk_base { + +namespace { + +class InstanceCounter { + public: + explicit InstanceCounter(int* num_instances) + : num_instances_(num_instances) { + ++(*num_instances_); + } + ~InstanceCounter() { + --(*num_instances_); + } + + private: + int* num_instances_; + + DISALLOW_COPY_AND_ASSIGN(InstanceCounter); +}; + +} // namespace + +class ScopedPtrCollectionTest : public testing::Test { + protected: + ScopedPtrCollectionTest() + : num_instances_(0), + collection_(new ScopedPtrCollection()) { + } + + int num_instances_; + scoped_ptr > collection_; +}; + +TEST_F(ScopedPtrCollectionTest, PushBack) { + EXPECT_EQ(0u, collection_->collection().size()); + EXPECT_EQ(0, num_instances_); + const int kNum = 100; + for (int i = 0; i < kNum; ++i) { + collection_->PushBack(new InstanceCounter(&num_instances_)); + } + EXPECT_EQ(static_cast(kNum), collection_->collection().size()); + EXPECT_EQ(kNum, num_instances_); + collection_.reset(); + EXPECT_EQ(0, num_instances_); +} + +TEST_F(ScopedPtrCollectionTest, Remove) { + InstanceCounter* ic = new InstanceCounter(&num_instances_); + collection_->PushBack(ic); + EXPECT_EQ(1u, collection_->collection().size()); + collection_->Remove(ic); + EXPECT_EQ(1, num_instances_); + collection_.reset(); + EXPECT_EQ(1, num_instances_); + delete ic; + EXPECT_EQ(0, num_instances_); +} + + +} // namespace talk_base diff --git a/talk/base/thread.h b/talk/base/thread.h index 4dc09f641..316f04197 100644 --- a/talk/base/thread.h +++ b/talk/base/thread.h @@ -223,33 +223,6 @@ class Thread : public MessageQueue { void Join(); private: - // Helper class to facilitate executing a functor on a thread. - template - class FunctorMessageHandler : public MessageHandler { - public: - explicit FunctorMessageHandler(const FunctorT& functor) - : functor_(functor) {} - virtual void OnMessage(Message* msg) { - result_ = functor_(); - } - const ReturnT& result() const { return result_; } - private: - FunctorT functor_; - ReturnT result_; - }; - - // Specialization for ReturnT of void. - template - class FunctorMessageHandler : public MessageHandler { - public: - explicit FunctorMessageHandler(const FunctorT& functor) - : functor_(functor) {} - virtual void OnMessage(Message* msg) { functor_(); } - void result() const {} - private: - FunctorT functor_; - }; - static void *PreRun(void *pv); // ThreadManager calls this instead WrapCurrent() because diff --git a/talk/base/thread_unittest.cc b/talk/base/thread_unittest.cc index 896fbabc5..ddf6326a2 100644 --- a/talk/base/thread_unittest.cc +++ b/talk/base/thread_unittest.cc @@ -25,6 +25,7 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "talk/base/asyncinvoker.h" #include "talk/base/asyncudpsocket.h" #include "talk/base/event.h" #include "talk/base/gunit.h" @@ -150,16 +151,22 @@ class SignalWhenDestroyedThread : public Thread { }; // Function objects to test Thread::Invoke. -struct Functor1 { +struct FunctorA { int operator()() { return 42; } }; -class Functor2 { +class FunctorB { public: - explicit Functor2(bool* flag) : flag_(flag) {} + explicit FunctorB(bool* flag) : flag_(flag) {} void operator()() { if (flag_) *flag_ = true; } private: bool* flag_; }; +struct FunctorC { + int operator()() { + Thread::Current()->ProcessMessages(50); + return 24; + } +}; // See: https://code.google.com/p/webrtc/issues/detail?id=2409 TEST(ThreadTest, DISABLED_Main) { @@ -289,9 +296,9 @@ TEST(ThreadTest, Invoke) { Thread thread; thread.Start(); // Try calling functors. - EXPECT_EQ(42, thread.Invoke(Functor1())); + EXPECT_EQ(42, thread.Invoke(FunctorA())); bool called = false; - Functor2 f2(&called); + FunctorB f2(&called); thread.Invoke(f2); EXPECT_TRUE(called); // Try calling bare functions. @@ -303,6 +310,152 @@ TEST(ThreadTest, Invoke) { thread.Invoke(&LocalFuncs::Func2); } +class AsyncInvokeTest : public testing::Test { + public: + void IntCallback(int value) { + EXPECT_EQ(expected_thread_, Thread::Current()); + int_value_ = value; + } + void AsyncInvokeIntCallback(AsyncInvoker* invoker, Thread* thread) { + expected_thread_ = thread; + invoker->AsyncInvoke(thread, FunctorC(), + &AsyncInvokeTest::IntCallback, + static_cast(this)); + invoke_started_.Set(); + } + void SetExpectedThreadForIntCallback(Thread* thread) { + expected_thread_ = thread; + } + + protected: + enum { kWaitTimeout = 1000 }; + AsyncInvokeTest() + : int_value_(0), + invoke_started_(true, false), + expected_thread_(NULL) {} + + int int_value_; + Event invoke_started_; + Thread* expected_thread_; +}; + +TEST_F(AsyncInvokeTest, FireAndForget) { + AsyncInvoker invoker; + // Create and start the thread. + Thread thread; + thread.Start(); + // Try calling functor. + bool called = false; + invoker.AsyncInvoke(&thread, FunctorB(&called)); + EXPECT_TRUE_WAIT(called, kWaitTimeout); +} + +TEST_F(AsyncInvokeTest, WithCallback) { + AsyncInvoker invoker; + // Create and start the thread. + Thread thread; + thread.Start(); + // Try calling functor. + SetExpectedThreadForIntCallback(Thread::Current()); + invoker.AsyncInvoke(&thread, FunctorA(), + &AsyncInvokeTest::IntCallback, + static_cast(this)); + EXPECT_EQ_WAIT(42, int_value_, kWaitTimeout); +} + +TEST_F(AsyncInvokeTest, CancelInvoker) { + // Create and start the thread. + Thread thread; + thread.Start(); + // Try destroying invoker during call. + { + AsyncInvoker invoker; + invoker.AsyncInvoke(&thread, FunctorC(), + &AsyncInvokeTest::IntCallback, + static_cast(this)); + } + // With invoker gone, callback should be cancelled. + Thread::Current()->ProcessMessages(kWaitTimeout); + EXPECT_EQ(0, int_value_); +} + +TEST_F(AsyncInvokeTest, CancelCallingThread) { + AsyncInvoker invoker; + { // Create and start the thread. + Thread thread; + thread.Start(); + // Try calling functor. + thread.Invoke(Bind(&AsyncInvokeTest::AsyncInvokeIntCallback, + static_cast(this), + &invoker, Thread::Current())); + // Wait for the call to begin. + ASSERT_TRUE(invoke_started_.Wait(kWaitTimeout)); + } + // Calling thread is gone. Return message shouldn't happen. + Thread::Current()->ProcessMessages(kWaitTimeout); + EXPECT_EQ(0, int_value_); +} + +TEST_F(AsyncInvokeTest, KillInvokerBeforeExecute) { + Thread thread; + thread.Start(); + { + AsyncInvoker invoker; + // Try calling functor. + thread.Invoke(Bind(&AsyncInvokeTest::AsyncInvokeIntCallback, + static_cast(this), + &invoker, Thread::Current())); + // Wait for the call to begin. + ASSERT_TRUE(invoke_started_.Wait(kWaitTimeout)); + } + // Invoker is destroyed. Function should not execute. + Thread::Current()->ProcessMessages(kWaitTimeout); + EXPECT_EQ(0, int_value_); +} + +TEST_F(AsyncInvokeTest, Flush) { + AsyncInvoker invoker; + bool flag1 = false; + bool flag2 = false; + // Queue two async calls to the current thread. + invoker.AsyncInvoke(Thread::Current(), + FunctorB(&flag1)); + invoker.AsyncInvoke(Thread::Current(), + FunctorB(&flag2)); + // Because we haven't pumped messages, these should not have run yet. + EXPECT_FALSE(flag1); + EXPECT_FALSE(flag2); + // Force them to run now. + invoker.Flush(Thread::Current()); + EXPECT_TRUE(flag1); + EXPECT_TRUE(flag2); +} + +TEST_F(AsyncInvokeTest, FlushWithIds) { + AsyncInvoker invoker; + bool flag1 = false; + bool flag2 = false; + // Queue two async calls to the current thread, one with a message id. + invoker.AsyncInvoke(Thread::Current(), + FunctorB(&flag1), + 5); + invoker.AsyncInvoke(Thread::Current(), + FunctorB(&flag2)); + // Because we haven't pumped messages, these should not have run yet. + EXPECT_FALSE(flag1); + EXPECT_FALSE(flag2); + // Execute pending calls with id == 5. + invoker.Flush(Thread::Current(), 5); + EXPECT_TRUE(flag1); + EXPECT_FALSE(flag2); + flag1 = false; + // Execute all pending calls. The id == 5 call should not execute again. + invoker.Flush(Thread::Current()); + EXPECT_FALSE(flag1); + EXPECT_TRUE(flag2); +} + + #ifdef WIN32 class ComThreadTest : public testing::Test, public MessageHandler { public: diff --git a/talk/libjingle.gyp b/talk/libjingle.gyp index bde7ee0a6..3904a0feb 100755 --- a/talk/libjingle.gyp +++ b/talk/libjingle.gyp @@ -260,6 +260,8 @@ 'base/asyncfile.h', 'base/asynchttprequest.cc', 'base/asynchttprequest.h', + 'base/asyncinvoker.cc', + 'base/asyncinvoker.h', 'base/asyncpacketsocket.h', 'base/asyncresolverinterface.h', 'base/asyncsocket.cc', @@ -282,6 +284,7 @@ 'base/bytebuffer.cc', 'base/bytebuffer.h', 'base/byteorder.h', + 'base/callback.h', 'base/checks.cc', 'base/checks.h', 'base/common.cc', @@ -383,6 +386,7 @@ 'base/scoped_autorelease_pool.h', 'base/scoped_ptr.h', 'base/scoped_ref_ptr.h', + 'base/scopedptrcollection.h', 'base/sec_buffer.h', 'base/sha1.cc', 'base/sha1.h', diff --git a/talk/libjingle.scons b/talk/libjingle.scons index d879cc277..dc5e9a067 100644 --- a/talk/libjingle.scons +++ b/talk/libjingle.scons @@ -154,6 +154,7 @@ talk.Library(env, name = "jingle", srcs = [ "base/asyncfile.cc", "base/asynchttprequest.cc", + "base/asyncinvoker.cc", "base/asyncsocket.cc", "base/asynctcpsocket.cc", "base/asyncudpsocket.cc", @@ -540,6 +541,7 @@ talk.Unittest(env, name = "base", "base/buffer_unittest.cc", "base/bytebuffer_unittest.cc", "base/byteorder_unittest.cc", + "base/callback_unittest.cc", "base/cpumonitor_unittest.cc", "base/crc32_unittest.cc", "base/event_unittest.cc", @@ -569,6 +571,7 @@ talk.Unittest(env, name = "base", "base/ratetracker_unittest.cc", "base/referencecountedsingletonfactory_unittest.cc", "base/rollingaccumulator_unittest.cc", + "base/scopedptrcollection_unittest.cc", "base/sha1digest_unittest.cc", "base/sharedexclusivelock_unittest.cc", "base/signalthread_unittest.cc", diff --git a/talk/libjingle_tests.gyp b/talk/libjingle_tests.gyp index e36a369ef..038fb4f1a 100755 --- a/talk/libjingle_tests.gyp +++ b/talk/libjingle_tests.gyp @@ -120,6 +120,7 @@ 'base/buffer_unittest.cc', 'base/bytebuffer_unittest.cc', 'base/byteorder_unittest.cc', + 'base/callback_unittest.cc', 'base/cpumonitor_unittest.cc', 'base/crc32_unittest.cc', 'base/event_unittest.cc', @@ -148,6 +149,7 @@ 'base/ratetracker_unittest.cc', 'base/referencecountedsingletonfactory_unittest.cc', 'base/rollingaccumulator_unittest.cc', + 'base/scopedptrcollection_unittest.cc', 'base/sha1digest_unittest.cc', 'base/sharedexclusivelock_unittest.cc', 'base/signalthread_unittest.cc', diff --git a/talk/media/base/mediachannel.h b/talk/media/base/mediachannel.h index c2ea26f1b..ef87c9e92 100644 --- a/talk/media/base/mediachannel.h +++ b/talk/media/base/mediachannel.h @@ -168,6 +168,7 @@ struct AudioOptions { adjust_agc_delta.SetFrom(change.adjust_agc_delta); experimental_agc.SetFrom(change.experimental_agc); experimental_aec.SetFrom(change.experimental_aec); + experimental_ns.SetFrom(change.experimental_ns); aec_dump.SetFrom(change.aec_dump); experimental_acm.SetFrom(change.experimental_acm); tx_agc_target_dbov.SetFrom(change.tx_agc_target_dbov); @@ -195,6 +196,7 @@ struct AudioOptions { conference_mode == o.conference_mode && experimental_agc == o.experimental_agc && experimental_aec == o.experimental_aec && + experimental_ns == o.experimental_ns && adjust_agc_delta == o.adjust_agc_delta && aec_dump == o.aec_dump && experimental_acm == o.experimental_acm && @@ -224,6 +226,7 @@ struct AudioOptions { ost << ToStringIfSet("agc_delta", adjust_agc_delta); ost << ToStringIfSet("experimental_agc", experimental_agc); ost << ToStringIfSet("experimental_aec", experimental_aec); + ost << ToStringIfSet("experimental_ns", experimental_ns); ost << ToStringIfSet("aec_dump", aec_dump); ost << ToStringIfSet("experimental_acm", experimental_acm); ost << ToStringIfSet("tx_agc_target_dbov", tx_agc_target_dbov); @@ -261,6 +264,7 @@ struct AudioOptions { Settable adjust_agc_delta; Settable experimental_agc; Settable experimental_aec; + Settable experimental_ns; Settable aec_dump; Settable experimental_acm; // Note that tx_agc_* only applies to non-experimental AGC. @@ -763,8 +767,10 @@ struct VideoSenderInfo : public MediaSenderInfo { : packets_cached(0), firs_rcvd(0), nacks_rcvd(0), - frame_width(0), - frame_height(0), + input_frame_width(0), + input_frame_height(0), + send_frame_width(0), + send_frame_height(0), framerate_input(0), framerate_sent(0), nominal_bitrate(0), @@ -780,8 +786,10 @@ struct VideoSenderInfo : public MediaSenderInfo { int packets_cached; int firs_rcvd; int nacks_rcvd; - int frame_width; - int frame_height; + int input_frame_width; + int input_frame_height; + int send_frame_width; + int send_frame_height; int framerate_input; int framerate_sent; int nominal_bitrate; diff --git a/talk/media/base/videoadapter.cc b/talk/media/base/videoadapter.cc index 3cd6cac96..29be80531 100644 --- a/talk/media/base/videoadapter.cc +++ b/talk/media/base/videoadapter.cc @@ -162,8 +162,9 @@ float VideoAdapter::FindLowerScale(int width, int height, VideoAdapter::VideoAdapter() : output_num_pixels_(INT_MAX), scale_third_(false), - frames_(0), - adapted_frames_(0), + frames_in_(0), + frames_out_(0), + frames_scaled_(0), adaption_changes_(0), previous_width_(0), previous_height_(0), @@ -177,9 +178,13 @@ VideoAdapter::~VideoAdapter() { void VideoAdapter::SetInputFormat(const VideoFormat& format) { talk_base::CritScope cs(&critical_section_); + int64 old_input_interval = input_format_.interval; input_format_ = format; output_format_.interval = talk_base::_max( output_format_.interval, input_format_.interval); + if (old_input_interval != input_format_.interval) { + LOG(LS_INFO) << "VAdapt Input Interval: " << input_format_.interval; + } } void CoordinatedVideoAdapter::SetInputFormat(const VideoFormat& format) { @@ -207,10 +212,14 @@ void CoordinatedVideoAdapter::SetInputFormat(const VideoFormat& format) { void VideoAdapter::SetOutputFormat(const VideoFormat& format) { talk_base::CritScope cs(&critical_section_); + int64 old_output_interval = output_format_.interval; output_format_ = format; output_num_pixels_ = output_format_.width * output_format_.height; output_format_.interval = talk_base::_max( output_format_.interval, input_format_.interval); + if (old_output_interval != output_format_.interval) { + LOG(LS_INFO) << "VAdapt Output Interval: " << output_format_.interval; + } } const VideoFormat& VideoAdapter::input_format() { @@ -245,7 +254,7 @@ bool VideoAdapter::AdaptFrame(const VideoFrame* in_frame, if (!in_frame || !out_frame) { return false; } - ++frames_; + ++frames_in_; // Update input to actual frame dimensions. VideoFormat format(static_cast(in_frame->GetWidth()), @@ -273,6 +282,23 @@ bool VideoAdapter::AdaptFrame(const VideoFrame* in_frame, } } if (should_drop) { + // Show VAdapt log every 90 frames dropped. (3 seconds) + // TODO(fbarchard): Consider GetLogSeverity() to change interval to less + // for LS_VERBOSE and more for LS_INFO. + bool show = (frames_in_ - frames_out_) % 90 == 0; + + if (show) { + // TODO(fbarchard): Reduce to LS_VERBOSE when adapter info is not needed + // in default calls. + LOG(LS_INFO) << "VAdapt Drop Frame: " << frames_scaled_ + << " / " << frames_out_ + << " / " << frames_in_ + << " Changes: " << adaption_changes_ + << " Input: " << in_frame->GetWidth() + << "x" << in_frame->GetHeight() + << " i" << input_format_.interval + << " Output: i" << output_format_.interval; + } *out_frame = NULL; return true; } @@ -289,19 +315,22 @@ bool VideoAdapter::AdaptFrame(const VideoFrame* in_frame, } if (!StretchToOutputFrame(in_frame)) { + LOG(LS_VERBOSE) << "VAdapt Stretch Failed."; return false; } *out_frame = output_frame_.get(); - // Show VAdapt log every 300 frames. (10 seconds) - // TODO(fbarchard): Consider GetLogSeverity() to change interval to less - // for LS_VERBOSE and more for LS_INFO. - bool show = frames_ % 300 == 0; + ++frames_out_; if (in_frame->GetWidth() != (*out_frame)->GetWidth() || in_frame->GetHeight() != (*out_frame)->GetHeight()) { - ++adapted_frames_; + ++frames_scaled_; } + // Show VAdapt log every 90 frames output. (3 seconds) + // TODO(fbarchard): Consider GetLogSeverity() to change interval to less + // for LS_VERBOSE and more for LS_INFO. + bool show = (frames_out_) % 90 == 0; + // TODO(fbarchard): LOG the previous output resolution and track input // resolution changes as well. Consider dropping the statistics into their // own class which could be queried publically. @@ -315,14 +344,17 @@ bool VideoAdapter::AdaptFrame(const VideoFrame* in_frame, if (show) { // TODO(fbarchard): Reduce to LS_VERBOSE when adapter info is not needed // in default calls. - LOG(LS_INFO) << "VAdapt Frame: " << adapted_frames_ - << " / " << frames_ + LOG(LS_INFO) << "VAdapt Frame: " << frames_scaled_ + << " / " << frames_out_ + << " / " << frames_in_ << " Changes: " << adaption_changes_ << " Input: " << in_frame->GetWidth() << "x" << in_frame->GetHeight() + << " i" << input_format_.interval << " Scale: " << scale << " Output: " << (*out_frame)->GetWidth() << "x" << (*out_frame)->GetHeight() + << " i" << output_format_.interval << " Changed: " << (changed ? "true" : "false"); } previous_width_ = (*out_frame)->GetWidth(); diff --git a/talk/media/base/videoadapter.h b/talk/media/base/videoadapter.h index 272df72de..6b1cdc5f9 100644 --- a/talk/media/base/videoadapter.h +++ b/talk/media/base/videoadapter.h @@ -87,8 +87,9 @@ class VideoAdapter { VideoFormat output_format_; int output_num_pixels_; bool scale_third_; // True if adapter allows scaling to 1/3 and 2/3. - int frames_; // Number of input frames. - int adapted_frames_; // Number of frames scaled. + int frames_in_; // Number of input frames. + int frames_out_; // Number of output frames. + int frames_scaled_; // Number of frames scaled. int adaption_changes_; // Number of changes in scale factor. size_t previous_width_; // Previous adapter output width. size_t previous_height_; // Previous adapter output height. diff --git a/talk/media/base/videoengine_unittest.h b/talk/media/base/videoengine_unittest.h index b667cb0f0..e1c4a7d5c 100644 --- a/talk/media/base/videoengine_unittest.h +++ b/talk/media/base/videoengine_unittest.h @@ -790,8 +790,8 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_EQ(0.0, info.senders[0].fraction_lost); EXPECT_EQ(0, info.senders[0].firs_rcvd); EXPECT_EQ(0, info.senders[0].nacks_rcvd); - EXPECT_EQ(DefaultCodec().width, info.senders[0].frame_width); - EXPECT_EQ(DefaultCodec().height, info.senders[0].frame_height); + EXPECT_EQ(DefaultCodec().width, info.senders[0].send_frame_width); + EXPECT_EQ(DefaultCodec().height, info.senders[0].send_frame_height); EXPECT_GT(info.senders[0].framerate_input, 0); EXPECT_GT(info.senders[0].framerate_sent, 0); @@ -848,8 +848,8 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_EQ(0.0, info.senders[0].fraction_lost); EXPECT_EQ(0, info.senders[0].firs_rcvd); EXPECT_EQ(0, info.senders[0].nacks_rcvd); - EXPECT_EQ(DefaultCodec().width, info.senders[0].frame_width); - EXPECT_EQ(DefaultCodec().height, info.senders[0].frame_height); + EXPECT_EQ(DefaultCodec().width, info.senders[0].send_frame_width); + EXPECT_EQ(DefaultCodec().height, info.senders[0].send_frame_height); EXPECT_GT(info.senders[0].framerate_input, 0); EXPECT_GT(info.senders[0].framerate_sent, 0); @@ -918,12 +918,12 @@ class VideoMediaChannelTest : public testing::Test, info.senders[0].packets_sent + info.senders[1].packets_sent); EXPECT_EQ(1U, info.senders[0].ssrcs().size()); EXPECT_EQ(1234U, info.senders[0].ssrcs()[0]); - EXPECT_EQ(DefaultCodec().width, info.senders[0].frame_width); - EXPECT_EQ(DefaultCodec().height, info.senders[0].frame_height); + EXPECT_EQ(DefaultCodec().width, info.senders[0].send_frame_width); + EXPECT_EQ(DefaultCodec().height, info.senders[0].send_frame_height); EXPECT_EQ(1U, info.senders[1].ssrcs().size()); EXPECT_EQ(5678U, info.senders[1].ssrcs()[0]); - EXPECT_EQ(kTestWidth, info.senders[1].frame_width); - EXPECT_EQ(kTestHeight, info.senders[1].frame_height); + EXPECT_EQ(kTestWidth, info.senders[1].send_frame_width); + EXPECT_EQ(kTestHeight, info.senders[1].send_frame_height); // The capturer must be unregistered here as it runs out of it's scope next. EXPECT_TRUE(channel_->SetCapturer(5678, NULL)); } diff --git a/talk/media/devices/v4llookup.cc b/talk/media/devices/v4llookup.cc index ff128a4ae..e1ef9feaa 100644 --- a/talk/media/devices/v4llookup.cc +++ b/talk/media/devices/v4llookup.cc @@ -50,10 +50,10 @@ bool V4LLookup::CheckIsV4L2Device(const std::string& device_path) { is_v4l2 = true; } else { - LOG(LS_ERROR) << "VIDIOC_QUERYCAP failed for " << device_path; + LOG_ERRNO(LS_ERROR) << "VIDIOC_QUERYCAP failed for " << device_path; } } else { - LOG(LS_ERROR) << "Failed to open " << device_path; + LOG_ERRNO(LS_ERROR) << "Failed to open " << device_path; } } } diff --git a/talk/media/webrtc/fakewebrtcvideocapturemodule.h b/talk/media/webrtc/fakewebrtcvideocapturemodule.h index 5628e01bb..d63e710a7 100644 --- a/talk/media/webrtc/fakewebrtcvideocapturemodule.h +++ b/talk/media/webrtc/fakewebrtcvideocapturemodule.h @@ -83,13 +83,18 @@ class FakeWebRtcVideoCaptureModule : public webrtc::VideoCaptureModule { virtual int32_t RegisterCaptureDataCallback( webrtc::VideoCaptureDataCallback& callback) { callback_ = &callback; + return 0; } - virtual void DeRegisterCaptureDataCallback() { callback_ = NULL; } - virtual void RegisterCaptureCallback(webrtc::VideoCaptureFeedBack& callback) { - // Not implemented. + virtual int32_t DeRegisterCaptureDataCallback() { + callback_ = NULL; + return 0; } - virtual void DeRegisterCaptureCallback() { - // Not implemented. + virtual int32_t RegisterCaptureCallback( + webrtc::VideoCaptureFeedBack& callback) { + return -1; // not implemented + } + virtual int32_t DeRegisterCaptureCallback() { + return 0; } virtual int32_t SetCaptureDelay(int32_t delay) { delay_ = delay; diff --git a/talk/media/webrtc/webrtcvideoengine.cc b/talk/media/webrtc/webrtcvideoengine.cc index 178d538c2..d2e73eae5 100644 --- a/talk/media/webrtc/webrtcvideoengine.cc +++ b/talk/media/webrtc/webrtcvideoengine.cc @@ -2300,8 +2300,18 @@ bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) { sinfo.firs_rcvd = -1; sinfo.nacks_rcvd = -1; sinfo.rtt_ms = -1; - sinfo.frame_width = static_cast(channel_stream_info->width()); - sinfo.frame_height = static_cast(channel_stream_info->height()); + sinfo.input_frame_width = static_cast(channel_stream_info->width()); + sinfo.input_frame_height = + static_cast(channel_stream_info->height()); + webrtc::VideoCodec vie_codec; + if (engine()->vie()->codec()->GetSendCodec(channel_id, vie_codec) == 0) { + sinfo.send_frame_width = vie_codec.width; + sinfo.send_frame_height = vie_codec.height; + } else { + sinfo.send_frame_width = -1; + sinfo.send_frame_height = -1; + LOG_RTCERR1(GetSendCodec, channel_id); + } sinfo.framerate_input = channel_stream_info->framerate(); sinfo.framerate_sent = send_channel->encoder_observer()->framerate(); sinfo.nominal_bitrate = send_channel->encoder_observer()->bitrate(); diff --git a/talk/media/webrtc/webrtcvoiceengine.cc b/talk/media/webrtc/webrtcvoiceengine.cc index 4ad2ff502..f1030bd62 100644 --- a/talk/media/webrtc/webrtcvoiceengine.cc +++ b/talk/media/webrtc/webrtcvoiceengine.cc @@ -229,6 +229,7 @@ static AudioOptions GetDefaultEngineOptions() { options.adjust_agc_delta.Set(0); options.experimental_agc.Set(false); options.experimental_aec.Set(false); + options.experimental_ns.Set(false); options.aec_dump.Set(false); options.experimental_acm.Set(false); return options; @@ -717,6 +718,7 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { options.typing_detection.Set(false); options.experimental_agc.Set(false); options.experimental_aec.Set(false); + options.experimental_ns.Set(false); #endif LOG(LS_INFO) << "Applying audio options: " << options.ToString(); @@ -804,6 +806,25 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { } } +#ifdef USE_WEBRTC_DEV_BRANCH + bool experimental_ns; + if (options.experimental_ns.Get(&experimental_ns)) { + webrtc::AudioProcessing* audioproc = + voe_wrapper_->base()->audio_processing(); + // We check audioproc for the benefit of tests, since FakeWebRtcVoiceEngine + // returns NULL on audio_processing(). + if (audioproc) { + if (audioproc->EnableExperimentalNs(experimental_ns) == -1) { + LOG_RTCERR1(EnableExperimentalNs, experimental_ns); + return false; + } + } else { + LOG(LS_VERBOSE) << "Experimental noise suppression set to " + << experimental_ns; + } + } +#endif // USE_WEBRTC_DEV_BRANCH + bool highpass_filter; if (options.highpass_filter.Get(&highpass_filter)) { if (voep->EnableHighPassFilter(highpass_filter) == -1) { diff --git a/talk/p2p/base/stunport.cc b/talk/p2p/base/stunport.cc index def3c9b4d..cee2fc4ee 100644 --- a/talk/p2p/base/stunport.cc +++ b/talk/p2p/base/stunport.cc @@ -328,7 +328,7 @@ void UDPPort::OnStunBindingRequestSucceeded( // related address is local socket address. set_related_address(socket_->GetLocalAddress()); AddAddress(stun_addr, socket_->GetLocalAddress(), UDP_PROTOCOL_NAME, - STUN_PORT_TYPE, ICE_TYPE_PREFERENCE_PRFLX, false); + STUN_PORT_TYPE, ICE_TYPE_PREFERENCE_SRFLX, false); } SetResult(true); } diff --git a/talk/session/media/channel.cc b/talk/session/media/channel.cc index 9a76d51f8..bc6bd0981 100644 --- a/talk/session/media/channel.cc +++ b/talk/session/media/channel.cc @@ -27,6 +27,7 @@ #include "talk/session/media/channel.h" +#include "talk/base/bind.h" #include "talk/base/buffer.h" #include "talk/base/byteorder.h" #include "talk/base/common.h" @@ -42,44 +43,17 @@ namespace cricket { +using talk_base::Bind; + enum { - MSG_ENABLE = 1, - MSG_DISABLE, - MSG_MUTESTREAM, - MSG_ISSTREAMMUTED, - MSG_SETREMOTECONTENT, - MSG_SETLOCALCONTENT, - MSG_EARLYMEDIATIMEOUT, - MSG_CANINSERTDTMF, - MSG_INSERTDTMF, - MSG_GETSTATS, - MSG_SETRENDERER, - MSG_ADDRECVSTREAM, - MSG_REMOVERECVSTREAM, - MSG_ADDSENDSTREAM, - MSG_REMOVESENDSTREAM, - MSG_SETRINGBACKTONE, - MSG_PLAYRINGBACKTONE, - MSG_ADDSCREENCAST, - MSG_REMOVESCREENCAST, - MSG_SENDINTRAFRAME, - MSG_REQUESTINTRAFRAME, + MSG_EARLYMEDIATIMEOUT = 1, MSG_SCREENCASTWINDOWEVENT, MSG_RTPPACKET, MSG_RTCPPACKET, MSG_CHANNEL_ERROR, - MSG_SETCHANNELOPTIONS, - MSG_SCALEVOLUME, - MSG_HANDLEVIEWREQUEST, MSG_READYTOSENDDATA, - MSG_SENDDATA, MSG_DATARECEIVED, - MSG_SETCAPTURER, - MSG_ISSCREENCASTING, - MSG_GETSCREENCASTDETAILS, - MSG_SETSCREENCASTFACTORY, MSG_FIRSTPACKETRECEIVED, - MSG_SESSION_ERROR, }; // Value specified in RFC 5764. @@ -112,133 +86,11 @@ VideoChannel::ScreenCapturerFactory* CreateScreenCapturerFactory() { return new NullScreenCapturerFactory(); } -struct SetContentData : public talk_base::MessageData { - SetContentData(const MediaContentDescription* content, - ContentAction action, - std::string* error_desc) - : content(content), - action(action), - error_desc(error_desc), - result(false) { - } - const MediaContentDescription* content; - ContentAction action; - std::string* error_desc; - bool result; -}; - -struct SetBandwidthData : public talk_base::MessageData { - explicit SetBandwidthData(int value) : value(value), result(false) {} - int value; - bool result; -}; - -struct SetRingbackToneMessageData : public talk_base::MessageData { - SetRingbackToneMessageData(const void* b, int l) - : buf(b), - len(l), - result(false) { - } - const void* buf; - int len; - bool result; -}; - -struct PlayRingbackToneMessageData : public talk_base::MessageData { - PlayRingbackToneMessageData(uint32 s, bool p, bool l) - : ssrc(s), - play(p), - loop(l), - result(false) { - } - uint32 ssrc; - bool play; - bool loop; - bool result; -}; -typedef talk_base::TypedMessageData BoolMessageData; -struct DtmfMessageData : public talk_base::MessageData { - DtmfMessageData(uint32 ssrc, int event, int duration, int flags) - : ssrc(ssrc), - event(event), - duration(duration), - flags(flags), - result(false) { - } - uint32 ssrc; - int event; - int duration; - int flags; - bool result; -}; -struct ScaleVolumeMessageData : public talk_base::MessageData { - ScaleVolumeMessageData(uint32 s, double l, double r) - : ssrc(s), - left(l), - right(r), - result(false) { - } - uint32 ssrc; - double left; - double right; - bool result; -}; - -struct VoiceStatsMessageData : public talk_base::MessageData { - explicit VoiceStatsMessageData(VoiceMediaInfo* stats) - : result(false), - stats(stats) { - } - bool result; - VoiceMediaInfo* stats; -}; - -struct VideoStatsMessageData : public talk_base::MessageData { - explicit VideoStatsMessageData(VideoMediaInfo* stats) - : result(false), - stats(stats) { - } - bool result; - VideoMediaInfo* stats; -}; - struct PacketMessageData : public talk_base::MessageData { talk_base::Buffer packet; talk_base::DiffServCodePoint dscp; }; -struct AudioRenderMessageData: public talk_base::MessageData { - AudioRenderMessageData(uint32 s, AudioRenderer* r, bool l) - : ssrc(s), renderer(r), is_local(l), result(false) {} - uint32 ssrc; - AudioRenderer* renderer; - bool is_local; - bool result; -}; - -struct VideoRenderMessageData : public talk_base::MessageData { - VideoRenderMessageData(uint32 s, VideoRenderer* r) : ssrc(s), renderer(r) {} - uint32 ssrc; - VideoRenderer* renderer; -}; - -struct AddScreencastMessageData : public talk_base::MessageData { - AddScreencastMessageData(uint32 s, const ScreencastId& id) - : ssrc(s), - window_id(id), - result(NULL) { - } - uint32 ssrc; - ScreencastId window_id; - VideoCapturer* result; -}; - -struct RemoveScreencastMessageData : public talk_base::MessageData { - explicit RemoveScreencastMessageData(uint32 s) : ssrc(s), result(false) {} - uint32 ssrc; - bool result; -}; - struct ScreencastEventMessageData : public talk_base::MessageData { ScreencastEventMessageData(uint32 s, talk_base::WindowEvent we) : ssrc(s), @@ -248,15 +100,6 @@ struct ScreencastEventMessageData : public talk_base::MessageData { talk_base::WindowEvent event; }; -struct ViewRequestMessageData : public talk_base::MessageData { - explicit ViewRequestMessageData(const ViewRequest& r) - : request(r), - result(false) { - } - ViewRequest request; - bool result; -}; - struct VoiceChannelErrorMessageData : public talk_base::MessageData { VoiceChannelErrorMessageData(uint32 in_ssrc, VoiceMediaChannel::Error in_error) @@ -286,78 +129,9 @@ struct DataChannelErrorMessageData : public talk_base::MessageData { DataMediaChannel::Error error; }; -struct SessionErrorMessageData : public talk_base::MessageData { - SessionErrorMessageData(cricket::BaseSession::Error error, - const std::string& error_desc) - : error_(error), - error_desc_(error_desc) {} - BaseSession::Error error_; - std::string error_desc_; -}; - -struct SsrcMessageData : public talk_base::MessageData { - explicit SsrcMessageData(uint32 ssrc) : ssrc(ssrc), result(false) {} - uint32 ssrc; - bool result; -}; - -struct StreamMessageData : public talk_base::MessageData { - explicit StreamMessageData(const StreamParams& in_sp) - : sp(in_sp), - result(false) { - } - StreamParams sp; - bool result; -}; - -struct MuteStreamData : public talk_base::MessageData { - MuteStreamData(uint32 ssrc, bool mute) - : ssrc(ssrc), mute(mute), result(false) {} - uint32 ssrc; - bool mute; - bool result; -}; - -struct AudioOptionsMessageData : public talk_base::MessageData { - explicit AudioOptionsMessageData(const AudioOptions& options) - : options(options), - result(false) { - } - AudioOptions options; - bool result; -}; - -struct VideoOptionsMessageData : public talk_base::MessageData { - explicit VideoOptionsMessageData(const VideoOptions& options) - : options(options), - result(false) { - } - VideoOptions options; - bool result; -}; - -struct SetCapturerMessageData : public talk_base::MessageData { - SetCapturerMessageData(uint32 s, VideoCapturer* c) - : ssrc(s), - capturer(c), - result(false) { - } - uint32 ssrc; - VideoCapturer* capturer; - bool result; -}; - -struct IsScreencastingMessageData : public talk_base::MessageData { - IsScreencastingMessageData() - : result(false) { - } - bool result; -}; - -struct VideoChannel::ScreencastDetailsMessageData : - public talk_base::MessageData { - explicit ScreencastDetailsMessageData(uint32 s) +struct VideoChannel::ScreencastDetailsData { + explicit ScreencastDetailsData(uint32 s) : ssrc(s), fps(0), screencast_max_pixels(0) { } uint32 ssrc; @@ -365,14 +139,6 @@ struct VideoChannel::ScreencastDetailsMessageData : int screencast_max_pixels; }; -struct SetScreenCaptureFactoryMessageData : public talk_base::MessageData { - explicit SetScreenCaptureFactoryMessageData( - VideoChannel::ScreenCapturerFactory* f) - : screencapture_factory(f) { - } - VideoChannel::ScreenCapturerFactory* screencapture_factory; -}; - static const char* PacketType(bool rtcp) { return (!rtcp) ? "RTP" : "RTCP"; } @@ -430,7 +196,7 @@ BaseChannel::~BaseChannel() { Deinit(); StopConnectionMonitor(); FlushRtcpMessages(); // Send any outstanding RTCP packets. - Clear(); // eats any outstanding messages or packets + worker_thread_->Clear(this); // eats any outstanding messages or packets // We must destroy the media channel before the transport channel, otherwise // the media channel may try to send on the dead transport channel. NULLing // is not an effective strategy since the sends will come on another thread. @@ -478,63 +244,51 @@ void BaseChannel::Deinit() { media_channel_->SetInterface(NULL); } -// Can be called from thread other than worker thread bool BaseChannel::Enable(bool enable) { - Send(enable ? MSG_ENABLE : MSG_DISABLE); + worker_thread_->Invoke(Bind( + enable ? &BaseChannel::EnableMedia_w : &BaseChannel::DisableMedia_w, + this)); return true; } -// Can be called from thread other than worker thread bool BaseChannel::MuteStream(uint32 ssrc, bool mute) { - MuteStreamData data(ssrc, mute); - Send(MSG_MUTESTREAM, &data); - return data.result; + return InvokeOnWorker(Bind(&BaseChannel::MuteStream_w, this, ssrc, mute)); } bool BaseChannel::IsStreamMuted(uint32 ssrc) { - SsrcMessageData data(ssrc); - Send(MSG_ISSTREAMMUTED, &data); - return data.result; + return InvokeOnWorker(Bind(&BaseChannel::IsStreamMuted_w, this, ssrc)); } bool BaseChannel::AddRecvStream(const StreamParams& sp) { - StreamMessageData data(sp); - Send(MSG_ADDRECVSTREAM, &data); - return data.result; + return InvokeOnWorker(Bind(&BaseChannel::AddRecvStream_w, this, sp)); } bool BaseChannel::RemoveRecvStream(uint32 ssrc) { - SsrcMessageData data(ssrc); - Send(MSG_REMOVERECVSTREAM, &data); - return data.result; + return InvokeOnWorker(Bind(&BaseChannel::RemoveRecvStream_w, this, ssrc)); } bool BaseChannel::AddSendStream(const StreamParams& sp) { - StreamMessageData data(sp); - Send(MSG_ADDSENDSTREAM, &data); - return data.result; + return InvokeOnWorker( + Bind(&MediaChannel::AddSendStream, media_channel(), sp)); } bool BaseChannel::RemoveSendStream(uint32 ssrc) { - SsrcMessageData data(ssrc); - Send(MSG_REMOVESENDSTREAM, &data); - return data.result; + return InvokeOnWorker( + Bind(&MediaChannel::RemoveSendStream, media_channel(), ssrc)); } bool BaseChannel::SetLocalContent(const MediaContentDescription* content, ContentAction action, std::string* error_desc) { - SetContentData data(content, action, error_desc); - Send(MSG_SETLOCALCONTENT, &data); - return data.result; + return InvokeOnWorker(Bind(&BaseChannel::SetLocalContent_w, + this, content, action, error_desc)); } bool BaseChannel::SetRemoteContent(const MediaContentDescription* content, ContentAction action, std::string* error_desc) { - SetContentData data(content, action, error_desc); - Send(MSG_SETREMOTECONTENT, &data); - return data.result; + return InvokeOnWorker(Bind(&BaseChannel::SetRemoteContent_w, + this, content, action, error_desc)); } void BaseChannel::StartConnectionMonitor(int cms) { @@ -955,9 +709,12 @@ void BaseChannel::ChannelWritable_w() { if (!SetupDtlsSrtp(false)) { const std::string error_desc = "Couldn't set up DTLS-SRTP on RTP channel."; - SessionErrorMessageData data(BaseSession::ERROR_TRANSPORT, error_desc); // Sent synchronously. - signaling_thread()->Send(this, MSG_SESSION_ERROR, &data); + signaling_thread()->Invoke(Bind( + &SetSessionError, + session_, + BaseSession::ERROR_TRANSPORT, + error_desc)); return; } @@ -965,9 +722,12 @@ void BaseChannel::ChannelWritable_w() { if (!SetupDtlsSrtp(true)) { const std::string error_desc = "Couldn't set up DTLS-SRTP on RTCP channel"; - SessionErrorMessageData data(BaseSession::ERROR_TRANSPORT, error_desc); // Sent synchronously. - signaling_thread()->Send(this, MSG_SESSION_ERROR, &data); + signaling_thread()->Invoke(Bind( + &SetSessionError, + session_, + BaseSession::ERROR_TRANSPORT, + error_desc)); return; } } @@ -1217,16 +977,6 @@ bool BaseChannel::RemoveRecvStream_w(uint32 ssrc) { return media_channel()->RemoveRecvStream(ssrc); } -bool BaseChannel::AddSendStream_w(const StreamParams& sp) { - ASSERT(worker_thread() == talk_base::Thread::Current()); - return media_channel()->AddSendStream(sp); -} - -bool BaseChannel::RemoveSendStream_w(uint32 ssrc) { - ASSERT(worker_thread() == talk_base::Thread::Current()); - return media_channel()->RemoveSendStream(ssrc); -} - bool BaseChannel::UpdateLocalStreams_w(const std::vector& streams, ContentAction action, std::string* error_desc) { @@ -1435,57 +1185,6 @@ bool BaseChannel::SetBaseRemoteContent_w(const MediaContentDescription* content, void BaseChannel::OnMessage(talk_base::Message *pmsg) { switch (pmsg->message_id) { - case MSG_ENABLE: - EnableMedia_w(); - break; - case MSG_DISABLE: - DisableMedia_w(); - break; - case MSG_MUTESTREAM: { - MuteStreamData* data = static_cast(pmsg->pdata); - data->result = MuteStream_w(data->ssrc, data->mute); - break; - } - case MSG_ISSTREAMMUTED: { - SsrcMessageData* data = static_cast(pmsg->pdata); - data->result = IsStreamMuted_w(data->ssrc); - break; - } - case MSG_SETLOCALCONTENT: { - SetContentData* data = static_cast(pmsg->pdata); - data->result = SetLocalContent_w(data->content, - data->action, - data->error_desc); - break; - } - case MSG_SETREMOTECONTENT: { - SetContentData* data = static_cast(pmsg->pdata); - data->result = SetRemoteContent_w(data->content, - data->action, - data->error_desc); - break; - } - case MSG_ADDRECVSTREAM: { - StreamMessageData* data = static_cast(pmsg->pdata); - data->result = AddRecvStream_w(data->sp); - break; - } - case MSG_REMOVERECVSTREAM: { - SsrcMessageData* data = static_cast(pmsg->pdata); - data->result = RemoveRecvStream_w(data->ssrc); - break; - } - case MSG_ADDSENDSTREAM: { - StreamMessageData* data = static_cast(pmsg->pdata); - data->result = AddSendStream_w(data->sp); - break; - } - case MSG_REMOVESENDSTREAM: { - SsrcMessageData* data = static_cast(pmsg->pdata); - data->result = RemoveSendStream_w(data->ssrc); - break; - } - case MSG_RTPPACKET: case MSG_RTCPPACKET: { PacketMessageData* data = static_cast(pmsg->pdata); @@ -1497,41 +1196,18 @@ void BaseChannel::OnMessage(talk_base::Message *pmsg) { SignalFirstPacketReceived(this); break; } - case MSG_SESSION_ERROR: { - SessionErrorMessageData* data = static_cast - (pmsg->pdata); - SetSessionError(session_, data->error_, data->error_desc_); - break; - } } } -void BaseChannel::Send(uint32 id, talk_base::MessageData *pdata) { - worker_thread_->Send(this, id, pdata); -} - -void BaseChannel::Post(uint32 id, talk_base::MessageData *pdata) { - worker_thread_->Post(this, id, pdata); -} - -void BaseChannel::PostDelayed(int cmsDelay, uint32 id, - talk_base::MessageData *pdata) { - worker_thread_->PostDelayed(cmsDelay, this, id, pdata); -} - -void BaseChannel::Clear(uint32 id, talk_base::MessageList* removed) { - worker_thread_->Clear(this, id, removed); -} - void BaseChannel::FlushRtcpMessages() { // Flush all remaining RTCP messages. This should only be called in // destructor. ASSERT(talk_base::Thread::Current() == worker_thread_); talk_base::MessageList rtcp_messages; - Clear(MSG_RTCPPACKET, &rtcp_messages); + worker_thread_->Clear(this, MSG_RTCPPACKET, &rtcp_messages); for (talk_base::MessageList::iterator it = rtcp_messages.begin(); it != rtcp_messages.end(); ++it) { - Send(MSG_RTCPPACKET, it->pdata); + worker_thread_->Send(this, MSG_RTCPPACKET, it->pdata); } } @@ -1570,21 +1246,17 @@ bool VoiceChannel::Init() { } bool VoiceChannel::SetRemoteRenderer(uint32 ssrc, AudioRenderer* renderer) { - AudioRenderMessageData data(ssrc, renderer, false); - Send(MSG_SETRENDERER, &data); - return data.result; + return InvokeOnWorker(Bind(&VoiceMediaChannel::SetRemoteRenderer, + media_channel(), ssrc, renderer)); } bool VoiceChannel::SetLocalRenderer(uint32 ssrc, AudioRenderer* renderer) { - AudioRenderMessageData data(ssrc, renderer, true); - Send(MSG_SETRENDERER, &data); - return data.result; + return InvokeOnWorker(Bind(&VoiceMediaChannel::SetLocalRenderer, + media_channel(), ssrc, renderer)); } bool VoiceChannel::SetRingbackTone(const void* buf, int len) { - SetRingbackToneMessageData data(buf, len); - Send(MSG_SETRINGBACKTONE, &data); - return data.result; + return InvokeOnWorker(Bind(&VoiceChannel::SetRingbackTone_w, this, buf, len)); } // TODO(juberti): Handle early media the right way. We should get an explicit @@ -1595,17 +1267,17 @@ bool VoiceChannel::SetRingbackTone(const void* buf, int len) { void VoiceChannel::SetEarlyMedia(bool enable) { if (enable) { // Start the early media timeout - PostDelayed(kEarlyMediaTimeout, MSG_EARLYMEDIATIMEOUT); + worker_thread()->PostDelayed(kEarlyMediaTimeout, this, + MSG_EARLYMEDIATIMEOUT); } else { // Stop the timeout if currently going. - Clear(MSG_EARLYMEDIATIMEOUT); + worker_thread()->Clear(this, MSG_EARLYMEDIATIMEOUT); } } bool VoiceChannel::PlayRingbackTone(uint32 ssrc, bool play, bool loop) { - PlayRingbackToneMessageData data(ssrc, play, loop); - Send(MSG_PLAYRINGBACKTONE, &data); - return data.result; + return InvokeOnWorker(Bind(&VoiceChannel::PlayRingbackTone_w, + this, ssrc, play, loop)); } bool VoiceChannel::PressDTMF(int digit, bool playout) { @@ -1618,27 +1290,24 @@ bool VoiceChannel::PressDTMF(int digit, bool playout) { } bool VoiceChannel::CanInsertDtmf() { - BoolMessageData data(false); - Send(MSG_CANINSERTDTMF, &data); - return data.data(); + return InvokeOnWorker(Bind(&VoiceMediaChannel::CanInsertDtmf, + media_channel())); } bool VoiceChannel::InsertDtmf(uint32 ssrc, int event_code, int duration, int flags) { - DtmfMessageData data(ssrc, event_code, duration, flags); - Send(MSG_INSERTDTMF, &data); - return data.result; + return InvokeOnWorker(Bind(&VoiceChannel::InsertDtmf_w, this, + ssrc, event_code, duration, flags)); } bool VoiceChannel::SetOutputScaling(uint32 ssrc, double left, double right) { - ScaleVolumeMessageData data(ssrc, left, right); - Send(MSG_SCALEVOLUME, &data); - return data.result; + return InvokeOnWorker(Bind(&VoiceMediaChannel::SetOutputScaling, + media_channel(), ssrc, left, right)); } + bool VoiceChannel::GetStats(VoiceMediaInfo* stats) { - VoiceStatsMessageData data(stats); - Send(MSG_GETSTATS, &data); - return data.result; + return InvokeOnWorker(Bind(&VoiceMediaChannel::GetStats, + media_channel(), stats)); } void VoiceChannel::StartMediaMonitor(int cms) { @@ -1855,10 +1524,6 @@ void VoiceChannel::HandleEarlyMediaTimeout() { } } -bool VoiceChannel::CanInsertDtmf_w() { - return media_channel()->CanInsertDtmf(); -} - bool VoiceChannel::InsertDtmf_w(uint32 ssrc, int event, int duration, int flags) { if (!enabled()) { @@ -1868,74 +1533,16 @@ bool VoiceChannel::InsertDtmf_w(uint32 ssrc, int event, int duration, return media_channel()->InsertDtmf(ssrc, event, duration, flags); } -bool VoiceChannel::SetOutputScaling_w(uint32 ssrc, double left, double right) { - return media_channel()->SetOutputScaling(ssrc, left, right); -} - -bool VoiceChannel::GetStats_w(VoiceMediaInfo* stats) { - return media_channel()->GetStats(stats); -} - bool VoiceChannel::SetChannelOptions(const AudioOptions& options) { - AudioOptionsMessageData data(options); - Send(MSG_SETCHANNELOPTIONS, &data); - return data.result; -} - -bool VoiceChannel::SetChannelOptions_w(const AudioOptions& options) { - return media_channel()->SetOptions(options); -} - -bool VoiceChannel::SetRenderer_w(uint32 ssrc, AudioRenderer* renderer, - bool is_local) { - if (is_local) - return media_channel()->SetLocalRenderer(ssrc, renderer); - - return media_channel()->SetRemoteRenderer(ssrc, renderer); + return InvokeOnWorker(Bind(&VoiceMediaChannel::SetOptions, + media_channel(), options)); } void VoiceChannel::OnMessage(talk_base::Message *pmsg) { switch (pmsg->message_id) { - case MSG_SETRINGBACKTONE: { - SetRingbackToneMessageData* data = - static_cast(pmsg->pdata); - data->result = SetRingbackTone_w(data->buf, data->len); - break; - } - case MSG_PLAYRINGBACKTONE: { - PlayRingbackToneMessageData* data = - static_cast(pmsg->pdata); - data->result = PlayRingbackTone_w(data->ssrc, data->play, data->loop); - break; - } case MSG_EARLYMEDIATIMEOUT: HandleEarlyMediaTimeout(); break; - case MSG_CANINSERTDTMF: { - BoolMessageData* data = - static_cast(pmsg->pdata); - data->data() = CanInsertDtmf_w(); - break; - } - case MSG_INSERTDTMF: { - DtmfMessageData* data = - static_cast(pmsg->pdata); - data->result = InsertDtmf_w(data->ssrc, data->event, data->duration, - data->flags); - break; - } - case MSG_SCALEVOLUME: { - ScaleVolumeMessageData* data = - static_cast(pmsg->pdata); - data->result = SetOutputScaling_w(data->ssrc, data->left, data->right); - break; - } - case MSG_GETSTATS: { - VoiceStatsMessageData* data = - static_cast(pmsg->pdata); - data->result = GetStats_w(data->stats); - break; - } case MSG_CHANNEL_ERROR: { VoiceChannelErrorMessageData* data = static_cast(pmsg->pdata); @@ -1943,18 +1550,6 @@ void VoiceChannel::OnMessage(talk_base::Message *pmsg) { delete data; break; } - case MSG_SETCHANNELOPTIONS: { - AudioOptionsMessageData* data = - static_cast(pmsg->pdata); - data->result = SetChannelOptions_w(data->options); - break; - } - case MSG_SETRENDERER: { - AudioRenderMessageData* data = - static_cast(pmsg->pdata); - data->result = SetRenderer_w(data->ssrc, data->renderer, data->is_local); - break; - } default: BaseChannel::OnMessage(pmsg); break; @@ -2068,68 +1663,65 @@ VideoChannel::~VideoChannel() { } bool VideoChannel::SetRenderer(uint32 ssrc, VideoRenderer* renderer) { - VideoRenderMessageData data(ssrc, renderer); - Send(MSG_SETRENDERER, &data); + worker_thread()->Invoke(Bind( + &VideoMediaChannel::SetRenderer, media_channel(), ssrc, renderer)); return true; } bool VideoChannel::ApplyViewRequest(const ViewRequest& request) { - ViewRequestMessageData data(request); - Send(MSG_HANDLEVIEWREQUEST, &data); - return data.result; + return InvokeOnWorker(Bind(&VideoChannel::ApplyViewRequest_w, this, request)); } VideoCapturer* VideoChannel::AddScreencast( uint32 ssrc, const ScreencastId& id) { - AddScreencastMessageData data(ssrc, id); - Send(MSG_ADDSCREENCAST, &data); - return data.result; + return worker_thread()->Invoke(Bind( + &VideoChannel::AddScreencast_w, this, ssrc, id)); } bool VideoChannel::SetCapturer(uint32 ssrc, VideoCapturer* capturer) { - SetCapturerMessageData data(ssrc, capturer); - Send(MSG_SETCAPTURER, &data); - return data.result; + return InvokeOnWorker(Bind(&VideoMediaChannel::SetCapturer, + media_channel(), ssrc, capturer)); } bool VideoChannel::RemoveScreencast(uint32 ssrc) { - RemoveScreencastMessageData data(ssrc); - Send(MSG_REMOVESCREENCAST, &data); - return data.result; + return InvokeOnWorker(Bind(&VideoChannel::RemoveScreencast_w, this, ssrc)); } bool VideoChannel::IsScreencasting() { - IsScreencastingMessageData data; - Send(MSG_ISSCREENCASTING, &data); - return data.result; + return InvokeOnWorker(Bind(&VideoChannel::IsScreencasting_w, this)); } int VideoChannel::GetScreencastFps(uint32 ssrc) { - ScreencastDetailsMessageData data(ssrc); - Send(MSG_GETSCREENCASTDETAILS, &data); + ScreencastDetailsData data(ssrc); + worker_thread()->Invoke(Bind( + &VideoChannel::GetScreencastDetails_w, this, &data)); return data.fps; } int VideoChannel::GetScreencastMaxPixels(uint32 ssrc) { - ScreencastDetailsMessageData data(ssrc); - Send(MSG_GETSCREENCASTDETAILS, &data); + ScreencastDetailsData data(ssrc); + worker_thread()->Invoke(Bind( + &VideoChannel::GetScreencastDetails_w, this, &data)); return data.screencast_max_pixels; } bool VideoChannel::SendIntraFrame() { - Send(MSG_SENDINTRAFRAME); + worker_thread()->Invoke(Bind( + &VideoMediaChannel::SendIntraFrame, media_channel())); return true; } bool VideoChannel::RequestIntraFrame() { - Send(MSG_REQUESTINTRAFRAME); + worker_thread()->Invoke(Bind( + &VideoMediaChannel::RequestIntraFrame, media_channel())); return true; } void VideoChannel::SetScreenCaptureFactory( ScreenCapturerFactory* screencapture_factory) { - SetScreenCaptureFactoryMessageData data(screencapture_factory); - Send(MSG_SETSCREENCASTFACTORY, &data); + worker_thread()->Invoke(Bind( + &VideoChannel::SetScreenCaptureFactory_w, + this, screencapture_factory)); } void VideoChannel::ChangeState() { @@ -2153,9 +1745,8 @@ void VideoChannel::ChangeState() { } bool VideoChannel::GetStats(VideoMediaInfo* stats) { - VideoStatsMessageData data(stats); - Send(MSG_GETSTATS, &data); - return data.result; + return InvokeOnWorker(Bind(&VideoMediaChannel::GetStats, + media_channel(), stats)); } void VideoChannel::StartMediaMonitor(int cms) { @@ -2307,10 +1898,6 @@ bool VideoChannel::ApplyViewRequest_w(const ViewRequest& request) { return ret; } -void VideoChannel::SetRenderer_w(uint32 ssrc, VideoRenderer* renderer) { - media_channel()->SetRenderer(ssrc, renderer); -} - VideoCapturer* VideoChannel::AddScreencast_w( uint32 ssrc, const ScreencastId& id) { if (screencast_capturers_.find(ssrc) != screencast_capturers_.end()) { @@ -2327,10 +1914,6 @@ VideoCapturer* VideoChannel::AddScreencast_w( return screen_capturer; } -bool VideoChannel::SetCapturer_w(uint32 ssrc, VideoCapturer* capturer) { - return media_channel()->SetCapturer(ssrc, capturer); -} - bool VideoChannel::RemoveScreencast_w(uint32 ssrc) { ScreencastMap::iterator iter = screencast_capturers_.find(ssrc); if (iter == screencast_capturers_.end()) { @@ -2346,8 +1929,8 @@ bool VideoChannel::IsScreencasting_w() const { return !screencast_capturers_.empty(); } -void VideoChannel::ScreencastDetails_w( - ScreencastDetailsMessageData* data) const { +void VideoChannel::GetScreencastDetails_w( + ScreencastDetailsData* data) const { ScreencastMap::const_iterator iter = screencast_capturers_.find(data->ssrc); if (iter == screencast_capturers_.end()) { return; @@ -2367,10 +1950,6 @@ void VideoChannel::SetScreenCaptureFactory_w( } } -bool VideoChannel::GetStats_w(VideoMediaInfo* stats) { - return media_channel()->GetStats(stats); -} - void VideoChannel::OnScreencastWindowEvent_s(uint32 ssrc, talk_base::WindowEvent we) { ASSERT(signaling_thread() == talk_base::Thread::Current()); @@ -2378,41 +1957,12 @@ void VideoChannel::OnScreencastWindowEvent_s(uint32 ssrc, } bool VideoChannel::SetChannelOptions(const VideoOptions &options) { - VideoOptionsMessageData data(options); - Send(MSG_SETCHANNELOPTIONS, &data); - return data.result; -} - -bool VideoChannel::SetChannelOptions_w(const VideoOptions &options) { - return media_channel()->SetOptions(options); + return InvokeOnWorker(Bind(&VideoMediaChannel::SetOptions, + media_channel(), options)); } void VideoChannel::OnMessage(talk_base::Message *pmsg) { switch (pmsg->message_id) { - case MSG_SETRENDERER: { - const VideoRenderMessageData* data = - static_cast(pmsg->pdata); - SetRenderer_w(data->ssrc, data->renderer); - break; - } - case MSG_ADDSCREENCAST: { - AddScreencastMessageData* data = - static_cast(pmsg->pdata); - data->result = AddScreencast_w(data->ssrc, data->window_id); - break; - } - case MSG_SETCAPTURER: { - SetCapturerMessageData* data = - static_cast(pmsg->pdata); - data->result = SetCapturer_w(data->ssrc, data->capturer); - break; - } - case MSG_REMOVESCREENCAST: { - RemoveScreencastMessageData* data = - static_cast(pmsg->pdata); - data->result = RemoveScreencast_w(data->ssrc); - break; - } case MSG_SCREENCASTWINDOWEVENT: { const ScreencastEventMessageData* data = static_cast(pmsg->pdata); @@ -2420,32 +1970,6 @@ void VideoChannel::OnMessage(talk_base::Message *pmsg) { delete data; break; } - case MSG_ISSCREENCASTING: { - IsScreencastingMessageData* data = - static_cast(pmsg->pdata); - data->result = IsScreencasting_w(); - break; - } - case MSG_GETSCREENCASTDETAILS: { - ScreencastDetailsMessageData* data = - static_cast(pmsg->pdata); - ScreencastDetails_w(data); - break; - } - case MSG_SENDINTRAFRAME: { - SendIntraFrame_w(); - break; - } - case MSG_REQUESTINTRAFRAME: { - RequestIntraFrame_w(); - break; - } - case MSG_SETCHANNELOPTIONS: { - VideoOptionsMessageData* data = - static_cast(pmsg->pdata); - data->result = SetChannelOptions_w(data->options); - break; - } case MSG_CHANNEL_ERROR: { const VideoChannelErrorMessageData* data = static_cast(pmsg->pdata); @@ -2453,24 +1977,6 @@ void VideoChannel::OnMessage(talk_base::Message *pmsg) { delete data; break; } - case MSG_HANDLEVIEWREQUEST: { - ViewRequestMessageData* data = - static_cast(pmsg->pdata); - data->result = ApplyViewRequest_w(data->request); - break; - } - case MSG_SETSCREENCASTFACTORY: { - SetScreenCaptureFactoryMessageData* data = - static_cast(pmsg->pdata); - SetScreenCaptureFactory_w(data->screencapture_factory); - break; - } - case MSG_GETSTATS: { - VideoStatsMessageData* data = - static_cast(pmsg->pdata); - data->result = GetStats_w(data->stats); - break; - } default: BaseChannel::OnMessage(pmsg); break; @@ -2516,9 +2022,8 @@ void VideoChannel::OnStateChange(VideoCapturer* capturer, CaptureState ev) { if (!GetLocalSsrc(capturer, &ssrc)) { return; } - ScreencastEventMessageData* pdata = - new ScreencastEventMessageData(ssrc, we); - signaling_thread()->Post(this, MSG_SCREENCASTWINDOWEVENT, pdata); + + OnScreencastWindowEvent(ssrc, we); } bool VideoChannel::GetLocalSsrc(const VideoCapturer* capturer, uint32* ssrc) { @@ -2611,9 +2116,8 @@ bool DataChannel::Init() { bool DataChannel::SendData(const SendDataParams& params, const talk_base::Buffer& payload, SendDataResult* result) { - SendDataMessageData message_data(params, &payload, result); - Send(MSG_SENDDATA, &message_data); - return message_data.succeeded; + return InvokeOnWorker(Bind(&DataMediaChannel::SendData, + media_channel(), params, payload, result)); } const ContentInfo* DataChannel::GetFirstContent( @@ -2810,9 +2314,8 @@ void DataChannel::ChangeState() { LOG(LS_ERROR) << "Failed to SetSend on data channel"; } - // Post to trigger SignalReadyToSendData. - signaling_thread()->Post(this, MSG_READYTOSENDDATA, - new DataChannelReadyToSendMessageData(send)); + // Trigger SignalReadyToSendData asynchronously. + OnDataChannelReadyToSend(send); LOG(LS_INFO) << "Changing data state, recv=" << recv << " send=" << send; } @@ -2827,13 +2330,6 @@ void DataChannel::OnMessage(talk_base::Message *pmsg) { delete data; break; } - case MSG_SENDDATA: { - SendDataMessageData* msg = - static_cast(pmsg->pdata); - msg->succeeded = media_channel()->SendData( - msg->params, *(msg->payload), msg->result); - break; - } case MSG_DATARECEIVED: { DataReceivedMessageData* data = static_cast(pmsg->pdata); diff --git a/talk/session/media/channel.h b/talk/session/media/channel.h index 91ea6222d..f3793cbd2 100644 --- a/talk/session/media/channel.h +++ b/talk/session/media/channel.h @@ -248,12 +248,6 @@ class BaseChannel SrtpFilter* srtp_filter() { return &srtp_filter_; } bool rtcp() const { return rtcp_; } - void Send(uint32 id, talk_base::MessageData* pdata = NULL); - void Post(uint32 id, talk_base::MessageData* pdata = NULL); - void PostDelayed(int cmsDelay, uint32 id = 0, - talk_base::MessageData* pdata = NULL); - void Clear(uint32 id = talk_base::MQID_ANY, - talk_base::MessageList* removed = NULL); void FlushRtcpMessages(); // NetworkInterface implementation, called by MediaEngine @@ -346,6 +340,12 @@ class BaseChannel virtual void OnConnectionMonitorUpdate(SocketMonitor* monitor, const std::vector& infos) = 0; + // Helper function for invoking bool-returning methods on the worker thread. + template + bool InvokeOnWorker(const FunctorT& functor) { + return worker_thread_->Invoke(functor); + } + private: sigslot::signal3 SignalSendPacketPreCrypto; sigslot::signal3 SignalSendPacketPostCrypto; @@ -470,7 +470,6 @@ class VoiceChannel : public BaseChannel { bool SetRingbackTone_w(const void* buf, int len); bool PlayRingbackTone_w(uint32 ssrc, bool play, bool loop); void HandleEarlyMediaTimeout(); - bool CanInsertDtmf_w(); bool InsertDtmf_w(uint32 ssrc, int event, int duration, int flags); bool SetOutputScaling_w(uint32 ssrc, double left, double right); bool GetStats_w(VoiceMediaInfo* stats); @@ -485,9 +484,6 @@ class VoiceChannel : public BaseChannel { void OnVoiceChannelError(uint32 ssrc, VoiceMediaChannel::Error error); void SendLastMediaError(); void OnSrtpError(uint32 ssrc, SrtpFilter::Mode mode, SrtpFilter::Error error); - // Configuration and setting. - bool SetChannelOptions_w(const AudioOptions& options); - bool SetRenderer_w(uint32 ssrc, AudioRenderer* renderer, bool is_local); static const int kEarlyMediaTimeout = 1000; bool received_media_; @@ -557,7 +553,7 @@ class VideoChannel : public BaseChannel { private: typedef std::map ScreencastMap; - struct ScreencastDetailsMessageData; + struct ScreencastDetailsData; // overrides from BaseChannel virtual void ChangeState(); @@ -568,22 +564,13 @@ class VideoChannel : public BaseChannel { virtual bool SetRemoteContent_w(const MediaContentDescription* content, ContentAction action, std::string* error_desc); - void SendIntraFrame_w() { - media_channel()->SendIntraFrame(); - } - void RequestIntraFrame_w() { - media_channel()->RequestIntraFrame(); - } - bool ApplyViewRequest_w(const ViewRequest& request); - void SetRenderer_w(uint32 ssrc, VideoRenderer* renderer); VideoCapturer* AddScreencast_w(uint32 ssrc, const ScreencastId& id); - bool SetCapturer_w(uint32 ssrc, VideoCapturer* capturer); bool RemoveScreencast_w(uint32 ssrc); void OnScreencastWindowEvent_s(uint32 ssrc, talk_base::WindowEvent we); bool IsScreencasting_w() const; - void ScreencastDetails_w(ScreencastDetailsMessageData* d) const; + void GetScreencastDetails_w(ScreencastDetailsData* d) const; void SetScreenCaptureFactory_w( ScreenCapturerFactory* screencapture_factory); bool GetStats_w(VideoMediaInfo* stats); @@ -601,8 +588,6 @@ class VideoChannel : public BaseChannel { void OnVideoChannelError(uint32 ssrc, VideoMediaChannel::Error error); void OnSrtpError(uint32 ssrc, SrtpFilter::Mode mode, SrtpFilter::Error error); - // Configuration and setting. - bool SetChannelOptions_w(const VideoOptions& options); VoiceChannel* voice_channel_; VideoRenderer* renderer_;