Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/audio/fake_audio_input_stream.h" | 5 #include "media/audio/fake_audio_input_stream.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/command_line.h" | |
| 9 #include "base/files/file.h" | |
| 8 #include "base/lazy_instance.h" | 10 #include "base/lazy_instance.h" |
| 9 #include "media/audio/audio_manager_base.h" | 11 #include "media/audio/audio_manager_base.h" |
| 10 #include "media/base/audio_bus.h" | 12 #include "media/base/audio_bus.h" |
| 13 #include "media/base/media_switches.h" | |
| 11 | 14 |
| 12 using base::TimeTicks; | 15 using base::TimeTicks; |
| 13 using base::TimeDelta; | 16 using base::TimeDelta; |
| 14 | 17 |
| 15 namespace media { | 18 namespace media { |
| 16 | 19 |
| 17 namespace { | 20 namespace { |
| 18 | 21 |
| 19 // These values are based on experiments for local-to-local | 22 // These values are based on experiments for local-to-local |
| 20 // PeerConnection to demonstrate audio/video synchronization. | 23 // PeerConnection to demonstrate audio/video synchronization. |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 46 base::AutoLock auto_lock(lock_); | 49 base::AutoLock auto_lock(lock_); |
| 47 return automatic_beep_; | 50 return automatic_beep_; |
| 48 } | 51 } |
| 49 | 52 |
| 50 private: | 53 private: |
| 51 mutable base::Lock lock_; | 54 mutable base::Lock lock_; |
| 52 bool beep_once_; | 55 bool beep_once_; |
| 53 bool automatic_beep_; | 56 bool automatic_beep_; |
| 54 }; | 57 }; |
| 55 | 58 |
| 59 // Opens |wav_filename|, reads it and loads it as a wav file. This function will | |
| 60 // bluntly trigger CHECKs if we can't read the file or if it's malformed. The | |
| 61 // caller takes ownership of the returned data. The size of the data is stored | |
| 62 // in |read_length|. | |
| 63 scoped_ptr<char[]> ReadWavFile(const base::FilePath& wav_filename, | |
| 64 size_t* file_length) { | |
| 65 base::File wav_file( | |
| 66 wav_filename, base::File::FLAG_OPEN | base::File::FLAG_READ); | |
| 67 if (!wav_file.IsValid()) { | |
| 68 CHECK(false) << "Failed to read " << wav_filename.value() << " as input to " | |
| 69 << "the fake device."; | |
| 70 return scoped_ptr<char[]>(); | |
|
DaleCurtis
2014/11/18 21:59:17
Does nullptr work?
phoglund_chromium
2014/11/19 09:56:09
Done.
| |
| 71 } | |
| 72 | |
| 73 size_t wav_file_length = wav_file.GetLength(); | |
| 74 | |
| 75 CHECK(wav_file_length < 16 * 1024 * 1024) << "Can't use fake file input " | |
|
DaleCurtis
2014/11/18 21:59:17
Why?
phoglund_chromium
2014/11/19 09:56:09
I reckoned I should put some reasonable limit here
| |
| 76 << "stream with wav files > 16 MiB."; | |
| 77 | |
| 78 char* wav_file_data = new char[wav_file_length]; | |
| 79 size_t read_bytes = wav_file.Read(0, wav_file_data, wav_file_length); | |
| 80 if (read_bytes != wav_file_length) { | |
| 81 CHECK(false) << "Failed to read all bytes of " << wav_filename.value(); | |
| 82 return scoped_ptr<char[]>(); | |
|
DaleCurtis
2014/11/18 21:59:17
Ditto?
phoglund_chromium
2014/11/19 09:56:09
Done.
| |
| 83 } | |
| 84 *file_length = wav_file_length; | |
| 85 return scoped_ptr<char[]>(wav_file_data); | |
| 86 } | |
| 87 | |
| 88 // Opens |wav_filename|, reads it and loads it as a Wav file. This function will | |
| 89 // bluntly trigger CHECKs if the file doesn't have the sampling frequency, bits | |
| 90 // per sample or number of channels as specified in |expected_params|. We also | |
| 91 // trigger CHECKs if we can't read the file or if it's malformed. | |
| 92 scoped_ptr<media::WavAudioHandler> CreateWavAudioHandler( | |
| 93 const base::FilePath& wav_filename, const char* wav_file_data, | |
| 94 size_t wav_file_length, const AudioParameters& expected_params) { | |
| 95 base::StringPiece wav_data(wav_file_data, wav_file_length); | |
| 96 scoped_ptr<media::WavAudioHandler> wav_audio_handler( | |
| 97 new media::WavAudioHandler(wav_data)); | |
| 98 | |
| 99 // Ensure the input file matches what the audio bus wants, otherwise bail out. | |
| 100 CHECK_EQ(wav_audio_handler->params().channels(), | |
| 101 expected_params.channels()) | |
| 102 << "Failed to read " << wav_filename.value() << " to fake device."; | |
| 103 CHECK_EQ(wav_audio_handler->params().sample_rate(), | |
| 104 expected_params.sample_rate()) | |
| 105 << "Failed to read " << wav_filename.value() << " to fake device."; | |
| 106 CHECK_EQ(wav_audio_handler->params().bits_per_sample(), | |
| 107 expected_params.bits_per_sample()) | |
| 108 << "Failed to read " << wav_filename.value() << " to fake device."; | |
| 109 | |
| 110 return wav_audio_handler.Pass(); | |
| 111 } | |
| 112 | |
| 56 static base::LazyInstance<BeepContext> g_beep_context = | 113 static base::LazyInstance<BeepContext> g_beep_context = |
| 57 LAZY_INSTANCE_INITIALIZER; | 114 LAZY_INSTANCE_INITIALIZER; |
| 58 | 115 |
| 59 } // namespace | 116 } // namespace |
| 60 | 117 |
| 61 AudioInputStream* FakeAudioInputStream::MakeFakeStream( | 118 AudioInputStream* FakeAudioInputStream::MakeFakeStream( |
| 62 AudioManagerBase* manager, | 119 AudioManagerBase* manager, |
| 63 const AudioParameters& params) { | 120 const AudioParameters& params) { |
| 64 return new FakeAudioInputStream(manager, params); | 121 return new FakeAudioInputStream(manager, params); |
| 65 } | 122 } |
| 66 | 123 |
| 67 FakeAudioInputStream::FakeAudioInputStream(AudioManagerBase* manager, | 124 FakeAudioInputStream::FakeAudioInputStream(AudioManagerBase* manager, |
| 68 const AudioParameters& params) | 125 const AudioParameters& params) |
| 69 : audio_manager_(manager), | 126 : audio_manager_(manager), |
| 70 callback_(NULL), | 127 callback_(NULL), |
| 71 buffer_size_((params.channels() * params.bits_per_sample() * | 128 buffer_size_((params.channels() * params.bits_per_sample() * |
| 72 params.frames_per_buffer()) / | 129 params.frames_per_buffer()) / |
| 73 8), | 130 8), |
| 74 params_(params), | 131 params_(params), |
| 75 task_runner_(manager->GetTaskRunner()), | 132 task_runner_(manager->GetTaskRunner()), |
| 76 callback_interval_(base::TimeDelta::FromMilliseconds( | 133 callback_interval_(base::TimeDelta::FromMilliseconds( |
| 77 (params.frames_per_buffer() * 1000) / params.sample_rate())), | 134 (params.frames_per_buffer() * 1000) / params.sample_rate())), |
| 78 beep_duration_in_buffers_(kBeepDurationMilliseconds * | 135 beep_duration_in_buffers_(kBeepDurationMilliseconds * |
| 79 params.sample_rate() / | 136 params.sample_rate() / |
| 80 params.frames_per_buffer() / | 137 params.frames_per_buffer() / |
| 81 1000), | 138 1000), |
| 82 beep_generated_in_buffers_(0), | 139 beep_generated_in_buffers_(0), |
| 83 beep_period_in_frames_(params.sample_rate() / kBeepFrequency), | 140 beep_period_in_frames_(params.sample_rate() / kBeepFrequency), |
| 84 frames_elapsed_(0), | |
| 85 audio_bus_(AudioBus::Create(params)), | 141 audio_bus_(AudioBus::Create(params)), |
| 142 wav_file_read_pos_(0), | |
| 86 weak_factory_(this) { | 143 weak_factory_(this) { |
| 87 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); | 144 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
| 88 } | 145 } |
| 89 | 146 |
| 90 FakeAudioInputStream::~FakeAudioInputStream() {} | 147 FakeAudioInputStream::~FakeAudioInputStream() {} |
| 91 | 148 |
| 92 bool FakeAudioInputStream::Open() { | 149 bool FakeAudioInputStream::Open() { |
| 93 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); | 150 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
| 94 buffer_.reset(new uint8[buffer_size_]); | 151 buffer_.reset(new char[buffer_size_]); |
| 95 memset(buffer_.get(), 0, buffer_size_); | 152 memset(buffer_.get(), 0, buffer_size_); |
| 96 audio_bus_->Zero(); | 153 audio_bus_->Zero(); |
| 154 | |
| 155 if (CommandLine::ForCurrentProcess()->HasSwitch( | |
| 156 switches::kUseFileForFakeAudioCapture)) { | |
| 157 OpenInFileMode(CommandLine::ForCurrentProcess()->GetSwitchValuePath( | |
| 158 switches::kUseFileForFakeAudioCapture)); | |
| 159 } | |
| 160 | |
| 97 return true; | 161 return true; |
| 98 } | 162 } |
| 99 | 163 |
| 100 void FakeAudioInputStream::Start(AudioInputCallback* callback) { | 164 void FakeAudioInputStream::Start(AudioInputCallback* callback) { |
| 101 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); | 165 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
| 102 DCHECK(!callback_); | 166 DCHECK(!callback_); |
| 103 callback_ = callback; | 167 callback_ = callback; |
| 104 last_callback_time_ = TimeTicks::Now(); | 168 last_callback_time_ = TimeTicks::Now(); |
| 169 | |
| 105 task_runner_->PostDelayedTask( | 170 task_runner_->PostDelayedTask( |
| 106 FROM_HERE, | 171 FROM_HERE, |
| 107 base::Bind(&FakeAudioInputStream::DoCallback, weak_factory_.GetWeakPtr()), | 172 base::Bind(&FakeAudioInputStream::DoCallback, weak_factory_.GetWeakPtr()), |
| 108 callback_interval_); | 173 callback_interval_); |
| 109 } | 174 } |
| 110 | 175 |
| 111 void FakeAudioInputStream::DoCallback() { | 176 void FakeAudioInputStream::DoCallback() { |
| 112 DCHECK(callback_); | 177 DCHECK(callback_); |
| 178 DCHECK(kAutomaticBeepIntervalInMs); // TODO(phoglund); | |
|
DaleCurtis
2014/11/18 21:59:17
Doesn't seem useful?
phoglund_chromium
2014/11/19 09:56:09
Oops, nope that should go away.
| |
| 113 | 179 |
| 114 const TimeTicks now = TimeTicks::Now(); | 180 const TimeTicks now = TimeTicks::Now(); |
| 115 base::TimeDelta next_callback_time = | 181 base::TimeDelta next_callback_time = |
| 116 last_callback_time_ + callback_interval_ * 2 - now; | 182 last_callback_time_ + callback_interval_ * 2 - now; |
| 117 | 183 |
| 118 // If we are falling behind, try to catch up as much as we can in the next | 184 // If we are falling behind, try to catch up as much as we can in the next |
| 119 // callback. | 185 // callback. |
| 120 if (next_callback_time < base::TimeDelta()) | 186 if (next_callback_time < base::TimeDelta()) |
| 121 next_callback_time = base::TimeDelta(); | 187 next_callback_time = base::TimeDelta(); |
| 122 | 188 |
| 123 // Accumulate the time from the last beep. | |
| 124 interval_from_last_beep_ += now - last_callback_time_; | |
| 125 | |
| 126 last_callback_time_ = now; | 189 last_callback_time_ = now; |
| 127 | 190 |
| 191 if (PlayingFromFile()) { | |
| 192 PlayFileLooping(); | |
| 193 } else { | |
| 194 PlayBeep(); | |
| 195 } | |
| 196 | |
| 197 task_runner_->PostDelayedTask( | |
| 198 FROM_HERE, | |
| 199 base::Bind(&FakeAudioInputStream::DoCallback, weak_factory_.GetWeakPtr()), | |
| 200 next_callback_time); | |
| 201 } | |
| 202 | |
| 203 void FakeAudioInputStream::OpenInFileMode(const base::FilePath& wav_filename) { | |
| 204 CHECK(!wav_filename.empty()) | |
| 205 << "You must pass the file to use as argument to --" | |
| 206 << switches::kUseFileForFakeAudioCapture << "."; | |
| 207 | |
| 208 // Read the file, and put its data in a scoped_ptr so it gets deleted later. | |
| 209 size_t file_length = 0; | |
| 210 wav_file_data_ = ReadWavFile(wav_filename, &file_length); | |
| 211 wav_audio_handler_ = CreateWavAudioHandler( | |
| 212 wav_filename, wav_file_data_.get(), file_length, params_); | |
| 213 } | |
| 214 | |
| 215 bool FakeAudioInputStream::PlayingFromFile() { | |
| 216 return wav_audio_handler_.get() != nullptr; | |
| 217 } | |
| 218 | |
| 219 void FakeAudioInputStream::PlayFileLooping() { | |
| 220 audio_bus_->FromInterleaved( | |
|
DaleCurtis
2014/11/18 21:59:17
Why? You're just overwriting this below.
phoglund_chromium
2014/11/19 09:56:09
Good catch, that shouldn't be there either.
| |
| 221 buffer_.get(), audio_bus_->frames(), params_.bits_per_sample() / 8); | |
| 222 | |
| 223 size_t bytes_written; | |
| 224 wav_audio_handler_->CopyTo(audio_bus_.get(), wav_file_read_pos_, | |
| 225 &bytes_written); | |
| 226 wav_file_read_pos_ += bytes_written; | |
|
DaleCurtis
2014/11/18 21:59:17
If bytes_written doesn't completely fill the Audio
phoglund_chromium
2014/11/19 09:56:09
Right; I think the wav_audio_handler deals with th
DaleCurtis
2014/11/20 19:13:47
Add a comment here that unfilled frames are zeroed
phoglund_chromium
2014/11/20 19:48:31
Done.
| |
| 227 if (wav_audio_handler_->AtEnd(wav_file_read_pos_)) | |
| 228 wav_file_read_pos_ = 0; | |
| 229 callback_->OnData(this, audio_bus_.get(), buffer_size_, 1.0); | |
| 230 } | |
| 231 | |
| 232 void FakeAudioInputStream::PlayBeep() { | |
| 233 // Accumulate the time from the last beep. | |
| 234 interval_from_last_beep_ += TimeTicks::Now() - last_callback_time_; | |
| 235 | |
| 128 memset(buffer_.get(), 0, buffer_size_); | 236 memset(buffer_.get(), 0, buffer_size_); |
| 129 | |
| 130 bool should_beep = false; | 237 bool should_beep = false; |
| 131 { | 238 { |
| 132 BeepContext* beep_context = g_beep_context.Pointer(); | 239 BeepContext* beep_context = g_beep_context.Pointer(); |
| 133 if (beep_context->automatic_beep()) { | 240 if (beep_context->automatic_beep()) { |
| 134 base::TimeDelta delta = interval_from_last_beep_ - | 241 base::TimeDelta delta = interval_from_last_beep_ - |
| 135 TimeDelta::FromMilliseconds(kAutomaticBeepIntervalInMs); | 242 TimeDelta::FromMilliseconds(kAutomaticBeepIntervalInMs); |
| 136 if (delta > base::TimeDelta()) { | 243 if (delta > base::TimeDelta()) { |
| 137 should_beep = true; | 244 should_beep = true; |
| 138 interval_from_last_beep_ = delta; | 245 interval_from_last_beep_ = delta; |
| 139 } | 246 } |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 163 } | 270 } |
| 164 | 271 |
| 165 ++beep_generated_in_buffers_; | 272 ++beep_generated_in_buffers_; |
| 166 if (beep_generated_in_buffers_ >= beep_duration_in_buffers_) | 273 if (beep_generated_in_buffers_ >= beep_duration_in_buffers_) |
| 167 beep_generated_in_buffers_ = 0; | 274 beep_generated_in_buffers_ = 0; |
| 168 } | 275 } |
| 169 | 276 |
| 170 audio_bus_->FromInterleaved( | 277 audio_bus_->FromInterleaved( |
| 171 buffer_.get(), audio_bus_->frames(), params_.bits_per_sample() / 8); | 278 buffer_.get(), audio_bus_->frames(), params_.bits_per_sample() / 8); |
| 172 callback_->OnData(this, audio_bus_.get(), buffer_size_, 1.0); | 279 callback_->OnData(this, audio_bus_.get(), buffer_size_, 1.0); |
| 173 frames_elapsed_ += params_.frames_per_buffer(); | |
| 174 | |
| 175 task_runner_->PostDelayedTask( | |
| 176 FROM_HERE, | |
| 177 base::Bind(&FakeAudioInputStream::DoCallback, weak_factory_.GetWeakPtr()), | |
| 178 next_callback_time); | |
| 179 } | 280 } |
| 180 | 281 |
| 181 void FakeAudioInputStream::Stop() { | 282 void FakeAudioInputStream::Stop() { |
| 182 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); | 283 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
| 183 weak_factory_.InvalidateWeakPtrs(); | 284 weak_factory_.InvalidateWeakPtrs(); |
| 184 callback_ = NULL; | 285 callback_ = NULL; |
| 185 } | 286 } |
| 186 | 287 |
| 187 void FakeAudioInputStream::Close() { | 288 void FakeAudioInputStream::Close() { |
| 188 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); | 289 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 214 return true; | 315 return true; |
| 215 } | 316 } |
| 216 | 317 |
| 217 // static | 318 // static |
| 218 void FakeAudioInputStream::BeepOnce() { | 319 void FakeAudioInputStream::BeepOnce() { |
| 219 BeepContext* beep_context = g_beep_context.Pointer(); | 320 BeepContext* beep_context = g_beep_context.Pointer(); |
| 220 beep_context->SetBeepOnce(true); | 321 beep_context->SetBeepOnce(true); |
| 221 } | 322 } |
| 222 | 323 |
| 223 } // namespace media | 324 } // namespace media |
| OLD | NEW |