Adds trunk/talk folder of revision 359 from libjingles google code to
trunk/talk


git-svn-id: http://webrtc.googlecode.com/svn/trunk@4318 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/app/webrtc/test/fakeaudiocapturemodule.cc b/talk/app/webrtc/test/fakeaudiocapturemodule.cc
new file mode 100644
index 0000000..4bdaf89
--- /dev/null
+++ b/talk/app/webrtc/test/fakeaudiocapturemodule.cc
@@ -0,0 +1,716 @@
+/*
+ * 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.
+ */
+
+#include "talk/app/webrtc/test/fakeaudiocapturemodule.h"
+
+#include "talk/base/common.h"
+#include "talk/base/refcount.h"
+#include "talk/base/thread.h"
+#include "talk/base/timeutils.h"
+
+// Audio sample value that is high enough that it doesn't occur naturally when
+// frames are being faked. E.g. NetEq will not generate this large sample value
+// unless it has received an audio frame containing a sample of this value.
+// Even simpler buffers would likely just contain audio sample values of 0.
+static const int kHighSampleValue = 10000;
+
+// Same value as src/modules/audio_device/main/source/audio_device_config.h in
+// https://code.google.com/p/webrtc/
+static const uint32 kAdmMaxIdleTimeProcess = 1000;
+
+// Constants here are derived by running VoE using a real ADM.
+// The constants correspond to 10ms of mono audio at 44kHz.
+static const int kTimePerFrameMs = 10;
+static const int kNumberOfChannels = 1;
+static const int kSamplesPerSecond = 44000;
+static const int kTotalDelayMs = 0;
+static const int kClockDriftMs = 0;
+static const uint32_t kMaxVolume = 14392;
+
+enum {
+  MSG_RUN_PROCESS,
+  MSG_STOP_PROCESS,
+};
+
+FakeAudioCaptureModule::FakeAudioCaptureModule(
+    talk_base::Thread* process_thread)
+    : last_process_time_ms_(0),
+      audio_callback_(NULL),
+      recording_(false),
+      playing_(false),
+      play_is_initialized_(false),
+      rec_is_initialized_(false),
+      current_mic_level_(kMaxVolume),
+      started_(false),
+      next_frame_time_(0),
+      process_thread_(process_thread),
+      frames_received_(0) {
+}
+
+FakeAudioCaptureModule::~FakeAudioCaptureModule() {
+  // Ensure that thread stops calling ProcessFrame().
+  process_thread_->Send(this, MSG_STOP_PROCESS);
+}
+
+talk_base::scoped_refptr<FakeAudioCaptureModule> FakeAudioCaptureModule::Create(
+    talk_base::Thread* process_thread) {
+  if (process_thread == NULL) return NULL;
+
+  talk_base::scoped_refptr<FakeAudioCaptureModule> capture_module(
+      new talk_base::RefCountedObject<FakeAudioCaptureModule>(process_thread));
+  if (!capture_module->Initialize()) {
+    return NULL;
+  }
+  return capture_module;
+}
+
+int FakeAudioCaptureModule::frames_received() const {
+  return frames_received_;
+}
+
+int32_t FakeAudioCaptureModule::Version(char* /*version*/,
+                                        uint32_t& /*remaining_buffer_in_bytes*/,
+                                        uint32_t& /*position*/) const {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::TimeUntilNextProcess() {
+  const uint32 current_time = talk_base::Time();
+  if (current_time < last_process_time_ms_) {
+    // TODO: wraparound could be handled more gracefully.
+    return 0;
+  }
+  const uint32 elapsed_time = current_time - last_process_time_ms_;
+  if (kAdmMaxIdleTimeProcess < elapsed_time) {
+    return 0;
+  }
+  return kAdmMaxIdleTimeProcess - elapsed_time;
+}
+
+int32_t FakeAudioCaptureModule::Process() {
+  last_process_time_ms_ = talk_base::Time();
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::ChangeUniqueId(const int32_t /*id*/) {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::ActiveAudioLayer(
+    AudioLayer* /*audio_layer*/) const {
+  ASSERT(false);
+  return 0;
+}
+
+webrtc::AudioDeviceModule::ErrorCode FakeAudioCaptureModule::LastError() const {
+  ASSERT(false);
+  return webrtc::AudioDeviceModule::kAdmErrNone;
+}
+
+int32_t FakeAudioCaptureModule::RegisterEventObserver(
+    webrtc::AudioDeviceObserver* /*event_callback*/) {
+  // Only used to report warnings and errors. This fake implementation won't
+  // generate any so discard this callback.
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::RegisterAudioCallback(
+    webrtc::AudioTransport* audio_callback) {
+  audio_callback_ = audio_callback;
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::Init() {
+  // Initialize is called by the factory method. Safe to ignore this Init call.
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::Terminate() {
+  // Clean up in the destructor. No action here, just success.
+  return 0;
+}
+
+bool FakeAudioCaptureModule::Initialized() const {
+  ASSERT(false);
+  return 0;
+}
+
+int16_t FakeAudioCaptureModule::PlayoutDevices() {
+  ASSERT(false);
+  return 0;
+}
+
+int16_t FakeAudioCaptureModule::RecordingDevices() {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::PlayoutDeviceName(
+    uint16_t /*index*/,
+    char /*name*/[webrtc::kAdmMaxDeviceNameSize],
+    char /*guid*/[webrtc::kAdmMaxGuidSize]) {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::RecordingDeviceName(
+    uint16_t /*index*/,
+    char /*name*/[webrtc::kAdmMaxDeviceNameSize],
+    char /*guid*/[webrtc::kAdmMaxGuidSize]) {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SetPlayoutDevice(uint16_t /*index*/) {
+  // No playout device, just playing from file. Return success.
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SetPlayoutDevice(WindowsDeviceType /*device*/) {
+  if (play_is_initialized_) {
+    return -1;
+  }
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SetRecordingDevice(uint16_t /*index*/) {
+  // No recording device, just dropping audio. Return success.
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SetRecordingDevice(
+    WindowsDeviceType /*device*/) {
+  if (rec_is_initialized_) {
+    return -1;
+  }
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::PlayoutIsAvailable(bool* /*available*/) {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::InitPlayout() {
+  play_is_initialized_ = true;
+  return 0;
+}
+
+bool FakeAudioCaptureModule::PlayoutIsInitialized() const {
+  return play_is_initialized_;
+}
+
+int32_t FakeAudioCaptureModule::RecordingIsAvailable(bool* /*available*/) {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::InitRecording() {
+  rec_is_initialized_ = true;
+  return 0;
+}
+
+bool FakeAudioCaptureModule::RecordingIsInitialized() const {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::StartPlayout() {
+  if (!play_is_initialized_) {
+    return -1;
+  }
+  playing_ = true;
+  UpdateProcessing();
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::StopPlayout() {
+  playing_ = false;
+  UpdateProcessing();
+  return 0;
+}
+
+bool FakeAudioCaptureModule::Playing() const {
+  return playing_;
+}
+
+int32_t FakeAudioCaptureModule::StartRecording() {
+  if (!rec_is_initialized_) {
+    return -1;
+  }
+  recording_ = true;
+  UpdateProcessing();
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::StopRecording() {
+  recording_ = false;
+  UpdateProcessing();
+  return 0;
+}
+
+bool FakeAudioCaptureModule::Recording() const {
+  return recording_;
+}
+
+int32_t FakeAudioCaptureModule::SetAGC(bool /*enable*/) {
+  // No AGC but not needed since audio is pregenerated. Return success.
+  return 0;
+}
+
+bool FakeAudioCaptureModule::AGC() const {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SetWaveOutVolume(uint16_t /*volume_left*/,
+                                                 uint16_t /*volume_right*/) {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::WaveOutVolume(
+    uint16_t* /*volume_left*/,
+    uint16_t* /*volume_right*/) const {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SpeakerIsAvailable(bool* available) {
+  // No speaker, just dropping audio. Return success.
+  *available = true;
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::InitSpeaker() {
+  // No speaker, just playing from file. Return success.
+  return 0;
+}
+
+bool FakeAudioCaptureModule::SpeakerIsInitialized() const {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::MicrophoneIsAvailable(bool* available) {
+  // No microphone, just playing from file. Return success.
+  *available = true;
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::InitMicrophone() {
+  // No microphone, just playing from file. Return success.
+  return 0;
+}
+
+bool FakeAudioCaptureModule::MicrophoneIsInitialized() const {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SpeakerVolumeIsAvailable(bool* /*available*/) {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SetSpeakerVolume(uint32_t /*volume*/) {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SpeakerVolume(uint32_t* /*volume*/) const {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::MaxSpeakerVolume(
+    uint32_t* /*max_volume*/) const {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::MinSpeakerVolume(
+    uint32_t* /*min_volume*/) const {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SpeakerVolumeStepSize(
+    uint16_t* /*step_size*/) const {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::MicrophoneVolumeIsAvailable(
+    bool* /*available*/) {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SetMicrophoneVolume(uint32_t /*volume*/) {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::MicrophoneVolume(uint32_t* volume) const {
+  *volume = current_mic_level_;
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::MaxMicrophoneVolume(
+    uint32_t* max_volume) const {
+  *max_volume = kMaxVolume;
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::MinMicrophoneVolume(
+    uint32_t* /*min_volume*/) const {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::MicrophoneVolumeStepSize(
+    uint16_t* /*step_size*/) const {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SpeakerMuteIsAvailable(bool* /*available*/) {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SetSpeakerMute(bool /*enable*/) {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SpeakerMute(bool* /*enabled*/) const {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::MicrophoneMuteIsAvailable(bool* /*available*/) {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SetMicrophoneMute(bool /*enable*/) {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::MicrophoneMute(bool* /*enabled*/) const {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::MicrophoneBoostIsAvailable(
+    bool* /*available*/) {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SetMicrophoneBoost(bool /*enable*/) {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::MicrophoneBoost(bool* /*enabled*/) const {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::StereoPlayoutIsAvailable(
+    bool* available) const {
+  // No recording device, just dropping audio. Stereo can be dropped just
+  // as easily as mono.
+  *available = true;
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SetStereoPlayout(bool /*enable*/) {
+  // No recording device, just dropping audio. Stereo can be dropped just
+  // as easily as mono.
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::StereoPlayout(bool* /*enabled*/) const {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::StereoRecordingIsAvailable(
+    bool* available) const {
+  // Keep thing simple. No stereo recording.
+  *available = false;
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SetStereoRecording(bool enable) {
+  if (!enable) {
+    return 0;
+  }
+  return -1;
+}
+
+int32_t FakeAudioCaptureModule::StereoRecording(bool* /*enabled*/) const {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SetRecordingChannel(
+    const ChannelType channel) {
+  if (channel != AudioDeviceModule::kChannelBoth) {
+    // There is no right or left in mono. I.e. kChannelBoth should be used for
+    // mono.
+    ASSERT(false);
+    return -1;
+  }
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::RecordingChannel(ChannelType* channel) const {
+  // Stereo recording not supported. However, WebRTC ADM returns kChannelBoth
+  // in that case. Do the same here.
+  *channel = AudioDeviceModule::kChannelBoth;
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SetPlayoutBuffer(const BufferType /*type*/,
+                                                 uint16_t /*size_ms*/) {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::PlayoutBuffer(BufferType* /*type*/,
+                                              uint16_t* /*size_ms*/) const {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::PlayoutDelay(uint16_t* delay_ms) const {
+  // No delay since audio frames are dropped.
+  *delay_ms = 0;
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::RecordingDelay(uint16_t* /*delay_ms*/) const {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::CPULoad(uint16_t* /*load*/) const {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::StartRawOutputFileRecording(
+    const char /*pcm_file_name_utf8*/[webrtc::kAdmMaxFileNameSize]) {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::StopRawOutputFileRecording() {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::StartRawInputFileRecording(
+    const char /*pcm_file_name_utf8*/[webrtc::kAdmMaxFileNameSize]) {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::StopRawInputFileRecording() {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SetRecordingSampleRate(
+    const uint32_t /*samples_per_sec*/) {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::RecordingSampleRate(
+    uint32_t* /*samples_per_sec*/) const {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SetPlayoutSampleRate(
+    const uint32_t /*samples_per_sec*/) {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::PlayoutSampleRate(
+    uint32_t* /*samples_per_sec*/) const {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::ResetAudioDevice() {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::SetLoudspeakerStatus(bool /*enable*/) {
+  ASSERT(false);
+  return 0;
+}
+
+int32_t FakeAudioCaptureModule::GetLoudspeakerStatus(bool* /*enabled*/) const {
+  ASSERT(false);
+  return 0;
+}
+
+void FakeAudioCaptureModule::OnMessage(talk_base::Message* msg) {
+  switch (msg->message_id) {
+    case MSG_RUN_PROCESS:
+      ProcessFrameP();
+      break;
+    case MSG_STOP_PROCESS:
+      StopProcessP();
+      break;
+    default:
+      // All existing messages should be caught. Getting here should never
+      // happen.
+      ASSERT(false);
+  }
+}
+
+bool FakeAudioCaptureModule::Initialize() {
+  // Set the send buffer samples high enough that it would not occur on the
+  // remote side unless a packet containing a sample of that magnitude has been
+  // sent to it. Note that the audio processing pipeline will likely distort the
+  // original signal.
+  SetSendBuffer(kHighSampleValue);
+  last_process_time_ms_ = talk_base::Time();
+  return true;
+}
+
+void FakeAudioCaptureModule::SetSendBuffer(int value) {
+  Sample* buffer_ptr = reinterpret_cast<Sample*>(send_buffer_);
+  const int buffer_size_in_samples = sizeof(send_buffer_) /
+      kNumberBytesPerSample;
+  for (int i = 0; i < buffer_size_in_samples; ++i) {
+    buffer_ptr[i] = value;
+  }
+}
+
+void FakeAudioCaptureModule::ResetRecBuffer() {
+  memset(rec_buffer_, 0, sizeof(rec_buffer_));
+}
+
+bool FakeAudioCaptureModule::CheckRecBuffer(int value) {
+  const Sample* buffer_ptr = reinterpret_cast<const Sample*>(rec_buffer_);
+  const int buffer_size_in_samples = sizeof(rec_buffer_) /
+      kNumberBytesPerSample;
+  for (int i = 0; i < buffer_size_in_samples; ++i) {
+    if (buffer_ptr[i] >= value) return true;
+  }
+  return false;
+}
+
+void FakeAudioCaptureModule::UpdateProcessing() {
+  const bool process = recording_ || playing_;
+  if (process) {
+    if (started_) {
+      // Already started.
+      return;
+    }
+    process_thread_->Post(this, MSG_RUN_PROCESS);
+  } else {
+    process_thread_->Send(this, MSG_STOP_PROCESS);
+  }
+}
+
+void FakeAudioCaptureModule::ProcessFrameP() {
+  ASSERT(talk_base::Thread::Current() == process_thread_);
+  if (!started_) {
+    next_frame_time_ = talk_base::Time();
+    started_ = true;
+  }
+  // Receive and send frames every kTimePerFrameMs.
+  if (audio_callback_ != NULL) {
+    if (playing_) {
+      ReceiveFrameP();
+    }
+    if (recording_) {
+      SendFrameP();
+    }
+  }
+
+  next_frame_time_ += kTimePerFrameMs;
+  const uint32 current_time = talk_base::Time();
+  const uint32 wait_time = (next_frame_time_ > current_time) ?
+      next_frame_time_ - current_time : 0;
+  process_thread_->PostDelayed(wait_time, this, MSG_RUN_PROCESS);
+}
+
+void FakeAudioCaptureModule::ReceiveFrameP() {
+  ASSERT(talk_base::Thread::Current() == process_thread_);
+  ResetRecBuffer();
+  uint32_t nSamplesOut = 0;
+  if (audio_callback_->NeedMorePlayData(kNumberSamples, kNumberBytesPerSample,
+                                       kNumberOfChannels, kSamplesPerSecond,
+                                       rec_buffer_, nSamplesOut) != 0) {
+    ASSERT(false);
+  }
+  ASSERT(nSamplesOut == kNumberSamples);
+  // The SetBuffer() function ensures that after decoding, the audio buffer
+  // should contain samples of similar magnitude (there is likely to be some
+  // distortion due to the audio pipeline). If one sample is detected to
+  // have the same or greater magnitude somewhere in the frame, an actual frame
+  // has been received from the remote side (i.e. faked frames are not being
+  // pulled).
+  if (CheckRecBuffer(kHighSampleValue)) ++frames_received_;
+}
+
+void FakeAudioCaptureModule::SendFrameP() {
+  ASSERT(talk_base::Thread::Current() == process_thread_);
+  bool key_pressed = false;
+  if (audio_callback_->RecordedDataIsAvailable(send_buffer_, kNumberSamples,
+                                              kNumberBytesPerSample,
+                                              kNumberOfChannels,
+                                              kSamplesPerSecond, kTotalDelayMs,
+                                              kClockDriftMs, current_mic_level_,
+                                              key_pressed,
+                                              current_mic_level_) != 0) {
+    ASSERT(false);
+  }
+}
+
+void FakeAudioCaptureModule::StopProcessP() {
+  ASSERT(talk_base::Thread::Current() == process_thread_);
+  started_ = false;
+  process_thread_->Clear(this);
+}
diff --git a/talk/app/webrtc/test/fakeaudiocapturemodule.h b/talk/app/webrtc/test/fakeaudiocapturemodule.h
new file mode 100644
index 0000000..c32fa1f
--- /dev/null
+++ b/talk/app/webrtc/test/fakeaudiocapturemodule.h
@@ -0,0 +1,280 @@
+/*
+ * 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.
+ */
+
+// This class implements an AudioCaptureModule that can be used to detect if
+// audio is being received properly if it is fed by another AudioCaptureModule
+// in some arbitrary audio pipeline where they are connected. It does not play
+// out or record any audio so it does not need access to any hardware and can
+// therefore be used in the gtest testing framework.
+
+// Note P postfix of a function indicates that it should only be called by the
+// processing thread.
+
+#ifndef TALK_APP_WEBRTC_TEST_FAKEAUDIOCAPTUREMODULE_H_
+#define TALK_APP_WEBRTC_TEST_FAKEAUDIOCAPTUREMODULE_H_
+
+#include "talk/base/basictypes.h"
+#include "talk/base/messagehandler.h"
+#include "talk/base/scoped_ref_ptr.h"
+#include "webrtc/common_types.h"
+#include "webrtc/modules/audio_device/include/audio_device.h"
+
+namespace talk_base {
+
+class Thread;
+
+}  // namespace talk_base
+
+class FakeAudioCaptureModule
+    : public webrtc::AudioDeviceModule,
+      public talk_base::MessageHandler {
+ public:
+  typedef uint16 Sample;
+
+  // The value for the following constants have been derived by running VoE
+  // using a real ADM. The constants correspond to 10ms of mono audio at 44kHz.
+  enum{kNumberSamples = 440};
+  enum{kNumberBytesPerSample = sizeof(Sample)};
+
+  // Creates a FakeAudioCaptureModule or returns NULL on failure.
+  // |process_thread| is used to push and pull audio frames to and from the
+  // returned instance. Note: ownership of |process_thread| is not handed over.
+  static talk_base::scoped_refptr<FakeAudioCaptureModule> Create(
+      talk_base::Thread* process_thread);
+
+  // Returns the number of frames that have been successfully pulled by the
+  // instance. Note that correctly detecting success can only be done if the
+  // pulled frame was generated/pushed from a FakeAudioCaptureModule.
+  int frames_received() const;
+
+  // Following functions are inherited from webrtc::AudioDeviceModule.
+  // Only functions called by PeerConnection are implemented, the rest do
+  // nothing and return success. If a function is not expected to be called by
+  // PeerConnection an assertion is triggered if it is in fact called.
+  virtual int32_t Version(char* version,
+                          uint32_t& remaining_buffer_in_bytes,
+                          uint32_t& position) const;
+  virtual int32_t TimeUntilNextProcess();
+  virtual int32_t Process();
+  virtual int32_t ChangeUniqueId(const int32_t id);
+
+  virtual int32_t ActiveAudioLayer(AudioLayer* audio_layer) const;
+
+  virtual ErrorCode LastError() const;
+  virtual int32_t RegisterEventObserver(
+      webrtc::AudioDeviceObserver* event_callback);
+
+  virtual int32_t RegisterAudioCallback(webrtc::AudioTransport* audio_callback);
+
+  virtual int32_t Init();
+  virtual int32_t Terminate();
+  virtual bool Initialized() const;
+
+  virtual int16_t PlayoutDevices();
+  virtual int16_t RecordingDevices();
+  virtual int32_t PlayoutDeviceName(uint16_t index,
+                                    char name[webrtc::kAdmMaxDeviceNameSize],
+                                    char guid[webrtc::kAdmMaxGuidSize]);
+  virtual int32_t RecordingDeviceName(uint16_t index,
+                                      char name[webrtc::kAdmMaxDeviceNameSize],
+                                      char guid[webrtc::kAdmMaxGuidSize]);
+
+  virtual int32_t SetPlayoutDevice(uint16_t index);
+  virtual int32_t SetPlayoutDevice(WindowsDeviceType device);
+  virtual int32_t SetRecordingDevice(uint16_t index);
+  virtual int32_t SetRecordingDevice(WindowsDeviceType device);
+
+  virtual int32_t PlayoutIsAvailable(bool* available);
+  virtual int32_t InitPlayout();
+  virtual bool PlayoutIsInitialized() const;
+  virtual int32_t RecordingIsAvailable(bool* available);
+  virtual int32_t InitRecording();
+  virtual bool RecordingIsInitialized() const;
+
+  virtual int32_t StartPlayout();
+  virtual int32_t StopPlayout();
+  virtual bool Playing() const;
+  virtual int32_t StartRecording();
+  virtual int32_t StopRecording();
+  virtual bool Recording() const;
+
+  virtual int32_t SetAGC(bool enable);
+  virtual bool AGC() const;
+
+  virtual int32_t SetWaveOutVolume(uint16_t volume_left,
+                                   uint16_t volume_right);
+  virtual int32_t WaveOutVolume(uint16_t* volume_left,
+                                uint16_t* volume_right) const;
+
+  virtual int32_t SpeakerIsAvailable(bool* available);
+  virtual int32_t InitSpeaker();
+  virtual bool SpeakerIsInitialized() const;
+  virtual int32_t MicrophoneIsAvailable(bool* available);
+  virtual int32_t InitMicrophone();
+  virtual bool MicrophoneIsInitialized() const;
+
+  virtual int32_t SpeakerVolumeIsAvailable(bool* available);
+  virtual int32_t SetSpeakerVolume(uint32_t volume);
+  virtual int32_t SpeakerVolume(uint32_t* volume) const;
+  virtual int32_t MaxSpeakerVolume(uint32_t* max_volume) const;
+  virtual int32_t MinSpeakerVolume(uint32_t* min_volume) const;
+  virtual int32_t SpeakerVolumeStepSize(uint16_t* step_size) const;
+
+  virtual int32_t MicrophoneVolumeIsAvailable(bool* available);
+  virtual int32_t SetMicrophoneVolume(uint32_t volume);
+  virtual int32_t MicrophoneVolume(uint32_t* volume) const;
+  virtual int32_t MaxMicrophoneVolume(uint32_t* max_volume) const;
+
+  virtual int32_t MinMicrophoneVolume(uint32_t* min_volume) const;
+  virtual int32_t MicrophoneVolumeStepSize(uint16_t* step_size) const;
+
+  virtual int32_t SpeakerMuteIsAvailable(bool* available);
+  virtual int32_t SetSpeakerMute(bool enable);
+  virtual int32_t SpeakerMute(bool* enabled) const;
+
+  virtual int32_t MicrophoneMuteIsAvailable(bool* available);
+  virtual int32_t SetMicrophoneMute(bool enable);
+  virtual int32_t MicrophoneMute(bool* enabled) const;
+
+  virtual int32_t MicrophoneBoostIsAvailable(bool* available);
+  virtual int32_t SetMicrophoneBoost(bool enable);
+  virtual int32_t MicrophoneBoost(bool* enabled) const;
+
+  virtual int32_t StereoPlayoutIsAvailable(bool* available) const;
+  virtual int32_t SetStereoPlayout(bool enable);
+  virtual int32_t StereoPlayout(bool* enabled) const;
+  virtual int32_t StereoRecordingIsAvailable(bool* available) const;
+  virtual int32_t SetStereoRecording(bool enable);
+  virtual int32_t StereoRecording(bool* enabled) const;
+  virtual int32_t SetRecordingChannel(const ChannelType channel);
+  virtual int32_t RecordingChannel(ChannelType* channel) const;
+
+  virtual int32_t SetPlayoutBuffer(const BufferType type,
+                                   uint16_t size_ms = 0);
+  virtual int32_t PlayoutBuffer(BufferType* type,
+                                uint16_t* size_ms) const;
+  virtual int32_t PlayoutDelay(uint16_t* delay_ms) const;
+  virtual int32_t RecordingDelay(uint16_t* delay_ms) const;
+
+  virtual int32_t CPULoad(uint16_t* load) const;
+
+  virtual int32_t StartRawOutputFileRecording(
+      const char pcm_file_name_utf8[webrtc::kAdmMaxFileNameSize]);
+  virtual int32_t StopRawOutputFileRecording();
+  virtual int32_t StartRawInputFileRecording(
+      const char pcm_file_name_utf8[webrtc::kAdmMaxFileNameSize]);
+  virtual int32_t StopRawInputFileRecording();
+
+  virtual int32_t SetRecordingSampleRate(const uint32_t samples_per_sec);
+  virtual int32_t RecordingSampleRate(uint32_t* samples_per_sec) const;
+  virtual int32_t SetPlayoutSampleRate(const uint32_t samples_per_sec);
+  virtual int32_t PlayoutSampleRate(uint32_t* samples_per_sec) const;
+
+  virtual int32_t ResetAudioDevice();
+  virtual int32_t SetLoudspeakerStatus(bool enable);
+  virtual int32_t GetLoudspeakerStatus(bool* enabled) const;
+  // End of functions inherited from webrtc::AudioDeviceModule.
+
+  // The following function is inherited from talk_base::MessageHandler.
+  virtual void OnMessage(talk_base::Message* msg);
+
+ protected:
+  // The constructor is protected because the class needs to be created as a
+  // reference counted object (for memory managment reasons). It could be
+  // exposed in which case the burden of proper instantiation would be put on
+  // the creator of a FakeAudioCaptureModule instance. To create an instance of
+  // this class use the Create(..) API.
+  explicit FakeAudioCaptureModule(talk_base::Thread* process_thread);
+  // The destructor is protected because it is reference counted and should not
+  // be deleted directly.
+  virtual ~FakeAudioCaptureModule();
+
+ private:
+  // Initializes the state of the FakeAudioCaptureModule. This API is called on
+  // creation by the Create() API.
+  bool Initialize();
+  // SetBuffer() sets all samples in send_buffer_ to |value|.
+  void SetSendBuffer(int value);
+  // Resets rec_buffer_. I.e., sets all rec_buffer_ samples to 0.
+  void ResetRecBuffer();
+  // Returns true if rec_buffer_ contains one or more sample greater than or
+  // equal to |value|.
+  bool CheckRecBuffer(int value);
+
+  // Starts or stops the pushing and pulling of audio frames depending on if
+  // recording or playback has been enabled/started.
+  void UpdateProcessing();
+
+  // Periodcally called function that ensures that frames are pulled and pushed
+  // periodically if enabled/started.
+  void ProcessFrameP();
+  // Pulls frames from the registered webrtc::AudioTransport.
+  void ReceiveFrameP();
+  // Pushes frames to the registered webrtc::AudioTransport.
+  void SendFrameP();
+  // Stops the periodic calling of ProcessFrame() in a thread safe way.
+  void StopProcessP();
+
+  // The time in milliseconds when Process() was last called or 0 if no call
+  // has been made.
+  uint32 last_process_time_ms_;
+
+  // Callback for playout and recording.
+  webrtc::AudioTransport* audio_callback_;
+
+  bool recording_; // True when audio is being pushed from the instance.
+  bool playing_; // True when audio is being pulled by the instance.
+
+  bool play_is_initialized_; // True when the instance is ready to pull audio.
+  bool rec_is_initialized_; // True when the instance is ready to push audio.
+
+  // Input to and output from RecordedDataIsAvailable(..) makes it possible to
+  // modify the current mic level. The implementation does not care about the
+  // mic level so it just feeds back what it receives.
+  uint32_t current_mic_level_;
+
+  // next_frame_time_ is updated in a non-drifting manner to indicate the next
+  // wall clock time the next frame should be generated and received. started_
+  // ensures that next_frame_time_ can be initialized properly on first call.
+  bool started_;
+  uint32 next_frame_time_;
+
+  // User provided thread context.
+  talk_base::Thread* process_thread_;
+
+  // Buffer for storing samples received from the webrtc::AudioTransport.
+  char rec_buffer_[kNumberSamples * kNumberBytesPerSample];
+  // Buffer for samples to send to the webrtc::AudioTransport.
+  char send_buffer_[kNumberSamples * kNumberBytesPerSample];
+
+  // Counter of frames received that have samples of high enough amplitude to
+  // indicate that the frames are not faked somewhere in the audio pipeline
+  // (e.g. by a jitter buffer).
+  int frames_received_;
+};
+
+#endif  // TALK_APP_WEBRTC_TEST_FAKEAUDIOCAPTUREMODULE_H_
diff --git a/talk/app/webrtc/test/fakeaudiocapturemodule_unittest.cc b/talk/app/webrtc/test/fakeaudiocapturemodule_unittest.cc
new file mode 100644
index 0000000..5738955
--- /dev/null
+++ b/talk/app/webrtc/test/fakeaudiocapturemodule_unittest.cc
@@ -0,0 +1,212 @@
+/*
+ * 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.
+ */
+
+#include "talk/app/webrtc/test/fakeaudiocapturemodule.h"
+
+#include <algorithm>
+
+#include "talk/base/gunit.h"
+#include "talk/base/scoped_ref_ptr.h"
+#include "talk/base/thread.h"
+
+using std::min;
+
+class FakeAdmTest : public testing::Test,
+                    public webrtc::AudioTransport {
+ protected:
+  static const int kMsInSecond = 1000;
+
+  FakeAdmTest()
+      : push_iterations_(0),
+        pull_iterations_(0),
+        rec_buffer_bytes_(0) {
+    memset(rec_buffer_, 0, sizeof(rec_buffer_));
+  }
+
+  virtual void SetUp() {
+    fake_audio_capture_module_ = FakeAudioCaptureModule::Create(
+        talk_base::Thread::Current());
+    EXPECT_TRUE(fake_audio_capture_module_.get() != NULL);
+  }
+
+  // Callbacks inherited from webrtc::AudioTransport.
+  // ADM is pushing data.
+  virtual int32_t RecordedDataIsAvailable(const void* audioSamples,
+                                          const uint32_t nSamples,
+                                          const uint8_t nBytesPerSample,
+                                          const uint8_t nChannels,
+                                          const uint32_t samplesPerSec,
+                                          const uint32_t totalDelayMS,
+                                          const int32_t clockDrift,
+                                          const uint32_t currentMicLevel,
+                                          const bool keyPressed,
+                                          uint32_t& newMicLevel) {
+    rec_buffer_bytes_ = nSamples * nBytesPerSample;
+    if ((rec_buffer_bytes_ <= 0) ||
+        (rec_buffer_bytes_ > FakeAudioCaptureModule::kNumberSamples *
+         FakeAudioCaptureModule::kNumberBytesPerSample)) {
+      ADD_FAILURE();
+      return -1;
+    }
+    memcpy(rec_buffer_, audioSamples, rec_buffer_bytes_);
+    ++push_iterations_;
+    newMicLevel = currentMicLevel;
+    return 0;
+  }
+
+  // ADM is pulling data.
+  virtual int32_t NeedMorePlayData(const uint32_t nSamples,
+                                   const uint8_t nBytesPerSample,
+                                   const uint8_t nChannels,
+                                   const uint32_t samplesPerSec,
+                                   void* audioSamples,
+                                   uint32_t& nSamplesOut) {
+    ++pull_iterations_;
+    const uint32_t audio_buffer_size = nSamples * nBytesPerSample;
+    const uint32_t bytes_out = RecordedDataReceived() ?
+        CopyFromRecBuffer(audioSamples, audio_buffer_size):
+        GenerateZeroBuffer(audioSamples, audio_buffer_size);
+    nSamplesOut = bytes_out / nBytesPerSample;
+    return 0;
+  }
+
+  int push_iterations() const { return push_iterations_; }
+  int pull_iterations() const { return pull_iterations_; }
+
+  talk_base::scoped_refptr<FakeAudioCaptureModule> fake_audio_capture_module_;
+
+ private:
+  bool RecordedDataReceived() const {
+    return rec_buffer_bytes_ != 0;
+  }
+  int32_t GenerateZeroBuffer(void* audio_buffer, uint32_t audio_buffer_size) {
+    memset(audio_buffer, 0, audio_buffer_size);
+    return audio_buffer_size;
+  }
+  int32_t CopyFromRecBuffer(void* audio_buffer, uint32_t audio_buffer_size) {
+    EXPECT_EQ(audio_buffer_size, rec_buffer_bytes_);
+    const uint32_t min_buffer_size = min(audio_buffer_size, rec_buffer_bytes_);
+    memcpy(audio_buffer, rec_buffer_, min_buffer_size);
+    return min_buffer_size;
+  }
+
+  int push_iterations_;
+  int pull_iterations_;
+
+  char rec_buffer_[FakeAudioCaptureModule::kNumberSamples *
+                   FakeAudioCaptureModule::kNumberBytesPerSample];
+  uint32_t rec_buffer_bytes_;
+};
+
+TEST_F(FakeAdmTest, TestProccess) {
+  // Next process call must be some time in the future (or now).
+  EXPECT_LE(0, fake_audio_capture_module_->TimeUntilNextProcess());
+  // Process call updates TimeUntilNextProcess() but there are no guarantees on
+  // timing so just check that Process can ba called successfully.
+  EXPECT_LE(0, fake_audio_capture_module_->Process());
+}
+
+TEST_F(FakeAdmTest, PlayoutTest) {
+  EXPECT_EQ(0, fake_audio_capture_module_->RegisterAudioCallback(this));
+
+  bool speaker_available = false;
+  EXPECT_EQ(0, fake_audio_capture_module_->SpeakerIsAvailable(
+      &speaker_available));
+  EXPECT_TRUE(speaker_available);
+
+  bool stereo_available = false;
+  EXPECT_EQ(0,
+            fake_audio_capture_module_->StereoPlayoutIsAvailable(
+                &stereo_available));
+  EXPECT_TRUE(stereo_available);
+
+  EXPECT_NE(0, fake_audio_capture_module_->StartPlayout());
+  EXPECT_FALSE(fake_audio_capture_module_->PlayoutIsInitialized());
+  EXPECT_FALSE(fake_audio_capture_module_->Playing());
+  EXPECT_EQ(0, fake_audio_capture_module_->StopPlayout());
+
+  EXPECT_EQ(0, fake_audio_capture_module_->InitPlayout());
+  EXPECT_TRUE(fake_audio_capture_module_->PlayoutIsInitialized());
+  EXPECT_FALSE(fake_audio_capture_module_->Playing());
+
+  EXPECT_EQ(0, fake_audio_capture_module_->StartPlayout());
+  EXPECT_TRUE(fake_audio_capture_module_->Playing());
+
+  uint16_t delay_ms = 10;
+  EXPECT_EQ(0, fake_audio_capture_module_->PlayoutDelay(&delay_ms));
+  EXPECT_EQ(0, delay_ms);
+
+  EXPECT_TRUE_WAIT(pull_iterations() > 0, kMsInSecond);
+  EXPECT_GE(0, push_iterations());
+
+  EXPECT_EQ(0, fake_audio_capture_module_->StopPlayout());
+  EXPECT_FALSE(fake_audio_capture_module_->Playing());
+}
+
+TEST_F(FakeAdmTest, RecordTest) {
+  EXPECT_EQ(0, fake_audio_capture_module_->RegisterAudioCallback(this));
+
+  bool microphone_available = false;
+  EXPECT_EQ(0, fake_audio_capture_module_->MicrophoneIsAvailable(
+      &microphone_available));
+  EXPECT_TRUE(microphone_available);
+
+  bool stereo_available = false;
+  EXPECT_EQ(0, fake_audio_capture_module_->StereoRecordingIsAvailable(
+      &stereo_available));
+  EXPECT_FALSE(stereo_available);
+
+  EXPECT_NE(0, fake_audio_capture_module_->StartRecording());
+  EXPECT_FALSE(fake_audio_capture_module_->Recording());
+  EXPECT_EQ(0, fake_audio_capture_module_->StopRecording());
+
+  EXPECT_EQ(0, fake_audio_capture_module_->InitRecording());
+  EXPECT_EQ(0, fake_audio_capture_module_->StartRecording());
+  EXPECT_TRUE(fake_audio_capture_module_->Recording());
+
+  EXPECT_TRUE_WAIT(push_iterations() > 0, kMsInSecond);
+  EXPECT_GE(0, pull_iterations());
+
+  EXPECT_EQ(0, fake_audio_capture_module_->StopRecording());
+  EXPECT_FALSE(fake_audio_capture_module_->Recording());
+}
+
+TEST_F(FakeAdmTest, DuplexTest) {
+  EXPECT_EQ(0, fake_audio_capture_module_->RegisterAudioCallback(this));
+
+  EXPECT_EQ(0, fake_audio_capture_module_->InitPlayout());
+  EXPECT_EQ(0, fake_audio_capture_module_->StartPlayout());
+
+  EXPECT_EQ(0, fake_audio_capture_module_->InitRecording());
+  EXPECT_EQ(0, fake_audio_capture_module_->StartRecording());
+
+  EXPECT_TRUE_WAIT(push_iterations() > 0, kMsInSecond);
+  EXPECT_TRUE_WAIT(pull_iterations() > 0, kMsInSecond);
+
+  EXPECT_EQ(0, fake_audio_capture_module_->StopPlayout());
+  EXPECT_EQ(0, fake_audio_capture_module_->StopRecording());
+}
diff --git a/talk/app/webrtc/test/fakeconstraints.h b/talk/app/webrtc/test/fakeconstraints.h
new file mode 100644
index 0000000..0299afa
--- /dev/null
+++ b/talk/app/webrtc/test/fakeconstraints.h
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ *
+ */
+#ifndef TALK_APP_WEBRTC_TEST_FAKECONSTRAINTS_H_
+#define TALK_APP_WEBRTC_TEST_FAKECONSTRAINTS_H_
+
+#include <string>
+#include <vector>
+
+#include "talk/app/webrtc/mediaconstraintsinterface.h"
+#include "talk/base/stringencode.h"
+
+namespace webrtc {
+
+class FakeConstraints : public webrtc::MediaConstraintsInterface {
+ public:
+  FakeConstraints() { }
+  virtual ~FakeConstraints() { }
+
+  virtual const Constraints& GetMandatory() const {
+    return mandatory_;
+  }
+
+  virtual const Constraints& GetOptional() const {
+    return optional_;
+  }
+
+  template <class T>
+  void AddMandatory(const std::string& key, const T& value) {
+    mandatory_.push_back(Constraint(key, talk_base::ToString<T>(value)));
+  }
+
+  template <class T>
+  void AddOptional(const std::string& key, const T& value) {
+    optional_.push_back(Constraint(key, talk_base::ToString<T>(value)));
+  }
+
+  void SetMandatoryMinAspectRatio(double ratio) {
+    AddMandatory(MediaConstraintsInterface::kMinAspectRatio, ratio);
+  }
+
+  void SetMandatoryMinWidth(int width) {
+    AddMandatory(MediaConstraintsInterface::kMinWidth, width);
+  }
+
+  void SetMandatoryMinHeight(int height) {
+    AddMandatory(MediaConstraintsInterface::kMinHeight, height);
+  }
+
+  void SetOptionalMaxWidth(int width) {
+    AddOptional(MediaConstraintsInterface::kMaxWidth, width);
+  }
+
+  void SetMandatoryMaxFrameRate(int frame_rate) {
+    AddMandatory(MediaConstraintsInterface::kMaxFrameRate, frame_rate);
+  }
+
+  void SetMandatoryReceiveAudio(bool enable) {
+    AddMandatory(MediaConstraintsInterface::kOfferToReceiveAudio, enable);
+  }
+
+  void SetMandatoryReceiveVideo(bool enable) {
+    AddMandatory(MediaConstraintsInterface::kOfferToReceiveVideo, enable);
+  }
+
+  void SetMandatoryUseRtpMux(bool enable) {
+    AddMandatory(MediaConstraintsInterface::kUseRtpMux, enable);
+  }
+
+  void SetMandatoryIceRestart(bool enable) {
+    AddMandatory(MediaConstraintsInterface::kIceRestart, enable);
+  }
+
+  void SetAllowRtpDataChannels() {
+    AddMandatory(MediaConstraintsInterface::kEnableRtpDataChannels, true);
+  }
+
+  void SetOptionalVAD(bool enable) {
+    AddOptional(MediaConstraintsInterface::kVoiceActivityDetection, enable);
+  }
+
+  void SetAllowDtlsSctpDataChannels() {
+    AddMandatory(MediaConstraintsInterface::kEnableSctpDataChannels, true);
+    AddMandatory(MediaConstraintsInterface::kEnableDtlsSrtp, true);
+  }
+
+ private:
+  Constraints mandatory_;
+  Constraints optional_;
+};
+
+}  // namespace webrtc
+
+#endif  // TALK_APP_WEBRTC_TEST_FAKECONSTRAINTS_H_
diff --git a/talk/app/webrtc/test/fakeperiodicvideocapturer.h b/talk/app/webrtc/test/fakeperiodicvideocapturer.h
new file mode 100644
index 0000000..7f70ae2
--- /dev/null
+++ b/talk/app/webrtc/test/fakeperiodicvideocapturer.h
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+// FakePeriodicVideoCapturer implements a fake cricket::VideoCapturer that
+// creates video frames periodically after it has been started.
+
+#ifndef TALK_APP_WEBRTC_TEST_FAKEPERIODICVIDEOCAPTURER_H_
+#define TALK_APP_WEBRTC_TEST_FAKEPERIODICVIDEOCAPTURER_H_
+
+#include "talk/base/thread.h"
+#include "talk/media/base/fakevideocapturer.h"
+
+namespace webrtc {
+
+class FakePeriodicVideoCapturer : public cricket::FakeVideoCapturer {
+ public:
+  FakePeriodicVideoCapturer() {
+    std::vector<cricket::VideoFormat> formats;
+    formats.push_back(cricket::VideoFormat(1280, 720,
+            cricket::VideoFormat::FpsToInterval(30), cricket::FOURCC_I420));
+    formats.push_back(cricket::VideoFormat(640, 480,
+        cricket::VideoFormat::FpsToInterval(30), cricket::FOURCC_I420));
+    formats.push_back(cricket::VideoFormat(640, 360,
+            cricket::VideoFormat::FpsToInterval(30), cricket::FOURCC_I420));
+    formats.push_back(cricket::VideoFormat(320, 240,
+        cricket::VideoFormat::FpsToInterval(30), cricket::FOURCC_I420));
+    formats.push_back(cricket::VideoFormat(160, 120,
+        cricket::VideoFormat::FpsToInterval(30), cricket::FOURCC_I420));
+    ResetSupportedFormats(formats);
+  };
+
+  virtual cricket::CaptureState Start(const cricket::VideoFormat& format) {
+    cricket::CaptureState state = FakeVideoCapturer::Start(format);
+    if (state != cricket::CS_FAILED) {
+      talk_base::Thread::Current()->Post(this, MSG_CREATEFRAME);
+    }
+    return state;
+  }
+  virtual void Stop() {
+    talk_base::Thread::Current()->Clear(this);
+  }
+  // Inherited from MesageHandler.
+  virtual void OnMessage(talk_base::Message* msg) {
+    if (msg->message_id == MSG_CREATEFRAME) {
+      if (IsRunning()) {
+        CaptureFrame();
+        talk_base::Thread::Current()->PostDelayed(static_cast<int>(
+            GetCaptureFormat()->interval / talk_base::kNumNanosecsPerMillisec),
+            this, MSG_CREATEFRAME);
+        }
+    } else {
+      FakeVideoCapturer::OnMessage(msg);
+    }
+  }
+
+ private:
+  enum {
+    // Offset  0xFF to make sure this don't collide with base class messages.
+    MSG_CREATEFRAME = 0xFF
+  };
+};
+
+}  // namespace webrtc
+
+#endif  //  TALK_APP_WEBRTC_TEST_FAKEPERIODICVIDEOCAPTURER_H_
diff --git a/talk/app/webrtc/test/fakevideotrackrenderer.h b/talk/app/webrtc/test/fakevideotrackrenderer.h
new file mode 100644
index 0000000..0030a0c
--- /dev/null
+++ b/talk/app/webrtc/test/fakevideotrackrenderer.h
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#ifndef TALK_APP_WEBRTC_TEST_FAKEVIDEOTRACKRENDERER_H_
+#define TALK_APP_WEBRTC_TEST_FAKEVIDEOTRACKRENDERER_H_
+
+#include "talk/app/webrtc/mediastreaminterface.h"
+#include "talk/media/base/fakevideorenderer.h"
+
+namespace webrtc {
+
+class FakeVideoTrackRenderer : public VideoRendererInterface {
+ public:
+  explicit FakeVideoTrackRenderer(VideoTrackInterface* video_track)
+      : video_track_(video_track) {
+    video_track_->AddRenderer(this);
+  }
+  ~FakeVideoTrackRenderer() {
+    video_track_->RemoveRenderer(this);
+  }
+
+  // Implements VideoRendererInterface
+  virtual void SetSize(int width, int height) {
+    fake_renderer_.SetSize(width, height, 0);
+  }
+
+  virtual void RenderFrame(const cricket::VideoFrame* frame) {
+    fake_renderer_.RenderFrame(frame);
+  }
+
+  int errors() const { return fake_renderer_.errors(); }
+  int width() const { return fake_renderer_.width(); }
+  int height() const { return fake_renderer_.height(); }
+  int num_set_sizes() const { return fake_renderer_.num_set_sizes(); }
+  int num_rendered_frames() const {
+    return fake_renderer_.num_rendered_frames();
+  }
+
+ private:
+  cricket::FakeVideoRenderer fake_renderer_;
+  talk_base::scoped_refptr<VideoTrackInterface> video_track_;
+};
+
+}  // namespace webrtc
+
+#endif  // TALK_APP_WEBRTC_TEST_FAKEVIDEOTRACKRENDERER_H_
diff --git a/talk/app/webrtc/test/mockpeerconnectionobservers.h b/talk/app/webrtc/test/mockpeerconnectionobservers.h
new file mode 100644
index 0000000..e2de379
--- /dev/null
+++ b/talk/app/webrtc/test/mockpeerconnectionobservers.h
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+
+// This file contains mock implementations of observers used in PeerConnection.
+
+#ifndef TALK_APP_WEBRTC_TEST_MOCKPEERCONNECTIONOBSERVERS_H_
+#define TALK_APP_WEBRTC_TEST_MOCKPEERCONNECTIONOBSERVERS_H_
+
+#include <string>
+
+#include "talk/app/webrtc/datachannelinterface.h"
+
+namespace webrtc {
+
+class MockCreateSessionDescriptionObserver
+    : public webrtc::CreateSessionDescriptionObserver {
+ public:
+  MockCreateSessionDescriptionObserver()
+      : called_(false),
+        result_(false) {}
+  virtual ~MockCreateSessionDescriptionObserver() {}
+  virtual void OnSuccess(SessionDescriptionInterface* desc) {
+    called_ = true;
+    result_ = true;
+    desc_.reset(desc);
+  }
+  virtual void OnFailure(const std::string& error) {
+    called_ = true;
+    result_ = false;
+  }
+  bool called() const { return called_; }
+  bool result() const { return result_; }
+  SessionDescriptionInterface* release_desc() {
+    return desc_.release();
+  }
+
+ private:
+  bool called_;
+  bool result_;
+  talk_base::scoped_ptr<SessionDescriptionInterface> desc_;
+};
+
+class MockSetSessionDescriptionObserver
+    : public webrtc::SetSessionDescriptionObserver {
+ public:
+  MockSetSessionDescriptionObserver()
+      : called_(false),
+        result_(false) {}
+  virtual ~MockSetSessionDescriptionObserver() {}
+  virtual void OnSuccess() {
+    called_ = true;
+    result_ = true;
+  }
+  virtual void OnFailure(const std::string& error) {
+    called_ = true;
+    result_ = false;
+  }
+  bool called() const { return called_; }
+  bool result() const { return result_; }
+
+ private:
+  bool called_;
+  bool result_;
+};
+
+class MockDataChannelObserver : public webrtc::DataChannelObserver {
+ public:
+  explicit MockDataChannelObserver(webrtc::DataChannelInterface* channel)
+     : channel_(channel) {
+    channel_->RegisterObserver(this);
+    state_ = channel_->state();
+  }
+  virtual ~MockDataChannelObserver() {
+    channel_->UnregisterObserver();
+  }
+
+  virtual void OnStateChange() { state_ = channel_->state(); }
+  virtual void OnMessage(const DataBuffer& buffer) {
+    last_message_.assign(buffer.data.data(), buffer.data.length());
+  }
+
+  bool IsOpen() const { return state_ == DataChannelInterface::kOpen; }
+  const std::string& last_message() const { return last_message_; }
+
+ private:
+  talk_base::scoped_refptr<webrtc::DataChannelInterface> channel_;
+  DataChannelInterface::DataState state_;
+  std::string last_message_;
+};
+
+class MockStatsObserver : public webrtc::StatsObserver {
+ public:
+  MockStatsObserver()
+      : called_(false) {}
+  virtual ~MockStatsObserver() {}
+  virtual void OnComplete(const std::vector<webrtc::StatsReport>& reports) {
+    called_ = true;
+    reports_ = reports;
+  }
+
+  bool called() const { return called_; }
+  size_t number_of_reports() const { return reports_.size(); }
+
+  int AudioOutputLevel() {
+    return GetSsrcStatsValue(
+        webrtc::StatsReport::kStatsValueNameAudioOutputLevel);
+  }
+
+  int AudioInputLevel() {
+    return GetSsrcStatsValue(
+        webrtc::StatsReport::kStatsValueNameAudioInputLevel);
+  }
+
+  int BytesReceived() {
+    return GetSsrcStatsValue(
+        webrtc::StatsReport::kStatsValueNameBytesReceived);
+  }
+
+  int BytesSent() {
+    return GetSsrcStatsValue(webrtc::StatsReport::kStatsValueNameBytesSent);
+  }
+
+ private:
+  int GetSsrcStatsValue(const std::string name) {
+    if (reports_.empty()) {
+      return 0;
+    }
+    for (size_t i = 0; i < reports_.size(); ++i) {
+      if (reports_[i].type != StatsReport::kStatsReportTypeSsrc)
+        continue;
+      webrtc::StatsReport::Values::const_iterator it =
+          reports_[i].values.begin();
+      for (; it != reports_[i].values.end(); ++it) {
+        if (it->name == name) {
+          return talk_base::FromString<int>(it->value);
+        }
+      }
+    }
+    return 0;
+  }
+
+  bool called_;
+  std::vector<webrtc::StatsReport> reports_;
+};
+
+}  // namespace webrtc
+
+#endif  // TALK_APP_WEBRTC_TEST_MOCKPEERCONNECTIONOBSERVERS_H_
diff --git a/talk/app/webrtc/test/testsdpstrings.h b/talk/app/webrtc/test/testsdpstrings.h
new file mode 100644
index 0000000..9f95d36
--- /dev/null
+++ b/talk/app/webrtc/test/testsdpstrings.h
@@ -0,0 +1,144 @@
+/*
+ * 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.
+ */
+
+// This file contain SDP strings used for testing.
+
+#ifndef TALK_APP_WEBRTC_TEST_TESTSDPSTRINGS_H_
+#define TALK_APP_WEBRTC_TEST_TESTSDPSTRINGS_H_
+
+namespace webrtc {
+
+// SDP offer string from a Nightly FireFox build.
+static const char kFireFoxSdpOffer[] =
+    "v=0\r\n"
+    "o=Mozilla-SIPUA 23551 0 IN IP4 0.0.0.0\r\n"
+    "s=SIP Call\r\n"
+    "t=0 0\r\n"
+    "a=ice-ufrag:e5785931\r\n"
+    "a=ice-pwd:36fb7878390db89481c1d46daa4278d8\r\n"
+    "a=fingerprint:sha-256 A7:24:72:CA:6E:02:55:39:BA:66:DF:6E:CC:4C:D8:B0:1A:"
+    "BF:1A:56:65:7D:F4:03:AD:7E:77:43:2A:29:EC:93\r\n"
+    "m=audio 36993 RTP/SAVPF 109 0 8 101\r\n"
+    "c=IN IP4 74.95.2.170\r\n"
+    "a=rtpmap:109 opus/48000/2\r\n"
+    "a=ptime:20\r\n"
+    "a=rtpmap:0 PCMU/8000\r\n"
+    "a=rtpmap:8 PCMA/8000\r\n"
+    "a=rtpmap:101 telephone-event/8000\r\n"
+    "a=fmtp:101 0-15\r\n"
+    "a=sendrecv\r\n"
+    "a=candidate:0 1 UDP 2112946431 172.16.191.1 61725 typ host\r\n"
+    "a=candidate:2 1 UDP 2112487679 172.16.131.1 58798 typ host\r\n"
+    "a=candidate:4 1 UDP 2113667327 10.0.254.2 58122 typ host\r\n"
+    "a=candidate:5 1 UDP 1694302207 74.95.2.170 36993 typ srflx raddr "
+    "10.0.254.2 rport 58122\r\n"
+    "a=candidate:0 2 UDP 2112946430 172.16.191.1 55025 typ host\r\n"
+    "a=candidate:2 2 UDP 2112487678 172.16.131.1 63576 typ host\r\n"
+    "a=candidate:4 2 UDP 2113667326 10.0.254.2 50962 typ host\r\n"
+    "a=candidate:5 2 UDP 1694302206 74.95.2.170 41028 typ srflx raddr"
+    " 10.0.254.2 rport 50962\r\n"
+    "m=video 38826 RTP/SAVPF 120\r\n"
+    "c=IN IP4 74.95.2.170\r\n"
+    "a=rtpmap:120 VP8/90000\r\n"
+    "a=sendrecv\r\n"
+    "a=candidate:0 1 UDP 2112946431 172.16.191.1 62017 typ host\r\n"
+    "a=candidate:2 1 UDP 2112487679 172.16.131.1 59741 typ host\r\n"
+    "a=candidate:4 1 UDP 2113667327 10.0.254.2 62652 typ host\r\n"
+    "a=candidate:5 1 UDP 1694302207 74.95.2.170 38826 typ srflx raddr"
+    " 10.0.254.2 rport 62652\r\n"
+    "a=candidate:0 2 UDP 2112946430 172.16.191.1 63440 typ host\r\n"
+    "a=candidate:2 2 UDP 2112487678 172.16.131.1 51847 typ host\r\n"
+    "a=candidate:4 2 UDP 2113667326 10.0.254.2 58890 typ host\r\n"
+    "a=candidate:5 2 UDP 1694302206 74.95.2.170 33611 typ srflx raddr"
+    " 10.0.254.2 rport 58890\r\n"
+    "m=application 45536 SCTP/DTLS 5000\r\n"
+    "c=IN IP4 74.95.2.170\r\n"
+    "a=fmtp:5000 protocol=webrtc-datachannel;streams=16\r\n"
+    "a=sendrecv\r\n"
+    "a=candidate:0 1 UDP 2112946431 172.16.191.1 60248 typ host\r\n"
+    "a=candidate:2 1 UDP 2112487679 172.16.131.1 55925 typ host\r\n"
+    "a=candidate:4 1 UDP 2113667327 10.0.254.2 65268 typ host\r\n"
+    "a=candidate:5 1 UDP 1694302207 74.95.2.170 45536 typ srflx raddr"
+    " 10.0.254.2 rport 65268\r\n"
+    "a=candidate:0 2 UDP 2112946430 172.16.191.1 49162 typ host\r\n"
+    "a=candidate:2 2 UDP 2112487678 172.16.131.1 59635 typ host\r\n"
+    "a=candidate:4 2 UDP 2113667326 10.0.254.2 61232 typ host\r\n"
+    "a=candidate:5 2 UDP 1694302206 74.95.2.170 45468 typ srflx raddr"
+    " 10.0.254.2 rport 61232\r\n";
+
+// Audio SDP with a limited set of audio codecs.
+static const char kAudioSdp[] =
+    "v=0\r\n"
+    "o=- 7859371131 2 IN IP4 192.168.30.208\r\n"
+    "s=-\r\n"
+    "c=IN IP4 192.168.30.208\r\n"
+    "t=0 0\r\n"
+    "m=audio 16000 RTP/SAVPF 0 8 126\r\n"
+    "a=rtpmap:0 PCMU/8000\r\n"
+    "a=rtpmap:8 PCMA/8000\r\n"
+    "a=rtpmap:126 telephone-event/8000\r\n"
+    "a=sendrecv\r\n"
+    "a=rtcp:16000 IN IP4 192.168.30.208\r\n"
+    "a=rtcp-mux\r\n"
+    "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
+    "inline:tvKIFjbMQ7W0/C2RzhwN0oQglj/7GJg+frdsNRxt\r\n"
+    "a=ice-ufrag:AI2sRT3r\r\n"
+    "a=ice-pwd:lByS9z2RSQlSE9XurlvjYmEm\r\n"
+    "a=ssrc:4227871655 cname:GeAAgb6XCPNLVMX5\r\n"
+    "a=ssrc:4227871655 msid:1NFAV3iD08ioO2339rQS9pfOI9mDf6GeG9F4 a0\r\n"
+    "a=ssrc:4227871655 mslabel:1NFAV3iD08ioO2339rQS9pfOI9mDf6GeG9F4\r\n"
+    "a=ssrc:4227871655 label:1NFAV3iD08ioO2339rQS9pfOI9mDf6GeG9F4a0\r\n"
+    "a=mid:audio\r\n";
+
+static const char kAudioSdpWithUnsupportedCodecs[] =
+    "v=0\r\n"
+    "o=- 6858750541 2 IN IP4 192.168.30.208\r\n"
+    "s=-\r\n"
+    "c=IN IP4 192.168.30.208\r\n"
+    "t=0 0\r\n"
+    "m=audio 16000 RTP/SAVPF 0 8 18 110 126\r\n"
+    "a=rtpmap:0 PCMU/8000\r\n"
+    "a=rtpmap:8 PCMA/8000\r\n"
+    "a=rtpmap:18 WeirdCodec1/8000\r\n"
+    "a=rtpmap:110 WeirdCodec2/8000\r\n"
+    "a=rtpmap:126 telephone-event/8000\r\n"
+    "a=sendonly\r\n"
+    "a=rtcp:16000 IN IP4 192.168.30.208\r\n"
+    "a=rtcp-mux\r\n"
+    "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
+    "inline:tvKIFjbMQ7W0/C2RzhwN0oQglj/7GJg+frdsNRxt\r\n"
+    "a=ice-ufrag:AI2sRT3r\r\n"
+    "a=ice-pwd:lByS9z2RSQlSE9XurlvjYmEm\r\n"
+    "a=ssrc:4227871655 cname:TsmD02HRfhkJBm4m\r\n"
+    "a=ssrc:4227871655 msid:7nU0TApbB-n4dfPlCplWT9QTEsbBDS1IlpW3 a0\r\n"
+    "a=ssrc:4227871655 mslabel:7nU0TApbB-n4dfPlCplWT9QTEsbBDS1IlpW3\r\n"
+    "a=ssrc:4227871655 label:7nU0TApbB-n4dfPlCplWT9QTEsbBDS1IlpW3a0\r\n"
+    "a=mid:audio\r\n";
+
+}  // namespace webrtc
+
+#endif  // TALK_APP_WEBRTC_TEST_TESTSDPSTRINGS_H_