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