Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(141)

Side by Side Diff: components/copresence/handlers/audio/audio_directive_handler.cc

Issue 637223011: Redesign the copresence audio handlers. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git/+/master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/copresence/handlers/audio/audio_directive_handler.h" 5 #include "components/copresence/handlers/audio/audio_directive_handler.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/memory/scoped_ptr.h" 9 #include "base/memory/scoped_ptr.h"
10 #include "base/strings/string_util.h" 10 #include "base/time/default_tick_clock.h"
11 #include "base/time/tick_clock.h"
11 #include "base/time/time.h" 12 #include "base/time/time.h"
Daniel Erat 2014/10/22 16:34:34 don't need this here; it's already included by the
rkc 2014/10/22 18:21:47 Done.
12 #include "components/copresence/mediums/audio/audio_player.h" 13 #include "base/timer/timer.h"
13 #include "components/copresence/mediums/audio/audio_recorder.h" 14 #include "components/copresence/mediums/audio/audio_manager_impl.h"
14 #include "components/copresence/proto/data.pb.h" 15 #include "components/copresence/proto/data.pb.h"
16 #include "components/copresence/public/copresence_constants.h"
15 #include "media/base/audio_bus.h" 17 #include "media/base/audio_bus.h"
16 18
19 namespace copresence {
20
17 namespace { 21 namespace {
18 22
19 // UrlSafe is defined as: 23 base::TimeTicks GetEarliestEventTime(AudioDirectiveList* list,
20 // '/' represented by a '_' and '+' represented by a '-' 24 base::TimeTicks event_time) {
21 // TODO(rkc): Move this processing to the whispernet wrapper. 25 if (!list->GetActiveDirective())
22 std::string FromUrlSafe(std::string token) { 26 return event_time;
23 base::ReplaceChars(token, "-", "+", &token); 27
24 base::ReplaceChars(token, "_", "/", &token); 28 if (event_time.is_null())
25 return token; 29 return list->GetActiveDirective()->end_time;
30 else
31 return std::min(list->GetActiveDirective()->end_time, event_time);
26 } 32 }
27 33
28 const int kSampleExpiryTimeMs = 60 * 60 * 1000; // 60 minutes.
29 const int kMaxSamples = 10000;
30
31 } // namespace 34 } // namespace
32 35
33 namespace copresence {
34
35 // Public methods. 36 // Public methods.
36 37
37 AudioDirectiveHandler::AudioDirectiveHandler( 38 AudioDirectiveHandler::AudioDirectiveHandler()
38 const AudioRecorder::DecodeSamplesCallback& decode_cb, 39 : audio_event_timer_(new base::OneShotTimer<AudioDirectiveHandler>),
39 const AudioDirectiveHandler::EncodeTokenCallback& encode_cb) 40 clock_(new base::DefaultTickClock) {
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 } 41 }
52 42
53 AudioDirectiveHandler::~AudioDirectiveHandler() { 43 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 } 44 }
61 45
62 void AudioDirectiveHandler::Initialize() { 46 void AudioDirectiveHandler::Initialize(
63 player_audible_ = new AudioPlayer(); 47 const AudioManager::DecodeSamplesCallback& decode_cb,
64 player_audible_->Initialize(); 48 const AudioManager::EncodeTokenCallback& encode_cb) {
65 49 if (!audio_manager_.get())
Daniel Erat 2014/10/22 16:34:34 nit: just do "if (!audio_manager_)"
rkc 2014/10/22 18:21:47 Done.
66 player_inaudible_ = new AudioPlayer(); 50 audio_manager_.reset(new AudioManagerImpl());
67 player_inaudible_->Initialize(); 51 audio_manager_->Initialize(decode_cb, encode_cb);
68
69 recorder_ = new AudioRecorder(decode_cb_);
70 recorder_->Initialize();
71 } 52 }
72 53
73 void AudioDirectiveHandler::AddInstruction(const TokenInstruction& instruction, 54 void AudioDirectiveHandler::AddInstruction(const TokenInstruction& instruction,
74 const std::string& op_id, 55 const std::string& op_id,
75 base::TimeDelta ttl) { 56 base::TimeDelta ttl) {
76 switch (instruction.token_instruction_type()) { 57 switch (instruction.token_instruction_type()) {
77 case TRANSMIT: 58 case TRANSMIT:
78 DVLOG(2) << "Audio Transmit Directive received. Token: " 59 DVLOG(2) << "Audio Transmit Directive received. Token: "
79 << instruction.token_id() 60 << instruction.token_id()
61 << " with medium= " << instruction.medium()
Daniel Erat 2014/10/22 16:34:34 nit: remove extra space after '=' to match the res
rkc 2014/10/22 18:21:47 Done.
80 << " with TTL=" << ttl.InMilliseconds(); 62 << " with TTL=" << ttl.InMilliseconds();
81 switch (instruction.medium()) { 63 switch (instruction.medium()) {
82 case AUDIO_ULTRASOUND_PASSBAND: 64 case AUDIO_ULTRASOUND_PASSBAND:
83 transmits_list_inaudible_.AddDirective(op_id, ttl); 65 transmits_list_[INAUDIBLE].AddDirective(op_id, ttl);
84 PlayToken(instruction.token_id(), false); 66 audio_manager_->SetToken(INAUDIBLE, instruction.token_id());
85 break; 67 break;
86 case AUDIO_AUDIBLE_DTMF: 68 case AUDIO_AUDIBLE_DTMF:
87 transmits_list_audible_.AddDirective(op_id, ttl); 69 transmits_list_[AUDIBLE].AddDirective(op_id, ttl);
88 PlayToken(instruction.token_id(), true); 70 audio_manager_->SetToken(AUDIBLE, instruction.token_id());
89 break; 71 break;
90 default: 72 default:
91 NOTREACHED(); 73 NOTREACHED();
92 } 74 }
93 break; 75 break;
94 case RECEIVE: 76 case RECEIVE:
95 DVLOG(2) << "Audio Receive Directive received. TTL=" 77 DVLOG(2) << "Audio Receive Directive received."
96 << ttl.InMilliseconds(); 78 << " with medium= " << instruction.medium()
Daniel Erat 2014/10/22 16:34:34 same here
rkc 2014/10/22 18:21:47 Done.
97 receives_list_.AddDirective(op_id, ttl); 79 << " with TTL=" << ttl.InMilliseconds();
98 ProcessNextReceive(); 80 switch (instruction.medium()) {
81 case AUDIO_ULTRASOUND_PASSBAND:
82 receives_list_[INAUDIBLE].AddDirective(op_id, ttl);
83 break;
84 case AUDIO_AUDIBLE_DTMF:
85 receives_list_[AUDIBLE].AddDirective(op_id, ttl);
86 break;
87 default:
88 NOTREACHED();
89 }
99 break; 90 break;
100 case UNKNOWN_TOKEN_INSTRUCTION_TYPE: 91 case UNKNOWN_TOKEN_INSTRUCTION_TYPE:
101 default: 92 default:
102 LOG(WARNING) << "Unknown Audio Transmit Directive received."; 93 LOG(WARNING) << "Unknown Audio Transmit Directive received. type = "
94 << instruction.token_instruction_type();
103 } 95 }
96 ProcessNextInstruction();
104 } 97 }
105 98
106 void AudioDirectiveHandler::RemoveInstructions(const std::string& op_id) { 99 void AudioDirectiveHandler::RemoveInstructions(const std::string& op_id) {
107 transmits_list_audible_.RemoveDirective(op_id); 100 transmits_list_[AUDIBLE].RemoveDirective(op_id);
108 transmits_list_inaudible_.RemoveDirective(op_id); 101 transmits_list_[INAUDIBLE].RemoveDirective(op_id);
109 receives_list_.RemoveDirective(op_id); 102 receives_list_[AUDIBLE].RemoveDirective(op_id);
103 receives_list_[INAUDIBLE].RemoveDirective(op_id);
110 104
111 ProcessNextTransmit(); 105 ProcessNextInstruction();
112 ProcessNextReceive();
113 } 106 }
114 107
115 // Private methods. 108 // Private methods.
116 109
117 void AudioDirectiveHandler::ProcessNextTransmit() { 110 void AudioDirectiveHandler::ProcessNextInstruction() {
118 // If we have an active directive for audible or inaudible audio, ensure that 111 DCHECK(audio_event_timer_);
119 // we are playing our respective token; if we do not have a directive, then 112 audio_event_timer_->Stop();
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 113
123 scoped_ptr<AudioDirective> audible_transmit( 114 // Change audio_manager_ state for audible transmits.
Daniel Erat 2014/10/22 16:34:34 nit: s/audio_manager_/|audio_manager_|/
rkc 2014/10/22 18:21:47 Done.
124 transmits_list_audible_.GetActiveDirective()); 115 if (transmits_list_[AUDIBLE].GetActiveDirective())
125 if (audible_transmit && !player_audible_->IsPlaying() && 116 audio_manager_->StartPlaying(AUDIBLE);
126 samples_cache_audible_.HasKey(current_token_audible_)) { 117 else
127 DVLOG(3) << "Playing audible for op_id: " << audible_transmit->op_id; 118 audio_manager_->StopPlaying(AUDIBLE);
128 player_audible_->Play( 119
129 samples_cache_audible_.GetValue(current_token_audible_)); 120 // Change audio_manager_ state for inaudible transmits.
130 stop_audible_playback_timer_.Start( 121 if (transmits_list_[INAUDIBLE].GetActiveDirective())
122 audio_manager_->StartPlaying(INAUDIBLE);
123 else
124 audio_manager_->StopPlaying(INAUDIBLE);
125
126 // Change audio_manager_ state for audible receives.
127 if (receives_list_[AUDIBLE].GetActiveDirective())
128 audio_manager_->StartRecording(AUDIBLE);
129 else
130 audio_manager_->StopRecording(AUDIBLE);
131
132 // Change audio_manager_ state for inaudible receives.
133 if (receives_list_[INAUDIBLE].GetActiveDirective())
134 audio_manager_->StartRecording(INAUDIBLE);
135 else
136 audio_manager_->StopRecording(INAUDIBLE);
137
138 base::TimeTicks next_event_time;
139 if (GetNextInstructionExpiry(&next_event_time)) {
140 // This timer will be destructed before us, hence it is safe to use
Daniel Erat 2014/10/22 16:34:34 nit: i don't think that this comment is necessary;
rkc 2014/10/22 18:21:47 Done.
141 // unretained here.
142 audio_event_timer_->Start(
131 FROM_HERE, 143 FROM_HERE,
132 audible_transmit->end_time - base::Time::Now(), 144 next_event_time - clock_->NowTicks(),
133 this, 145 base::Bind(&AudioDirectiveHandler::ProcessNextInstruction,
134 &AudioDirectiveHandler::ProcessNextTransmit); 146 base::Unretained(this)));
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 samples_cache_inaudible_.HasKey(current_token_inaudible_)) {
146 DVLOG(3) << "Playing inaudible for op_id: " << inaudible_transmit->op_id;
147 player_inaudible_->Play(
148 samples_cache_inaudible_.GetValue(current_token_inaudible_));
149 stop_inaudible_playback_timer_.Start(
150 FROM_HERE,
151 inaudible_transmit->end_time - base::Time::Now(),
152 this,
153 &AudioDirectiveHandler::ProcessNextTransmit);
154 } else if (!inaudible_transmit && player_inaudible_->IsPlaying()) {
155 DVLOG(3) << "Stopping inaudible playback.";
156 current_token_inaudible_.clear();
157 stop_inaudible_playback_timer_.Stop();
158 player_inaudible_->Stop();
159 } 147 }
160 } 148 }
161 149
162 void AudioDirectiveHandler::ProcessNextReceive() { 150 bool AudioDirectiveHandler::GetNextInstructionExpiry(
163 scoped_ptr<AudioDirective> receive(receives_list_.GetActiveDirective()); 151 base::TimeTicks* next_event_ptr) {
152 DCHECK(next_event_ptr);
153 base::TimeTicks& next_event = *next_event_ptr;
Daniel Erat 2014/10/22 16:34:34 nit: rename the argument to |expiry_out| and get r
rkc 2014/10/22 18:21:47 Renaming to expiry does prevent wrapping on all li
164 154
165 if (receive && !recorder_->IsRecording()) { 155 next_event =
166 DVLOG(3) << "Recording for op_id: " << receive->op_id; 156 GetEarliestEventTime(&transmits_list_[AUDIBLE], base::TimeTicks());
167 recorder_->Record(); 157 next_event = GetEarliestEventTime(&transmits_list_[INAUDIBLE], next_event);
168 stop_recording_timer_.Start(FROM_HERE, 158 next_event = GetEarliestEventTime(&receives_list_[AUDIBLE], next_event);
169 receive->end_time - base::Time::Now(), 159 next_event = GetEarliestEventTime(&receives_list_[INAUDIBLE], next_event);
170 this,
171 &AudioDirectiveHandler::ProcessNextReceive);
172 } else if (!receive && recorder_->IsRecording()) {
173 DVLOG(3) << "Stopping Recording";
174 stop_recording_timer_.Stop();
175 recorder_->Stop();
176 }
177 }
178 160
179 void AudioDirectiveHandler::PlayToken(const std::string token, bool audible) { 161 // If our next event is still at max, means we don't have any active
Daniel Erat 2014/10/22 16:34:34 this max comment is obsolete
rkc 2014/10/22 18:21:47 Done.
180 std::string valid_token = FromUrlSafe(token); 162 // directives; or that they are all due to expire a bazzilion years from now.
Daniel Erat 2014/10/22 16:34:34 s/bazzilion/bazillion/
rkc 2014/10/22 18:21:47 I didn't know there was a widely accepted spelling
181 163 // The latter is not possible since the max life of a directive is 1 day,
182 // If the token has been encoded already, use the cached samples. 164 // this is enforced by the API.
183 if (audible && samples_cache_audible_.HasKey(valid_token)) { 165 if (next_event.is_null())
Daniel Erat 2014/10/22 16:34:34 nit: "return !expiry_out->is_null();"
rkc 2014/10/22 18:21:47 Done.
184 current_token_audible_ = token; 166 return false;
185 ProcessNextTransmit(); 167 else
186 } else if (!audible && samples_cache_inaudible_.HasKey(valid_token)) { 168 return true;
187 current_token_inaudible_ = token;
188 ProcessNextTransmit();
189 } else {
190 // Otherwise, encode the token and then play it.
191 encode_cb_.Run(valid_token,
192 audible,
193 base::Bind(&AudioDirectiveHandler::PlayEncodedToken,
194 base::Unretained(this)));
195 }
196 }
197
198 void AudioDirectiveHandler::PlayEncodedToken(
199 const std::string& token,
200 bool audible,
201 const scoped_refptr<media::AudioBusRefCounted>& samples) {
202 DVLOG(3) << "Token " << token << "[audible:" << audible << "] encoded.";
203 if (audible) {
204 samples_cache_audible_.Add(token, samples);
205 current_token_audible_ = token;
206 // Force process transmits to pick up the new token.
207 if (player_audible_->IsPlaying())
208 player_audible_->Stop();
209 } else {
210 samples_cache_inaudible_.Add(token, samples);
211 current_token_inaudible_ = token;
212 // Force process transmits to pick up the new token.
213 if (player_inaudible_->IsPlaying())
214 player_inaudible_->Stop();
215 }
216
217 ProcessNextTransmit();
218 } 169 }
219 170
220 } // namespace copresence 171 } // namespace copresence
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698