Log audio network adapter decisions in event log.

BUG=webrtc:6845

Review-Url: https://codereview.webrtc.org/2559953002
Cr-Commit-Position: refs/heads/master@{#16053}
diff --git a/webrtc/logging/BUILD.gn b/webrtc/logging/BUILD.gn
index 2daaef4..8bce39a 100644
--- a/webrtc/logging/BUILD.gn
+++ b/webrtc/logging/BUILD.gn
@@ -42,6 +42,7 @@
     ":rtc_event_log_api",
     "..:webrtc_common",
     "../call:call_interfaces",
+    "../modules/audio_coding:audio_network_adaptor",
     "../modules/rtp_rtcp",
   ]
 
@@ -84,7 +85,8 @@
     sources = [
       "rtc_event_log/rtc_event_log.proto",
     ]
-    proto_out_dir = "webrtc/logging/rtc_event_log"
+    proto_in_dir = "//"
+    proto_out_dir = "."
   }
 }
 
diff --git a/webrtc/logging/rtc_event_log/DEPS b/webrtc/logging/rtc_event_log/DEPS
index 39d2020..e340150 100644
--- a/webrtc/logging/rtc_event_log/DEPS
+++ b/webrtc/logging/rtc_event_log/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+webrtc/base",
   "+webrtc/call",
+  "+webrtc/modules/audio_coding/audio_network_adaptor",
   "+webrtc/modules/rtp_rtcp",
   "+webrtc/system_wrappers",
 ]
diff --git a/webrtc/logging/rtc_event_log/mock/mock_rtc_event_log.h b/webrtc/logging/rtc_event_log/mock/mock_rtc_event_log.h
index 315967b..4db33a1 100644
--- a/webrtc/logging/rtc_event_log/mock/mock_rtc_event_log.h
+++ b/webrtc/logging/rtc_event_log/mock/mock_rtc_event_log.h
@@ -58,6 +58,8 @@
                void(int32_t bitrate,
                     uint8_t fraction_loss,
                     int32_t total_packets));
+  MOCK_METHOD1(LogAudioNetworkAdaptation,
+               void(const AudioNetworkAdaptor::EncoderRuntimeConfig& config));
 };
 
 }  // namespace webrtc
diff --git a/webrtc/logging/rtc_event_log/rtc_event_log.cc b/webrtc/logging/rtc_event_log/rtc_event_log.cc
index 21ca5e1..65ee7d8 100644
--- a/webrtc/logging/rtc_event_log/rtc_event_log.cc
+++ b/webrtc/logging/rtc_event_log/rtc_event_log.cc
@@ -77,6 +77,8 @@
   void LogBwePacketLossEvent(int32_t bitrate,
                              uint8_t fraction_loss,
                              int32_t total_packets) override;
+  void LogAudioNetworkAdaptation(
+      const AudioNetworkAdaptor::EncoderRuntimeConfig& config) override;
 
  private:
   void StoreEvent(std::unique_ptr<rtclog::Event>* event);
@@ -434,6 +436,29 @@
   StoreEvent(&event);
 }
 
+void RtcEventLogImpl::LogAudioNetworkAdaptation(
+    const AudioNetworkAdaptor::EncoderRuntimeConfig& config) {
+  std::unique_ptr<rtclog::Event> event(new rtclog::Event());
+  event->set_timestamp_us(rtc::TimeMicros());
+  event->set_type(rtclog::Event::AUDIO_NETWORK_ADAPTATION_EVENT);
+  auto audio_network_adaptation = event->mutable_audio_network_adaptation();
+  if (config.bitrate_bps)
+    audio_network_adaptation->set_bitrate_bps(*config.bitrate_bps);
+  if (config.frame_length_ms)
+    audio_network_adaptation->set_frame_length_ms(*config.frame_length_ms);
+  if (config.uplink_packet_loss_fraction) {
+    audio_network_adaptation->set_uplink_packet_loss_fraction(
+        *config.uplink_packet_loss_fraction);
+  }
+  if (config.enable_fec)
+    audio_network_adaptation->set_enable_fec(*config.enable_fec);
+  if (config.enable_dtx)
+    audio_network_adaptation->set_enable_dtx(*config.enable_dtx);
+  if (config.num_channels)
+    audio_network_adaptation->set_num_channels(*config.num_channels);
+  StoreEvent(&event);
+}
+
 void RtcEventLogImpl::StoreEvent(std::unique_ptr<rtclog::Event>* event) {
   if (!event_queue_.Insert(event)) {
     LOG(LS_ERROR) << "WebRTC event log queue full. Dropping event.";
diff --git a/webrtc/logging/rtc_event_log/rtc_event_log.h b/webrtc/logging/rtc_event_log/rtc_event_log.h
index ccf6094..5d221d4 100644
--- a/webrtc/logging/rtc_event_log/rtc_event_log.h
+++ b/webrtc/logging/rtc_event_log/rtc_event_log.h
@@ -17,6 +17,7 @@
 #include "webrtc/base/platform_file.h"
 #include "webrtc/call/audio_receive_stream.h"
 #include "webrtc/call/audio_send_stream.h"
+#include "webrtc/modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h"
 #include "webrtc/video_receive_stream.h"
 #include "webrtc/video_send_stream.h"
 
@@ -114,6 +115,10 @@
                                      uint8_t fraction_loss,
                                      int32_t total_packets) = 0;
 
+  // Logs audio encoder re-configuration driven by audio network adaptor.
+  virtual void LogAudioNetworkAdaptation(
+      const AudioNetworkAdaptor::EncoderRuntimeConfig& config) = 0;
+
   // Reads an RtcEventLog file and returns true when reading was successful.
   // The result is stored in the given EventStream object.
   // The order of the events in the EventStream is implementation defined.
@@ -155,6 +160,8 @@
   void LogBwePacketLossEvent(int32_t bitrate,
                              uint8_t fraction_loss,
                              int32_t total_packets) override {}
+  void LogAudioNetworkAdaptation(
+      const AudioNetworkAdaptor::EncoderRuntimeConfig& config) override{};
 };
 
 }  // namespace webrtc
