Hook up libjingle WebRtcVoiceEngine to Call API for combined A/V BWE.

BUG=4574,3109
R=pbos@webrtc.org, tommi@webrtc.org

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

Cr-Commit-Position: refs/heads/master@{#9150}
diff --git a/talk/app/webrtc/webrtcsession.cc b/talk/app/webrtc/webrtcsession.cc
index f235cd5..dd5d858 100644
--- a/talk/app/webrtc/webrtcsession.cc
+++ b/talk/app/webrtc/webrtcsession.cc
@@ -505,7 +505,7 @@
   }
   if (voice_channel_) {
     SignalVoiceChannelDestroyed();
-    channel_manager_->DestroyVoiceChannel(voice_channel_.release());
+    channel_manager_->DestroyVoiceChannel(voice_channel_.release(), nullptr);
   }
   if (data_channel_) {
     SignalDataChannelDestroyed();
@@ -1553,7 +1553,8 @@
     mediastream_signaling_->OnAudioChannelClose();
     SignalVoiceChannelDestroyed();
     const std::string content_name = voice_channel_->content_name();
-    channel_manager_->DestroyVoiceChannel(voice_channel_.release());
+    channel_manager_->DestroyVoiceChannel(voice_channel_.release(),
+                                          video_channel_.get());
     DestroyTransportProxy(content_name);
   }
 
diff --git a/talk/media/base/fakemediaengine.h b/talk/media/base/fakemediaengine.h
index 7deaed7..ead9f79 100644
--- a/talk/media/base/fakemediaengine.h
+++ b/talk/media/base/fakemediaengine.h
@@ -516,6 +516,7 @@
     return RtpHelper<VideoMediaChannel>::RemoveSendStream(ssrc);
   }
 
