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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/renderer/media/audio_track_recorder.h"
6
7 #include "base/run_loop.h"
8 #include "base/stl_util.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "content/renderer/media/media_stream_audio_source.h"
11 #include "content/renderer/media/mock_media_constraint_factory.h"
12 #include "content/renderer/media/webrtc/webrtc_local_audio_track_adapter.h"
13 #include "content/renderer/media/webrtc_local_audio_track.h"
14 #include "media/audio/simple_sources.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 #include "third_party/WebKit/public/web/WebHeap.h"
18 #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
19
20 using ::testing::_;
21 using ::testing::DoAll;
22 using ::testing::InSequence;
23 using ::testing::Mock;
24 using ::testing::Return;
25 using ::testing::SaveArg;
26 using ::testing::TestWithParam;
27 using ::testing::ValuesIn;
28
29 namespace {
30
31 // Input audio format.
32 const media::AudioParameters::Format kDefaultInputFormat =
33 media::AudioParameters::AUDIO_PCM_LOW_LATENCY;
34 const int kDefaultBitsPerSample = 16;
35 const int kDefaultSampleRate = 48000;
36 // The |frames_per_buffer| field of AudioParameters is not used by ATR.
37 const int kIgnoreFramesPerBuffer = 1;
38 const int kOpusMaxBufferDurationMS = 120;
39
40 } // namespace
41
42 namespace content {
43
44 ACTION_P(RunClosure, closure) {
45 closure.Run();
46 }
47
48 struct ATRTestParams {
49 const media::AudioParameters::Format input_format;
50 const media::ChannelLayout channel_layout;
51 const int sample_rate;
52 const int bits_per_sample;
53 };
54
55 const ATRTestParams kATRTestParams[] = {
56 // Equivalent to default settings:
57 {media::AudioParameters::AUDIO_PCM_LOW_LATENCY, /* input format */
58 media::CHANNEL_LAYOUT_STEREO, /* channel layout */
59 kDefaultSampleRate, /* sample rate */
60 kDefaultBitsPerSample /* bits per sample */ },
61 // Change to mono:
62 {media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
63 media::CHANNEL_LAYOUT_MONO,
64 kDefaultSampleRate,
65 kDefaultBitsPerSample},
66 // Different sampling rate as well:
67 {media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
68 media::CHANNEL_LAYOUT_MONO,
69 24000,
70 kDefaultBitsPerSample},
71 };
72
73 class AudioTrackRecorderTest : public TestWithParam<ATRTestParams> {
74 public:
75 // Initialize |first_params_| based on test parameters, and |second_params_|
76 // to always be the same thing.
77 AudioTrackRecorderTest()
78 : first_params_(GetParam().input_format,
79 GetParam().channel_layout,
80 GetParam().sample_rate,
81 GetParam().bits_per_sample,
82 kIgnoreFramesPerBuffer),
83 second_params_(kDefaultInputFormat,
84 media::CHANNEL_LAYOUT_STEREO,
85 kDefaultSampleRate,
86 kDefaultBitsPerSample,
87 kIgnoreFramesPerBuffer),
88 first_source_(first_params_.channels(), /* # channels */
89 440, /* frequency */
90 first_params_.sample_rate() /* sample rate */ ),
91 second_source_(second_params_.channels(),
92 440,
93 second_params_.sample_rate()),
94 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.
95 new uint8[opus_decoder_get_size(first_params_.channels())]),
96 opus_decoder_(reinterpret_cast<OpusDecoder*>(decoder_memory_.get())),
97 max_frames_per_buffer_(
98 kOpusMaxBufferDurationMS * first_params_.sample_rate() / 1000),
99 buffer_(new float[max_frames_per_buffer_ * first_params_.channels()]) {
100 PrepareBlinkTrack();
101 audio_track_recorder_.reset(new AudioTrackRecorder(
102 blink_track_, base::Bind(&AudioTrackRecorderTest::OnEncodedAudio,
103 base::Unretained(this))));
104 EXPECT_EQ(opus_decoder_init(opus_decoder_, first_params_.sample_rate(),
105 first_params_.channels()), OPUS_OK);
106 }
107
108 ~AudioTrackRecorderTest() {
109 audio_track_recorder_.reset();
110 blink_track_.reset();
111 blink::WebHeap::collectAllGarbageForTesting();
112 }
113
114 scoped_ptr<media::AudioBus> GetFirstSourceAudioBus() {
115 scoped_ptr<media::AudioBus> bus(
116 media::AudioBus::Create(
117 first_params_.channels(),
118 first_params_.sample_rate() *
119 audio_track_recorder_->BufferDurationForTesting(
120 first_params_.sample_rate()) / 1000));
121 first_source_.OnMoreData(bus.get(), 0);
122 return bus.Pass();
123 }
124 scoped_ptr<media::AudioBus> GetSecondSourceAudioBus() {
125 scoped_ptr<media::AudioBus> bus(
126 media::AudioBus::Create(
127 second_params_.channels(),
128 second_params_.sample_rate() *
129 audio_track_recorder_->BufferDurationForTesting(
130 second_params_.sample_rate()) / 1000));
131 second_source_.OnMoreData(bus.get(), 0);
132 return bus.Pass();
133 }
134
135 MOCK_METHOD3(DoOnEncodedAudio,
136 void(const media::AudioParameters& params,
137 std::string encoded_data,
138 base::TimeTicks timestamp));
139
140 void OnEncodedAudio(const media::AudioParameters& params,
141 scoped_ptr<std::string> encoded_data,
142 base::TimeTicks timestamp) {
143 EXPECT_TRUE(!encoded_data->empty());
144
145 // The decoder has only been initialized to handle data matching
146 // first_params_.
147 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.
148 EXPECT_EQ(first_params_.sample_rate() *
149 audio_track_recorder_->BufferDurationForTesting(
150 first_params_.sample_rate()) / 1000,
151 opus_decode_float(
152 opus_decoder_,
153 reinterpret_cast<uint8*>(
154 string_as_array(encoded_data.get())),
155 encoded_data->size(),
156 buffer_.get(),
157 max_frames_per_buffer_,
158 0));
159 }
160
161 DoOnEncodedAudio(params, *encoded_data, timestamp);
162 }
163
164 const base::MessageLoop message_loop_;
165
166 // ATR and WebMediaStreamTrack for fooling it.
167 scoped_ptr<AudioTrackRecorder> audio_track_recorder_;
168 blink::WebMediaStreamTrack blink_track_;
169
170 // Two different sets of AudioParameters for testing re-init of ATR.
171 const media::AudioParameters first_params_;
172 const media::AudioParameters second_params_;
173
174 // AudioSources for creating AudioBuses.
175 media::SineWaveAudioSource first_source_;
176 media::SineWaveAudioSource second_source_;
177
178 // Decoder for verifying data was properly encoded.
179 // Note that this is only intended to decode audio data matching first_params_
180 const scoped_ptr<uint8[]> decoder_memory_;
181 OpusDecoder* const opus_decoder_;
182 const int max_frames_per_buffer_;
183 const scoped_ptr<float[]> buffer_;
184
185 private:
186 // Prepares a blink track of a given MediaStreamType and attaches the native
187 // track, which can be used to capture audio data and pass it to the producer.
188 // Adapted from media::WebRTCLocalAudioSourceProviderTest.
189 void PrepareBlinkTrack() {
190 MockMediaConstraintFactory constraint_factory;
191 scoped_refptr<WebRtcAudioCapturer> capturer(
192 WebRtcAudioCapturer::CreateCapturer(
193 -1, StreamDeviceInfo(),
194 constraint_factory.CreateWebMediaConstraints(), NULL, NULL));
195 scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter(
196 WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL));
197 scoped_ptr<WebRtcLocalAudioTrack> native_track(
198 new WebRtcLocalAudioTrack(adapter.get(), capturer, NULL));
199 blink::WebMediaStreamSource audio_source;
200 audio_source.initialize(base::UTF8ToUTF16("dummy_source_id"),
201 blink::WebMediaStreamSource::TypeAudio,
202 base::UTF8ToUTF16("dummy_source_name"),
203 false /* remote */, true /* readonly */);
204 blink_track_.initialize(blink::WebString::fromUTF8("audio_track"),
205 audio_source);
206 blink_track_.setExtraData(native_track.release());
207 }
208
209 DISALLOW_COPY_AND_ASSIGN(AudioTrackRecorderTest);
210 };
211
212 TEST_P(AudioTrackRecorderTest, OnData) {
213 InSequence s;
214 base::RunLoop run_loop;
215 base::Closure quit_closure = run_loop.QuitClosure();
216
217 // Give ATR initial audio parameters.
218 audio_track_recorder_->OnSetFormat(first_params_);
219 // TODO(ajose): consider adding WillOnce(SaveArg...) and inspecting, as done
220 // in VTR unittests. http://crbug.com/548856
221 const base::TimeTicks time1 = base::TimeTicks::Now();
222 EXPECT_CALL(*this, DoOnEncodedAudio(_, _, time1)).Times(1);
223 audio_track_recorder_->OnData(*GetFirstSourceAudioBus(), time1);
224
225 // Send more audio.
226 const base::TimeTicks time2 = base::TimeTicks::Now();
227 EXPECT_CALL(*this, DoOnEncodedAudio(_, _, _)).Times(1);
228 audio_track_recorder_->OnData(*GetFirstSourceAudioBus(), time2);
229
230 // Give ATR new audio parameters.
231 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.
232 // Send audio with different params.
233 const base::TimeTicks time3 = base::TimeTicks::Now();
234 EXPECT_CALL(*this, DoOnEncodedAudio(_, _, _))
235 .Times(1)
236 .WillOnce(RunClosure(quit_closure));
237 audio_track_recorder_->OnData(*GetSecondSourceAudioBus(), time3);
238
239 run_loop.Run();
240 Mock::VerifyAndClearExpectations(this);
241 }
242
243 INSTANTIATE_TEST_CASE_P(,
244 AudioTrackRecorderTest,
245 ValuesIn(kATRTestParams));
246
247 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698