| 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 nullptr; |
| 71 } |
| 72 |
| 73 size_t wav_file_length = wav_file.GetLength(); |
| 74 |
| 75 char* wav_file_data = new char[wav_file_length]; |
| 76 size_t read_bytes = wav_file.Read(0, wav_file_data, wav_file_length); |
| 77 if (read_bytes != wav_file_length) { |
| 78 CHECK(false) << "Failed to read all bytes of " << wav_filename.value(); |
| 79 return nullptr; |
| 80 } |
| 81 *file_length = wav_file_length; |
| 82 return scoped_ptr<char[]>(wav_file_data); |
| 83 } |
| 84 |
| 85 // Opens |wav_filename|, reads it and loads it as a Wav file. This function will |
| 86 // bluntly trigger CHECKs if the file doesn't have the sampling frequency, bits |
| 87 // per sample or number of channels as specified in |expected_params|. We also |
| 88 // trigger CHECKs if we can't read the file or if it's malformed. |
| 89 scoped_ptr<media::WavAudioHandler> CreateWavAudioHandler( |
| 90 const base::FilePath& wav_filename, const char* wav_file_data, |
| 91 size_t wav_file_length, const AudioParameters& expected_params) { |
| 92 base::StringPiece wav_data(wav_file_data, wav_file_length); |
| 93 scoped_ptr<media::WavAudioHandler> wav_audio_handler( |
| 94 new media::WavAudioHandler(wav_data)); |
| 95 |
| 96 // Ensure the input file matches what the audio bus wants, otherwise bail out. |
| 97 CHECK_EQ(wav_audio_handler->params().channels(), |
| 98 expected_params.channels()) |
| 99 << "Failed to read " << wav_filename.value() << " to fake device."; |
| 100 CHECK_EQ(wav_audio_handler->params().sample_rate(), |
| 101 expected_params.sample_rate()) |
| 102 << "Failed to read " << wav_filename.value() << " to fake device."; |
| 103 CHECK_EQ(wav_audio_handler->params().bits_per_sample(), |
| 104 expected_params.bits_per_sample()) |
| 105 << "Failed to read " << wav_filename.value() << " to fake device."; |
| 106 |
| 107 return wav_audio_handler.Pass(); |
| 108 } |
| 109 |
| 56 static base::LazyInstance<BeepContext> g_beep_context = | 110 static base::LazyInstance<BeepContext> g_beep_context = |
| 57 LAZY_INSTANCE_INITIALIZER; | 111 LAZY_INSTANCE_INITIALIZER; |
| 58 | 112 |
| 59 } // namespace | 113 } // namespace |
| 60 | 114 |
| 61 AudioInputStream* FakeAudioInputStream::MakeFakeStream( | 115 AudioInputStream* FakeAudioInputStream::MakeFakeStream( |
| 62 AudioManagerBase* manager, | 116 AudioManagerBase* manager, |
| 63 const AudioParameters& params) { | 117 const AudioParameters& params) { |
| 64 return new FakeAudioInputStream(manager, params); | 118 return new FakeAudioInputStream(manager, params); |
| 65 } | 119 } |
| 66 | 120 |
| 67 FakeAudioInputStream::FakeAudioInputStream(AudioManagerBase* manager, | 121 FakeAudioInputStream::FakeAudioInputStream(AudioManagerBase* manager, |
| 68 const AudioParameters& params) | 122 const AudioParameters& params) |
| 69 : audio_manager_(manager), | 123 : audio_manager_(manager), |
| 70 callback_(NULL), | 124 callback_(NULL), |
| 71 buffer_size_((params.channels() * params.bits_per_sample() * | 125 buffer_size_((params.channels() * params.bits_per_sample() * |
| 72 params.frames_per_buffer()) / | 126 params.frames_per_buffer()) / |
| 73 8), | 127 8), |
| 74 params_(params), | 128 params_(params), |
| 75 task_runner_(manager->GetTaskRunner()), | 129 task_runner_(manager->GetTaskRunner()), |
| 76 callback_interval_(base::TimeDelta::FromMilliseconds( | 130 callback_interval_(base::TimeDelta::FromMilliseconds( |
| 77 (params.frames_per_buffer() * 1000) / params.sample_rate())), | 131 (params.frames_per_buffer() * 1000) / params.sample_rate())), |
| 78 beep_duration_in_buffers_(kBeepDurationMilliseconds * | 132 beep_duration_in_buffers_(kBeepDurationMilliseconds * |
| 79 params.sample_rate() / | 133 params.sample_rate() / |
| 80 params.frames_per_buffer() / | 134 params.frames_per_buffer() / |
| 81 1000), | 135 1000), |
| 82 beep_generated_in_buffers_(0), | 136 beep_generated_in_buffers_(0), |
| 83 beep_period_in_frames_(params.sample_rate() / kBeepFrequency), | 137 beep_period_in_frames_(params.sample_rate() / kBeepFrequency), |
| 84 frames_elapsed_(0), | |
| 85 audio_bus_(AudioBus::Create(params)), | 138 audio_bus_(AudioBus::Create(params)), |
| 139 wav_file_read_pos_(0), |
| 86 weak_factory_(this) { | 140 weak_factory_(this) { |
| 87 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); | 141 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
| 88 } | 142 } |
| 89 | 143 |
| 90 FakeAudioInputStream::~FakeAudioInputStream() {} | 144 FakeAudioInputStream::~FakeAudioInputStream() {} |
| 91 | 145 |
| 92 bool FakeAudioInputStream::Open() { | 146 bool FakeAudioInputStream::Open() { |
| 93 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); | 147 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
| 94 buffer_.reset(new uint8[buffer_size_]); | 148 buffer_.reset(new char[buffer_size_]); |
| 95 memset(buffer_.get(), 0, buffer_size_); | 149 memset(buffer_.get(), 0, buffer_size_); |
| 96 audio_bus_->Zero(); | 150 audio_bus_->Zero(); |
| 151 |
| 152 if (CommandLine::ForCurrentProcess()->HasSwitch( |
| 153 switches::kUseFileForFakeAudioCapture)) { |
| 154 OpenInFileMode(CommandLine::ForCurrentProcess()->GetSwitchValuePath( |
| 155 switches::kUseFileForFakeAudioCapture)); |
| 156 } |
| 157 |
| 97 return true; | 158 return true; |
| 98 } | 159 } |
| 99 | 160 |
| 100 void FakeAudioInputStream::Start(AudioInputCallback* callback) { | 161 void FakeAudioInputStream::Start(AudioInputCallback* callback) { |
| 101 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); | 162 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
| 102 DCHECK(!callback_); | 163 DCHECK(!callback_); |
| 103 callback_ = callback; | 164 callback_ = callback; |
| 104 last_callback_time_ = TimeTicks::Now(); | 165 last_callback_time_ = TimeTicks::Now(); |
| 166 |
| 105 task_runner_->PostDelayedTask( | 167 task_runner_->PostDelayedTask( |
| 106 FROM_HERE, | 168 FROM_HERE, |
| 107 base::Bind(&FakeAudioInputStream::DoCallback, weak_factory_.GetWeakPtr()), | 169 base::Bind(&FakeAudioInputStream::DoCallback, weak_factory_.GetWeakPtr()), |
| 108 callback_interval_); | 170 callback_interval_); |
| 109 } | 171 } |
| 110 | 172 |
| 111 void FakeAudioInputStream::DoCallback() { | 173 void FakeAudioInputStream::DoCallback() { |
| 112 DCHECK(callback_); | 174 DCHECK(callback_); |
| 113 | 175 |
| 114 const TimeTicks now = TimeTicks::Now(); | 176 const TimeTicks now = TimeTicks::Now(); |
| 115 base::TimeDelta next_callback_time = | 177 base::TimeDelta next_callback_time = |
| 116 last_callback_time_ + callback_interval_ * 2 - now; | 178 last_callback_time_ + callback_interval_ * 2 - now; |
| 117 | 179 |
| 118 // If we are falling behind, try to catch up as much as we can in the next | 180 // If we are falling behind, try to catch up as much as we can in the next |
| 119 // callback. | 181 // callback. |
| 120 if (next_callback_time < base::TimeDelta()) | 182 if (next_callback_time < base::TimeDelta()) |
| 121 next_callback_time = base::TimeDelta(); | 183 next_callback_time = base::TimeDelta(); |
| 122 | 184 |
| 123 // Accumulate the time from the last beep. | 185 if (PlayingFromFile()) { |
| 124 interval_from_last_beep_ += now - last_callback_time_; | 186 PlayFileLooping(); |
| 187 } else { |
| 188 PlayBeep(); |
| 189 } |
| 125 | 190 |
| 126 last_callback_time_ = now; | 191 last_callback_time_ = now; |
| 127 | 192 |
| 193 task_runner_->PostDelayedTask( |
| 194 FROM_HERE, |
| 195 base::Bind(&FakeAudioInputStream::DoCallback, weak_factory_.GetWeakPtr()), |
| 196 next_callback_time); |
| 197 } |
| 198 |
| 199 void FakeAudioInputStream::OpenInFileMode(const base::FilePath& wav_filename) { |
| 200 CHECK(!wav_filename.empty()) |
| 201 << "You must pass the file to use as argument to --" |
| 202 << switches::kUseFileForFakeAudioCapture << "."; |
| 203 |
| 204 // Read the file, and put its data in a scoped_ptr so it gets deleted later. |
| 205 size_t file_length = 0; |
| 206 wav_file_data_ = ReadWavFile(wav_filename, &file_length); |
| 207 wav_audio_handler_ = CreateWavAudioHandler( |
| 208 wav_filename, wav_file_data_.get(), file_length, params_); |
| 209 } |
| 210 |
| 211 bool FakeAudioInputStream::PlayingFromFile() { |
| 212 return wav_audio_handler_.get() != nullptr; |
| 213 } |
| 214 |
| 215 void FakeAudioInputStream::PlayFileLooping() { |
| 216 size_t bytes_written; |
| 217 wav_audio_handler_->CopyTo(audio_bus_.get(), wav_file_read_pos_, |
| 218 &bytes_written); |
| 219 wav_file_read_pos_ += bytes_written; |
| 220 if (wav_audio_handler_->AtEnd(wav_file_read_pos_)) |
| 221 wav_file_read_pos_ = 0; |
| 222 callback_->OnData(this, audio_bus_.get(), buffer_size_, 1.0); |
| 223 } |
| 224 |
| 225 void FakeAudioInputStream::PlayBeep() { |
| 226 // Accumulate the time from the last beep. |
| 227 interval_from_last_beep_ += TimeTicks::Now() - last_callback_time_; |
| 228 |
| 128 memset(buffer_.get(), 0, buffer_size_); | 229 memset(buffer_.get(), 0, buffer_size_); |
| 129 | |
| 130 bool should_beep = false; | 230 bool should_beep = false; |
| 131 { | 231 { |
| 132 BeepContext* beep_context = g_beep_context.Pointer(); | 232 BeepContext* beep_context = g_beep_context.Pointer(); |
| 133 if (beep_context->automatic_beep()) { | 233 if (beep_context->automatic_beep()) { |
| 134 base::TimeDelta delta = interval_from_last_beep_ - | 234 base::TimeDelta delta = interval_from_last_beep_ - |
| 135 TimeDelta::FromMilliseconds(kAutomaticBeepIntervalInMs); | 235 TimeDelta::FromMilliseconds(kAutomaticBeepIntervalInMs); |
| 136 if (delta > base::TimeDelta()) { | 236 if (delta > base::TimeDelta()) { |
| 137 should_beep = true; | 237 should_beep = true; |
| 138 interval_from_last_beep_ = delta; | 238 interval_from_last_beep_ = delta; |
| 139 } | 239 } |
| (...skipping 23 matching lines...) Expand all Loading... |
| 163 } | 263 } |
| 164 | 264 |
| 165 ++beep_generated_in_buffers_; | 265 ++beep_generated_in_buffers_; |
| 166 if (beep_generated_in_buffers_ >= beep_duration_in_buffers_) | 266 if (beep_generated_in_buffers_ >= beep_duration_in_buffers_) |
| 167 beep_generated_in_buffers_ = 0; | 267 beep_generated_in_buffers_ = 0; |
| 168 } | 268 } |
| 169 | 269 |
| 170 audio_bus_->FromInterleaved( | 270 audio_bus_->FromInterleaved( |
| 171 buffer_.get(), audio_bus_->frames(), params_.bits_per_sample() / 8); | 271 buffer_.get(), audio_bus_->frames(), params_.bits_per_sample() / 8); |
| 172 callback_->OnData(this, audio_bus_.get(), buffer_size_, 1.0); | 272 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 } | 273 } |
| 180 | 274 |
| 181 void FakeAudioInputStream::Stop() { | 275 void FakeAudioInputStream::Stop() { |
| 182 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); | 276 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
| 183 weak_factory_.InvalidateWeakPtrs(); | 277 weak_factory_.InvalidateWeakPtrs(); |
| 184 callback_ = NULL; | 278 callback_ = NULL; |
| 185 } | 279 } |
| 186 | 280 |
| 187 void FakeAudioInputStream::Close() { | 281 void FakeAudioInputStream::Close() { |
| 188 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); | 282 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
| (...skipping 25 matching lines...) Expand all Loading... |
| 214 return true; | 308 return true; |
| 215 } | 309 } |
| 216 | 310 |
| 217 // static | 311 // static |
| 218 void FakeAudioInputStream::BeepOnce() { | 312 void FakeAudioInputStream::BeepOnce() { |
| 219 BeepContext* beep_context = g_beep_context.Pointer(); | 313 BeepContext* beep_context = g_beep_context.Pointer(); |
| 220 beep_context->SetBeepOnce(true); | 314 beep_context->SetBeepOnce(true); |
| 221 } | 315 } |
| 222 | 316 |
| 223 } // namespace media | 317 } // namespace media |
| OLD | NEW |