+  void DetachVoiceChannel() override {}
   virtual bool SetRecvCodecs(const std::vector<VideoCodec>& codecs) {
     if (fail_set_recv_codecs()) {
       // Fake the failure in SetRecvCodecs.
diff --git a/talk/media/base/filemediaengine.h b/talk/media/base/filemediaengine.h
index 1cd9901..c2e80c3 100644
--- a/talk/media/base/filemediaengine.h
+++ b/talk/media/base/filemediaengine.h
@@ -262,8 +262,8 @@
       rtc::StreamInterface* output_file_stream,
       rtc::Thread* rtp_sender_thread);
   virtual ~FileVideoChannel();
-
   // Implement pure virtual methods of VideoMediaChannel.
+  void DetachVoiceChannel() override {}
   virtual bool SetRecvCodecs(const std::vector<VideoCodec>& codecs) {
     return true;
   }
diff --git a/talk/media/base/mediachannel.h b/talk/media/base/mediachannel.h
index 12bb519..dd78f0e 100644
--- a/talk/media/base/mediachannel.h
+++ b/talk/media/base/mediachannel.h
@@ -1084,6 +1084,8 @@
 
   VideoMediaChannel() : renderer_(NULL) {}
   virtual ~VideoMediaChannel() {}
+  // Allow video channel to unhook itself from an associated voice channel.
+  virtual void DetachVoiceChannel() = 0;
   // Sets the codecs/payload types to be used for incoming media.
   virtual bool SetRecvCodecs(const std::vector<VideoCodec>& codecs) = 0;
   // Sets the codecs/payload types to be used for outgoing media.
diff --git a/talk/media/webrtc/fakewebrtccall.cc b/talk/media/webrtc/fakewebrtccall.cc
index d5201ed..257c018 100644
--- a/talk/media/webrtc/fakewebrtccall.cc
+++ b/talk/media/webrtc/fakewebrtccall.cc
@@ -27,10 +27,26 @@
 
 #include "talk/media/webrtc/fakewebrtccall.h"
 
+#include <algorithm>
+
 #include "talk/media/base/rtputils.h"
 #include "webrtc/base/gunit.h"
 
 namespace cricket {
+FakeAudioReceiveStream::FakeAudioReceiveStream(
+    const webrtc::AudioReceiveStream::Config& config)
+    : config_(config), received_packets_(0) {
+}
+
+const webrtc::AudioReceiveStream::Config&
+    FakeAudioReceiveStream::GetConfig() const {
+  return config_;
+}
+
+void FakeAudioReceiveStream::IncrementReceivedPackets() {
+  received_packets_++;
+}
+
 FakeVideoSendStream::FakeVideoSendStream(
     const webrtc::VideoSendStream::Config& config,
     const webrtc::VideoEncoderConfig& encoder_config)
@@ -181,30 +197,56 @@
 FakeCall::~FakeCall() {
   EXPECT_EQ(0u, video_send_streams_.size());
   EXPECT_EQ(0u, video_receive_streams_.size());
+  EXPECT_EQ(0u, audio_receive_streams_.size());
 }
 
 webrtc::Call::Config FakeCall::GetConfig() const {
   return config_;
 }
 
-std::vector<FakeVideoSendStream*> FakeCall::GetVideoSendStreams() {
+const std::vector<FakeVideoSendStream*>& FakeCall::GetVideoSendStreams() {
   return video_send_streams_;
 }
 
-std::vector<FakeVideoReceiveStream*> FakeCall::GetVideoReceiveStreams() {
+const std::vector<FakeVideoReceiveStream*>& FakeCall::GetVideoReceiveStreams() {
   return video_receive_streams_;
 }
 
+const std::vector<FakeAudioReceiveStream*>& FakeCall::GetAudioReceiveStreams() {
+  return audio_receive_streams_;
+}
+
+const FakeAudioReceiveStream* FakeCall::GetAudioReceiveStream(uint32_t ssrc) {
+  for (const auto p : GetAudioReceiveStreams()) {
+    if (p->GetConfig().rtp.remote_ssrc == ssrc) {
+      return p;
+    }
+  }
+  return nullptr;
+}
+
 webrtc::Call::NetworkState FakeCall::GetNetworkState() const {
   return network_state_;
 }
 
 webrtc::AudioReceiveStream* FakeCall::CreateAudioReceiveStream(
     const webrtc::AudioReceiveStream::Config& config) {
-  return nullptr;
+  audio_receive_streams_.push_back(new FakeAudioReceiveStream(config));
+  ++num_created_receive_streams_;
+  return audio_receive_streams_.back();
 }
+
 void FakeCall::DestroyAudioReceiveStream(
     webrtc::AudioReceiveStream* receive_stream) {
+  auto it = std::find(audio_receive_streams_.begin(),
+                      audio_receive_streams_.end(),
+                      static_cast<FakeAudioReceiveStream*>(receive_stream));
+  if (it == audio_receive_streams_.end()) {
+    ADD_FAILURE() << "DestroyAudioReceiveStream called with unknown paramter.";
+  } else {
+    delete *it;
+    audio_receive_streams_.erase(it);
+  }
 }
 
 webrtc::VideoSendStream* FakeCall::CreateVideoSendStream(
@@ -218,37 +260,35 @@
 }
 
 void FakeCall::DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) {
-  FakeVideoSendStream* fake_stream =
-      static_cast<FakeVideoSendStream*>(send_stream);
-  for (size_t i = 0; i < video_send_streams_.size(); ++i) {
-    if (video_send_streams_[i] == fake_stream) {
-      delete video_send_streams_[i];
-      video_send_streams_.erase(video_send_streams_.begin() + i);
-      return;
-    }
+  auto it = std::find(video_send_streams_.begin(),
+                      video_send_streams_.end(),
+                      static_cast<FakeVideoSendStream*>(send_stream));
+  if (it == video_send_streams_.end()) {
+    ADD_FAILURE() << "DestroyVideoSendStream called with unknown paramter.";
+  } else {
+    delete *it;
+    video_send_streams_.erase(it);
   }
-  ADD_FAILURE() << "DestroyVideoSendStream called with unknown paramter.";
 }
 
 webrtc::VideoReceiveStream* FakeCall::CreateVideoReceiveStream(
     const webrtc::VideoReceiveStream::Config& config) {
   video_receive_streams_.push_back(new FakeVideoReceiveStream(config));
   ++num_created_receive_streams_;
-  return video_receive_streams_[video_receive_streams_.size() - 1];
+  return video_receive_streams_.back();
 }
 
 void FakeCall::DestroyVideoReceiveStream(
     webrtc::VideoReceiveStream* receive_stream) {
-  FakeVideoReceiveStream* fake_stream =
-      static_cast<FakeVideoReceiveStream*>(receive_stream);
-  for (size_t i = 0; i < video_receive_streams_.size(); ++i) {
-    if (video_receive_streams_[i] == fake_stream) {
-      delete video_receive_streams_[i];
-      video_receive_streams_.erase(video_receive_streams_.begin() + i);
-      return;
-    }
+  auto it = std::find(video_receive_streams_.begin(),
+                      video_receive_streams_.end(),
+                      static_cast<FakeVideoReceiveStream*>(receive_stream));
+  if (it == video_receive_streams_.end()) {
+    ADD_FAILURE() << "DestroyVideoReceiveStream called with unknown paramter.";
+  } else {
+    delete *it;
+    video_receive_streams_.erase(it);
   }
-  ADD_FAILURE() << "DestroyVideoReceiveStream called with unknown paramter.";
 }
 
 webrtc::PacketReceiver* FakeCall::Receiver() {
@@ -258,16 +298,26 @@
 FakeCall::DeliveryStatus FakeCall::DeliverPacket(webrtc::MediaType media_type,
                                                  const uint8_t* packet,
                                                  size_t length) {
-  EXPECT_TRUE(media_type == webrtc::MediaType::ANY ||
-              media_type == webrtc::MediaType::VIDEO);
   EXPECT_GE(length, 12u);
   uint32_t ssrc;
   if (!GetRtpSsrc(packet, length, &ssrc))
     return DELIVERY_PACKET_ERROR;
 
-  for (auto receiver : video_receive_streams_) {
-    if (receiver->GetConfig().rtp.remote_ssrc == ssrc)
+  if (media_type == webrtc::MediaType::ANY ||
+      media_type == webrtc::MediaType::VIDEO) {
+    for (auto receiver : video_receive_streams_) {
+      if (receiver->GetConfig().rtp.remote_ssrc == ssrc)
         return DELIVERY_OK;
+    }
+  }
+  if (media_type == webrtc::MediaType::ANY ||
+      media_type == webrtc::MediaType::AUDIO) {
+    for (auto receiver : audio_receive_streams_) {
+      if (receiver->GetConfig().rtp.remote_ssrc == ssrc) {
+        receiver->IncrementReceivedPackets();
+        return DELIVERY_OK;
+      }
+    }
   }
   return DELIVERY_UNKNOWN_SSRC;
 }
diff --git a/talk/media/webrtc/fakewebrtccall.h b/talk/media/webrtc/fakewebrtccall.h
index 1588de4..e4b00d2 100644
--- a/talk/media/webrtc/fakewebrtccall.h
+++ b/talk/media/webrtc/fakewebrtccall.h
@@ -31,11 +31,27 @@
 #include <vector>
 
 #include "webrtc/call.h"
+#include "webrtc/audio_receive_stream.h"
 #include "webrtc/video_frame.h"
 #include "webrtc/video_receive_stream.h"
 #include "webrtc/video_send_stream.h"
 
 namespace cricket {
+class FakeAudioReceiveStream : public webrtc::AudioReceiveStream {
+ public:
+  explicit FakeAudioReceiveStream(
+      const webrtc::AudioReceiveStream::Config& config);
+
+  const webrtc::AudioReceiveStream::Config& GetConfig() const;
+
+  int received_packets() const { return received_packets_; }
+  void IncrementReceivedPackets();
+
+ private:
+  webrtc::AudioReceiveStream::Config config_;
+  int received_packets_;
+};
+
 class FakeVideoSendStream : public webrtc::VideoSendStream,
                             public webrtc::VideoSendStreamInput {
  public:
@@ -109,8 +125,11 @@
   ~FakeCall();
 
   webrtc::Call::Config GetConfig() const;
-  std::vector<FakeVideoSendStream*> GetVideoSendStreams();
-  std::vector<FakeVideoReceiveStream*> GetVideoReceiveStreams();
+  const std::vector<FakeVideoSendStream*>& GetVideoSendStreams();
+  const std::vector<FakeVideoReceiveStream*>& GetVideoReceiveStreams();
+
+  const std::vector<FakeAudioReceiveStream*>& GetAudioReceiveStreams();
+  const FakeAudioReceiveStream* GetAudioReceiveStream(uint32_t ssrc);
 
   webrtc::Call::NetworkState GetNetworkState() const;
   int GetNumCreatedSendStreams() const;
@@ -148,6 +167,7 @@
   webrtc::Call::Stats stats_;
   std::vector<FakeVideoSendStream*> video_send_streams_;
   std::vector<FakeVideoReceiveStream*> video_receive_streams_;
+  std::vector<FakeAudioReceiveStream*> audio_receive_streams_;
 
   int num_created_send_streams_;
   int num_created_receive_streams_;
diff --git a/talk/media/webrtc/webrtcvideoengine2.cc b/talk/media/webrtc/webrtcvideoengine2.cc
index 93bd447..fbb5fc2 100644
--- a/talk/media/webrtc/webrtcvideoengine2.cc
+++ b/talk/media/webrtc/webrtcvideoengine2.cc
@@ -610,12 +610,9 @@
                << (voice_channel != NULL ? "With" : "Without")
                << " voice channel. Options: " << options.ToString();
   WebRtcVideoChannel2* channel =
-      new WebRtcVideoChannel2(call_factory_,
-                              voice_engine_,
-                              voice_channel,
-                              options,
-                              external_encoder_factory_,
-                              external_decoder_factory_);
+      new WebRtcVideoChannel2(call_factory_, voice_engine_,
+          static_cast<WebRtcVoiceMediaChannel*>(voice_channel), options,
+          external_encoder_factory_, external_decoder_factory_);
   if (!channel->Init()) {
     delete channel;
     return NULL;
@@ -780,17 +777,16 @@
 WebRtcVideoChannel2::WebRtcVideoChannel2(
     WebRtcCallFactory* call_factory,
     WebRtcVoiceEngine* voice_engine,
-    VoiceMediaChannel* voice_channel,
+    WebRtcVoiceMediaChannel* voice_channel,
     const VideoOptions& options,
     WebRtcVideoEncoderFactory* external_encoder_factory,
     WebRtcVideoDecoderFactory* external_decoder_factory)
     : unsignalled_ssrc_handler_(&default_unsignalled_ssrc_handler_),
-      voice_channel_id_(voice_channel != nullptr
-                            ? static_cast<WebRtcVoiceMediaChannel*>(
-                                  voice_channel)->voe_channel()
-                            : -1),
+      voice_channel_(voice_channel),
+      voice_channel_id_(voice_channel ? voice_channel->voe_channel() : -1),
       external_encoder_factory_(external_encoder_factory),
       external_decoder_factory_(external_decoder_factory) {
+  DCHECK(thread_checker_.CalledOnValidThread());
   SetDefaultOptions();
   options_.SetAll(options);
   options_.cpu_overuse_detection.Get(&signal_cpu_adaptation_);
@@ -803,7 +799,9 @@
   config.bitrate_config.start_bitrate_bps = kStartBandwidthBps;
   config.bitrate_config.max_bitrate_bps = kMaxBandwidthBps;
   call_.reset(call_factory->CreateCall(config));
-
+  if (voice_channel_) {
+    voice_channel_->SetCall(call_.get());
+  }
   rtcp_receiver_report_ssrc_ = kDefaultRtcpReceiverReportSsrc;
   sending_ = false;
   default_send_ssrc_ = 0;
@@ -818,6 +816,7 @@
 }
 
 WebRtcVideoChannel2::~WebRtcVideoChannel2() {
+  DetachVoiceChannel();
   for (auto& kv : send_streams_)
     delete kv.second;
   for (auto& kv : receive_streams_)
@@ -826,6 +825,14 @@
 
 bool WebRtcVideoChannel2::Init() { return true; }
 
+void WebRtcVideoChannel2::DetachVoiceChannel() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (voice_channel_) {
+    voice_channel_->SetCall(nullptr);
+    voice_channel_ = nullptr;
+  }
+}
+
 bool WebRtcVideoChannel2::CodecIsExternallySupported(
     const std::string& name) const {
   if (external_encoder_factory_ == NULL) {
@@ -1121,6 +1128,8 @@
 
 bool WebRtcVideoChannel2::AddRecvStream(const StreamParams& sp,
                                         bool default_stream) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
   LOG(LS_INFO) << "AddRecvStream" << (default_stream ? " (default stream)" : "")
                << ": " << sp.ToString();
   if (!ValidateStreamParams(sp))
diff --git a/talk/media/webrtc/webrtcvideoengine2.h b/talk/media/webrtc/webrtcvideoengine2.h
index a0fde8f..b7775db 100644
--- a/talk/media/webrtc/webrtcvideoengine2.h
+++ b/talk/media/webrtc/webrtcvideoengine2.h
@@ -40,6 +40,7 @@
 #include "webrtc/base/criticalsection.h"
 #include "webrtc/base/scoped_ptr.h"
 #include "webrtc/base/thread_annotations.h"
+#include "webrtc/base/thread_checker.h"
 #include "webrtc/call.h"
 #include "webrtc/transport.h"
 #include "webrtc/video_frame.h"
@@ -71,6 +72,7 @@
 class WebRtcVideoChannelRecvInfo;
 class WebRtcVideoChannelSendInfo;
 class WebRtcVoiceEngine;
+class WebRtcVoiceMediaChannel;
 
 struct CapturedFrame;
 struct Device;
@@ -178,7 +180,7 @@
  public:
   WebRtcVideoChannel2(WebRtcCallFactory* call_factory,
                       WebRtcVoiceEngine* voice_engine,
-                      VoiceMediaChannel* voice_channel,
+                      WebRtcVoiceMediaChannel* voice_channel,
                       const VideoOptions& options,
                       WebRtcVideoEncoderFactory* external_encoder_factory,
                       WebRtcVideoDecoderFactory* external_decoder_factory);
@@ -186,6 +188,7 @@
   bool Init();
 
   // VideoMediaChannel implementation
+  void DetachVoiceChannel() override;
   bool SetRecvCodecs(const std::vector<VideoCodec>& codecs) override;
   bool SetSendCodecs(const std::vector<VideoCodec>& codecs) override;
   bool GetSendCodec(VideoCodec* send_codec) override;
@@ -484,6 +487,8 @@
   void FillBandwidthEstimationStats(const webrtc::Call::Stats& stats,
                                     VideoMediaInfo* info);
 
+  rtc::ThreadChecker thread_checker_;
+
   uint32_t rtcp_receiver_report_ssrc_;
   bool sending_;
   rtc::scoped_ptr<webrtc::Call> call_;
@@ -513,6 +518,7 @@
   Settable<VideoCodecSettings> send_codec_;
   std::vector<webrtc::RtpExtension> send_rtp_extensions_;
 
+  WebRtcVoiceMediaChannel* voice_channel_;
   const int voice_channel_id_;
   WebRtcVideoEncoderFactory* const external_encoder_factory_;
   WebRtcVideoDecoderFactory* const external_decoder_factory_;
diff --git a/talk/media/webrtc/webrtcvoiceengine.cc b/talk/media/webrtc/webrtcvoiceengine.cc
index 74a4b25..ac36ed5 100644
--- a/talk/media/webrtc/webrtcvoiceengine.cc
+++ b/talk/media/webrtc/webrtcvoiceengine.cc
@@ -1889,11 +1889,11 @@
       send_(SEND_NOTHING),
       shared_bwe_vie_(NULL),
       shared_bwe_vie_channel_(-1),
+      call_(nullptr),
       default_receive_ssrc_(0) {
   engine->RegisterChannel(this);
   LOG(LS_VERBOSE) << "WebRtcVoiceMediaChannel::WebRtcVoiceMediaChannel "
                   << voe_channel();
-
   ConfigureSendChannel(voe_channel());
 }
 
@@ -1901,6 +1901,7 @@
   LOG(LS_VERBOSE) << "WebRtcVoiceMediaChannel::~WebRtcVoiceMediaChannel "
                   << voe_channel();
   SetupSharedBandwidthEstimation(NULL, -1);
+  DCHECK(receive_streams_.empty() || call_);
 
   // Remove any remaining send streams, the default channel will be deleted
   // later.
@@ -1913,6 +1914,7 @@
   while (!receive_channels_.empty()) {
     RemoveRecvStream(receive_channels_.begin()->first);
   }
+  DCHECK(receive_streams_.empty());
 
   // Delete the default channel.
   DeleteChannel(voe_channel());
@@ -2006,6 +2008,7 @@
                                       shared_bwe_vie_channel_)) {
     return false;
   }
+  SetCall(call_);
 
   LOG(LS_INFO) << "Set voice channel options.  Current options: "
                << options_.ToString();
@@ -2398,6 +2401,29 @@
   }
 
   receive_extensions_ = extensions;
+
+  // Recreate AudioReceiveStream:s.
+  {
+    std::vector<webrtc::RtpExtension> exts;
+
+    const RtpHeaderExtension* audio_level_extension =
+        FindHeaderExtension(extensions, kRtpAudioLevelHeaderExtension);
+    if (audio_level_extension) {
+      exts.push_back({
+          kRtpAudioLevelHeaderExtension, audio_level_extension->id});
+    }
+
+    const RtpHeaderExtension* send_time_extension =
+        FindHeaderExtension(extensions, kRtpAbsoluteSenderTimeHeaderExtension);
+    if (send_time_extension) {
+      exts.push_back({
+          kRtpAbsoluteSenderTimeHeaderExtension, send_time_extension->id});
+    }
+
+    recv_rtp_extensions_.swap(exts);
+    SetCall(call_);
+  }
+
   return true;
 }
 
@@ -2418,6 +2444,7 @@
       send_time_extension)) {
     return false;
   }
+
   return true;
 }
 
@@ -2711,6 +2738,7 @@
 }
 
 bool WebRtcVoiceMediaChannel::AddRecvStream(const StreamParams& sp) {
+  DCHECK(thread_checker_.CalledOnValidThread());
   rtc::CritScope lock(&receive_channels_cs_);
 
   if (!VERIFY(sp.ssrcs.size() == 1))
@@ -2727,14 +2755,15 @@
     return false;
   }
 
+  TryAddAudioRecvStream(ssrc);
+
   // Reuse default channel for recv stream in non-conference mode call
   // when the default channel is not being used.
   webrtc::AudioTransport* audio_transport =
       engine()->voe()->base()->audio_transport();
   if (!InConferenceMode() && default_receive_ssrc_ == 0) {
-    LOG(LS_INFO) << "Recv stream " << sp.first_ssrc()
-                 << " reuse default channel";
-    default_receive_ssrc_ = sp.first_ssrc();
+    LOG(LS_INFO) << "Recv stream " << ssrc << " reuse default channel";
+    default_receive_ssrc_ = ssrc;
     receive_channels_.insert(std::make_pair(
         default_receive_ssrc_,
         new WebRtcVoiceChannelRenderer(voe_channel(), audio_transport)));
@@ -2837,6 +2866,7 @@
 }
 
 bool WebRtcVoiceMediaChannel::RemoveRecvStream(uint32 ssrc) {
+  DCHECK(thread_checker_.CalledOnValidThread());
   rtc::CritScope lock(&receive_channels_cs_);
   ChannelMap::iterator it = receive_channels_.find(ssrc);
   if (it == receive_channels_.end()) {
@@ -2845,6 +2875,8 @@
     return false;
   }
 
+  TryRemoveAudioRecvStream(ssrc);
+
   // Delete the WebRtcVoiceChannelRenderer object connected to the channel, this
   // will disconnect the audio renderer with the receive channel.
   // Cache the channel before the deletion.
@@ -3173,6 +3205,14 @@
 
 void WebRtcVoiceMediaChannel::OnPacketReceived(
     rtc::Buffer* packet, const rtc::PacketTime& packet_time) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // If hooked up to a "Call", forward packet there too.
+  if (call_) {
+    call_->Receiver()->DeliverPacket(webrtc::MediaType::AUDIO,
+        reinterpret_cast<const uint8_t*>(packet->data()), packet->size());
+  }
+
   // Pick which channel to send this packet to. If this packet doesn't match
   // any multiplexed streams, just send it to the default channel. Otherwise,
   // send it to the specific decoder instance for that stream.
@@ -3206,6 +3246,14 @@
 
 void WebRtcVoiceMediaChannel::OnRtcpReceived(
     rtc::Buffer* packet, const rtc::PacketTime& packet_time) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // If hooked up to a "Call", forward packet there too.
+  if (call_) {
+    call_->Receiver()->DeliverPacket(webrtc::MediaType::AUDIO,
+        reinterpret_cast<const uint8_t*>(packet->data()), packet->size());
+  }
+
   // Sending channels need all RTCP packets with feedback information.
   // Even sender reports can contain attached report blocks.
   // Receiving channels need sender reports in order to create
@@ -3608,6 +3656,22 @@
   return true;
 }
 
+void WebRtcVoiceMediaChannel::SetCall(webrtc::Call* call) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!call || !shared_bwe_vie_);
+  DCHECK(!call || shared_bwe_vie_channel_ == -1);
+
+  for (const auto& it : receive_channels_) {
+    TryRemoveAudioRecvStream(it.first);
+  }
+
+  call_ = call;
+
+  for (const auto& it : receive_channels_) {
+    TryAddAudioRecvStream(it.first);
+  }
+}
+
 bool WebRtcVoiceMediaChannel::GetRedSendCodec(const AudioCodec& red_codec,
     const std::vector<AudioCodec>& all_codecs, webrtc::CodecInst* send_codec) {
   // Get the RED encodings from the parameter with no name. This may
@@ -3776,6 +3840,32 @@
   return true;
 }
 
+void WebRtcVoiceMediaChannel::TryAddAudioRecvStream(uint32 ssrc) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  // If we are hooked up to a webrtc::Call, create an AudioReceiveStream too.
+  if (call_ && options_.combined_audio_video_bwe.GetWithDefaultIfUnset(false)) {
+    DCHECK(receive_streams_.find(ssrc) == receive_streams_.end());
+    webrtc::AudioReceiveStream::Config config;
+    config.rtp.remote_ssrc = ssrc;
+    config.rtp.extensions = recv_rtp_extensions_;
+    webrtc::AudioReceiveStream* s = call_->CreateAudioReceiveStream(config);
+    receive_streams_.insert(std::make_pair(ssrc, s));
+  }
+}
+
+void WebRtcVoiceMediaChannel::TryRemoveAudioRecvStream(uint32 ssrc) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  // If we are hooked up to a webrtc::Call, assume there is an
+  // AudioReceiveStream to destroy too.
+  if (call_) {
+    auto stream_it = receive_streams_.find(ssrc);
+    if (stream_it != receive_streams_.end()) {
+      call_->DestroyAudioReceiveStream(stream_it->second);
+      receive_streams_.erase(stream_it);
+    }
+  }
+}
+
 int WebRtcSoundclipStream::Read(void *buf, size_t len) {
   size_t res = 0;
   mem_.Read(buf, len, &res, NULL);
diff --git a/talk/media/webrtc/webrtcvoiceengine.h b/talk/media/webrtc/webrtcvoiceengine.h
index 56665de..29a5656 100644
--- a/talk/media/webrtc/webrtcvoiceengine.h
+++ b/talk/media/webrtc/webrtcvoiceengine.h
@@ -43,6 +43,8 @@
 #include "webrtc/base/logging.h"
 #include "webrtc/base/scoped_ptr.h"
 #include "webrtc/base/stream.h"
+#include "webrtc/base/thread_checker.h"
+#include "webrtc/call.h"
 #include "webrtc/common.h"
 
 #if !defined(LIBPEERCONNECTION_LIB) && \
@@ -394,6 +396,7 @@
 
   bool SetupSharedBandwidthEstimation(webrtc::VideoEngine* vie,
                                       int vie_channel);
+  void SetCall(webrtc::Call* call);
  protected:
   int GetLastEngineError() { return engine()->GetLastEngineError(); }
   int GetOutputLevel(int channel);
@@ -438,6 +441,9 @@
                           const RtpHeaderExtension* extension);
   bool SetupSharedBweOnChannel(int voe_channel);
 
