| /* |
| * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "modules/audio_device/include/test_audio_device.h" |
| |
| #include <algorithm> |
| #include <array> |
| #include <memory> |
| #include <utility> |
| |
| #include "absl/types/optional.h" |
| #include "api/array_view.h" |
| #include "api/task_queue/task_queue_factory.h" |
| #include "api/units/time_delta.h" |
| #include "api/units/timestamp.h" |
| #include "common_audio/wav_file.h" |
| #include "common_audio/wav_header.h" |
| #include "modules/audio_device/include/audio_device_defines.h" |
| #include "rtc_base/checks.h" |
| #include "rtc_base/logging.h" |
| #include "rtc_base/synchronization/mutex.h" |
| #include "test/gmock.h" |
| #include "test/gtest.h" |
| #include "test/testsupport/file_utils.h" |
| #include "test/time_controller/simulated_time_controller.h" |
| |
| namespace webrtc { |
| namespace { |
| |
| void RunWavTest(const std::vector<int16_t>& input_samples, |
| const std::vector<int16_t>& expected_samples) { |
| const ::testing::TestInfo* const test_info = |
| ::testing::UnitTest::GetInstance()->current_test_info(); |
| |
| const std::string output_filename = |
| test::OutputPath() + "BoundedWavFileWriterTest_" + test_info->name() + |
| "_" + std::to_string(std::rand()) + ".wav"; |
| |
| static const size_t kSamplesPerFrame = 8; |
| static const int kSampleRate = kSamplesPerFrame * 100; |
| EXPECT_EQ(TestAudioDeviceModule::SamplesPerFrame(kSampleRate), |
| kSamplesPerFrame); |
| |
| // Test through file name API. |
| { |
| std::unique_ptr<TestAudioDeviceModule::Renderer> writer = |
| TestAudioDeviceModule::CreateBoundedWavFileWriter(output_filename, 800); |
| |
| for (size_t i = 0; i < input_samples.size(); i += kSamplesPerFrame) { |
| EXPECT_TRUE(writer->Render(rtc::ArrayView<const int16_t>( |
| &input_samples[i], |
| std::min(kSamplesPerFrame, input_samples.size() - i)))); |
| } |
| } |
| |
| { |
| WavReader reader(output_filename); |
| std::vector<int16_t> read_samples(expected_samples.size()); |
| EXPECT_EQ(expected_samples.size(), |
| reader.ReadSamples(read_samples.size(), read_samples.data())); |
| EXPECT_EQ(expected_samples, read_samples); |
| |
| EXPECT_EQ(0u, reader.ReadSamples(read_samples.size(), read_samples.data())); |
| } |
| |
| remove(output_filename.c_str()); |
| } |
| |
| TEST(BoundedWavFileWriterTest, NoSilence) { |
| static const std::vector<int16_t> kInputSamples = { |
| 75, 1234, 243, -1231, -22222, 0, 3, 88, |
| 1222, -1213, -13222, -7, -3525, 5787, -25247, 8}; |
| static const std::vector<int16_t> kExpectedSamples = kInputSamples; |
| RunWavTest(kInputSamples, kExpectedSamples); |
| } |
| |
| TEST(BoundedWavFileWriterTest, SomeStartSilence) { |
| static const std::vector<int16_t> kInputSamples = { |
| 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, -13222, -7, -3525, 5787, -25247, 8}; |
| static const std::vector<int16_t> kExpectedSamples(kInputSamples.begin() + 10, |
| kInputSamples.end()); |
| RunWavTest(kInputSamples, kExpectedSamples); |
| } |
| |
| TEST(BoundedWavFileWriterTest, NegativeStartSilence) { |
| static const std::vector<int16_t> kInputSamples = { |
| 0, -4, -6, 0, 3, 0, 0, 0, 0, 3, -13222, -7, -3525, 5787, -25247, 8}; |
| static const std::vector<int16_t> kExpectedSamples(kInputSamples.begin() + 2, |
| kInputSamples.end()); |
| RunWavTest(kInputSamples, kExpectedSamples); |
| } |
| |
| TEST(BoundedWavFileWriterTest, SomeEndSilence) { |
| static const std::vector<int16_t> kInputSamples = { |
| 75, 1234, 243, -1231, -22222, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| static const std::vector<int16_t> kExpectedSamples(kInputSamples.begin(), |
| kInputSamples.end() - 9); |
| RunWavTest(kInputSamples, kExpectedSamples); |
| } |
| |
| TEST(BoundedWavFileWriterTest, DoubleEndSilence) { |
| static const std::vector<int16_t> kInputSamples = { |
| 75, 1234, 243, -1231, -22222, 0, 0, 0, |
| 0, -1213, -13222, -7, -3525, 5787, 0, 0}; |
| static const std::vector<int16_t> kExpectedSamples(kInputSamples.begin(), |
| kInputSamples.end() - 2); |
| RunWavTest(kInputSamples, kExpectedSamples); |
| } |
| |
| TEST(BoundedWavFileWriterTest, DoubleSilence) { |
| static const std::vector<int16_t> kInputSamples = {0, -1213, -13222, -7, |
| -3525, 5787, 0, 0}; |
| static const std::vector<int16_t> kExpectedSamples(kInputSamples.begin() + 1, |
| kInputSamples.end() - 2); |
| RunWavTest(kInputSamples, kExpectedSamples); |
| } |
| |
| TEST(BoundedWavFileWriterTest, EndSilenceCutoff) { |
| static const std::vector<int16_t> kInputSamples = { |
| 75, 1234, 243, -1231, -22222, 0, 1, 0, 0, 0, 0}; |
| static const std::vector<int16_t> kExpectedSamples(kInputSamples.begin(), |
| kInputSamples.end() - 4); |
| RunWavTest(kInputSamples, kExpectedSamples); |
| } |
| |
| TEST(WavFileReaderTest, RepeatedTrueWithSingleFrameFileReadTwice) { |
| static const std::vector<int16_t> kInputSamples = {75, 1234, 243, -1231, |
| -22222, 0, 3, 88}; |
| static const rtc::BufferT<int16_t> kExpectedSamples(kInputSamples.data(), |
| kInputSamples.size()); |
| |
| const std::string output_filename = test::OutputPath() + |
| "WavFileReaderTest_RepeatedTrue_" + |
| std::to_string(std::rand()) + ".wav"; |
| |
| static const size_t kSamplesPerFrame = 8; |
| static const int kSampleRate = kSamplesPerFrame * 100; |
| EXPECT_EQ(TestAudioDeviceModule::SamplesPerFrame(kSampleRate), |
| kSamplesPerFrame); |
| |
| // Create raw file to read. |
| { |
| std::unique_ptr<TestAudioDeviceModule::Renderer> writer = |
| TestAudioDeviceModule::CreateWavFileWriter(output_filename, 800); |
| |
| for (size_t i = 0; i < kInputSamples.size(); i += kSamplesPerFrame) { |
| EXPECT_TRUE(writer->Render(rtc::ArrayView<const int16_t>( |
| &kInputSamples[i], |
| std::min(kSamplesPerFrame, kInputSamples.size() - i)))); |
| } |
| } |
| |
| { |
| std::unique_ptr<TestAudioDeviceModule::Capturer> reader = |
| TestAudioDeviceModule::CreateWavFileReader(output_filename, true); |
| rtc::BufferT<int16_t> buffer(kExpectedSamples.size()); |
| EXPECT_TRUE(reader->Capture(&buffer)); |
| EXPECT_EQ(kExpectedSamples, buffer); |
| EXPECT_TRUE(reader->Capture(&buffer)); |
| EXPECT_EQ(kExpectedSamples, buffer); |
| } |
| |
| remove(output_filename.c_str()); |
| } |
| |
| void RunRawTestNoRepeat(const std::vector<int16_t>& input_samples, |
| const std::vector<int16_t>& expected_samples) { |
| const ::testing::TestInfo* const test_info = |
| ::testing::UnitTest::GetInstance()->current_test_info(); |
| |
| const std::string output_filename = test::OutputPath() + "RawFileTest_" + |
| test_info->name() + "_" + |
| std::to_string(std::rand()) + ".raw"; |
| |
| static const size_t kSamplesPerFrame = 8; |
| static const int kSampleRate = kSamplesPerFrame * 100; |
| EXPECT_EQ(TestAudioDeviceModule::SamplesPerFrame(kSampleRate), |
| kSamplesPerFrame); |
| |
| // Test through file name API. |
| { |
| std::unique_ptr<TestAudioDeviceModule::Renderer> writer = |
| TestAudioDeviceModule::CreateRawFileWriter( |
| output_filename, /*sampling_frequency_in_hz=*/800); |
| |
| for (size_t i = 0; i < input_samples.size(); i += kSamplesPerFrame) { |
| EXPECT_TRUE(writer->Render(rtc::ArrayView<const int16_t>( |
| &input_samples[i], |
| std::min(kSamplesPerFrame, input_samples.size() - i)))); |
| } |
| } |
| |
| { |
| std::unique_ptr<TestAudioDeviceModule::Capturer> reader = |
| TestAudioDeviceModule::CreateRawFileReader( |
| output_filename, /*sampling_frequency_in_hz=*/800, |
| /*num_channels=*/2, /*repeat=*/false); |
| rtc::BufferT<int16_t> buffer(expected_samples.size()); |
| rtc::BufferT<int16_t> expected_buffer(expected_samples.size()); |
| expected_buffer.SetData(expected_samples); |
| EXPECT_TRUE(reader->Capture(&buffer)); |
| EXPECT_EQ(expected_buffer, buffer); |
| EXPECT_FALSE(reader->Capture(&buffer)); |
| EXPECT_TRUE(buffer.empty()); |
| } |
| |
| remove(output_filename.c_str()); |
| } |
| |
| TEST(RawFileWriterTest, NoSilence) { |
| static const std::vector<int16_t> kInputSamples = { |
| 75, 1234, 243, -1231, -22222, 0, 3, 88, |
| 1222, -1213, -13222, -7, -3525, 5787, -25247, 8}; |
| static const std::vector<int16_t> kExpectedSamples = kInputSamples; |
| RunRawTestNoRepeat(kInputSamples, kExpectedSamples); |
| } |
| |
| TEST(RawFileWriterTest, SomeStartSilence) { |
| static const std::vector<int16_t> kInputSamples = { |
| 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, -13222, -7, -3525, 5787, -25247, 8}; |
| static const std::vector<int16_t> kExpectedSamples(kInputSamples.begin() + 10, |
| kInputSamples.end()); |
| RunRawTestNoRepeat(kInputSamples, kExpectedSamples); |
| } |
| |
| TEST(RawFileWriterTest, NegativeStartSilence) { |
| static const std::vector<int16_t> kInputSamples = { |
| 0, -4, -6, 0, 3, 0, 0, 0, 0, 3, -13222, -7, -3525, 5787, -25247, 8}; |
| static const std::vector<int16_t> kExpectedSamples(kInputSamples.begin() + 2, |
| kInputSamples.end()); |
| RunRawTestNoRepeat(kInputSamples, kExpectedSamples); |
| } |
| |
| TEST(RawFileWriterTest, SomeEndSilence) { |
| static const std::vector<int16_t> kInputSamples = { |
| 75, 1234, 243, -1231, -22222, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| static const std::vector<int16_t> kExpectedSamples(kInputSamples.begin(), |
| kInputSamples.end() - 9); |
| RunRawTestNoRepeat(kInputSamples, kExpectedSamples); |
| } |
| |
| TEST(RawFileWriterTest, DoubleEndSilence) { |
| static const std::vector<int16_t> kInputSamples = { |
| 75, 1234, 243, -1231, -22222, 0, 0, 0, |
| 0, -1213, -13222, -7, -3525, 5787, 0, 0}; |
| static const std::vector<int16_t> kExpectedSamples(kInputSamples.begin(), |
| kInputSamples.end() - 2); |
| RunRawTestNoRepeat(kInputSamples, kExpectedSamples); |
| } |
| |
| TEST(RawFileWriterTest, DoubleSilence) { |
| static const std::vector<int16_t> kInputSamples = {0, -1213, -13222, -7, |
| -3525, 5787, 0, 0}; |
| static const std::vector<int16_t> kExpectedSamples(kInputSamples.begin() + 1, |
| kInputSamples.end() - 2); |
| RunRawTestNoRepeat(kInputSamples, kExpectedSamples); |
| } |
| |
| TEST(RawFileWriterTest, EndSilenceCutoff) { |
| static const std::vector<int16_t> kInputSamples = { |
| 75, 1234, 243, -1231, -22222, 0, 1, 0, 0, 0, 0}; |
| static const std::vector<int16_t> kExpectedSamples(kInputSamples.begin(), |
| kInputSamples.end() - 4); |
| RunRawTestNoRepeat(kInputSamples, kExpectedSamples); |
| } |
| |
| TEST(RawFileWriterTest, Repeat) { |
| static const std::vector<int16_t> kInputSamples = { |
| 75, 1234, 243, -1231, -22222, 0, 3, 88, |
| 1222, -1213, -13222, -7, -3525, 5787, -25247, 8}; |
| static const rtc::BufferT<int16_t> kExpectedSamples(kInputSamples.data(), |
| kInputSamples.size()); |
| |
| const ::testing::TestInfo* const test_info = |
| ::testing::UnitTest::GetInstance()->current_test_info(); |
| |
| const std::string output_filename = test::OutputPath() + "RawFileTest_" + |
| test_info->name() + "_" + |
| std::to_string(std::rand()) + ".raw"; |
| |
| static const size_t kSamplesPerFrame = 8; |
| static const int kSampleRate = kSamplesPerFrame * 100; |
| EXPECT_EQ(TestAudioDeviceModule::SamplesPerFrame(kSampleRate), |
| kSamplesPerFrame); |
| |
| // Test through file name API. |
| { |
| std::unique_ptr<TestAudioDeviceModule::Renderer> writer = |
| TestAudioDeviceModule::CreateRawFileWriter( |
| output_filename, /*sampling_frequency_in_hz=*/800); |
| |
| for (size_t i = 0; i < kInputSamples.size(); i += kSamplesPerFrame) { |
| EXPECT_TRUE(writer->Render(rtc::ArrayView<const int16_t>( |
| &kInputSamples[i], |
| std::min(kSamplesPerFrame, kInputSamples.size() - i)))); |
| } |
| } |
| |
| { |
| std::unique_ptr<TestAudioDeviceModule::Capturer> reader = |
| TestAudioDeviceModule::CreateRawFileReader( |
| output_filename, /*sampling_frequency_in_hz=*/800, |
| /*num_channels=*/2, /*repeat=*/true); |
| rtc::BufferT<int16_t> buffer(kExpectedSamples.size()); |
| EXPECT_TRUE(reader->Capture(&buffer)); |
| EXPECT_EQ(kExpectedSamples, buffer); |
| EXPECT_TRUE(reader->Capture(&buffer)); |
| EXPECT_EQ(kExpectedSamples, buffer); |
| } |
| |
| remove(output_filename.c_str()); |
| } |
| |
| TEST(PulsedNoiseCapturerTest, SetMaxAmplitude) { |
| const int16_t kAmplitude = 50; |
| std::unique_ptr<TestAudioDeviceModule::PulsedNoiseCapturer> capturer = |
| TestAudioDeviceModule::CreatePulsedNoiseCapturer( |
| kAmplitude, /*sampling_frequency_in_hz=*/8000); |
| rtc::BufferT<int16_t> recording_buffer; |
| |
| // Verify that the capturer doesn't create entries louder than than |
| // kAmplitude. Since the pulse generator alternates between writing |
| // zeroes and actual entries, we need to do the capturing twice. |
| capturer->Capture(&recording_buffer); |
| capturer->Capture(&recording_buffer); |
| int16_t max_sample = |
| *std::max_element(recording_buffer.begin(), recording_buffer.end()); |
| EXPECT_LE(max_sample, kAmplitude); |
| |
| // Increase the amplitude and verify that the samples can now be louder |
| // than the previous max. |
| capturer->SetMaxAmplitude(kAmplitude * 2); |
| capturer->Capture(&recording_buffer); |
| capturer->Capture(&recording_buffer); |
| max_sample = |
| *std::max_element(recording_buffer.begin(), recording_buffer.end()); |
| EXPECT_GT(max_sample, kAmplitude); |
| } |
| |
| using ::testing::ElementsAre; |
| |
| constexpr Timestamp kStartTime = Timestamp::Millis(10000); |
| |
| class TestAudioTransport : public AudioTransport { |
| public: |
| enum class Mode { kPlaying, kRecording }; |
| |
| explicit TestAudioTransport(Mode mode) : mode_(mode) {} |
| ~TestAudioTransport() override = default; |
| |
| int32_t RecordedDataIsAvailable( |
| const void* audioSamples, |
| size_t samples_per_channel, |
| size_t bytes_per_sample, |
| size_t number_of_channels, |
| uint32_t samples_per_second, |
| uint32_t total_delay_ms, |
| int32_t clock_drift, |
| uint32_t current_mic_level, |
| bool key_pressed, |
| uint32_t& new_mic_level, |
| absl::optional<int64_t> estimated_capture_time_ns) override { |
| new_mic_level = 1; |
| |
| if (mode_ != Mode::kRecording) { |
| EXPECT_TRUE(false) |
| << "NeedMorePlayData mustn't be called when mode isn't kRecording"; |
| return -1; |
| } |
| |
| MutexLock lock(&mutex_); |
| samples_per_channel_.push_back(samples_per_channel); |
| number_of_channels_.push_back(number_of_channels); |
| bytes_per_sample_.push_back(bytes_per_sample); |
| samples_per_second_.push_back(samples_per_second); |
| return 0; |
| } |
| |
| int32_t NeedMorePlayData(size_t samples_per_channel, |
| size_t bytes_per_sample, |
| size_t number_of_channels, |
| uint32_t samples_per_second, |
| void* audio_samples, |
| size_t& samples_out, |
| int64_t* elapsed_time_ms, |
| int64_t* ntp_time_ms) override { |
| const size_t num_bytes = samples_per_channel * number_of_channels; |
| std::memset(audio_samples, 1, num_bytes); |
| samples_out = samples_per_channel * number_of_channels; |
| *elapsed_time_ms = 0; |
| *ntp_time_ms = 0; |
| |
| if (mode_ != Mode::kPlaying) { |
| EXPECT_TRUE(false) |
| << "NeedMorePlayData mustn't be called when mode isn't kPlaying"; |
| return -1; |
| } |
| |
| MutexLock lock(&mutex_); |
| samples_per_channel_.push_back(samples_per_channel); |
| number_of_channels_.push_back(number_of_channels); |
| bytes_per_sample_.push_back(bytes_per_sample); |
| samples_per_second_.push_back(samples_per_second); |
| return 0; |
| } |
| |
| int32_t RecordedDataIsAvailable(const void* audio_samples, |
| size_t samples_per_channel, |
| size_t bytes_per_sample, |
| size_t number_of_channels, |
| uint32_t samples_per_second, |
| uint32_t total_delay_ms, |
| int32_t clockDrift, |
| uint32_t current_mic_level, |
| bool key_pressed, |
| uint32_t& new_mic_level) override { |
| RTC_CHECK(false) << "This methods should be never executed"; |
| } |
| |
| void PullRenderData(int bits_per_sample, |
| int sample_rate, |
| size_t number_of_channels, |
| size_t number_of_frames, |
| void* audio_data, |
| int64_t* elapsed_time_ms, |
| int64_t* ntp_time_ms) override { |
| RTC_CHECK(false) << "This methods should be never executed"; |
| } |
| |
| std::vector<size_t> samples_per_channel() const { |
| MutexLock lock(&mutex_); |
| return samples_per_channel_; |
| } |
| std::vector<size_t> number_of_channels() const { |
| MutexLock lock(&mutex_); |
| return number_of_channels_; |
| } |
| std::vector<size_t> bytes_per_sample() const { |
| MutexLock lock(&mutex_); |
| return bytes_per_sample_; |
| } |
| std::vector<size_t> samples_per_second() const { |
| MutexLock lock(&mutex_); |
| return samples_per_second_; |
| } |
| |
| private: |
| const Mode mode_; |
| |
| mutable Mutex mutex_; |
| std::vector<size_t> samples_per_channel_ RTC_GUARDED_BY(mutex_); |
| std::vector<size_t> number_of_channels_ RTC_GUARDED_BY(mutex_); |
| std::vector<size_t> bytes_per_sample_ RTC_GUARDED_BY(mutex_); |
| std::vector<size_t> samples_per_second_ RTC_GUARDED_BY(mutex_); |
| }; |
| |
| TEST(TestAudioDeviceModuleTest, CreatedADMCanRecord) { |
| GlobalSimulatedTimeController time_controller(kStartTime); |
| TestAudioTransport audio_transport(TestAudioTransport::Mode::kRecording); |
| std::unique_ptr<TestAudioDeviceModule::PulsedNoiseCapturer> capturer = |
| TestAudioDeviceModule::CreatePulsedNoiseCapturer( |
| /*max_amplitude=*/1000, |
| /*sampling_frequency_in_hz=*/48000, /*num_channels=*/2); |
| |
| rtc::scoped_refptr<AudioDeviceModule> adm = TestAudioDeviceModule::Create( |
| time_controller.GetTaskQueueFactory(), std::move(capturer), |
| /*renderer=*/nullptr); |
| |
| ASSERT_EQ(adm->RegisterAudioCallback(&audio_transport), 0); |
| ASSERT_EQ(adm->Init(), 0); |
| |
| EXPECT_FALSE(adm->RecordingIsInitialized()); |
| ASSERT_EQ(adm->InitRecording(), 0); |
| EXPECT_TRUE(adm->RecordingIsInitialized()); |
| ASSERT_EQ(adm->StartRecording(), 0); |
| time_controller.AdvanceTime(TimeDelta::Millis(10)); |
| ASSERT_TRUE(adm->Recording()); |
| time_controller.AdvanceTime(TimeDelta::Millis(10)); |
| ASSERT_EQ(adm->StopRecording(), 0); |
| |
| EXPECT_THAT(audio_transport.samples_per_channel(), |
| ElementsAre(480, 480, 480)); |
| EXPECT_THAT(audio_transport.number_of_channels(), ElementsAre(2, 2, 2)); |
| EXPECT_THAT(audio_transport.bytes_per_sample(), ElementsAre(4, 4, 4)); |
| EXPECT_THAT(audio_transport.samples_per_second(), |
| ElementsAre(48000, 48000, 48000)); |
| } |
| |
| TEST(TestAudioDeviceModuleTest, CreatedADMCanPlay) { |
| GlobalSimulatedTimeController time_controller(kStartTime); |
| TestAudioTransport audio_transport(TestAudioTransport::Mode::kPlaying); |
| std::unique_ptr<TestAudioDeviceModule::Renderer> renderer = |
| TestAudioDeviceModule::CreateDiscardRenderer( |
| /*sampling_frequency_in_hz=*/48000, /*num_channels=*/2); |
| |
| rtc::scoped_refptr<AudioDeviceModule> adm = |
| TestAudioDeviceModule::Create(time_controller.GetTaskQueueFactory(), |
| /*capturer=*/nullptr, std::move(renderer)); |
| |
| ASSERT_EQ(adm->RegisterAudioCallback(&audio_transport), 0); |
| ASSERT_EQ(adm->Init(), 0); |
| |
| EXPECT_FALSE(adm->PlayoutIsInitialized()); |
| ASSERT_EQ(adm->InitPlayout(), 0); |
| EXPECT_TRUE(adm->PlayoutIsInitialized()); |
| ASSERT_EQ(adm->StartPlayout(), 0); |
| time_controller.AdvanceTime(TimeDelta::Millis(10)); |
| ASSERT_TRUE(adm->Playing()); |
| time_controller.AdvanceTime(TimeDelta::Millis(10)); |
| ASSERT_EQ(adm->StopPlayout(), 0); |
| |
| EXPECT_THAT(audio_transport.samples_per_channel(), |
| ElementsAre(480, 480, 480)); |
| EXPECT_THAT(audio_transport.number_of_channels(), ElementsAre(2, 2, 2)); |
| EXPECT_THAT(audio_transport.bytes_per_sample(), ElementsAre(4, 4, 4)); |
| EXPECT_THAT(audio_transport.samples_per_second(), |
| ElementsAre(48000, 48000, 48000)); |
| } |
| |
| } // namespace |
| } // namespace webrtc |