Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 | |
| OLD | NEW |