+  void TryAddAudioRecvStream(uint32 ssrc);
+  void TryRemoveAudioRecvStream(uint32 ssrc);
+
   bool SetChannelRecvRtpHeaderExtensions(
     int channel_id,
     const std::vector<RtpHeaderExtension>& extensions);
@@ -445,6 +451,8 @@
     int channel_id,
     const std::vector<RtpHeaderExtension>& extensions);
 
+  rtc::ThreadChecker thread_checker_;
+
   rtc::scoped_ptr<WebRtcSoundclipStream> ringback_tone_;
   std::set<int> ringback_channels_;  // channels playing ringback
   std::vector<AudioCodec> recv_codecs_;
@@ -465,6 +473,7 @@
   // to for Bandwidth Estimation purposes.
   webrtc::VideoEngine* shared_bwe_vie_;
   int shared_bwe_vie_channel_;
+  webrtc::Call* call_;
 
   // send_channels_ contains the channels which are being used for sending.
   // When the default channel (voe_channel) is used for sending, it is
@@ -476,11 +485,14 @@
   // receive_channels_ and send_channels_ in non-conference mode and in that
   // case it will only be there if a non-zero default_receive_ssrc_ is set.
   ChannelMap receive_channels_;  // for multiple sources
+  std::map<uint32, webrtc::AudioReceiveStream*> receive_streams_;
   // receive_channels_ can be read from WebRtc callback thread.  Access from
   // the WebRtc thread must be synchronized with edits on the worker thread.
   // Reads on the worker thread are ok.
   //
   std::vector<RtpHeaderExtension> receive_extensions_;
