Move documentScan.html from extensions to apps
[chromium-blink-merge.git] / components / copresence / mediums / audio / audio_manager_impl.cc
blob387eb54d33901ec0f65a2b9734945bdcf4c0d8be
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
3 // found in the LICENSE file.
5 #include "components/copresence/mediums/audio/audio_manager_impl.h"
7 #include <algorithm>
8 #include <vector>
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/logging.h"
13 #include "base/run_loop.h"
14 #include "base/strings/string_util.h"
15 #include "base/time/time.h"
16 #include "components/copresence/mediums/audio/audio_player_impl.h"
17 #include "components/copresence/mediums/audio/audio_recorder_impl.h"
18 #include "components/copresence/public/copresence_constants.h"
19 #include "components/copresence/public/whispernet_client.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "media/audio/audio_manager.h"
22 #include "media/audio/audio_manager_base.h"
23 #include "media/base/audio_bus.h"
25 namespace copresence {
27 namespace {
29 // UrlSafe is defined as:
30 // '/' represented by a '_' and '+' represented by a '-'
31 // TODO(rkc): Move this processing to the whispernet wrapper.
32 std::string FromUrlSafe(std::string token) {
33 base::ReplaceChars(token, "-", "+", &token);
34 base::ReplaceChars(token, "_", "/", &token);
35 return token;
38 const int kSampleExpiryTimeMs = 60 * 60 * 1000; // 60 minutes.
39 const int kMaxSamples = 10000;
40 const int kTokenTimeoutMs = 2000;
42 } // namespace
44 // Public methods.
46 AudioManagerImpl::AudioManagerImpl()
47 : whispernet_client_(nullptr), recorder_(nullptr) {
48 // TODO(rkc): Move all of these into initializer lists once it is allowed.
49 should_be_playing_[AUDIBLE] = false;
50 should_be_playing_[INAUDIBLE] = false;
51 should_be_recording_[AUDIBLE] = false;
52 should_be_recording_[INAUDIBLE] = false;
54 player_[AUDIBLE] = nullptr;
55 player_[INAUDIBLE] = nullptr;
56 token_length_[0] = 0;
57 token_length_[1] = 0;
60 void AudioManagerImpl::Initialize(WhispernetClient* whispernet_client,
61 const TokensCallback& tokens_cb) {
62 samples_cache_.resize(2);
63 samples_cache_[AUDIBLE] = new SamplesMap(
64 base::TimeDelta::FromMilliseconds(kSampleExpiryTimeMs), kMaxSamples);
65 samples_cache_[INAUDIBLE] = new SamplesMap(
66 base::TimeDelta::FromMilliseconds(kSampleExpiryTimeMs), kMaxSamples);
68 DCHECK(whispernet_client);
69 whispernet_client_ = whispernet_client;
70 tokens_cb_ = tokens_cb;
72 // These will be unregistered on destruction, so unretained is safe to use.
73 whispernet_client_->RegisterTokensCallback(
74 base::Bind(&AudioManagerImpl::OnTokensFound, base::Unretained(this)));
75 whispernet_client_->RegisterSamplesCallback(
76 base::Bind(&AudioManagerImpl::OnTokenEncoded, base::Unretained(this)));
78 if (!player_[AUDIBLE])
79 player_[AUDIBLE] = new AudioPlayerImpl();
80 player_[AUDIBLE]->Initialize();
82 if (!player_[INAUDIBLE])
83 player_[INAUDIBLE] = new AudioPlayerImpl();
84 player_[INAUDIBLE]->Initialize();
86 decode_cancelable_cb_.Reset(base::Bind(
87 &AudioManagerImpl::DecodeSamplesConnector, base::Unretained(this)));
88 if (!recorder_)
89 recorder_ = new AudioRecorderImpl();
90 recorder_->Initialize(decode_cancelable_cb_.callback());
93 AudioManagerImpl::~AudioManagerImpl() {
94 if (player_[AUDIBLE])
95 player_[AUDIBLE]->Finalize();
96 if (player_[INAUDIBLE])
97 player_[INAUDIBLE]->Finalize();
98 if (recorder_)
99 recorder_->Finalize();
101 // Whispernet initialization may never have completed.
102 if (whispernet_client_) {
103 whispernet_client_->RegisterTokensCallback(TokensCallback());
104 whispernet_client_->RegisterSamplesCallback(SamplesCallback());
108 void AudioManagerImpl::StartPlaying(AudioType type) {
109 DCHECK(type == AUDIBLE || type == INAUDIBLE);
110 should_be_playing_[type] = true;
111 // 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
113 // will call this code again (if we're still supposed to be playing).
114 if (samples_cache_[type]->HasKey(playing_token_[type])) {
115 DCHECK(!playing_token_[type].empty());
116 started_playing_[type] = base::Time::Now();
117 player_[type]->Play(samples_cache_[type]->GetValue(playing_token_[type]));
118 // If we're playing, we always record to hear what we are playing.
119 recorder_->Record();
123 void AudioManagerImpl::StopPlaying(AudioType type) {
124 DCHECK(type == AUDIBLE || type == INAUDIBLE);
125 should_be_playing_[type] = false;
126 player_[type]->Stop();
127 // If we were only recording to hear our own played tokens, stop.
128 if (!should_be_recording_[AUDIBLE] && !should_be_recording_[INAUDIBLE])
129 recorder_->Stop();
130 playing_token_[type] = std::string();
133 void AudioManagerImpl::StartRecording(AudioType type) {
134 DCHECK(type == AUDIBLE || type == INAUDIBLE);
135 should_be_recording_[type] = true;
136 recorder_->Record();
139 void AudioManagerImpl::StopRecording(AudioType type) {
140 DCHECK(type == AUDIBLE || type == INAUDIBLE);
141 should_be_recording_[type] = false;
142 recorder_->Stop();
145 void AudioManagerImpl::SetToken(AudioType type,
146 const std::string& url_safe_token) {
147 DCHECK(type == AUDIBLE || type == INAUDIBLE);
148 std::string token = FromUrlSafe(url_safe_token);
149 if (!samples_cache_[type]->HasKey(token)) {
150 whispernet_client_->EncodeToken(token, type);
151 } else {
152 UpdateToken(type, token);
156 const std::string AudioManagerImpl::GetToken(AudioType type) {
157 return playing_token_[type];
160 bool AudioManagerImpl::IsPlayingTokenHeard(AudioType type) {
161 base::TimeDelta tokenTimeout =
162 base::TimeDelta::FromMilliseconds(kTokenTimeoutMs);
164 // This is a bit of a hack. If we haven't been playing long enough,
165 // return true to avoid tripping an audio fail alarm.
166 if (base::Time::Now() - started_playing_[type] < tokenTimeout)
167 return true;
169 return base::Time::Now() - heard_own_token_[type] < tokenTimeout;
172 void AudioManagerImpl::SetTokenLength(AudioType type, size_t token_length) {
173 token_length_[type] = token_length;
176 // Private methods.
178 void AudioManagerImpl::OnTokenEncoded(
179 AudioType type,
180 const std::string& token,
181 const scoped_refptr<media::AudioBusRefCounted>& samples) {
182 samples_cache_[type]->Add(token, samples);
183 UpdateToken(type, token);
186 void AudioManagerImpl::OnTokensFound(const std::vector<AudioToken>& tokens) {
187 std::vector<AudioToken> tokens_to_report;
188 for (const auto& token : tokens) {
189 AudioType type = token.audible ? AUDIBLE : INAUDIBLE;
190 if (playing_token_[type] == token.token)
191 heard_own_token_[type] = base::Time::Now();
193 if (should_be_recording_[AUDIBLE] && token.audible) {
194 tokens_to_report.push_back(token);
195 } else if (should_be_recording_[INAUDIBLE] && !token.audible) {
196 tokens_to_report.push_back(token);
200 if (!tokens_to_report.empty())
201 tokens_cb_.Run(tokens_to_report);
204 void AudioManagerImpl::UpdateToken(AudioType type, const std::string& token) {
205 DCHECK(type == AUDIBLE || type == INAUDIBLE);
206 if (playing_token_[type] == token)
207 return;
209 // Update token.
210 playing_token_[type] = token;
212 // If we are supposed to be playing this token type at this moment, switch
213 // out playback with the new samples.
214 if (should_be_playing_[type])
215 RestartPlaying(type);
218 void AudioManagerImpl::RestartPlaying(AudioType type) {
219 DCHECK(type == AUDIBLE || type == INAUDIBLE);
220 // 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
222 // in the cache.
223 DCHECK(samples_cache_[type]->HasKey(playing_token_[type]));
225 started_playing_[type] = base::Time::Now();
226 player_[type]->Stop();
227 player_[type]->Play(samples_cache_[type]->GetValue(playing_token_[type]));
228 // If we're playing, we always record to hear what we are playing.
229 recorder_->Record();
232 void AudioManagerImpl::DecodeSamplesConnector(const std::string& samples) {
233 // 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
235 // 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
237 // server is checked for and handled in OnTokensFound.
239 bool decode_audible =
240 should_be_recording_[AUDIBLE] || should_be_playing_[AUDIBLE];
241 bool decode_inaudible =
242 should_be_recording_[INAUDIBLE] || should_be_playing_[INAUDIBLE];
244 if (decode_audible && decode_inaudible) {
245 whispernet_client_->DecodeSamples(BOTH, samples, token_length_);
246 } else if (decode_audible) {
247 whispernet_client_->DecodeSamples(AUDIBLE, samples, token_length_);
248 } else if (decode_inaudible) {
249 whispernet_client_->DecodeSamples(INAUDIBLE, samples, token_length_);
253 } // namespace copresence