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

Side by Side Diff: components/audio_modem/modem_impl.cc

Issue 2131993002: Delete the audio modem and copresence private APIs. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@eol
Patch Set: Sync again Created 4 years, 5 months 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 | « components/audio_modem/modem_impl.h ('k') | components/audio_modem/modem_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 "components/audio_modem/modem_impl.h"
6
7 #include <stdint.h>
8
9 #include <algorithm>
10 #include <limits>
11 #include <memory>
12 #include <vector>
13
14 #include "base/bind.h"
15 #include "base/bind_helpers.h"
16 #include "base/command_line.h"
17 #include "base/logging.h"
18 #include "base/memory/ptr_util.h"
19 #include "base/run_loop.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/strings/sys_string_conversions.h"
23 #include "base/time/time.h"
24 #include "build/build_config.h"
25 #include "components/audio_modem/audio_modem_switches.h"
26 #include "components/audio_modem/audio_player_impl.h"
27 #include "components/audio_modem/audio_recorder_impl.h"
28 #include "components/audio_modem/public/whispernet_client.h"
29 #include "content/public/browser/browser_thread.h"
30 #include "media/audio/audio_manager.h"
31 #include "media/audio/audio_manager_base.h"
32 #include "media/base/audio_bus.h"
33 #include "third_party/webrtc/common_audio/wav_file.h"
34
35 namespace audio_modem {
36
37 namespace {
38
39 const int kMaxSamples = 10000;
40 const int kTokenTimeoutMs = 2000;
41 const int kMonoChannelCount = 1;
42
43 // UrlSafe is defined as:
44 // '/' represented by a '_' and '+' represented by a '-'
45 // TODO(ckehoe): Move this to a central place.
46 std::string FromUrlSafe(std::string token) {
47 base::ReplaceChars(token, "-", "+", &token);
48 base::ReplaceChars(token, "_", "/", &token);
49 return token;
50 }
51 std::string ToUrlSafe(std::string token) {
52 base::ReplaceChars(token, "+", "-", &token);
53 base::ReplaceChars(token, "/", "_", &token);
54 return token;
55 }
56
57 // TODO(ckehoe): Move this to a central place.
58 std::string AudioTypeToString(AudioType audio_type) {
59 if (audio_type == AUDIBLE)
60 return "audible";
61 if (audio_type == INAUDIBLE)
62 return "inaudible";
63
64 NOTREACHED() << "Got unexpected token type " << audio_type;
65 return std::string();
66 }
67
68 bool ReadBooleanFlag(const std::string& flag, bool default_value) {
69 const std::string flag_value = base::ToLowerASCII(
70 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(flag));
71 if (flag_value == "true" || flag_value == "1")
72 return true;
73 if (flag_value == "false" || flag_value == "0")
74 return false;
75 LOG_IF(ERROR, !flag_value.empty())
76 << "Unrecognized value \"" << flag_value << " for flag "
77 << flag << ". Defaulting to " << default_value;
78 return default_value;
79 }
80
81 } // namespace
82
83
84 // Public functions.
85
86 ModemImpl::ModemImpl() : client_(nullptr), recorder_(nullptr) {
87 // TODO(rkc): Move all of these into initializer lists once it is allowed.
88 should_be_playing_[AUDIBLE] = false;
89 should_be_playing_[INAUDIBLE] = false;
90 should_be_recording_[AUDIBLE] = false;
91 should_be_recording_[INAUDIBLE] = false;
92
93 player_enabled_[AUDIBLE] = ReadBooleanFlag(
94 switches::kAudioModemEnableAudibleBroadcast, true);
95 player_enabled_[INAUDIBLE] = ReadBooleanFlag(
96 switches::kAudioModemEnableInaudibleBroadcast, true);
97 player_[AUDIBLE] = nullptr;
98 player_[INAUDIBLE] = nullptr;
99
100 samples_caches_.resize(2);
101 samples_caches_[AUDIBLE] = new SamplesMap(kMaxSamples);
102 samples_caches_[INAUDIBLE] = new SamplesMap(kMaxSamples);
103 }
104
105 void ModemImpl::Initialize(WhispernetClient* client,
106 const TokensCallback& tokens_cb) {
107 DCHECK(client);
108 client_ = client;
109 tokens_cb_ = tokens_cb;
110
111 // These will be unregistered on destruction, so unretained is safe to use.
112 client_->RegisterTokensCallback(
113 base::Bind(&ModemImpl::OnTokensFound, base::Unretained(this)));
114 client_->RegisterSamplesCallback(
115 base::Bind(&ModemImpl::OnTokenEncoded, base::Unretained(this)));
116
117 if (!player_[AUDIBLE])
118 player_[AUDIBLE] = new AudioPlayerImpl();
119 player_[AUDIBLE]->Initialize();
120
121 if (!player_[INAUDIBLE])
122 player_[INAUDIBLE] = new AudioPlayerImpl();
123 player_[INAUDIBLE]->Initialize();
124
125 decode_cancelable_cb_.Reset(base::Bind(
126 &ModemImpl::DecodeSamplesConnector, base::Unretained(this)));
127 if (!recorder_)
128 recorder_ = new AudioRecorderImpl();
129 recorder_->Initialize(decode_cancelable_cb_.callback());
130
131 dump_tokens_dir_ = base::FilePath(base::CommandLine::ForCurrentProcess()
132 ->GetSwitchValueNative(switches::kAudioModemDumpTokensToDir));
133 }
134
135 ModemImpl::~ModemImpl() {
136 if (player_[AUDIBLE])
137 player_[AUDIBLE]->Finalize();
138 if (player_[INAUDIBLE])
139 player_[INAUDIBLE]->Finalize();
140 if (recorder_)
141 recorder_->Finalize();
142
143 // Whispernet initialization may never have completed.
144 if (client_) {
145 client_->RegisterTokensCallback(TokensCallback());
146 client_->RegisterSamplesCallback(SamplesCallback());
147 }
148 }
149
150 void ModemImpl::StartPlaying(AudioType type) {
151 DCHECK(type == AUDIBLE || type == INAUDIBLE);
152 should_be_playing_[type] = true;
153 // If we don't have our token encoded yet, this check will be false, for now.
154 // Once our token is encoded, OnTokenEncoded will call UpdateToken, which
155 // will call this code again (if we're still supposed to be playing).
156 SamplesMap::iterator samples =
157 samples_caches_[type]->Get(playing_token_[type]);
158 if (samples != samples_caches_[type]->end()) {
159 DCHECK(!playing_token_[type].empty());
160 if (player_enabled_[type]) {
161 started_playing_[type] = base::Time::Now();
162 player_[type]->Play(samples->second);
163
164 // If we're playing, we always record to hear what we are playing.
165 recorder_->Record();
166 } else {
167 DVLOG(3) << "Skipping playback for disabled " << AudioTypeToString(type)
168 << " player.";
169 }
170 }
171 }
172
173 void ModemImpl::StopPlaying(AudioType type) {
174 DCHECK(type == AUDIBLE || type == INAUDIBLE);
175 should_be_playing_[type] = false;
176 player_[type]->Stop();
177 // If we were only recording to hear our own played tokens, stop.
178 if (!should_be_recording_[AUDIBLE] && !should_be_recording_[INAUDIBLE])
179 recorder_->Stop();
180 playing_token_[type] = std::string();
181 }
182
183 void ModemImpl::StartRecording(AudioType type) {
184 DCHECK(type == AUDIBLE || type == INAUDIBLE);
185 should_be_recording_[type] = true;
186 recorder_->Record();
187 }
188
189 void ModemImpl::StopRecording(AudioType type) {
190 DCHECK(type == AUDIBLE || type == INAUDIBLE);
191 should_be_recording_[type] = false;
192 recorder_->Stop();
193 }
194
195 void ModemImpl::SetToken(AudioType type,
196 const std::string& url_safe_token) {
197 DCHECK(type == AUDIBLE || type == INAUDIBLE);
198 std::string token = FromUrlSafe(url_safe_token);
199 if (samples_caches_[type]->Get(token) == samples_caches_[type]->end()) {
200 client_->EncodeToken(token, type, token_params_);
201 } else {
202 UpdateToken(type, token);
203 }
204 }
205
206 const std::string ModemImpl::GetToken(AudioType type) const {
207 return playing_token_[type];
208 }
209
210 bool ModemImpl::IsPlayingTokenHeard(AudioType type) const {
211 base::TimeDelta tokenTimeout =
212 base::TimeDelta::FromMilliseconds(kTokenTimeoutMs);
213
214 // This is a bit of a hack. If we haven't been playing long enough,
215 // return true to avoid tripping an audio fail alarm.
216 if (base::Time::Now() - started_playing_[type] < tokenTimeout)
217 return true;
218
219 return base::Time::Now() - heard_own_token_[type] < tokenTimeout;
220 }
221
222 void ModemImpl::SetTokenParams(AudioType type, const TokenParameters& params) {
223 DCHECK_GT(params.length, 0u);
224 token_params_[type] = params;
225
226 // TODO(ckehoe): Make whispernet handle different token lengths
227 // simultaneously without reinitializing the decoder over and over.
228 }
229
230 // static
231 std::unique_ptr<Modem> Modem::Create() {
232 return base::WrapUnique<Modem>(new ModemImpl);
233 }
234
235 // Private functions.
236
237 void ModemImpl::OnTokenEncoded(
238 AudioType type,
239 const std::string& token,
240 const scoped_refptr<media::AudioBusRefCounted>& samples) {
241 samples_caches_[type]->Put(token, samples);
242 DumpToken(type, token, samples.get());
243 UpdateToken(type, token);
244 }
245
246 void ModemImpl::OnTokensFound(const std::vector<AudioToken>& tokens) {
247 std::vector<AudioToken> tokens_to_report;
248 for (const auto& token : tokens) {
249 AudioType type = token.audible ? AUDIBLE : INAUDIBLE;
250 if (playing_token_[type] == token.token)
251 heard_own_token_[type] = base::Time::Now();
252
253 if (should_be_recording_[AUDIBLE] && token.audible) {
254 tokens_to_report.push_back(token);
255 } else if (should_be_recording_[INAUDIBLE] && !token.audible) {
256 tokens_to_report.push_back(token);
257 }
258 }
259
260 if (!tokens_to_report.empty())
261 tokens_cb_.Run(tokens_to_report);
262 }
263
264 void ModemImpl::UpdateToken(AudioType type, const std::string& token) {
265 DCHECK(type == AUDIBLE || type == INAUDIBLE);
266 if (playing_token_[type] == token)
267 return;
268
269 // Update token.
270 playing_token_[type] = token;
271
272 // If we are supposed to be playing this token type at this moment, switch
273 // out playback with the new samples.
274 if (should_be_playing_[type])
275 RestartPlaying(type);
276 }
277
278 void ModemImpl::RestartPlaying(AudioType type) {
279 DCHECK(type == AUDIBLE || type == INAUDIBLE);
280 // We should already have this token in the cache. This function is not
281 // called from anywhere except update token and only once we have our samples
282 // in the cache.
283 DCHECK(samples_caches_[type]->Get(playing_token_[type]) !=
284 samples_caches_[type]->end());
285
286 player_[type]->Stop();
287 StartPlaying(type);
288 }
289
290 void ModemImpl::DecodeSamplesConnector(const std::string& samples) {
291 // If we are either supposed to be recording *or* playing, audible or
292 // inaudible, we should be decoding that type. This is so that if we are
293 // just playing, we will still decode our recorded token so we can check
294 // if we heard our own token. Whether or not we report the token to the
295 // server is checked for and handled in OnTokensFound.
296
297 bool decode_audible =
298 should_be_recording_[AUDIBLE] || should_be_playing_[AUDIBLE];
299 bool decode_inaudible =
300 should_be_recording_[INAUDIBLE] || should_be_playing_[INAUDIBLE];
301
302 if (decode_audible && decode_inaudible) {
303 client_->DecodeSamples(BOTH, samples, token_params_);
304 } else if (decode_audible) {
305 client_->DecodeSamples(AUDIBLE, samples, token_params_);
306 } else if (decode_inaudible) {
307 client_->DecodeSamples(INAUDIBLE, samples, token_params_);
308 }
309 }
310
311 void ModemImpl::DumpToken(AudioType audio_type,
312 const std::string& token,
313 const media::AudioBus* samples) {
314 if (dump_tokens_dir_.empty())
315 return;
316
317 // Convert the samples to 16-bit integers.
318 std::vector<int16_t> int_samples;
319 int_samples.reserve(samples->frames());
320 for (int i = 0; i < samples->frames(); i++) {
321 int_samples.push_back(round(
322 samples->channel(0)[i] * std::numeric_limits<int16_t>::max()));
323 }
324 DCHECK_EQ(static_cast<int>(int_samples.size()), samples->frames());
325 DCHECK_EQ(kMonoChannelCount, samples->channels());
326
327 const std::string filename = base::StringPrintf("%s %s.wav",
328 AudioTypeToString(audio_type).c_str(), ToUrlSafe(token).c_str());
329 DVLOG(3) << "Dumping token " << filename;
330
331 std::string file_str;
332 #if defined(OS_WIN)
333 base::FilePath file_path = dump_tokens_dir_.Append(
334 base::SysNativeMBToWide(filename));
335 file_str = base::SysWideToNativeMB(file_path.value());
336 #else
337 file_str = dump_tokens_dir_.Append(filename).value();
338 #endif
339
340 webrtc::WavWriter writer(file_str, kDefaultSampleRate, kMonoChannelCount);
341 writer.WriteSamples(int_samples.data(), int_samples.size());
342 }
343
344 } // namespace audio_modem
OLDNEW
« no previous file with comments | « components/audio_modem/modem_impl.h ('k') | components/audio_modem/modem_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698