+  std::vector<webrtc::RtpExtension> recv_rtp_extensions_;
+
   // Do not lock this on the VoE media processor thread; potential for deadlock
   // exists.
   mutable rtc::CriticalSection receive_channels_cs_;
diff --git a/talk/media/webrtc/webrtcvoiceengine_unittest.cc b/talk/media/webrtc/webrtcvoiceengine_unittest.cc
index f0e4503..0a3e91c 100644
--- a/talk/media/webrtc/webrtcvoiceengine_unittest.cc
+++ b/talk/media/webrtc/webrtcvoiceengine_unittest.cc
@@ -37,6 +37,7 @@
 #include "talk/media/base/fakemediaprocessor.h"
 #include "talk/media/base/fakenetworkinterface.h"
 #include "talk/media/base/fakertp.h"
+#include "talk/media/webrtc/fakewebrtccall.h"
 #include "talk/media/webrtc/fakewebrtcvoiceengine.h"
 #include "talk/media/webrtc/webrtcvie.h"
 #include "talk/media/webrtc/webrtcvoiceengine.h"
@@ -3518,3 +3519,203 @@
     EXPECT_EQ(-1, voe_.GetVideoChannel(voe_channels[i]));
   }
 }
+
+TEST_F(WebRtcVoiceEngineTestFake, ChangeCombinedBweOption_Call) {
+  // Test that changing the combined_audio_video_bwe option results in the
+  // expected state changes on an associated Call.
+  cricket::FakeCall call(webrtc::Call::Config(nullptr));
+  const uint32 kAudioSsrc1 = 223;
+  const uint32 kAudioSsrc2 = 224;
+
+  EXPECT_TRUE(SetupEngine());
+  cricket::WebRtcVoiceMediaChannel* media_channel =
+      static_cast<cricket::WebRtcVoiceMediaChannel*>(channel_);
+  media_channel->SetCall(&call);
+  EXPECT_TRUE(media_channel->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(kAudioSsrc1)));
+  EXPECT_TRUE(media_channel->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(kAudioSsrc2)));
+
+  // Combined BWE should not be set up yet.
+  EXPECT_EQ(0, call.GetAudioReceiveStreams().size());
+
+  // Enable combined BWE option - now it should be set up.
+  cricket::AudioOptions options;
+  options.combined_audio_video_bwe.Set(true);
+  EXPECT_TRUE(media_channel->SetOptions(options));
+  EXPECT_EQ(2, call.GetAudioReceiveStreams().size());
+  EXPECT_NE(nullptr, call.GetAudioReceiveStream(kAudioSsrc1));
+  EXPECT_NE(nullptr, call.GetAudioReceiveStream(kAudioSsrc2));
+
+  // Disable combined BWE option - should be disabled again.
+  options.combined_audio_video_bwe.Set(false);
+  EXPECT_TRUE(media_channel->SetOptions(options));
+  EXPECT_EQ(0, call.GetAudioReceiveStreams().size());
+
+  media_channel->SetCall(nullptr);
+}
+
+TEST_F(WebRtcVoiceEngineTestFake, ConfigureCombinedBwe_Call) {
+  // Test that calling SetCall() on the voice media channel results in the
+  // expected state changes in Call.
+  cricket::FakeCall call(webrtc::Call::Config(nullptr));
+  cricket::FakeCall call2(webrtc::Call::Config(nullptr));
+  const uint32 kAudioSsrc1 = 223;
+  const uint32 kAudioSsrc2 = 224;
+
+  EXPECT_TRUE(SetupEngine());
+  cricket::WebRtcVoiceMediaChannel* media_channel =
+      static_cast<cricket::WebRtcVoiceMediaChannel*>(channel_);
+  cricket::AudioOptions options;
+  options.combined_audio_video_bwe.Set(true);
+  EXPECT_TRUE(media_channel->SetOptions(options));
+  EXPECT_TRUE(media_channel->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(kAudioSsrc1)));
+  EXPECT_TRUE(media_channel->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(kAudioSsrc2)));
+
+  // Combined BWE should not be set up yet.
+  EXPECT_EQ(0, call.GetAudioReceiveStreams().size());
+
+  // Register - should be enabled.
+  media_channel->SetCall(&call);
+  EXPECT_EQ(2, call.GetAudioReceiveStreams().size());
+  EXPECT_NE(nullptr, call.GetAudioReceiveStream(kAudioSsrc1));
+  EXPECT_NE(nullptr, call.GetAudioReceiveStream(kAudioSsrc2));
+
+  // Re-register - should now be enabled on new call.
+  media_channel->SetCall(&call2);
+  EXPECT_EQ(0, call.GetAudioReceiveStreams().size());
+  EXPECT_EQ(2, call2.GetAudioReceiveStreams().size());
+  EXPECT_NE(nullptr, call2.GetAudioReceiveStream(kAudioSsrc1));
+  EXPECT_NE(nullptr, call2.GetAudioReceiveStream(kAudioSsrc2));
+
+  // Unregister - should be disabled again.
+  media_channel->SetCall(nullptr);
+  EXPECT_EQ(0, call.GetAudioReceiveStreams().size());
+}
+
+TEST_F(WebRtcVoiceEngineTestFake, ConfigureCombinedBweForNewRecvStreams_Call) {
+  // Test that adding receive streams after enabling combined bandwidth
+  // estimation will correctly configure each channel.
+  cricket::FakeCall call(webrtc::Call::Config(nullptr));
+
+  EXPECT_TRUE(SetupEngine());
+  cricket::WebRtcVoiceMediaChannel* media_channel =
+      static_cast<cricket::WebRtcVoiceMediaChannel*>(channel_);
+  media_channel->SetCall(&call);
+  cricket::AudioOptions options;
+  options.combined_audio_video_bwe.Set(true);
+  EXPECT_TRUE(media_channel->SetOptions(options));
+
+  static const uint32 kSsrcs[] = {1, 2, 3, 4};
+  for (unsigned int i = 0; i < ARRAY_SIZE(kSsrcs); ++i) {
+    EXPECT_TRUE(media_channel->AddRecvStream(
+        cricket::StreamParams::CreateLegacy(kSsrcs[i])));
+    EXPECT_NE(nullptr, call.GetAudioReceiveStream(kSsrcs[i]));
+  }
+  EXPECT_EQ(ARRAY_SIZE(kSsrcs), call.GetAudioReceiveStreams().size());
+
+  media_channel->SetCall(nullptr);
+  EXPECT_EQ(0, call.GetAudioReceiveStreams().size());
+}
+
+TEST_F(WebRtcVoiceEngineTestFake, ConfigureCombinedBweExtensions_Call) {
+  // Test that setting the header extensions results in the expected state
+  // changes on an associated Call.
+  cricket::FakeCall call(webrtc::Call::Config(nullptr));
+  std::vector<uint32> ssrcs;
+  ssrcs.push_back(223);
+  ssrcs.push_back(224);
+
+  EXPECT_TRUE(SetupEngine());
+  cricket::WebRtcVoiceMediaChannel* media_channel =
+      static_cast<cricket::WebRtcVoiceMediaChannel*>(channel_);
+  media_channel->SetCall(&call);
+  cricket::AudioOptions options;
+  options.combined_audio_video_bwe.Set(true);
+  EXPECT_TRUE(media_channel->SetOptions(options));
+  for (uint32 ssrc : ssrcs) {
+    EXPECT_TRUE(media_channel->AddRecvStream(
+        cricket::StreamParams::CreateLegacy(ssrc)));
+  }
+
+  // Combined BWE should be set up, but with no configured extensions.
+  EXPECT_EQ(2, call.GetAudioReceiveStreams().size());
+  for (uint32 ssrc : ssrcs) {
+    const auto* s = call.GetAudioReceiveStream(ssrc);
+    EXPECT_NE(nullptr, s);
+    EXPECT_EQ(0, s->GetConfig().rtp.extensions.size());
+  }
+
+  // Set up receive extensions.
+  const auto& e_exts = engine_.rtp_header_extensions();
+  channel_->SetRecvRtpHeaderExtensions(e_exts);
+  EXPECT_EQ(2, call.GetAudioReceiveStreams().size());
+  for (uint32 ssrc : ssrcs) {
+    const auto* s = call.GetAudioReceiveStream(ssrc);
+    EXPECT_NE(nullptr, s);
+    const auto& s_exts = s->GetConfig().rtp.extensions;
+    EXPECT_EQ(e_exts.size(), s_exts.size());
+    for (const auto& e_ext : e_exts) {
+      for (const auto& s_ext : s_exts) {
+        if (e_ext.id == s_ext.id) {
+          EXPECT_EQ(e_ext.uri, s_ext.name);
+        }
+      }
+    }
+  }
+
+  // Disable receive extensions.
+  std::vector<cricket::RtpHeaderExtension> extensions;
+  channel_->SetRecvRtpHeaderExtensions(extensions);
+  for (uint32 ssrc : ssrcs) {
+    const auto* s = call.GetAudioReceiveStream(ssrc);
+    EXPECT_NE(nullptr, s);
+    EXPECT_EQ(0, s->GetConfig().rtp.extensions.size());
+  }
+
+  media_channel->SetCall(nullptr);
+}
+
+TEST_F(WebRtcVoiceEngineTestFake, DeliverAudioPacket_Call) {
+  // Test that packets are forwarded to the Call when configured accordingly.
+  cricket::FakeCall call(webrtc::Call::Config(nullptr));
+  const uint32 kAudioSsrc = 1;
+  rtc::Buffer kPcmuPacket(kPcmuFrame, sizeof(kPcmuFrame));
+  static const unsigned char kRtcp[] = {
+    0x80, 0xc9, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
+    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+  };
+  rtc::Buffer kRtcpPacket(kRtcp, sizeof(kRtcp));
+
+  EXPECT_TRUE(SetupEngine());
+  cricket::WebRtcVoiceMediaChannel* media_channel =
+      static_cast<cricket::WebRtcVoiceMediaChannel*>(channel_);
+  cricket::AudioOptions options;
+  options.combined_audio_video_bwe.Set(true);
+  EXPECT_TRUE(media_channel->SetOptions(options));
+  EXPECT_TRUE(media_channel->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(kAudioSsrc)));
+
+  // Call not set on media channel, so no packets can be forwarded.
+  EXPECT_EQ(0, call.GetAudioReceiveStreams().size());
+  channel_->OnPacketReceived(&kPcmuPacket, rtc::PacketTime());
+  channel_->OnRtcpReceived(&kRtcpPacket, rtc::PacketTime());
+  EXPECT_EQ(0, call.GetAudioReceiveStreams().size());
+
+  // Set Call, now there should be a receive stream which is forwarded packets.
+  media_channel->SetCall(&call);
+  EXPECT_EQ(1, call.GetAudioReceiveStreams().size());
+  const cricket::FakeAudioReceiveStream* s =
+      call.GetAudioReceiveStream(kAudioSsrc);
+  EXPECT_EQ(0, s->received_packets());
+  channel_->OnPacketReceived(&kPcmuPacket, rtc::PacketTime());
+  EXPECT_EQ(1, s->received_packets());
+  channel_->OnRtcpReceived(&kRtcpPacket, rtc::PacketTime());
+  EXPECT_EQ(2, s->received_packets());
+
+  media_channel->SetCall(nullptr);
+}
diff --git a/talk/session/media/channel.h b/talk/session/media/channel.h
index 14dae3b..441fe64 100644
--- a/talk/session/media/channel.h
+++ b/talk/session/media/channel.h
@@ -526,6 +526,11 @@
   ~VideoChannel();
   bool Init();
 
