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

Side by Side Diff: media/audio/fake_audio_input_stream.cc

Issue 734993002: Makes the WebRTC fake device capable of playing audio from a file. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698