diff --git a/webrtc/logging/rtc_event_log/rtc_event_log.proto b/webrtc/logging/rtc_event_log/rtc_event_log.proto
index a6d1695..e807722 100644
--- a/webrtc/logging/rtc_event_log/rtc_event_log.proto
+++ b/webrtc/logging/rtc_event_log/rtc_event_log.proto
@@ -37,6 +37,7 @@
     VIDEO_SENDER_CONFIG_EVENT = 9;
     AUDIO_RECEIVER_CONFIG_EVENT = 10;
     AUDIO_SENDER_CONFIG_EVENT = 11;
+    AUDIO_NETWORK_ADAPTATION_EVENT = 16;
   }
 
   // required - Indicates the type of this event
@@ -65,6 +66,9 @@
 
   // optional - but required if type == AUDIO_SENDER_CONFIG_EVENT
   optional AudioSendConfig audio_sender_config = 11;
+
+  // optional - but required if type == AUDIO_NETWORK_ADAPTATION_EVENT
+  optional AudioNetworkAdaptation audio_network_adaptation = 16;
 }
 
 message RtpPacket {
@@ -227,3 +231,24 @@
   // RTP header extensions used for the outgoing audio stream.
   repeated RtpHeaderExtension header_extensions = 2;
 }
+
+message AudioNetworkAdaptation {
+  // Bit rate that the audio encoder is operating at.
+  optional int32 bitrate_bps = 1;
+
+  // Frame length that each encoded audio packet consists of.
+  optional int32 frame_length_ms = 2;
+
+  // Packet loss fraction that the encoder's forward error correction (FEC) is
+  // optimized for.
+  optional float uplink_packet_loss_fraction = 3;
+
+  // Whether forward error correction (FEC) is turned on or off.
+  optional bool enable_fec = 4;
+
+  // Whether discontinuous transmission (DTX) is turned on or off.
+  optional bool enable_dtx = 5;
+
+  // Number of audio channels that each encoded packet consists of.
+  optional uint32 num_channels = 6;
+}
\ No newline at end of file
diff --git a/webrtc/logging/rtc_event_log/rtc_event_log_parser.cc b/webrtc/logging/rtc_event_log/rtc_event_log_parser.cc
index ce55a4f..3b808b2 100644
--- a/webrtc/logging/rtc_event_log/rtc_event_log_parser.cc
+++ b/webrtc/logging/rtc_event_log/rtc_event_log_parser.cc
@@ -79,6 +79,8 @@
       return ParsedRtcEventLog::EventType::AUDIO_RECEIVER_CONFIG_EVENT;
     case rtclog::Event::AUDIO_SENDER_CONFIG_EVENT:
       return ParsedRtcEventLog::EventType::AUDIO_SENDER_CONFIG_EVENT;
+    case rtclog::Event::AUDIO_NETWORK_ADAPTATION_EVENT:
+      return ParsedRtcEventLog::EventType::AUDIO_NETWORK_ADAPTATION_EVENT;
   }
   RTC_NOTREACHED();
   return ParsedRtcEventLog::EventType::UNKNOWN_EVENT;
@@ -454,4 +456,29 @@
   }
 }
 
+void ParsedRtcEventLog::GetAudioNetworkAdaptation(
+    size_t index,
+    AudioNetworkAdaptor::EncoderRuntimeConfig* config) const {
+  RTC_CHECK_LT(index, GetNumberOfEvents());
+  const rtclog::Event& event = events_[index];
+  RTC_CHECK(event.has_type());
+  RTC_CHECK_EQ(event.type(), rtclog::Event::AUDIO_NETWORK_ADAPTATION_EVENT);
+  RTC_CHECK(event.has_audio_network_adaptation());
+  const rtclog::AudioNetworkAdaptation& ana_event =
+      event.audio_network_adaptation();
+  if (ana_event.has_bitrate_bps())
+    config->bitrate_bps = rtc::Optional<int>(ana_event.bitrate_bps());
+  if (ana_event.has_enable_fec())
+    config->enable_fec = rtc::Optional<bool>(ana_event.enable_fec());
+  if (ana_event.has_enable_dtx())
+    config->enable_dtx = rtc::Optional<bool>(ana_event.enable_dtx());
+  if (ana_event.has_frame_length_ms())
+    config->frame_length_ms = rtc::Optional<int>(ana_event.frame_length_ms());
+  if (ana_event.has_num_channels())
+    config->num_channels = rtc::Optional<size_t>(ana_event.num_channels());
+  if (ana_event.has_uplink_packet_loss_fraction())
+    config->uplink_packet_loss_fraction =
+        rtc::Optional<float>(ana_event.uplink_packet_loss_fraction());
+}
+
 }  // namespace webrtc
