audio_directive_handler.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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.h" 6 7#include "base/bind.h" 8#include "base/logging.h" 9#include "base/memory/scoped_ptr.h" 10#include "base/strings/string_util.h" 11#include "base/time/time.h" 12#include "components/copresence/mediums/audio/audio_player.h" 13#include "components/copresence/mediums/audio/audio_recorder.h" 14#include "components/copresence/proto/data.pb.h" 15#include "media/base/audio_bus.h" 16 17namespace { 18 19// UrlSafe is defined as: 20// '/' represented by a '_' and '+' represented by a '-' 21// TODO(rkc): Move this processing to the whispernet wrapper. 22std::string FromUrlSafe(std::string token) { 23 base::ReplaceChars(token, "-", "+", &token); 24 base::ReplaceChars(token, "_", "/", &token); 25 return token; 26} 27 28const int kSampleExpiryTimeMs = 60 * 60 * 1000; // 60 minutes. 29const int kMaxSamples = 10000; 30 31} // namespace 32 33namespace copresence { 34 35// Public methods. 36 37AudioDirectiveHandler::AudioDirectiveHandler( 38 const AudioRecorder::DecodeSamplesCallback& decode_cb, 39 const AudioDirectiveHandler::EncodeTokenCallback& encode_cb) 40 : player_audible_(NULL), 41 player_inaudible_(NULL), 42 recorder_(NULL), 43 decode_cb_(decode_cb), 44 encode_cb_(encode_cb), 45 samples_cache_audible_( 46 base::TimeDelta::FromMilliseconds(kSampleExpiryTimeMs), 47 kMaxSamples), 48 samples_cache_inaudible_( 49 base::TimeDelta::FromMilliseconds(kSampleExpiryTimeMs), 50 kMaxSamples) { 51} 52 53AudioDirectiveHandler::~AudioDirectiveHandler() { 54 if (player_audible_) 55 player_audible_->Finalize(); 56 if (player_inaudible_) 57 player_inaudible_->Finalize(); 58 if (recorder_) 59 recorder_->Finalize(); 60} 61 62void AudioDirectiveHandler::Initialize() { 63 player_audible_ = new AudioPlayer(); 64 player_audible_->Initialize(); 65 66 player_inaudible_ = new AudioPlayer(); 67 player_inaudible_->Initialize(); 68 69 recorder_ = new AudioRecorder(decode_cb_); 70 recorder_->Initialize(); 71} 72 73void AudioDirectiveHandler::AddInstruction(const TokenInstruction& instruction, 74 const std::string& op_id, 75 base::TimeDelta ttl) { 76 switch (instruction.token_instruction_type()) { 77 case TRANSMIT: 78 DVLOG(2) << "Audio Transmit Directive received. Token: " 79 << instruction.token_id() 80 << " with TTL=" << ttl.InMilliseconds(); 81 switch (instruction.medium()) { 82 case AUDIO_ULTRASOUND_PASSBAND: 83 transmits_list_inaudible_.AddDirective(op_id, ttl); 84 PlayToken(instruction.token_id(), false); 85 break; 86 case AUDIO_AUDIBLE_DTMF: 87 transmits_list_audible_.AddDirective(op_id, ttl); 88 PlayToken(instruction.token_id(), true); 89 break; 90 default: 91 NOTREACHED(); 92 } 93 break; 94 case RECEIVE: 95 DVLOG(2) << "Audio Receive Directive received. TTL=" 96 << ttl.InMilliseconds(); 97 receives_list_.AddDirective(op_id, ttl); 98 ProcessNextReceive(); 99 break; 100 case UNKNOWN_TOKEN_INSTRUCTION_TYPE: 101 default: 102 LOG(WARNING) << "Unknown Audio Transmit Directive received."; 103 } 104} 105 106void AudioDirectiveHandler::RemoveInstructions(const std::string& op_id) { 107 transmits_list_audible_.RemoveDirective(op_id); 108 transmits_list_inaudible_.RemoveDirective(op_id); 109 receives_list_.RemoveDirective(op_id); 110 111 ProcessNextTransmit(); 112 ProcessNextReceive(); 113} 114 115// Private methods. 116 117void AudioDirectiveHandler::ProcessNextTransmit() { 118 // If we have an active directive for audible or inaudible audio, ensure that 119 // we are playing our respective token; if we do not have a directive, then 120 // make sure we aren't playing. This is duplicate code, but for just two 121 // elements, it has hard to make a case for processing a loop instead. 122 123 scoped_ptr<AudioDirective> audible_transmit( 124 transmits_list_audible_.GetActiveDirective()); 125 if (audible_transmit && !player_audible_->IsPlaying()) { 126 DVLOG(3) << "Playing audible for op_id: " << audible_transmit->op_id; 127 player_audible_->Play( 128 samples_cache_audible_.GetValue(current_token_audible_)); 129 stop_audible_playback_timer_.Start( 130 FROM_HERE, 131 audible_transmit->end_time - base::Time::Now(), 132 this, 133 &AudioDirectiveHandler::ProcessNextTransmit); 134 } else if (!audible_transmit && player_audible_->IsPlaying()) { 135 DVLOG(3) << "Stopping audible playback."; 136 current_token_audible_.clear(); 137 stop_audible_playback_timer_.Stop(); 138 player_audible_->Stop(); 139 } 140 141 scoped_ptr<AudioDirective> inaudible_transmit( 142 transmits_list_inaudible_.GetActiveDirective()); 143 if (inaudible_transmit && !player_inaudible_->IsPlaying()) { 144 DVLOG(3) << "Playing inaudible for op_id: " << inaudible_transmit->op_id; 145 player_inaudible_->Play( 146 samples_cache_inaudible_.GetValue(current_token_inaudible_)); 147 stop_inaudible_playback_timer_.Start( 148 FROM_HERE, 149 inaudible_transmit->end_time - base::Time::Now(), 150 this, 151 &AudioDirectiveHandler::ProcessNextTransmit); 152 } else if (!inaudible_transmit && player_inaudible_->IsPlaying()) { 153 DVLOG(3) << "Stopping inaudible playback."; 154 current_token_inaudible_.clear(); 155 stop_inaudible_playback_timer_.Stop(); 156 player_inaudible_->Stop(); 157 } 158} 159 160void AudioDirectiveHandler::ProcessNextReceive() { 161 scoped_ptr<AudioDirective> receive(receives_list_.GetActiveDirective()); 162 163 if (receive && !recorder_->IsRecording()) { 164 DVLOG(3) << "Recording for op_id: " << receive->op_id; 165 recorder_->Record(); 166 stop_recording_timer_.Start(FROM_HERE, 167 receive->end_time - base::Time::Now(), 168 this, 169 &AudioDirectiveHandler::ProcessNextReceive); 170 } else if (!receive && recorder_->IsRecording()) { 171 DVLOG(3) << "Stopping Recording"; 172 stop_recording_timer_.Stop(); 173 recorder_->Stop(); 174 } 175} 176 177void AudioDirectiveHandler::PlayToken(const std::string token, bool audible) { 178 std::string valid_token = FromUrlSafe(token); 179 180 // If the token has been encoded already, use the cached samples. 181 if (audible && samples_cache_audible_.HasKey(valid_token)) { 182 current_token_audible_ = token; 183 ProcessNextTransmit(); 184 } else if (!audible && samples_cache_inaudible_.HasKey(valid_token)) { 185 current_token_inaudible_ = token; 186 ProcessNextTransmit(); 187 } else { 188 // Otherwise, encode the token and then play it. 189 encode_cb_.Run(valid_token, 190 audible, 191 base::Bind(&AudioDirectiveHandler::PlayEncodedToken, 192 base::Unretained(this))); 193 } 194} 195 196void AudioDirectiveHandler::PlayEncodedToken( 197 const std::string& token, 198 bool audible, 199 const scoped_refptr<media::AudioBusRefCounted>& samples) { 200 DVLOG(3) << "Token " << token << "[audible:" << audible << "] encoded."; 201 if (audible) { 202 samples_cache_audible_.Add(token, samples); 203 current_token_audible_ = token; 204 // Force process transmits to pick up the new token. 205 if (player_audible_->IsPlaying()) 206 player_audible_->Stop(); 207 } else { 208 samples_cache_inaudible_.Add(token, samples); 209 current_token_inaudible_ = token; 210 // Force process transmits to pick up the new token. 211 if (player_inaudible_->IsPlaying()) 212 player_inaudible_->Stop(); 213 } 214 215 ProcessNextTransmit(); 216} 217 218} // namespace copresence 219