Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / components / copresence / handlers / audio / audio_directive_handler.cc
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
17 namespace {
18
19 // UrlSafe is defined as:
20 // '/' represented by a '_' and '+' represented by a '-'
21 // TODO(rkc): Move this processing to the whispernet wrapper.
22 std::string FromUrlSafe(std::string token) {
23   base::ReplaceChars(token, "-", "+", &token);
24   base::ReplaceChars(token, "_", "/", &token);
25   return token;
26 }
27
28 const int kSampleExpiryTimeMs = 60 * 60 * 1000;  // 60 minutes.
29 const int kMaxSamples = 10000;
30
31 }  // namespace
32
33 namespace copresence {
34
35 // Public methods.
36
37 AudioDirectiveHandler::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
53 AudioDirectiveHandler::~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
62 void 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
73 void 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
107 void 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
118 void 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
161 void 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
178 void 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
199 void 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