+  // downcasts a MediaChannel
+  virtual VideoMediaChannel* media_channel() const {
+    return static_cast<VideoMediaChannel*>(BaseChannel::media_channel());
+  }
+
   bool SetRenderer(uint32 ssrc, VideoRenderer* renderer);
   bool ApplyViewRequest(const ViewRequest& request);
 
@@ -559,12 +564,6 @@
   // Configuration and setting.
   bool SetChannelOptions(const VideoOptions& options);
 
- protected:
-  // downcasts a MediaChannel
-  virtual VideoMediaChannel* media_channel() const {
-    return static_cast<VideoMediaChannel*>(BaseChannel::media_channel());
-  }
-
  private:
   typedef std::map<uint32, VideoCapturer*> ScreencastMap;
   struct ScreencastDetailsData;
diff --git a/talk/session/media/channelmanager.cc b/talk/session/media/channelmanager.cc
index 9c30a97..5013fc4 100644
--- a/talk/session/media/channelmanager.cc
+++ b/talk/session/media/channelmanager.cc
@@ -309,7 +309,7 @@
     DestroyVideoChannel_w(video_channels_.back());
   }
   while (!voice_channels_.empty()) {
-    DestroyVoiceChannel_w(voice_channels_.back());
+    DestroyVoiceChannel_w(voice_channels_.back(), nullptr);
   }
   while (!soundclips_.empty()) {
     DestroySoundclip_w(soundclips_.back());
@@ -329,8 +329,8 @@
 
 VoiceChannel* ChannelManager::CreateVoiceChannel_w(
     BaseSession* session, const std::string& content_name, bool rtcp) {
-  // This is ok to alloc from a thread other than the worker thread
   ASSERT(initialized_);
+  ASSERT(worker_thread_ == rtc::Thread::Current());
   VoiceMediaChannel* media_channel = media_engine_->CreateChannel();
   if (media_channel == NULL)
     return NULL;
@@ -346,22 +346,29 @@
   return voice_channel;
 }
 
-void ChannelManager::DestroyVoiceChannel(VoiceChannel* voice_channel) {
+void ChannelManager::DestroyVoiceChannel(VoiceChannel* voice_channel,
+                                         VideoChannel* video_channel) {
   if (voice_channel) {
     worker_thread_->Invoke<void>(
-        Bind(&ChannelManager::DestroyVoiceChannel_w, this, voice_channel));
+        Bind(&ChannelManager::DestroyVoiceChannel_w, this, voice_channel,
+             video_channel));
   }
 }
 
-void ChannelManager::DestroyVoiceChannel_w(VoiceChannel* voice_channel) {
+void ChannelManager::DestroyVoiceChannel_w(VoiceChannel* voice_channel,
+                                           VideoChannel* video_channel) {
   // Destroy voice channel.
   ASSERT(initialized_);
+  ASSERT(worker_thread_ == rtc::Thread::Current());
   VoiceChannels::iterator it = std::find(voice_channels_.begin(),
       voice_channels_.end(), voice_channel);
   ASSERT(it != voice_channels_.end());
   if (it == voice_channels_.end())
     return;
 
+  if (video_channel) {
+    video_channel->media_channel()->DetachVoiceChannel();
+  }
   voice_channels_.erase(it);
   delete voice_channel;
 }
@@ -403,8 +410,8 @@
     bool rtcp,
     const VideoOptions& options,
     VoiceChannel* voice_channel) {
-  // This is ok to alloc from a thread other than the worker thread
   ASSERT(initialized_);
+  ASSERT(worker_thread_ == rtc::Thread::Current());
   VideoMediaChannel* media_channel =
       // voice_channel can be NULL in case of NullVoiceEngine.
       media_engine_->CreateVideoChannel(
@@ -433,6 +440,7 @@
 void ChannelManager::DestroyVideoChannel_w(VideoChannel* video_channel) {
   // Destroy video channel.
   ASSERT(initialized_);
+  ASSERT(worker_thread_ == rtc::Thread::Current());
   VideoChannels::iterator it = std::find(video_channels_.begin(),
       video_channels_.end(), video_channel);
   ASSERT(it != video_channels_.end());
diff --git a/talk/session/media/channelmanager.h b/talk/session/media/channelmanager.h
index a8eb88d..27f874b 100644
--- a/talk/session/media/channelmanager.h
+++ b/talk/session/media/channelmanager.h
@@ -107,7 +107,8 @@
   VoiceChannel* CreateVoiceChannel(
       BaseSession* session, const std::string& content_name, bool rtcp);
   // Destroys a voice channel created with the Create API.
-  void DestroyVoiceChannel(VoiceChannel* voice_channel);
+  void DestroyVoiceChannel(VoiceChannel* voice_channel,
+                           VideoChannel* video_channel);
   // TODO(pbos): Remove as soon as all call sites specify VideoOptions.
   VideoChannel* CreateVideoChannel(BaseSession* session,
                                    const std::string& content_name,
@@ -264,7 +265,8 @@
   void Terminate_w();
   VoiceChannel* CreateVoiceChannel_w(
       BaseSession* session, const std::string& content_name, bool rtcp);
-  void DestroyVoiceChannel_w(VoiceChannel* voice_channel);
+  void DestroyVoiceChannel_w(VoiceChannel* voice_channel,
+                             VideoChannel* video_channel);
   VideoChannel* CreateVideoChannel_w(BaseSession* session,
                                      const std::string& content_name,
                                      bool rtcp,
diff --git a/talk/session/media/channelmanager_unittest.cc b/talk/session/media/channelmanager_unittest.cc
index e57305c..b0abf04 100644
--- a/talk/session/media/channelmanager_unittest.cc
+++ b/talk/session/media/channelmanager_unittest.cc
@@ -135,7 +135,7 @@
                              false, cricket::DCT_RTP);
   EXPECT_TRUE(data_channel != NULL);
   cm_->DestroyVideoChannel(video_channel);
-  cm_->DestroyVoiceChannel(voice_channel);
+  cm_->DestroyVoiceChannel(voice_channel, nullptr);
   cm_->DestroyDataChannel(data_channel);
   cm_->Terminate();
 }
@@ -158,7 +158,7 @@
                              false, cricket::DCT_RTP);
   EXPECT_TRUE(data_channel != NULL);
   cm_->DestroyVideoChannel(video_channel);
-  cm_->DestroyVoiceChannel(voice_channel);
+  cm_->DestroyVoiceChannel(voice_channel, nullptr);
   cm_->DestroyDataChannel(data_channel);
   cm_->Terminate();
 }