diff --git a/webrtc/logging/rtc_event_log/rtc_event_log_parser.h b/webrtc/logging/rtc_event_log/rtc_event_log_parser.h
index 2d66b90..8472668 100644
--- a/webrtc/logging/rtc_event_log/rtc_event_log_parser.h
+++ b/webrtc/logging/rtc_event_log/rtc_event_log_parser.h
@@ -47,7 +47,8 @@
     VIDEO_RECEIVER_CONFIG_EVENT = 8,
     VIDEO_SENDER_CONFIG_EVENT = 9,
     AUDIO_RECEIVER_CONFIG_EVENT = 10,
-    AUDIO_SENDER_CONFIG_EVENT = 11
+    AUDIO_SENDER_CONFIG_EVENT = 11,
+    AUDIO_NETWORK_ADAPTATION_EVENT = 16
   };
 
   // Reads an RtcEventLog file and returns true if parsing was successful.
@@ -123,6 +124,13 @@
                              uint8_t* fraction_loss,
                              int32_t* total_packets) const;
 
+  // Reads a audio network adaptation event to a (non-NULL)
+  // AudioNetworkAdaptor::EncoderRuntimeConfig struct. Only the fields that are
+  // stored in the protobuf will be written.
+  void GetAudioNetworkAdaptation(
+      size_t index,
+      AudioNetworkAdaptor::EncoderRuntimeConfig* config) const;
+
  private:
   std::vector<rtclog::Event> events_;
 };
diff --git a/webrtc/logging/rtc_event_log/rtc_event_log_unittest.cc b/webrtc/logging/rtc_event_log/rtc_event_log_unittest.cc
index 1540466..f0ee973 100644
--- a/webrtc/logging/rtc_event_log/rtc_event_log_unittest.cc
+++ b/webrtc/logging/rtc_event_log/rtc_event_log_unittest.cc
@@ -228,6 +228,19 @@
   }
 }
 
+void GenerateAudioNetworkAdaptation(
+    uint32_t extensions_bitvector,
+    AudioNetworkAdaptor::EncoderRuntimeConfig* config,
+    Random* prng) {
+  config->bitrate_bps = rtc::Optional<int>(prng->Rand(0, 3000000));
+  config->enable_fec = rtc::Optional<bool>(prng->Rand<bool>());
+  config->enable_dtx = rtc::Optional<bool>(prng->Rand<bool>());
+  config->frame_length_ms = rtc::Optional<int>(prng->Rand(10, 120));
+  config->num_channels = rtc::Optional<size_t>(prng->Rand(1, 2));
+  config->uplink_packet_loss_fraction =
+      rtc::Optional<float>(prng->Rand<float>());
+}
+
 // Test for the RtcEventLog class. Dumps some RTP packets and other events
 // to disk, then reads them back to see if they match.
 void LogSessionAndReadBack(size_t rtp_count,
@@ -604,6 +617,22 @@
   VideoSendStream::Config config;
 };
 
+class AudioNetworkAdaptationReadWriteTest : public ConfigReadWriteTest {
+ public:
+  void GenerateConfig(uint32_t extensions_bitvector) override {
+    GenerateAudioNetworkAdaptation(extensions_bitvector, &config, &prng);
+  }
+  void LogConfig(RtcEventLog* event_log) override {
+    event_log->LogAudioNetworkAdaptation(config);
+  }
+  void VerifyConfig(const ParsedRtcEventLog& parsed_log,
+                    size_t index) override {
+    RtcEventLogTestHelper::VerifyAudioNetworkAdaptation(parsed_log, index,
+                                                        config);
+  }
+  AudioNetworkAdaptor::EncoderRuntimeConfig config;
+};
+
 TEST(RtcEventLogTest, LogAudioReceiveConfig) {
   AudioReceiveConfigReadWriteTest test;
   test.DoTest();
@@ -623,4 +652,10 @@
   VideoSendConfigReadWriteTest test;
   test.DoTest();
 }
+
+TEST(RtcEventLogTest, LogAudioNetworkAdaptation) {
+  AudioNetworkAdaptationReadWriteTest test;
+  test.DoTest();
+}
+
 }  // namespace webrtc
diff --git a/webrtc/logging/rtc_event_log/rtc_event_log_unittest_helper.cc b/webrtc/logging/rtc_event_log/rtc_event_log_unittest_helper.cc
index fb60dbc..19ca8aa 100644
--- a/webrtc/logging/rtc_event_log/rtc_event_log_unittest_helper.cc
+++ b/webrtc/logging/rtc_event_log/rtc_event_log_unittest_helper.cc
@@ -461,6 +461,21 @@
   EXPECT_EQ(total_packets, parsed_total_packets);
 }
 
