Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1631)

Unified Diff: content/renderer/media/audio_track_recorder_unittest.cc

Issue 1406113002: Add AudioTrackRecorder for audio component of MediaStream recording. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: comments Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/renderer/media/audio_track_recorder_unittest.cc
diff --git a/content/renderer/media/audio_track_recorder_unittest.cc b/content/renderer/media/audio_track_recorder_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..093b4b84459199e5457434a86088dac2f16f4b62
--- /dev/null
+++ b/content/renderer/media/audio_track_recorder_unittest.cc
@@ -0,0 +1,247 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/media/audio_track_recorder.h"
+
+#include "base/run_loop.h"
+#include "base/stl_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/renderer/media/media_stream_audio_source.h"
+#include "content/renderer/media/mock_media_constraint_factory.h"
+#include "content/renderer/media/webrtc/webrtc_local_audio_track_adapter.h"
+#include "content/renderer/media/webrtc_local_audio_track.h"
+#include "media/audio/simple_sources.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/web/WebHeap.h"
+#include "third_party/opus/src/include/opus.h"
minyue 2015/11/16 11:57:33 move up before WebKit/public/web/WebHeap...
ajose 2015/11/16 22:29:47 Hmm, seems the capital 'W' throws off emacs sort-l
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Mock;
+using ::testing::Return;
+using ::testing::SaveArg;
+using ::testing::TestWithParam;
+using ::testing::ValuesIn;
+
+namespace {
+
+// Input audio format.
+const media::AudioParameters::Format kDefaultInputFormat =
+ media::AudioParameters::AUDIO_PCM_LOW_LATENCY;
+const int kDefaultBitsPerSample = 16;
+const int kDefaultSampleRate = 48000;
+// The |frames_per_buffer| field of AudioParameters is not used by ATR.
+const int kIgnoreFramesPerBuffer = 1;
+const int kOpusMaxBufferDurationMS = 120;
+
+} // namespace
+
+namespace content {
+
+ACTION_P(RunClosure, closure) {
+ closure.Run();
+}
+
+struct ATRTestParams {
+ const media::AudioParameters::Format input_format;
+ const media::ChannelLayout channel_layout;
+ const int sample_rate;
+ const int bits_per_sample;
+};
+
+const ATRTestParams kATRTestParams[] = {
+ // Equivalent to default settings:
+ {media::AudioParameters::AUDIO_PCM_LOW_LATENCY, /* input format */
+ media::CHANNEL_LAYOUT_STEREO, /* channel layout */
+ kDefaultSampleRate, /* sample rate */
+ kDefaultBitsPerSample /* bits per sample */ },
+ // Change to mono:
+ {media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
+ media::CHANNEL_LAYOUT_MONO,
+ kDefaultSampleRate,
+ kDefaultBitsPerSample},
+ // Different sampling rate as well:
+ {media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
+ media::CHANNEL_LAYOUT_MONO,
+ 24000,
+ kDefaultBitsPerSample},
+};
+
+class AudioTrackRecorderTest : public TestWithParam<ATRTestParams> {
+ public:
+ // Initialize |first_params_| based on test parameters, and |second_params_|
+ // to always be the same thing.
+ AudioTrackRecorderTest()
+ : first_params_(GetParam().input_format,
+ GetParam().channel_layout,
+ GetParam().sample_rate,
+ GetParam().bits_per_sample,
+ kIgnoreFramesPerBuffer),
+ second_params_(kDefaultInputFormat,
+ media::CHANNEL_LAYOUT_STEREO,
+ kDefaultSampleRate,
+ kDefaultBitsPerSample,
+ kIgnoreFramesPerBuffer),
+ first_source_(first_params_.channels(), /* # channels */
+ 440, /* frequency */
+ first_params_.sample_rate() /* sample rate */ ),
+ second_source_(second_params_.channels(),
+ 440,
+ second_params_.sample_rate()),
+ decoder_memory_(
minyue 2015/11/16 11:57:32 I think the use of decoder_memory_ is not necessar
ajose 2015/11/16 22:29:47 Done.
+ new uint8[opus_decoder_get_size(first_params_.channels())]),
+ opus_decoder_(reinterpret_cast<OpusDecoder*>(decoder_memory_.get())),
+ max_frames_per_buffer_(
+ kOpusMaxBufferDurationMS * first_params_.sample_rate() / 1000),
+ buffer_(new float[max_frames_per_buffer_ * first_params_.channels()]) {
+ PrepareBlinkTrack();
+ audio_track_recorder_.reset(new AudioTrackRecorder(
+ blink_track_, base::Bind(&AudioTrackRecorderTest::OnEncodedAudio,
+ base::Unretained(this))));
+ EXPECT_EQ(opus_decoder_init(opus_decoder_, first_params_.sample_rate(),
+ first_params_.channels()), OPUS_OK);
+ }
+
+ ~AudioTrackRecorderTest() {
+ audio_track_recorder_.reset();
+ blink_track_.reset();
+ blink::WebHeap::collectAllGarbageForTesting();
+ }
+
+ scoped_ptr<media::AudioBus> GetFirstSourceAudioBus() {
+ scoped_ptr<media::AudioBus> bus(
+ media::AudioBus::Create(
+ first_params_.channels(),
+ first_params_.sample_rate() *
+ audio_track_recorder_->BufferDurationForTesting(
+ first_params_.sample_rate()) / 1000));
+ first_source_.OnMoreData(bus.get(), 0);
+ return bus.Pass();
+ }
+ scoped_ptr<media::AudioBus> GetSecondSourceAudioBus() {
+ scoped_ptr<media::AudioBus> bus(
+ media::AudioBus::Create(
+ second_params_.channels(),
+ second_params_.sample_rate() *
+ audio_track_recorder_->BufferDurationForTesting(
+ second_params_.sample_rate()) / 1000));
+ second_source_.OnMoreData(bus.get(), 0);
+ return bus.Pass();
+ }
+
+ MOCK_METHOD3(DoOnEncodedAudio,
+ void(const media::AudioParameters& params,
+ std::string encoded_data,
+ base::TimeTicks timestamp));
+
+ void OnEncodedAudio(const media::AudioParameters& params,
+ scoped_ptr<std::string> encoded_data,
+ base::TimeTicks timestamp) {
+ EXPECT_TRUE(!encoded_data->empty());
+
+ // The decoder has only been initialized to handle data matching
+ // first_params_.
+ if (params.Equals(first_params_)) {
minyue 2015/11/16 11:57:33 I hope you add a function to reset decoder so that
ajose 2015/11/16 22:29:47 Done.
+ EXPECT_EQ(first_params_.sample_rate() *
+ audio_track_recorder_->BufferDurationForTesting(
+ first_params_.sample_rate()) / 1000,
+ opus_decode_float(
+ opus_decoder_,
+ reinterpret_cast<uint8*>(
+ string_as_array(encoded_data.get())),
+ encoded_data->size(),
+ buffer_.get(),
+ max_frames_per_buffer_,
+ 0));
+ }
+
+ DoOnEncodedAudio(params, *encoded_data, timestamp);
+ }
+
+ const base::MessageLoop message_loop_;
+
+ // ATR and WebMediaStreamTrack for fooling it.
+ scoped_ptr<AudioTrackRecorder> audio_track_recorder_;
+ blink::WebMediaStreamTrack blink_track_;
+
+ // Two different sets of AudioParameters for testing re-init of ATR.
+ const media::AudioParameters first_params_;
+ const media::AudioParameters second_params_;
+
+ // AudioSources for creating AudioBuses.
+ media::SineWaveAudioSource first_source_;
+ media::SineWaveAudioSource second_source_;
+
+ // Decoder for verifying data was properly encoded.
+ // Note that this is only intended to decode audio data matching first_params_
+ const scoped_ptr<uint8[]> decoder_memory_;
+ OpusDecoder* const opus_decoder_;
+ const int max_frames_per_buffer_;
+ const scoped_ptr<float[]> buffer_;
+
+ private:
+ // Prepares a blink track of a given MediaStreamType and attaches the native
+ // track, which can be used to capture audio data and pass it to the producer.
+ // Adapted from media::WebRTCLocalAudioSourceProviderTest.
+ void PrepareBlinkTrack() {
+ MockMediaConstraintFactory constraint_factory;
+ scoped_refptr<WebRtcAudioCapturer> capturer(
+ WebRtcAudioCapturer::CreateCapturer(
+ -1, StreamDeviceInfo(),
+ constraint_factory.CreateWebMediaConstraints(), NULL, NULL));
+ scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter(
+ WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL));
+ scoped_ptr<WebRtcLocalAudioTrack> native_track(
+ new WebRtcLocalAudioTrack(adapter.get(), capturer, NULL));
+ blink::WebMediaStreamSource audio_source;
+ audio_source.initialize(base::UTF8ToUTF16("dummy_source_id"),
+ blink::WebMediaStreamSource::TypeAudio,
+ base::UTF8ToUTF16("dummy_source_name"),
+ false /* remote */, true /* readonly */);
+ blink_track_.initialize(blink::WebString::fromUTF8("audio_track"),
+ audio_source);
+ blink_track_.setExtraData(native_track.release());
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(AudioTrackRecorderTest);
+};
+
+TEST_P(AudioTrackRecorderTest, OnData) {
+ InSequence s;
+ base::RunLoop run_loop;
+ base::Closure quit_closure = run_loop.QuitClosure();
+
+ // Give ATR initial audio parameters.
+ audio_track_recorder_->OnSetFormat(first_params_);
+ // TODO(ajose): consider adding WillOnce(SaveArg...) and inspecting, as done
+ // in VTR unittests. http://crbug.com/548856
+ const base::TimeTicks time1 = base::TimeTicks::Now();
+ EXPECT_CALL(*this, DoOnEncodedAudio(_, _, time1)).Times(1);
+ audio_track_recorder_->OnData(*GetFirstSourceAudioBus(), time1);
+
+ // Send more audio.
+ const base::TimeTicks time2 = base::TimeTicks::Now();
+ EXPECT_CALL(*this, DoOnEncodedAudio(_, _, _)).Times(1);
+ audio_track_recorder_->OnData(*GetFirstSourceAudioBus(), time2);
+
+ // Give ATR new audio parameters.
+ audio_track_recorder_->OnSetFormat(second_params_);
minyue 2015/11/16 11:57:32 After this, call a decoder reset that I proposed.
ajose 2015/11/16 22:29:47 Done.
+ // Send audio with different params.
+ const base::TimeTicks time3 = base::TimeTicks::Now();
+ EXPECT_CALL(*this, DoOnEncodedAudio(_, _, _))
+ .Times(1)
+ .WillOnce(RunClosure(quit_closure));
+ audio_track_recorder_->OnData(*GetSecondSourceAudioBus(), time3);
+
+ run_loop.Run();
+ Mock::VerifyAndClearExpectations(this);
+}
+
+INSTANTIATE_TEST_CASE_P(,
+ AudioTrackRecorderTest,
+ ValuesIn(kATRTestParams));
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698