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