audio_directive_handler.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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 HandleToken(instruction.token_id(), false); 85 break; 86 case AUDIO_AUDIBLE_DTMF: 87 transmits_list_audible_.AddDirective(op_id, ttl); 88 HandleToken(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 break; 99 case UNKNOWN_TOKEN_INSTRUCTION_TYPE: 100 default: 101 LOG(WARNING) << "Unknown Audio Transmit Directive received."; 102 } 103 // ExecuteNextTransmit will be called by directive_list_ when Add is done. 104 ProcessNextReceive(); 105} 106 107void AudioDirectiveHandler::RemoveInstructions(const std::string& op_id) { 108 transmits_list_audible_.RemoveDirective(op_id); 109 transmits_list_inaudible_.RemoveDirective(op_id); 110 receives_list_.RemoveDirective(op_id); 111 112 ProcessNextTransmit(); 113 ProcessNextReceive(); 114} 115 116// Private methods. 117 118void AudioDirectiveHandler::ProcessNextTransmit() { 119 // If we have an active directive for audible or inaudible audio, ensure that 120 // we are playing our respective token; if we do not have a directive, then 121 // make sure we aren't playing. This is duplicate code, but for just two 122 // elements, it has hard to make a case for processing a loop instead. 123 124 scoped_ptr<AudioDirective> audible_transmit( 125 transmits_list_audible_.GetActiveDirective()); 126 if (audible_transmit && !player_audible_->IsPlaying()) { 127 DVLOG(3) << "Playing audible for op_id: " << audible_transmit->op_id; 128 player_audible_->Play( 129 samples_cache_audible_.GetValue(current_token_audible_)); 130 stop_audible_playback_timer_.Start( 131 FROM_HERE, 132 audible_transmit->end_time - base::Time::Now(), 133 this, 134 &AudioDirectiveHandler::ProcessNextTransmit); 135 } else if (!audible_transmit && player_audible_->IsPlaying()) { 136 DVLOG(3) << "Stopping audible playback."; 137 current_token_audible_.clear(); 138 stop_audible_playback_timer_.Stop(); 139 player_audible_->Stop(); 140 } 141 142 scoped_ptr<AudioDirective> inaudible_transmit( 143 transmits_list_inaudible_.GetActiveDirective()); 144 if (inaudible_transmit && !player_inaudible_->IsPlaying()) { 145 DVLOG(3) << "Playing inaudible for op_id: " << inaudible_transmit->op_id; 146 player_inaudible_->Play( 147 samples_cache_inaudible_.GetValue(current_token_inaudible_)); 148 stop_inaudible_playback_timer_.Start( 149 FROM_HERE, 150 inaudible_transmit->end_time - base::Time::Now(), 151 this, 152 &AudioDirectiveHandler::ProcessNextTransmit); 153 } else if (!inaudible_transmit && player_inaudible_->IsPlaying()) { 154 DVLOG(3) << "Stopping inaudible playback."; 155 current_token_inaudible_.clear(); 156 stop_inaudible_playback_timer_.Stop(); 157 player_inaudible_->Stop(); 158 } 159} 160 161void AudioDirectiveHandler::ProcessNextReceive() { 162 scoped_ptr<AudioDirective> receive(receives_list_.GetActiveDirective()); 163 164 if (receive && !recorder_->IsRecording()) { 165 DVLOG(3) << "Recording for op_id: " << receive->op_id; 166 recorder_->Record(); 167 stop_recording_timer_.Start(FROM_HERE, 168 receive->end_time - base::Time::Now(), 169 this, 170 &AudioDirectiveHandler::ProcessNextReceive); 171 } else if (!receive && recorder_->IsRecording()) { 172 DVLOG(3) << "Stopping Recording"; 173 stop_recording_timer_.Stop(); 174 recorder_->Stop(); 175 } 176} 177 178void AudioDirectiveHandler::HandleToken(const std::string token, bool audible) { 179 std::string valid_token = FromUrlSafe(token); 180 181 if (audible && samples_cache_audible_.HasKey(valid_token)) { 182 current_token_audible_ = token; 183 ProcessNextTransmit(); 184 return; 185 } 186 187 if (!audible && samples_cache_inaudible_.HasKey(valid_token)) { 188 current_token_inaudible_ = token; 189 ProcessNextTransmit(); 190 return; 191 } 192 193 encode_cb_.Run(valid_token, 194 audible, 195 base::Bind(&AudioDirectiveHandler::OnTokenEncoded, 196 base::Unretained(this))); 197} 198 199void AudioDirectiveHandler::OnTokenEncoded( 200 const std::string& token, 201 bool audible, 202 const scoped_refptr<media::AudioBusRefCounted>& samples) { 203 DVLOG(3) << "Token: " << token << "[audible:" << audible << "] encoded."; 204 if (audible) { 205 samples_cache_audible_.Add(token, samples); 206 current_token_audible_ = token; 207 // Force process transmits to pick up the new token. 208 if (player_audible_->IsPlaying()) 209 player_audible_->Stop(); 210 } else { 211 samples_cache_inaudible_.Add(token, samples); 212 current_token_inaudible_ = token; 213 // Force process transmits to pick up the new token. 214 if (player_inaudible_->IsPlaying()) 215 player_inaudible_->Stop(); 216 } 217 218 ProcessNextTransmit(); 219} 220 221} // namespace copresence 222