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 if (!list
->GetActiveDirective())
36 return event_time
.is_null() ?
37 list
->GetActiveDirective()->end_time
:
38 std::min(list
->GetActiveDirective()->end_time
, event_time
);
41 void ConvertDirectives(const std::vector
<AudioDirective
>& in_directives
,
42 std::vector
<Directive
>* out_directives
) {
43 for (const AudioDirective
& in_directive
: in_directives
)
44 out_directives
->push_back(in_directive
.server_directive
);
52 AudioDirectiveHandlerImpl::AudioDirectiveHandlerImpl(
53 const DirectivesCallback
& update_directives_callback
)
54 : update_directives_callback_(update_directives_callback
),
55 audio_modem_(audio_modem::Modem::Create()),
56 audio_event_timer_(new base::OneShotTimer
<AudioDirectiveHandler
>),
57 clock_(new TickClockRefCounted(new base::DefaultTickClock
)) {}
59 AudioDirectiveHandlerImpl::AudioDirectiveHandlerImpl(
60 const DirectivesCallback
& update_directives_callback
,
61 scoped_ptr
<audio_modem::Modem
> audio_modem
,
62 scoped_ptr
<base::Timer
> timer
,
63 const scoped_refptr
<TickClockRefCounted
>& clock
)
64 : update_directives_callback_(update_directives_callback
),
65 audio_modem_(audio_modem
.Pass()),
66 audio_event_timer_(timer
.Pass()),
69 AudioDirectiveHandlerImpl::~AudioDirectiveHandlerImpl() {}
71 void AudioDirectiveHandlerImpl::Initialize(
72 audio_modem::WhispernetClient
* whispernet_client
,
73 const audio_modem::TokensCallback
& tokens_cb
) {
75 audio_modem_
->Initialize(whispernet_client
, tokens_cb
);
77 DCHECK(transmits_lists_
.empty());
78 transmits_lists_
.push_back(new AudioDirectiveList(clock_
));
79 transmits_lists_
.push_back(new AudioDirectiveList(clock_
));
81 DCHECK(receives_lists_
.empty());
82 receives_lists_
.push_back(new AudioDirectiveList(clock_
));
83 receives_lists_
.push_back(new AudioDirectiveList(clock_
));
86 void AudioDirectiveHandlerImpl::AddInstruction(
87 const Directive
& directive
,
88 const std::string
& op_id
) {
89 DCHECK(transmits_lists_
.size() == 2u && receives_lists_
.size() == 2u)
90 << "Call Initialize() before other AudioDirectiveHandler methods";
92 const TokenInstruction
& instruction
= directive
.token_instruction();
94 base::TimeDelta::FromMilliseconds(directive
.ttl_millis());
95 const size_t token_length
= directive
.configuration().token_params().length();
97 switch (instruction
.token_instruction_type()) {
99 DVLOG(2) << "Audio Transmit Directive received. Token: "
100 << instruction
.token_id()
101 << " with medium=" << instruction
.medium()
102 << " with TTL=" << ttl
.InMilliseconds();
103 DCHECK_GT(token_length
, 0u);
104 switch (instruction
.medium()) {
105 case AUDIO_ULTRASOUND_PASSBAND
:
106 audio_modem_
->SetTokenParams(INAUDIBLE
,
107 TokenParameters(token_length
));
108 transmits_lists_
[INAUDIBLE
]->AddDirective(op_id
, directive
);
109 audio_modem_
->SetToken(INAUDIBLE
, instruction
.token_id());
111 case AUDIO_AUDIBLE_DTMF
:
112 audio_modem_
->SetTokenParams(AUDIBLE
, TokenParameters(token_length
));
113 transmits_lists_
[AUDIBLE
]->AddDirective(op_id
, directive
);
114 audio_modem_
->SetToken(AUDIBLE
, instruction
.token_id());
122 DVLOG(2) << "Audio Receive Directive received."
123 << " with medium=" << instruction
.medium()
124 << " with TTL=" << ttl
.InMilliseconds();
125 DCHECK_GT(token_length
, 0u);
126 switch (instruction
.medium()) {
127 case AUDIO_ULTRASOUND_PASSBAND
:
128 audio_modem_
->SetTokenParams(INAUDIBLE
,
129 TokenParameters(token_length
));
130 receives_lists_
[INAUDIBLE
]->AddDirective(op_id
, directive
);
132 case AUDIO_AUDIBLE_DTMF
:
133 audio_modem_
->SetTokenParams(AUDIBLE
, TokenParameters(token_length
));
134 receives_lists_
[AUDIBLE
]->AddDirective(op_id
, directive
);
141 case UNKNOWN_TOKEN_INSTRUCTION_TYPE
:
143 LOG(WARNING
) << "Unknown Audio Transmit Directive received. type = "
144 << instruction
.token_instruction_type();
147 ProcessNextInstruction();
150 void AudioDirectiveHandlerImpl::RemoveInstructions(const std::string
& op_id
) {
151 DCHECK(transmits_lists_
.size() == 2u && receives_lists_
.size() == 2u)
152 << "Call Initialize() before other AudioDirectiveHandler methods";
154 transmits_lists_
[AUDIBLE
]->RemoveDirective(op_id
);
155 transmits_lists_
[INAUDIBLE
]->RemoveDirective(op_id
);
156 receives_lists_
[AUDIBLE
]->RemoveDirective(op_id
);
157 receives_lists_
[INAUDIBLE
]->RemoveDirective(op_id
);
159 ProcessNextInstruction();
162 const std::string
AudioDirectiveHandlerImpl::PlayingToken(
163 audio_modem::AudioType type
) const {
164 return audio_modem_
->GetToken(type
);
167 bool AudioDirectiveHandlerImpl::IsPlayingTokenHeard(
168 audio_modem::AudioType type
) const {
169 return audio_modem_
->IsPlayingTokenHeard(type
);
173 // Private functions.
175 void AudioDirectiveHandlerImpl::ProcessNextInstruction() {
176 DCHECK(audio_event_timer_
);
177 audio_event_timer_
->Stop();
179 // Change |audio_modem_| state for audible transmits.
180 if (transmits_lists_
[AUDIBLE
]->GetActiveDirective())
181 audio_modem_
->StartPlaying(AUDIBLE
);
183 audio_modem_
->StopPlaying(AUDIBLE
);
185 // Change audio_modem_ state for inaudible transmits.
186 if (transmits_lists_
[INAUDIBLE
]->GetActiveDirective())
187 audio_modem_
->StartPlaying(INAUDIBLE
);
189 audio_modem_
->StopPlaying(INAUDIBLE
);
191 // Change audio_modem_ state for audible receives.
192 if (receives_lists_
[AUDIBLE
]->GetActiveDirective())
193 audio_modem_
->StartRecording(AUDIBLE
);
195 audio_modem_
->StopRecording(AUDIBLE
);
197 // Change audio_modem_ state for inaudible receives.
198 if (receives_lists_
[INAUDIBLE
]->GetActiveDirective())
199 audio_modem_
->StartRecording(INAUDIBLE
);
201 audio_modem_
->StopRecording(INAUDIBLE
);
203 base::TimeTicks next_event_time
;
204 if (GetNextInstructionExpiry(&next_event_time
)) {
205 audio_event_timer_
->Start(
207 next_event_time
- clock_
->NowTicks(),
208 base::Bind(&AudioDirectiveHandlerImpl::ProcessNextInstruction
,
209 base::Unretained(this)));
212 // TODO(crbug.com/436584): Instead of this, store the directives
213 // in a single list, and prune them when expired.
214 std::vector
<Directive
> directives
;
215 ConvertDirectives(transmits_lists_
[AUDIBLE
]->directives(), &directives
);
216 ConvertDirectives(transmits_lists_
[INAUDIBLE
]->directives(), &directives
);
217 ConvertDirectives(receives_lists_
[AUDIBLE
]->directives(), &directives
);
218 ConvertDirectives(receives_lists_
[INAUDIBLE
]->directives(), &directives
);
219 update_directives_callback_
.Run(directives
);
222 bool AudioDirectiveHandlerImpl::GetNextInstructionExpiry(
223 base::TimeTicks
* expiry
) {
226 *expiry
= GetEarliestEventTime(transmits_lists_
[AUDIBLE
], base::TimeTicks());
227 *expiry
= GetEarliestEventTime(transmits_lists_
[INAUDIBLE
], *expiry
);
228 *expiry
= GetEarliestEventTime(receives_lists_
[AUDIBLE
], *expiry
);
229 *expiry
= GetEarliestEventTime(receives_lists_
[INAUDIBLE
], *expiry
);
231 return !expiry
->is_null();
234 } // namespace copresence