Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / copresence / handlers / audio / audio_directive_handler_impl.cc
blob9b41629d56d4550157ed26e1ab7ef0bfe3efdf9c
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/handlers/audio/audio_directive_handler_impl.h"
7 #include <algorithm>
8 #include <vector>
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/time/default_tick_clock.h"
14 #include "base/time/time.h"
15 #include "base/timer/timer.h"
16 #include "components/audio_modem/public/modem.h"
17 #include "components/copresence/handlers/audio/audio_directive_list.h"
18 #include "components/copresence/handlers/audio/tick_clock_ref_counted.h"
19 #include "components/copresence/proto/data.pb.h"
20 #include "components/copresence/public/copresence_constants.h"
21 #include "media/base/audio_bus.h"
23 using audio_modem::AUDIBLE;
24 using audio_modem::INAUDIBLE;
25 using audio_modem::TokenParameters;
27 namespace copresence {
29 namespace {
31 base::TimeTicks GetEarliestEventTime(AudioDirectiveList* list,
32 base::TimeTicks event_time) {
33 scoped_ptr<AudioDirective> active_directive = list->GetActiveDirective();
35 if (!active_directive)
36 return event_time;
37 if (event_time.is_null())
38 return active_directive->end_time;
40 return std::min(active_directive->end_time, event_time);
43 void ConvertDirectives(const std::vector<AudioDirective>& in_directives,
44 std::vector<Directive>* out_directives) {
45 for (const AudioDirective& in_directive : in_directives)
46 out_directives->push_back(in_directive.server_directive);
49 } // namespace
52 // Public functions.
54 AudioDirectiveHandlerImpl::AudioDirectiveHandlerImpl(
55 const DirectivesCallback& update_directives_callback)
56 : update_directives_callback_(update_directives_callback),
57 audio_modem_(audio_modem::Modem::Create()),
58 audio_event_timer_(new base::OneShotTimer<AudioDirectiveHandler>),
59 clock_(new TickClockRefCounted(new base::DefaultTickClock)) {}
61 AudioDirectiveHandlerImpl::AudioDirectiveHandlerImpl(
62 const DirectivesCallback& update_directives_callback,
63 scoped_ptr<audio_modem::Modem> audio_modem,
64 scoped_ptr<base::Timer> timer,
65 const scoped_refptr<TickClockRefCounted>& clock)
66 : update_directives_callback_(update_directives_callback),
67 audio_modem_(audio_modem.Pass()),
68 audio_event_timer_(timer.Pass()),
69 clock_(clock) {}
71 AudioDirectiveHandlerImpl::~AudioDirectiveHandlerImpl() {}
73 void AudioDirectiveHandlerImpl::Initialize(
74 audio_modem::WhispernetClient* whispernet_client,
75 const audio_modem::TokensCallback& tokens_cb) {
76 DCHECK(audio_modem_);
77 audio_modem_->Initialize(whispernet_client, tokens_cb);
79 DCHECK(transmits_lists_.empty());
80 transmits_lists_.push_back(new AudioDirectiveList(clock_));
81 transmits_lists_.push_back(new AudioDirectiveList(clock_));
83 DCHECK(receives_lists_.empty());
84 receives_lists_.push_back(new AudioDirectiveList(clock_));
85 receives_lists_.push_back(new AudioDirectiveList(clock_));
88 void AudioDirectiveHandlerImpl::AddInstruction(
89 const Directive& directive,
90 const std::string& op_id) {
91 DCHECK(transmits_lists_.size() == 2u && receives_lists_.size() == 2u)
92 << "Call Initialize() before other AudioDirectiveHandler methods";
94 const TokenInstruction& instruction = directive.token_instruction();
95 base::TimeDelta ttl =
96 base::TimeDelta::FromMilliseconds(directive.ttl_millis());
97 const size_t token_length = directive.configuration().token_params().length();
99 switch (instruction.token_instruction_type()) {
100 case TRANSMIT:
101 DVLOG(2) << "Audio Transmit Directive received. Token: "
102 << instruction.token_id()
103 << " with medium=" << instruction.medium()
104 << " with TTL=" << ttl.InMilliseconds();
105 DCHECK_GT(token_length, 0u);
106 switch (instruction.medium()) {
107 case AUDIO_ULTRASOUND_PASSBAND:
108 audio_modem_->SetTokenParams(INAUDIBLE,
109 TokenParameters(token_length));
110 transmits_lists_[INAUDIBLE]->AddDirective(op_id, directive);
111 audio_modem_->SetToken(INAUDIBLE, instruction.token_id());
112 break;
113 case AUDIO_AUDIBLE_DTMF:
114 audio_modem_->SetTokenParams(AUDIBLE, TokenParameters(token_length));
115 transmits_lists_[AUDIBLE]->AddDirective(op_id, directive);
116 audio_modem_->SetToken(AUDIBLE, instruction.token_id());
117 break;
118 default:
119 NOTREACHED();
121 break;
123 case RECEIVE:
124 DVLOG(2) << "Audio Receive Directive received."
125 << " with medium=" << instruction.medium()
126 << " with TTL=" << ttl.InMilliseconds();
127 DCHECK_GT(token_length, 0u);
128 switch (instruction.medium()) {
129 case AUDIO_ULTRASOUND_PASSBAND:
130 audio_modem_->SetTokenParams(INAUDIBLE,
131 TokenParameters(token_length));
132 receives_lists_[INAUDIBLE]->AddDirective(op_id, directive);
133 break;
134 case AUDIO_AUDIBLE_DTMF:
135 audio_modem_->SetTokenParams(AUDIBLE, TokenParameters(token_length));
136 receives_lists_[AUDIBLE]->AddDirective(op_id, directive);
137 break;
138 default:
139 NOTREACHED();
141 break;
143 case UNKNOWN_TOKEN_INSTRUCTION_TYPE:
144 default:
145 LOG(WARNING) << "Unknown Audio Transmit Directive received. type = "
146 << instruction.token_instruction_type();
149 ProcessNextInstruction();
152 void AudioDirectiveHandlerImpl::RemoveInstructions(const std::string& op_id) {
153 DCHECK(transmits_lists_.size() == 2u && receives_lists_.size() == 2u)
154 << "Call Initialize() before other AudioDirectiveHandler methods";
156 transmits_lists_[AUDIBLE]->RemoveDirective(op_id);
157 transmits_lists_[INAUDIBLE]->RemoveDirective(op_id);
158 receives_lists_[AUDIBLE]->RemoveDirective(op_id);
159 receives_lists_[INAUDIBLE]->RemoveDirective(op_id);
161 ProcessNextInstruction();
164 const std::string AudioDirectiveHandlerImpl::PlayingToken(
165 audio_modem::AudioType type) const {
166 return audio_modem_->GetToken(type);
169 bool AudioDirectiveHandlerImpl::IsPlayingTokenHeard(
170 audio_modem::AudioType type) const {
171 return audio_modem_->IsPlayingTokenHeard(type);
175 // Private functions.
177 void AudioDirectiveHandlerImpl::ProcessNextInstruction() {
178 DCHECK(audio_event_timer_);
179 audio_event_timer_->Stop();
181 // Change |audio_modem_| state for audible transmits.
182 if (transmits_lists_[AUDIBLE]->GetActiveDirective())
183 audio_modem_->StartPlaying(AUDIBLE);
184 else
185 audio_modem_->StopPlaying(AUDIBLE);
187 // Change audio_modem_ state for inaudible transmits.
188 if (transmits_lists_[INAUDIBLE]->GetActiveDirective())
189 audio_modem_->StartPlaying(INAUDIBLE);
190 else
191 audio_modem_->StopPlaying(INAUDIBLE);
193 // Change audio_modem_ state for audible receives.
194 if (receives_lists_[AUDIBLE]->GetActiveDirective())
195 audio_modem_->StartRecording(AUDIBLE);
196 else
197 audio_modem_->StopRecording(AUDIBLE);
199 // Change audio_modem_ state for inaudible receives.
200 if (receives_lists_[INAUDIBLE]->GetActiveDirective())
201 audio_modem_->StartRecording(INAUDIBLE);
202 else
203 audio_modem_->StopRecording(INAUDIBLE);
205 base::TimeTicks next_event_time;
206 if (GetNextInstructionExpiry(&next_event_time)) {
207 audio_event_timer_->Start(
208 FROM_HERE,
209 next_event_time - clock_->NowTicks(),
210 base::Bind(&AudioDirectiveHandlerImpl::ProcessNextInstruction,
211 base::Unretained(this)));
214 // TODO(crbug.com/436584): Instead of this, store the directives
215 // in a single list, and prune them when expired.
216 if (!update_directives_callback_.is_null()) {
217 std::vector<Directive> directives;
218 ConvertDirectives(transmits_lists_[AUDIBLE]->directives(), &directives);
219 ConvertDirectives(transmits_lists_[INAUDIBLE]->directives(), &directives);
220 ConvertDirectives(receives_lists_[AUDIBLE]->directives(), &directives);
221 ConvertDirectives(receives_lists_[INAUDIBLE]->directives(), &directives);
222 update_directives_callback_.Run(directives);
226 bool AudioDirectiveHandlerImpl::GetNextInstructionExpiry(
227 base::TimeTicks* expiry) {
228 DCHECK(expiry);
230 *expiry = GetEarliestEventTime(transmits_lists_[AUDIBLE], base::TimeTicks());
231 *expiry = GetEarliestEventTime(transmits_lists_[INAUDIBLE], *expiry);
232 *expiry = GetEarliestEventTime(receives_lists_[AUDIBLE], *expiry);
233 *expiry = GetEarliestEventTime(receives_lists_[INAUDIBLE], *expiry);
235 return !expiry->is_null();
238 } // namespace copresence