+void RtcEventLogTestHelper::VerifyAudioNetworkAdaptation(
+    const ParsedRtcEventLog& parsed_log,
+    size_t index,
+    const AudioNetworkAdaptor::EncoderRuntimeConfig& config) {
+  AudioNetworkAdaptor::EncoderRuntimeConfig parsed_config;
+  parsed_log.GetAudioNetworkAdaptation(index, &parsed_config);
+  EXPECT_EQ(config.bitrate_bps, parsed_config.bitrate_bps);
+  EXPECT_EQ(config.enable_dtx, parsed_config.enable_dtx);
+  EXPECT_EQ(config.enable_fec, parsed_config.enable_fec);
+  EXPECT_EQ(config.frame_length_ms, parsed_config.frame_length_ms);
+  EXPECT_EQ(config.num_channels, parsed_config.num_channels);
+  EXPECT_EQ(config.uplink_packet_loss_fraction,
+            parsed_config.uplink_packet_loss_fraction);
+}
+
 void RtcEventLogTestHelper::VerifyLogStartEvent(
     const ParsedRtcEventLog& parsed_log,
     size_t index) {
diff --git a/webrtc/logging/rtc_event_log/rtc_event_log_unittest_helper.h b/webrtc/logging/rtc_event_log/rtc_event_log_unittest_helper.h
index 3f89e30..2f4e177 100644
--- a/webrtc/logging/rtc_event_log/rtc_event_log_unittest_helper.h
+++ b/webrtc/logging/rtc_event_log/rtc_event_log_unittest_helper.h
@@ -56,6 +56,11 @@
                                  uint8_t fraction_loss,
                                  int32_t total_packets);
 
+  static void VerifyAudioNetworkAdaptation(
+      const ParsedRtcEventLog& parsed_log,
+      size_t index,
+      const AudioNetworkAdaptor::EncoderRuntimeConfig& config);
+
   static void VerifyLogStartEvent(const ParsedRtcEventLog& parsed_log,
                                   size_t index);
   static void VerifyLogEndEvent(const ParsedRtcEventLog& parsed_log,
diff --git a/webrtc/modules/BUILD.gn b/webrtc/modules/BUILD.gn
index f3b2ac9..2b941a8 100644
--- a/webrtc/modules/BUILD.gn
+++ b/webrtc/modules/BUILD.gn
@@ -258,6 +258,7 @@
       "audio_coding/audio_network_adaptor/channel_controller_unittest.cc",
       "audio_coding/audio_network_adaptor/controller_manager_unittest.cc",
       "audio_coding/audio_network_adaptor/dtx_controller_unittest.cc",
+      "audio_coding/audio_network_adaptor/event_log_writer_unittest.cc",
       "audio_coding/audio_network_adaptor/fec_controller_unittest.cc",
       "audio_coding/audio_network_adaptor/frame_length_controller_unittest.cc",
       "audio_coding/audio_network_adaptor/mock/mock_controller.h",
diff --git a/webrtc/modules/audio_coding/BUILD.gn b/webrtc/modules/audio_coding/BUILD.gn
index c38cd47..a8799be 100644
--- a/webrtc/modules/audio_coding/BUILD.gn
+++ b/webrtc/modules/audio_coding/BUILD.gn
@@ -887,7 +887,8 @@
     sources = [
       "audio_network_adaptor/debug_dump.proto",
     ]
-    proto_out_dir = "webrtc/modules/audio_coding/audio_network_adaptor"
+    proto_in_dir = "//"
+    proto_out_dir = "."
   }
   proto_library("ana_config_proto") {
     sources = [
@@ -914,6 +915,8 @@
     "audio_network_adaptor/debug_dump_writer.h",
     "audio_network_adaptor/dtx_controller.cc",
     "audio_network_adaptor/dtx_controller.h",
+    "audio_network_adaptor/event_log_writer.cc",
+    "audio_network_adaptor/event_log_writer.h",
     "audio_network_adaptor/fec_controller.cc",
     "audio_network_adaptor/fec_controller.h",
     "audio_network_adaptor/frame_length_controller.cc",
@@ -925,6 +928,7 @@
     "../..:webrtc_common",
     "../../base:rtc_base_approved",
     "../../common_audio",
+    "../../logging:rtc_event_log_api",
     "../../system_wrappers",
   ]
 
@@ -935,6 +939,11 @@
     ]
     defines = [ "WEBRTC_AUDIO_NETWORK_ADAPTOR_DEBUG_DUMP" ]
   }
