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"
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
{
31 base::TimeTicks
GetEarliestEventTime(AudioDirectiveList
* list
,
32 base::TimeTicks event_time
) {
33 scoped_ptr
<AudioDirective
> active_directive
= list
->GetActiveDirective();
35 if (!active_directive
)
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
);
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()),
71 AudioDirectiveHandlerImpl::~AudioDirectiveHandlerImpl() {}
73 void AudioDirectiveHandlerImpl::Initialize(
74 audio_modem::WhispernetClient
* whispernet_client
,
75 const audio_modem::TokensCallback
& tokens_cb
) {
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();
96 base::TimeDelta::FromMilliseconds(directive
.ttl_millis());
97 const size_t token_length
= directive
.configuration().token_params().length();
99 switch (instruction
.token_instruction_type()) {
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());
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());
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
);
134 case AUDIO_AUDIBLE_DTMF
:
135 audio_modem_
->SetTokenParams(AUDIBLE
, TokenParameters(token_length
));
136 receives_lists_
[AUDIBLE
]->AddDirective(op_id
, directive
);
143 case UNKNOWN_TOKEN_INSTRUCTION_TYPE
:
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
);
185 audio_modem_
->StopPlaying(AUDIBLE
);
187 // Change audio_modem_ state for inaudible transmits.
188 if (transmits_lists_
[INAUDIBLE
]->GetActiveDirective())
189 audio_modem_
->StartPlaying(INAUDIBLE
);
191 audio_modem_
->StopPlaying(INAUDIBLE
);
193 // Change audio_modem_ state for audible receives.
194 if (receives_lists_
[AUDIBLE
]->GetActiveDirective())
195 audio_modem_
->StartRecording(AUDIBLE
);
197 audio_modem_
->StopRecording(AUDIBLE
);
199 // Change audio_modem_ state for inaudible receives.
200 if (receives_lists_
[INAUDIBLE
]->GetActiveDirective())
201 audio_modem_
->StartRecording(INAUDIBLE
);
203 audio_modem_
->StopRecording(INAUDIBLE
);
205 base::TimeTicks next_event_time
;
206 if (GetNextInstructionExpiry(&next_event_time
)) {
207 audio_event_timer_
->Start(
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
) {
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