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

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

Powered by Google App Engine
This is Rietveld 408576698