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

Side by Side Diff: components/copresence/mediums/audio/audio_manager_impl.cc

Issue 892423002: Adding some audio debugging flags (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 10 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
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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 "components/copresence/mediums/audio/audio_manager_impl.h" 5 #include "components/copresence/mediums/audio/audio_manager_impl.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <limits>
8 #include <vector> 9 #include <vector>
9 10
10 #include "base/bind.h" 11 #include "base/bind.h"
11 #include "base/bind_helpers.h" 12 #include "base/bind_helpers.h"
13 #include "base/command_line.h"
12 #include "base/logging.h" 14 #include "base/logging.h"
13 #include "base/run_loop.h" 15 #include "base/run_loop.h"
14 #include "base/strings/string_util.h" 16 #include "base/strings/string_util.h"
15 #include "base/time/time.h" 17 #include "base/time/time.h"
18 #include "components/copresence/copresence_switches.h"
16 #include "components/copresence/mediums/audio/audio_player_impl.h" 19 #include "components/copresence/mediums/audio/audio_player_impl.h"
17 #include "components/copresence/mediums/audio/audio_recorder_impl.h" 20 #include "components/copresence/mediums/audio/audio_recorder_impl.h"
18 #include "components/copresence/public/copresence_constants.h" 21 #include "components/copresence/public/copresence_constants.h"
19 #include "components/copresence/public/whispernet_client.h" 22 #include "components/copresence/public/whispernet_client.h"
20 #include "content/public/browser/browser_thread.h" 23 #include "content/public/browser/browser_thread.h"
21 #include "media/audio/audio_manager.h" 24 #include "media/audio/audio_manager.h"
22 #include "media/audio/audio_manager_base.h" 25 #include "media/audio/audio_manager_base.h"
23 #include "media/base/audio_bus.h" 26 #include "media/base/audio_bus.h"
27 #include "third_party/webrtc/common_audio/wav_file.h"
24 28
25 namespace copresence { 29 namespace copresence {
26 30
27 namespace { 31 namespace {
28 32
33 const int kSampleExpiryTimeMs = 60 * 60 * 1000; // 60 minutes.
34 const int kMaxSamples = 10000;
35 const int kTokenTimeoutMs = 2000;
36
29 // UrlSafe is defined as: 37 // UrlSafe is defined as:
30 // '/' represented by a '_' and '+' represented by a '-' 38 // '/' represented by a '_' and '+' represented by a '-'
31 // TODO(rkc): Move this processing to the whispernet wrapper. 39 // TODO(ckehoe): Move this to a central place.
32 std::string FromUrlSafe(std::string token) { 40 std::string FromUrlSafe(std::string token) {
33 base::ReplaceChars(token, "-", "+", &token); 41 base::ReplaceChars(token, "-", "+", &token);
34 base::ReplaceChars(token, "_", "/", &token); 42 base::ReplaceChars(token, "_", "/", &token);
35 return token; 43 return token;
36 } 44 }
45 std::string ToUrlSafe(std::string token) {
46 base::ReplaceChars(token, "+", "-", &token);
47 base::ReplaceChars(token, "/", "_", &token);
48 return token;
49 }
37 50
38 const int kSampleExpiryTimeMs = 60 * 60 * 1000; // 60 minutes. 51 std::string AudioTypeToString(AudioType audio_type) {
39 const int kMaxSamples = 10000; 52 if (audio_type == AUDIBLE)
40 const int kTokenTimeoutMs = 2000; 53 return "audible";
54 if (audio_type == INAUDIBLE)
55 return "inaudible";
56
57 NOTREACHED() << "Got unexpected token type " << audio_type;
58 return std::string();
59 }
60
61 bool ReadBooleanFlag(const std::string& flag, bool default_value) {
62 const std::string flag_value = base::StringToLowerASCII(
63 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(flag));
64 if (flag_value == "true" || flag_value == "1")
65 return true;
66 if (flag_value == "false" || flag_value == "0")
67 return false;
68 LOG_IF(ERROR, !flag_value.empty())
69 << "Unrecognized value \"" << flag_value << " for flag "
70 << flag << ". Defaulting to " << default_value;
71 return default_value;
72 }
41 73
42 } // namespace 74 } // namespace
43 75
44 // Public methods. 76
77 // Public functions.
45 78
46 AudioManagerImpl::AudioManagerImpl() 79 AudioManagerImpl::AudioManagerImpl()
47 : whispernet_client_(nullptr), recorder_(nullptr) { 80 : whispernet_client_(nullptr), recorder_(nullptr) {
48 // TODO(rkc): Move all of these into initializer lists once it is allowed. 81 // TODO(rkc): Move all of these into initializer lists once it is allowed.
49 should_be_playing_[AUDIBLE] = false; 82 should_be_playing_[AUDIBLE] = false;
50 should_be_playing_[INAUDIBLE] = false; 83 should_be_playing_[INAUDIBLE] = false;
51 should_be_recording_[AUDIBLE] = false; 84 should_be_recording_[AUDIBLE] = false;
52 should_be_recording_[INAUDIBLE] = false; 85 should_be_recording_[INAUDIBLE] = false;
53 86
87 player_enabled_[AUDIBLE] = ReadBooleanFlag(
88 switches::kCopresenceEnableAudibleBroadcast, true);
89 player_enabled_[INAUDIBLE] = ReadBooleanFlag(
90 switches::kCopresenceEnableInaudibleBroadcast, true);
54 player_[AUDIBLE] = nullptr; 91 player_[AUDIBLE] = nullptr;
55 player_[INAUDIBLE] = nullptr; 92 player_[INAUDIBLE] = nullptr;
56 token_length_[0] = 0; 93 token_length_[0] = 0;
57 token_length_[1] = 0; 94 token_length_[1] = 0;
58 } 95 }
59 96
60 void AudioManagerImpl::Initialize(WhispernetClient* whispernet_client, 97 void AudioManagerImpl::Initialize(WhispernetClient* whispernet_client,
61 const TokensCallback& tokens_cb) { 98 const TokensCallback& tokens_cb) {
62 samples_cache_.resize(2); 99 samples_cache_.resize(2);
63 samples_cache_[AUDIBLE] = new SamplesMap( 100 samples_cache_[AUDIBLE] = new SamplesMap(
(...skipping 17 matching lines...) Expand all
81 118
82 if (!player_[INAUDIBLE]) 119 if (!player_[INAUDIBLE])
83 player_[INAUDIBLE] = new AudioPlayerImpl(); 120 player_[INAUDIBLE] = new AudioPlayerImpl();
84 player_[INAUDIBLE]->Initialize(); 121 player_[INAUDIBLE]->Initialize();
85 122
86 decode_cancelable_cb_.Reset(base::Bind( 123 decode_cancelable_cb_.Reset(base::Bind(
87 &AudioManagerImpl::DecodeSamplesConnector, base::Unretained(this))); 124 &AudioManagerImpl::DecodeSamplesConnector, base::Unretained(this)));
88 if (!recorder_) 125 if (!recorder_)
89 recorder_ = new AudioRecorderImpl(); 126 recorder_ = new AudioRecorderImpl();
90 recorder_->Initialize(decode_cancelable_cb_.callback()); 127 recorder_->Initialize(decode_cancelable_cb_.callback());
128
129 dump_tokens_dir_ = base::FilePath(base::CommandLine::ForCurrentProcess()
130 ->GetSwitchValueASCII(switches::kCopresenceDumpTokensToDir));
91 } 131 }
92 132
93 AudioManagerImpl::~AudioManagerImpl() { 133 AudioManagerImpl::~AudioManagerImpl() {
94 if (player_[AUDIBLE]) 134 if (player_[AUDIBLE])
95 player_[AUDIBLE]->Finalize(); 135 player_[AUDIBLE]->Finalize();
96 if (player_[INAUDIBLE]) 136 if (player_[INAUDIBLE])
97 player_[INAUDIBLE]->Finalize(); 137 player_[INAUDIBLE]->Finalize();
98 if (recorder_) 138 if (recorder_)
99 recorder_->Finalize(); 139 recorder_->Finalize();
100 140
101 // Whispernet initialization may never have completed. 141 // Whispernet initialization may never have completed.
102 if (whispernet_client_) { 142 if (whispernet_client_) {
103 whispernet_client_->RegisterTokensCallback(TokensCallback()); 143 whispernet_client_->RegisterTokensCallback(TokensCallback());
104 whispernet_client_->RegisterSamplesCallback(SamplesCallback()); 144 whispernet_client_->RegisterSamplesCallback(SamplesCallback());
105 } 145 }
106 } 146 }
107 147
108 void AudioManagerImpl::StartPlaying(AudioType type) { 148 void AudioManagerImpl::StartPlaying(AudioType type) {
109 DCHECK(type == AUDIBLE || type == INAUDIBLE); 149 DCHECK(type == AUDIBLE || type == INAUDIBLE);
110 should_be_playing_[type] = true; 150 should_be_playing_[type] = true;
111 // If we don't have our token encoded yet, this check will be false, for now. 151 // If we don't have our token encoded yet, this check will be false, for now.
112 // Once our token is encoded, OnTokenEncoded will call UpdateToken, which 152 // Once our token is encoded, OnTokenEncoded will call UpdateToken, which
113 // will call this code again (if we're still supposed to be playing). 153 // will call this code again (if we're still supposed to be playing).
114 if (samples_cache_[type]->HasKey(playing_token_[type])) { 154 if (samples_cache_[type]->HasKey(playing_token_[type])) {
115 DCHECK(!playing_token_[type].empty()); 155 DCHECK(!playing_token_[type].empty());
116 started_playing_[type] = base::Time::Now(); 156 if (player_enabled_[type]) {
117 player_[type]->Play(samples_cache_[type]->GetValue(playing_token_[type])); 157 started_playing_[type] = base::Time::Now();
118 // If we're playing, we always record to hear what we are playing. 158 player_[type]->Play(samples_cache_[type]->GetValue(playing_token_[type]));
119 recorder_->Record(); 159
160 // If we're playing, we always record to hear what we are playing.
161 recorder_->Record();
162 } else {
163 DVLOG(3) << "Skipping playback for disabled " << AudioTypeToString(type)
164 << " player.";
165 }
120 } 166 }
121 } 167 }
122 168
123 void AudioManagerImpl::StopPlaying(AudioType type) { 169 void AudioManagerImpl::StopPlaying(AudioType type) {
124 DCHECK(type == AUDIBLE || type == INAUDIBLE); 170 DCHECK(type == AUDIBLE || type == INAUDIBLE);
125 should_be_playing_[type] = false; 171 should_be_playing_[type] = false;
126 player_[type]->Stop(); 172 player_[type]->Stop();
127 // If we were only recording to hear our own played tokens, stop. 173 // If we were only recording to hear our own played tokens, stop.
128 if (!should_be_recording_[AUDIBLE] && !should_be_recording_[INAUDIBLE]) 174 if (!should_be_recording_[AUDIBLE] && !should_be_recording_[INAUDIBLE])
129 recorder_->Stop(); 175 recorder_->Stop();
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
166 if (base::Time::Now() - started_playing_[type] < tokenTimeout) 212 if (base::Time::Now() - started_playing_[type] < tokenTimeout)
167 return true; 213 return true;
168 214
169 return base::Time::Now() - heard_own_token_[type] < tokenTimeout; 215 return base::Time::Now() - heard_own_token_[type] < tokenTimeout;
170 } 216 }
171 217
172 void AudioManagerImpl::SetTokenLength(AudioType type, size_t token_length) { 218 void AudioManagerImpl::SetTokenLength(AudioType type, size_t token_length) {
173 token_length_[type] = token_length; 219 token_length_[type] = token_length;
174 } 220 }
175 221
176 // Private methods. 222
223 // Private functions.
177 224
178 void AudioManagerImpl::OnTokenEncoded( 225 void AudioManagerImpl::OnTokenEncoded(
179 AudioType type, 226 AudioType type,
180 const std::string& token, 227 const std::string& token,
181 const scoped_refptr<media::AudioBusRefCounted>& samples) { 228 const scoped_refptr<media::AudioBusRefCounted>& samples) {
182 samples_cache_[type]->Add(token, samples); 229 samples_cache_[type]->Add(token, samples);
230 DumpToken(type, token, samples.get());
183 UpdateToken(type, token); 231 UpdateToken(type, token);
184 } 232 }
185 233
186 void AudioManagerImpl::OnTokensFound(const std::vector<AudioToken>& tokens) { 234 void AudioManagerImpl::OnTokensFound(const std::vector<AudioToken>& tokens) {
187 std::vector<AudioToken> tokens_to_report; 235 std::vector<AudioToken> tokens_to_report;
188 for (const auto& token : tokens) { 236 for (const auto& token : tokens) {
189 AudioType type = token.audible ? AUDIBLE : INAUDIBLE; 237 AudioType type = token.audible ? AUDIBLE : INAUDIBLE;
190 if (playing_token_[type] == token.token) 238 if (playing_token_[type] == token.token)
191 heard_own_token_[type] = base::Time::Now(); 239 heard_own_token_[type] = base::Time::Now();
192 240
(...skipping 22 matching lines...) Expand all
215 RestartPlaying(type); 263 RestartPlaying(type);
216 } 264 }
217 265
218 void AudioManagerImpl::RestartPlaying(AudioType type) { 266 void AudioManagerImpl::RestartPlaying(AudioType type) {
219 DCHECK(type == AUDIBLE || type == INAUDIBLE); 267 DCHECK(type == AUDIBLE || type == INAUDIBLE);
220 // We should already have this token in the cache. This function is not 268 // We should already have this token in the cache. This function is not
221 // called from anywhere except update token and only once we have our samples 269 // called from anywhere except update token and only once we have our samples
222 // in the cache. 270 // in the cache.
223 DCHECK(samples_cache_[type]->HasKey(playing_token_[type])); 271 DCHECK(samples_cache_[type]->HasKey(playing_token_[type]));
224 272
225 started_playing_[type] = base::Time::Now();
226 player_[type]->Stop(); 273 player_[type]->Stop();
227 player_[type]->Play(samples_cache_[type]->GetValue(playing_token_[type])); 274 StartPlaying(type);
228 // If we're playing, we always record to hear what we are playing.
229 recorder_->Record();
230 } 275 }
231 276
232 void AudioManagerImpl::DecodeSamplesConnector(const std::string& samples) { 277 void AudioManagerImpl::DecodeSamplesConnector(const std::string& samples) {
233 // If we are either supposed to be recording *or* playing, audible or 278 // If we are either supposed to be recording *or* playing, audible or
234 // inaudible, we should be decoding that type. This is so that if we are 279 // inaudible, we should be decoding that type. This is so that if we are
235 // just playing, we will still decode our recorded token so we can check 280 // just playing, we will still decode our recorded token so we can check
236 // if we heard our own token. Whether or not we report the token to the 281 // if we heard our own token. Whether or not we report the token to the
237 // server is checked for and handled in OnTokensFound. 282 // server is checked for and handled in OnTokensFound.
238 283
239 bool decode_audible = 284 bool decode_audible =
240 should_be_recording_[AUDIBLE] || should_be_playing_[AUDIBLE]; 285 should_be_recording_[AUDIBLE] || should_be_playing_[AUDIBLE];
241 bool decode_inaudible = 286 bool decode_inaudible =
242 should_be_recording_[INAUDIBLE] || should_be_playing_[INAUDIBLE]; 287 should_be_recording_[INAUDIBLE] || should_be_playing_[INAUDIBLE];
243 288
244 if (decode_audible && decode_inaudible) { 289 if (decode_audible && decode_inaudible) {
245 whispernet_client_->DecodeSamples(BOTH, samples, token_length_); 290 whispernet_client_->DecodeSamples(BOTH, samples, token_length_);
246 } else if (decode_audible) { 291 } else if (decode_audible) {
247 whispernet_client_->DecodeSamples(AUDIBLE, samples, token_length_); 292 whispernet_client_->DecodeSamples(AUDIBLE, samples, token_length_);
248 } else if (decode_inaudible) { 293 } else if (decode_inaudible) {
249 whispernet_client_->DecodeSamples(INAUDIBLE, samples, token_length_); 294 whispernet_client_->DecodeSamples(INAUDIBLE, samples, token_length_);
250 } 295 }
251 } 296 }
252 297
298 void AudioManagerImpl::DumpToken(AudioType audio_type,
299 const std::string& token,
300 const media::AudioBus* samples) {
301 if (dump_tokens_dir_.empty())
302 return;
303
304 // Convert the samples to 16-bit integers.
305 std::vector<int16_t> int_samples;
306 int_samples.reserve(samples->frames());
307 for (int i = 0; i < samples->frames(); i++) {
308 int_samples.push_back(round(
309 samples->channel(0)[i] * std::numeric_limits<int16_t>::max()));
310 }
311 DCHECK_EQ((int) int_samples.size(), samples->frames());
rkc 2015/02/04 23:37:18 How could this ever fail?
Charlie 2015/02/04 23:52:14 Someone could edit the code incorrectly. One of th
312
313 const std::string filename = AudioTypeToString(audio_type) +
314 " " + ToUrlSafe(token) + ".wav";
315 DVLOG(3) << "Dumping token " << filename;
316 webrtc::WavWriter writer(dump_tokens_dir_.Append(filename).value(),
317 kDefaultSampleRate,
318 1);
rkc 2015/02/04 23:37:18 It isn't evident what this number means. Add a com
Charlie 2015/02/04 23:52:14 Done.
319 writer.WriteSamples(int_samples.data(), int_samples.size());
320 }
321
253 } // namespace copresence 322 } // namespace copresence
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698