+
+  if (!build_with_chromium && is_clang) {
+    # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
+    suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+  }
 }
 
 rtc_static_library("neteq") {
diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl.cc b/webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl.cc
index 3b767d9..6ec92b0 100644
--- a/webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl.cc
+++ b/webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl.cc
@@ -12,8 +12,16 @@
 
 #include <utility>
 
+#include "webrtc/base/logging.h"
+
 namespace webrtc {
 
+namespace {
+constexpr int kEventLogMinBitrateChangeBps = 5000;
+constexpr float kEventLogMinBitrateChangeFraction = 0.25;
+constexpr float kEventLogMinPacketLossChangeFraction = 0.5;
+}  // namespace
+
 AudioNetworkAdaptorImpl::Config::Config()
     : event_log(nullptr), clock(nullptr){};
 
@@ -25,7 +33,14 @@
     std::unique_ptr<DebugDumpWriter> debug_dump_writer)
     : config_(config),
       controller_manager_(std::move(controller_manager)),
-      debug_dump_writer_(std::move(debug_dump_writer)) {
+      debug_dump_writer_(std::move(debug_dump_writer)),
+      event_log_writer_(
+          config.event_log
+              ? new EventLogWriter(config.event_log,
+                                   kEventLogMinBitrateChangeBps,
+                                   kEventLogMinBitrateChangeFraction,
+                                   kEventLogMinPacketLossChangeFraction)
+              : nullptr) {
   RTC_DCHECK(controller_manager_);
 }
 
@@ -68,11 +83,13 @@
        controller_manager_->GetSortedControllers(last_metrics_))
     controller->MakeDecision(last_metrics_, &config);
 
-  // TODO(minyue): Add debug dumping.
   if (debug_dump_writer_)
     debug_dump_writer_->DumpEncoderRuntimeConfig(
         config, config_.clock->TimeInMilliseconds());
 
+  if (event_log_writer_)
+    event_log_writer_->MaybeLogEncoderConfig(config);
+
   return config;
 }
 
diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl.h b/webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl.h
index 801a9cc..9c47125 100644
--- a/webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl.h
+++ b/webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl.h
@@ -17,6 +17,7 @@
 #include "webrtc/modules/audio_coding/audio_network_adaptor/controller.h"
 #include "webrtc/modules/audio_coding/audio_network_adaptor/controller_manager.h"
 #include "webrtc/modules/audio_coding/audio_network_adaptor/debug_dump_writer.h"
+#include "webrtc/modules/audio_coding/audio_network_adaptor/event_log_writer.h"
 #include "webrtc/modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h"
 #include "webrtc/system_wrappers/include/clock.h"
 
@@ -65,6 +66,8 @@
 
   std::unique_ptr<DebugDumpWriter> debug_dump_writer_;
 
+  const std::unique_ptr<EventLogWriter> event_log_writer_;
+
   Controller::NetworkMetrics last_metrics_;
 
   RTC_DISALLOW_COPY_AND_ASSIGN(AudioNetworkAdaptorImpl);
diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl_unittest.cc b/webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl_unittest.cc
index b26d2f5..9b608dc 100644
--- a/webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl_unittest.cc
+++ b/webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl_unittest.cc
@@ -11,6 +11,7 @@
 #include <utility>
 #include <vector>
 
+#include "webrtc/logging/rtc_event_log/mock/mock_rtc_event_log.h"
 #include "webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl.h"
 #include "webrtc/modules/audio_coding/audio_network_adaptor/mock/mock_controller.h"
 #include "webrtc/modules/audio_coding/audio_network_adaptor/mock/mock_controller_manager.h"
@@ -52,6 +53,7 @@
   std::unique_ptr<AudioNetworkAdaptorImpl> audio_network_adaptor;
   std::vector<std::unique_ptr<MockController>> mock_controllers;
   std::unique_ptr<SimulatedClock> simulated_clock;
+  std::unique_ptr<MockRtcEventLog> event_log;
   MockDebugDumpWriter* mock_debug_dump_writer;
 };
 
@@ -76,6 +78,7 @@
       .WillRepeatedly(Return(controllers));
 
   states.simulated_clock.reset(new SimulatedClock(kClockInitialTimeMs * 1000));
+  states.event_log.reset(new NiceMock<MockRtcEventLog>());
 
   auto debug_dump_writer =
       std::unique_ptr<MockDebugDumpWriter>(new NiceMock<MockDebugDumpWriter>());
@@ -84,6 +87,7 @@
 
   AudioNetworkAdaptorImpl::Config config;
   config.clock = states.simulated_clock.get();
+  config.event_log = states.event_log.get();
   // AudioNetworkAdaptorImpl governs the lifetime of controller manager.
   states.audio_network_adaptor.reset(new AudioNetworkAdaptorImpl(
       config,
@@ -205,4 +209,20 @@
   states.audio_network_adaptor->SetOverhead(kOverhead);
 }
 
+TEST(AudioNetworkAdaptorImplTest, LogRuntimeConfigOnGetEncoderRuntimeConfig) {
+  auto states = CreateAudioNetworkAdaptor();
+
+  AudioNetworkAdaptor::EncoderRuntimeConfig config;
+  config.bitrate_bps = rtc::Optional<int>(32000);
+  config.enable_fec = rtc::Optional<bool>(true);
+
+  EXPECT_CALL(*states.mock_controllers[0], MakeDecision(_, _))
+      .WillOnce(SetArgPointee<1>(config));
+
+  EXPECT_CALL(*states.event_log,
+              LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(config)))
+      .Times(1);
+  states.audio_network_adaptor->GetEncoderRuntimeConfig();
+}
+
 }  // namespace webrtc
diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/event_log_writer.cc b/webrtc/modules/audio_coding/audio_network_adaptor/event_log_writer.cc
new file mode 100644
index 0000000..619a247
--- /dev/null
+++ b/webrtc/modules/audio_coding/audio_network_adaptor/event_log_writer.cc
@@ -0,0 +1,68 @@
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <math.h>
+#include <algorithm>
+
+#include "webrtc/logging/rtc_event_log/rtc_event_log.h"
+#include "webrtc/modules/audio_coding/audio_network_adaptor/event_log_writer.h"
+
+namespace webrtc {
+
+EventLogWriter::EventLogWriter(RtcEventLog* event_log,
+                               int min_bitrate_change_bps,
+                               float min_bitrate_change_fraction,
+                               float min_packet_loss_change_fraction)
+    : event_log_(event_log),
+      min_bitrate_change_bps_(min_bitrate_change_bps),
+      min_bitrate_change_fraction_(min_bitrate_change_fraction),
+      min_packet_loss_change_fraction_(min_packet_loss_change_fraction) {
+  RTC_DCHECK(event_log_);
+}
+
+EventLogWriter::~EventLogWriter() = default;
+
+void EventLogWriter::MaybeLogEncoderConfig(
+    const AudioNetworkAdaptor::EncoderRuntimeConfig& config) {
+  if (last_logged_config_.num_channels != config.num_channels)
+    return LogEncoderConfig(config);
+  if (last_logged_config_.enable_dtx != config.enable_dtx)
+    return LogEncoderConfig(config);
+  if (last_logged_config_.enable_fec != config.enable_fec)
+    return LogEncoderConfig(config);
+  if (last_logged_config_.frame_length_ms != config.frame_length_ms)
+    return LogEncoderConfig(config);
+  if ((!last_logged_config_.bitrate_bps && config.bitrate_bps) ||
+      (last_logged_config_.bitrate_bps && config.bitrate_bps &&
+       std::abs(*last_logged_config_.bitrate_bps - *config.bitrate_bps) >=
+           std::min(static_cast<int>(*last_logged_config_.bitrate_bps *
+                                     min_bitrate_change_fraction_),
+                    min_bitrate_change_bps_))) {
+    return LogEncoderConfig(config);
+  }
+  if ((!last_logged_config_.uplink_packet_loss_fraction &&
+       config.uplink_packet_loss_fraction) ||
+      (last_logged_config_.uplink_packet_loss_fraction &&
+       config.uplink_packet_loss_fraction &&
+       fabs(*last_logged_config_.uplink_packet_loss_fraction -
+            *config.uplink_packet_loss_fraction) >=
+           min_packet_loss_change_fraction_ *
+               *last_logged_config_.uplink_packet_loss_fraction)) {
+    return LogEncoderConfig(config);
+  }
+}
+
+void EventLogWriter::LogEncoderConfig(
+    const AudioNetworkAdaptor::EncoderRuntimeConfig& config) {
+  event_log_->LogAudioNetworkAdaptation(config);
+  last_logged_config_ = config;
+}
+
+}  // namespace webrtc
diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/event_log_writer.h b/webrtc/modules/audio_coding/audio_network_adaptor/event_log_writer.h
new file mode 100644
index 0000000..740da8c
--- /dev/null
+++ b/webrtc/modules/audio_coding/audio_network_adaptor/event_log_writer.h
@@ -0,0 +1,44 @@
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_EVENT_LOG_WRITER_H_
+#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_EVENT_LOG_WRITER_H_
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h"
+
+namespace webrtc {
+class RtcEventLog;
+
+class EventLogWriter final {
+ public:
+  EventLogWriter(RtcEventLog* event_log,
+                 int min_bitrate_change_bps,
+                 float min_bitrate_change_fraction,
+                 float min_packet_loss_change_fraction);
+  ~EventLogWriter();
+  void MaybeLogEncoderConfig(
+      const AudioNetworkAdaptor::EncoderRuntimeConfig& config);
+
+ private:
+  void LogEncoderConfig(
+      const AudioNetworkAdaptor::EncoderRuntimeConfig& config);
+
+  RtcEventLog* const event_log_;
+  const int min_bitrate_change_bps_;
+  const float min_bitrate_change_fraction_;
+  const float min_packet_loss_change_fraction_;
+  AudioNetworkAdaptor::EncoderRuntimeConfig last_logged_config_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(EventLogWriter);
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_EVENT_LOG_WRITER_H_
diff --git a/webrtc/modules/audio_coding/audio_network_adaptor/event_log_writer_unittest.cc b/webrtc/modules/audio_coding/audio_network_adaptor/event_log_writer_unittest.cc
new file mode 100644
index 0000000..289b8e2
--- /dev/null
+++ b/webrtc/modules/audio_coding/audio_network_adaptor/event_log_writer_unittest.cc
@@ -0,0 +1,265 @@
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+
+#include "webrtc/logging/rtc_event_log/mock/mock_rtc_event_log.h"
+#include "webrtc/modules/audio_coding/audio_network_adaptor/event_log_writer.h"
+#include "webrtc/test/gtest.h"
+
+namespace webrtc {
+
+namespace {
+
+constexpr int kMinBitrateChangeBps = 5000;
+constexpr float kMinPacketLossChangeFraction = 0.5;
+constexpr float kMinBitrateChangeFraction = 0.25;
+
+constexpr int kHighBitrateBps = 70000;
+constexpr int kLowBitrateBps = 10000;
+constexpr int kFrameLengthMs = 60;
+constexpr bool kEnableFec = true;
+constexpr bool kEnableDtx = true;
+constexpr float kPacketLossFraction = 0.05f;
+constexpr size_t kNumChannels = 1;
+
+MATCHER_P(EncoderRuntimeConfigIs, config, "") {
+  return arg.bitrate_bps == config.bitrate_bps &&
+         arg.frame_length_ms == config.frame_length_ms &&
+         arg.uplink_packet_loss_fraction ==
+             config.uplink_packet_loss_fraction &&
+         arg.enable_fec == config.enable_fec &&
+         arg.enable_dtx == config.enable_dtx &&
+         arg.num_channels == config.num_channels;
+}
+
+struct EventLogWriterStates {
+  std::unique_ptr<EventLogWriter> event_log_writer;
+  std::unique_ptr<testing::StrictMock<MockRtcEventLog>> event_log;
+  AudioNetworkAdaptor::EncoderRuntimeConfig runtime_config;
+};
+
+EventLogWriterStates CreateEventLogWriter() {
+  EventLogWriterStates state;
+  state.event_log.reset(new testing::StrictMock<MockRtcEventLog>());
+  state.event_log_writer.reset(new EventLogWriter(
+      state.event_log.get(), kMinBitrateChangeBps, kMinBitrateChangeFraction,
+      kMinPacketLossChangeFraction));
+  state.runtime_config.bitrate_bps = rtc::Optional<int>(kHighBitrateBps);
+  state.runtime_config.frame_length_ms = rtc::Optional<int>(kFrameLengthMs);
+  state.runtime_config.uplink_packet_loss_fraction =
+      rtc::Optional<float>(kPacketLossFraction);
+  state.runtime_config.enable_fec = rtc::Optional<bool>(kEnableFec);
+  state.runtime_config.enable_dtx = rtc::Optional<bool>(kEnableDtx);
+  state.runtime_config.num_channels = rtc::Optional<size_t>(kNumChannels);
+  return state;
+}
+}  // namespace
+
+TEST(EventLogWriterTest, FirstConfigIsLogged) {
+  auto state = CreateEventLogWriter();
+  EXPECT_CALL(
+      *state.event_log,
+      LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
+      .Times(1);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+}
+
+TEST(EventLogWriterTest, SameConfigIsNotLogged) {
+  auto state = CreateEventLogWriter();
+  EXPECT_CALL(
+      *state.event_log,
+      LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
+      .Times(1);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+}
+
+TEST(EventLogWriterTest, LogFecStateChange) {
+  auto state = CreateEventLogWriter();
+  EXPECT_CALL(
+      *state.event_log,
+      LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
+      .Times(1);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+
+  state.runtime_config.enable_fec = rtc::Optional<bool>(!kEnableFec);
+  EXPECT_CALL(
+      *state.event_log,
+      LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
+      .Times(1);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+}
+
+TEST(EventLogWriterTest, LogDtxStateChange) {
+  auto state = CreateEventLogWriter();
+  EXPECT_CALL(
+      *state.event_log,
+      LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
+      .Times(1);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+
+  state.runtime_config.enable_dtx = rtc::Optional<bool>(!kEnableDtx);
+  EXPECT_CALL(
+      *state.event_log,
+      LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
+      .Times(1);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+}
+
+TEST(EventLogWriterTest, LogChannelChange) {
+  auto state = CreateEventLogWriter();
+  EXPECT_CALL(
+      *state.event_log,
+      LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
+      .Times(1);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+
+  state.runtime_config.num_channels = rtc::Optional<size_t>(kNumChannels + 1);
+  EXPECT_CALL(
+      *state.event_log,
+      LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
+      .Times(1);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+}
+
+TEST(EventLogWriterTest, LogFrameLengthChange) {
+  auto state = CreateEventLogWriter();
+  EXPECT_CALL(
+      *state.event_log,
+      LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
+      .Times(1);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+
+  state.runtime_config.frame_length_ms = rtc::Optional<int>(20);
+  EXPECT_CALL(
+      *state.event_log,
+      LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
+      .Times(1);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+}
+
+TEST(EventLogWriterTest, DoNotLogSmallBitrateChange) {
+  auto state = CreateEventLogWriter();
+  EXPECT_CALL(
+      *state.event_log,
+      LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
+      .Times(1);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+  state.runtime_config.bitrate_bps =
+      rtc::Optional<int>(kHighBitrateBps + kMinBitrateChangeBps - 1);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+}
+
+TEST(EventLogWriterTest, LogLargeBitrateChange) {
+  auto state = CreateEventLogWriter();
+  EXPECT_CALL(
+      *state.event_log,
+      LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
+      .Times(1);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+  // At high bitrate, the min fraction rule requires a larger change than the
+  // min change rule. We make sure that the min change rule applies.
+  RTC_DCHECK_GT(kHighBitrateBps * kMinBitrateChangeFraction,
+                kMinBitrateChangeBps);
+  state.runtime_config.bitrate_bps =
+      rtc::Optional<int>(kHighBitrateBps + kMinBitrateChangeBps);
+  EXPECT_CALL(
+      *state.event_log,
+      LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
+      .Times(1);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+}
+
+TEST(EventLogWriterTest, LogMinBitrateChangeFractionOnLowBitrateChange) {
+  auto state = CreateEventLogWriter();
+  state.runtime_config.bitrate_bps = rtc::Optional<int>(kLowBitrateBps);
+  EXPECT_CALL(
+      *state.event_log,
+      LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
+      .Times(1);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+  // At high bitrate, the min change rule requires a larger change than the min
+  // fraction rule. We make sure that the min fraction rule applies.
+  state.runtime_config.bitrate_bps = rtc::Optional<int>(
+      kLowBitrateBps + kLowBitrateBps * kMinBitrateChangeFraction);
+  EXPECT_CALL(
+      *state.event_log,
+      LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
+      .Times(1);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+}
+
+TEST(EventLogWriterTest, DoNotLogSmallPacketLossFractionChange) {
+  auto state = CreateEventLogWriter();
+  EXPECT_CALL(
+      *state.event_log,
+      LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
+      .Times(1);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+  state.runtime_config.uplink_packet_loss_fraction = rtc::Optional<float>(
+      kPacketLossFraction + kMinPacketLossChangeFraction * kPacketLossFraction -
+      0.001f);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+}
+
+TEST(EventLogWriterTest, LogLargePacketLossFractionChange) {
+  auto state = CreateEventLogWriter();
+  EXPECT_CALL(
+      *state.event_log,
+      LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
+      .Times(1);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+  state.runtime_config.uplink_packet_loss_fraction = rtc::Optional<float>(
+      kPacketLossFraction + kMinPacketLossChangeFraction * kPacketLossFraction);
+  EXPECT_CALL(
+      *state.event_log,
+      LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
+      .Times(1);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+}
+
+TEST(EventLogWriterTest, LogJustOnceOnMultipleChanges) {
+  auto state = CreateEventLogWriter();
+  EXPECT_CALL(
+      *state.event_log,
+      LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
+      .Times(1);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+  state.runtime_config.uplink_packet_loss_fraction = rtc::Optional<float>(
+      kPacketLossFraction + kMinPacketLossChangeFraction * kPacketLossFraction);
+  state.runtime_config.frame_length_ms = rtc::Optional<int>(20);
+  EXPECT_CALL(
+      *state.event_log,
+      LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
+      .Times(1);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+}
+
+TEST(EventLogWriterTest, LogAfterGradualChange) {
+  auto state = CreateEventLogWriter();
+  EXPECT_CALL(
+      *state.event_log,
+      LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
+      .Times(1);
+  state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+  state.runtime_config.bitrate_bps =
+      rtc::Optional<int>(kHighBitrateBps + kMinBitrateChangeBps);
+  EXPECT_CALL(
+      *state.event_log,
+      LogAudioNetworkAdaptation(EncoderRuntimeConfigIs(state.runtime_config)))
+      .Times(1);
+  for (int bitrate_bps = kHighBitrateBps;
+       bitrate_bps <= kHighBitrateBps + kMinBitrateChangeBps; bitrate_bps++) {
+    state.runtime_config.bitrate_bps = rtc::Optional<int>(bitrate_bps);
+    state.event_log_writer->MaybeLogEncoderConfig(state.runtime_config);
+  }
+}
+}  // namespace webrtc
diff --git a/webrtc/tools/BUILD.gn b/webrtc/tools/BUILD.gn
index 266f669..417e365 100644
--- a/webrtc/tools/BUILD.gn
+++ b/webrtc/tools/BUILD.gn
@@ -203,6 +203,7 @@
       "../call:call_interfaces",
       "../logging:rtc_event_log_impl",
       "../logging:rtc_event_log_parser",
+      "../modules/audio_coding:ana_debug_dump_proto",
       "../modules/congestion_controller",
       "../modules/rtp_rtcp",
       "../system_wrappers:system_wrappers_default",
diff --git a/webrtc/tools/event_log_visualizer/analyzer.cc b/webrtc/tools/event_log_visualizer/analyzer.cc
index 89e516a..3fd2c25 100644
--- a/webrtc/tools/event_log_visualizer/analyzer.cc
+++ b/webrtc/tools/event_log_visualizer/analyzer.cc
@@ -423,6 +423,9 @@
         bwe_loss_updates_.push_back(bwe_update);
         break;
       }
+      case ParsedRtcEventLog::AUDIO_NETWORK_ADAPTATION_EVENT: {
+        break;
+      }
       case ParsedRtcEventLog::BWE_PACKET_DELAY_EVENT: {
         break;
       }
diff --git a/webrtc/voice_engine/channel.cc b/webrtc/voice_engine/channel.cc
index 68f2e2d..1d39645 100644
--- a/webrtc/voice_engine/channel.cc
+++ b/webrtc/voice_engine/channel.cc
@@ -140,6 +140,14 @@
     }
   }
 
+  void LogAudioNetworkAdaptation(
+      const AudioNetworkAdaptor::EncoderRuntimeConfig& config) override {
+    rtc::CritScope lock(&crit_);
+    if (event_log_) {
+      event_log_->LogAudioNetworkAdaptation(config);
+    }
+  }
+
   void SetEventLog(RtcEventLog* event_log) {
     rtc::CritScope lock(&crit_);
     event_log_ = event_log;