| 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 |