Chromium Code Reviews| Index: components/copresence/handlers/audio/audio_directive_handler.cc |
| diff --git a/components/copresence/handlers/audio/audio_directive_handler.cc b/components/copresence/handlers/audio/audio_directive_handler.cc |
| index a5d3435c3c4864d71e83ef20d4416dccba1514cd..258a9695c9588788ec4448b999f016460629e6ca 100644 |
| --- a/components/copresence/handlers/audio/audio_directive_handler.cc |
| +++ b/components/copresence/handlers/audio/audio_directive_handler.cc |
| @@ -4,70 +4,88 @@ |
| #include "components/copresence/handlers/audio/audio_directive_handler.h" |
| -#include <algorithm> |
| - |
| #include "base/bind.h" |
| #include "base/logging.h" |
| #include "base/memory/scoped_ptr.h" |
| +#include "base/strings/string_util.h" |
| #include "base/time/time.h" |
| #include "components/copresence/mediums/audio/audio_player.h" |
| #include "components/copresence/mediums/audio/audio_recorder.h" |
| #include "components/copresence/proto/data.pb.h" |
| #include "media/base/audio_bus.h" |
| +namespace { |
| + |
| +// UrlSafe is defined as: |
| +// '/' represented by a '_' and '+' represented by a '-' |
| +// TODO(rkc): Move this processing to the whispernet wrapper. |
| +std::string FromUrlSafe(std::string token) { |
| + base::ReplaceChars(token, "-", "+", &token); |
| + base::ReplaceChars(token, "_", "/", &token); |
| + return token; |
| +} |
| + |
| +const int kSampleExpiryTimeMs = 60 * 60 * 1000; // 60 minutes. |
| +const int kMaxSamples = 10000; |
| + |
| +} // namespace |
| + |
| namespace copresence { |
| // Public methods. |
| AudioDirectiveHandler::AudioDirectiveHandler( |
| const AudioRecorder::DecodeSamplesCallback& decode_cb, |
| - const AudioDirectiveList::EncodeTokenCallback& encode_cb) |
| - : directive_list_inaudible_( |
| - encode_cb, |
| - base::Bind(&AudioDirectiveHandler::ExecuteNextTransmit, |
| - base::Unretained(this)), |
| - false), |
| - directive_list_audible_( |
| - encode_cb, |
| - base::Bind(&AudioDirectiveHandler::ExecuteNextTransmit, |
| - base::Unretained(this)), |
| - true), |
| - player_(NULL), |
| + const AudioDirectiveHandler::EncodeTokenCallback& encode_cb) |
| + : player_audible_(NULL), |
| + player_inaudible_(NULL), |
| recorder_(NULL), |
| - decode_cb_(decode_cb) { |
| + decode_cb_(decode_cb), |
| + encode_cb_(encode_cb), |
| + samples_cache_audible_( |
| + base::TimeDelta::FromMilliseconds(kSampleExpiryTimeMs), |
| + kMaxSamples), |
| + samples_cache_inaudible_( |
| + base::TimeDelta::FromMilliseconds(kSampleExpiryTimeMs), |
| + kMaxSamples) { |
| } |
| AudioDirectiveHandler::~AudioDirectiveHandler() { |
| - if (player_) |
| - player_->Finalize(); |
| + if (player_audible_) |
| + player_audible_->Finalize(); |
| + if (player_inaudible_) |
| + player_inaudible_->Finalize(); |
| if (recorder_) |
| recorder_->Finalize(); |
| } |
| void AudioDirectiveHandler::Initialize() { |
| - player_ = new AudioPlayer(); |
| - player_->Initialize(); |
| + player_audible_ = new AudioPlayer(); |
| + player_audible_->Initialize(); |
| + |
| + player_inaudible_ = new AudioPlayer(); |
| + player_inaudible_->Initialize(); |
| recorder_ = new AudioRecorder(decode_cb_); |
| recorder_->Initialize(); |
| } |
| void AudioDirectiveHandler::AddInstruction(const TokenInstruction& instruction, |
| + const std::string& op_id, |
| base::TimeDelta ttl) { |
| switch (instruction.token_instruction_type()) { |
| case TRANSMIT: |
| DVLOG(2) << "Audio Transmit Directive received. Token: " |
| << instruction.token_id() |
| << " with TTL=" << ttl.InMilliseconds(); |
| - // TODO(rkc): Fill in the op_id once we get it from the directive. |
| switch (instruction.medium()) { |
| case AUDIO_ULTRASOUND_PASSBAND: |
| - directive_list_inaudible_.AddTransmitDirective( |
| - instruction.token_id(), std::string(), ttl); |
| + transmits_list_inaudible_.AddDirective(op_id, ttl); |
| + HandleToken(instruction.token_id(), false); |
| break; |
| case AUDIO_AUDIBLE_DTMF: |
| - directive_list_audible_.AddTransmitDirective( |
| - instruction.token_id(), std::string(), ttl); |
| + transmits_list_audible_.AddDirective(op_id, ttl); |
| + HandleToken(instruction.token_id(), true); |
| break; |
| default: |
| NOTREACHED(); |
| @@ -76,88 +94,136 @@ void AudioDirectiveHandler::AddInstruction(const TokenInstruction& instruction, |
| case RECEIVE: |
| DVLOG(2) << "Audio Receive Directive received. TTL=" |
| << ttl.InMilliseconds(); |
| - // TODO(rkc): Fill in the op_id once we get it from the directive. |
| - switch (instruction.medium()) { |
| - case AUDIO_ULTRASOUND_PASSBAND: |
| - directive_list_inaudible_.AddReceiveDirective(std::string(), ttl); |
| - break; |
| - case AUDIO_AUDIBLE_DTMF: |
| - directive_list_audible_.AddReceiveDirective(std::string(), ttl); |
| - break; |
| - default: |
| - NOTREACHED(); |
| - } |
| + receives_list_.AddDirective(op_id, ttl); |
| break; |
| case UNKNOWN_TOKEN_INSTRUCTION_TYPE: |
| default: |
| LOG(WARNING) << "Unknown Audio Transmit Directive received."; |
| } |
| // ExecuteNextTransmit will be called by directive_list_ when Add is done. |
| - ExecuteNextReceive(); |
| + ProcessNextReceive(); |
| } |
| -// Protected methods. |
| +void AudioDirectiveHandler::RemoveInstructions(const std::string& op_id) { |
| + transmits_list_audible_.RemoveDirective(op_id); |
| + transmits_list_inaudible_.RemoveDirective(op_id); |
| + receives_list_.RemoveDirective(op_id); |
| + |
| + ProcessNextTransmit(); |
| + ProcessNextReceive(); |
| +} |
| -void AudioDirectiveHandler::PlayAudio( |
| - const scoped_refptr<media::AudioBusRefCounted>& samples, |
| - base::TimeDelta duration) { |
| - player_->Play(samples); |
| - stop_playback_timer_.Start( |
| - FROM_HERE, duration, this, &AudioDirectiveHandler::StopPlayback); |
| +const std::string& AudioDirectiveHandler::PlayingAudibleToken() { |
| + return current_token_audible_; |
| } |
| -void AudioDirectiveHandler::RecordAudio(base::TimeDelta duration) { |
| - recorder_->Record(); |
| - stop_recording_timer_.Start( |
| - FROM_HERE, duration, this, &AudioDirectiveHandler::StopRecording); |
| +const std::string& AudioDirectiveHandler::PlayingInaudibleToken() { |
| + return current_token_inaudible_; |
| } |
| // Private methods. |
| -void AudioDirectiveHandler::StopPlayback() { |
| - player_->Stop(); |
| - DVLOG(2) << "Done playing audio."; |
| - ExecuteNextTransmit(); |
| +void AudioDirectiveHandler::ProcessNextTransmit() { |
| + // If we have an active directive for audible or inaudible audio, ensure that |
| + // we are playing our respective token; if we do not have a directive, then |
| + // make sure we aren't playing. This is duplicate code, but for just two |
| + // elements, it has hard to make a case for processing a loop instead. |
| + |
| + scoped_ptr<AudioDirective> audible_transmit( |
| + transmits_list_audible_.GetActiveDirective()); |
| + if (audible_transmit && !player_audible_->IsPlaying()) { |
| + DVLOG(3) << "Playing audible for op_id: " << audible_transmit->op_id; |
| + player_audible_->Play( |
| + samples_cache_audible_.GetValue(current_token_audible_)); |
| + stop_audible_playback_timer_.Start( |
| + FROM_HERE, |
| + audible_transmit->end_time - base::Time::Now(), |
| + this, |
| + &AudioDirectiveHandler::ProcessNextTransmit); |
| + } else if (!audible_transmit && player_audible_->IsPlaying()) { |
| + DVLOG(3) << "Stopping audible playback."; |
| + current_token_audible_.clear(); |
| + stop_audible_playback_timer_.Stop(); |
| + player_audible_->Stop(); |
| + } |
| + |
| + scoped_ptr<AudioDirective> inaudible_transmit( |
| + transmits_list_inaudible_.GetActiveDirective()); |
| + if (inaudible_transmit && !player_inaudible_->IsPlaying()) { |
| + DVLOG(3) << "Playing inaudible for op_id: " << inaudible_transmit->op_id; |
| + player_inaudible_->Play( |
| + samples_cache_inaudible_.GetValue(current_token_inaudible_)); |
| + stop_inaudible_playback_timer_.Start( |
| + FROM_HERE, |
| + inaudible_transmit->end_time - base::Time::Now(), |
| + this, |
| + &AudioDirectiveHandler::ProcessNextTransmit); |
| + } else if (!inaudible_transmit && player_inaudible_->IsPlaying()) { |
| + DVLOG(3) << "Stopping inaudible playback."; |
| + current_token_inaudible_.clear(); |
| + stop_inaudible_playback_timer_.Stop(); |
| + player_inaudible_->Stop(); |
| + } |
| } |
| -void AudioDirectiveHandler::StopRecording() { |
| - recorder_->Stop(); |
| - DVLOG(2) << "Done recording audio."; |
| - ExecuteNextReceive(); |
| +void AudioDirectiveHandler::ProcessNextReceive() { |
| + scoped_ptr<AudioDirective> receive(receives_list_.GetActiveDirective()); |
|
xiyuan
2014/08/12 18:57:50
So we no longer need to differentiate audible rece
rkc
2014/08/13 00:28:59
Nope. I didn't realize this earlier but we actuall
|
| + |
| + if (receive && !recorder_->IsRecording()) { |
| + DVLOG(3) << "Recording for op_id: " << receive->op_id; |
| + recorder_->Record(); |
| + stop_recording_timer_.Start(FROM_HERE, |
| + receive->end_time - base::Time::Now(), |
| + this, |
| + &AudioDirectiveHandler::ProcessNextReceive); |
| + } else if (!receive && recorder_->IsRecording()) { |
| + DVLOG(3) << "Stopping Recording"; |
| + stop_recording_timer_.Stop(); |
| + recorder_->Stop(); |
| + } |
| } |
| -void AudioDirectiveHandler::ExecuteNextTransmit() { |
| - scoped_ptr<AudioDirective> audible_transmit( |
| - directive_list_audible_.GetNextTransmit()); |
| - scoped_ptr<AudioDirective> inaudible_transmit( |
| - directive_list_inaudible_.GetNextTransmit()); |
| +void AudioDirectiveHandler::HandleToken(const std::string token, bool audible) { |
| + std::string valid_token = FromUrlSafe(token); |
| - if (inaudible_transmit) { |
| - PlayAudio(inaudible_transmit->samples, |
| - inaudible_transmit->end_time - base::Time::Now()); |
| + if (audible && samples_cache_audible_.HasKey(valid_token)) { |
| + current_token_audible_ = token; |
| + ProcessNextTransmit(); |
| + return; |
| } |
| - if (audible_transmit) { |
| - PlayAudio(audible_transmit->samples, |
| - audible_transmit->end_time - base::Time::Now()); |
| + |
| + if (!audible && samples_cache_inaudible_.HasKey(valid_token)) { |
| + current_token_inaudible_ = token; |
| + ProcessNextTransmit(); |
| + return; |
| } |
| + |
| + encode_cb_.Run(valid_token, |
| + audible, |
| + base::Bind(&AudioDirectiveHandler::OnTokenEncoded, |
| + base::Unretained(this))); |
| } |
| -void AudioDirectiveHandler::ExecuteNextReceive() { |
| - scoped_ptr<AudioDirective> audible_receive( |
| - directive_list_audible_.GetNextReceive()); |
| - scoped_ptr<AudioDirective> inaudible_receive( |
| - directive_list_inaudible_.GetNextReceive()); |
| - |
| - base::TimeDelta record_duration; |
| - if (inaudible_receive) |
| - record_duration = inaudible_receive->end_time - base::Time::Now(); |
| - if (audible_receive) { |
| - record_duration = std::max(record_duration, |
| - audible_receive->end_time - base::Time::Now()); |
| +void AudioDirectiveHandler::OnTokenEncoded( |
| + const std::string& token, |
| + bool audible, |
| + const scoped_refptr<media::AudioBusRefCounted>& samples) { |
| + DVLOG(3) << "Token: " << token << "[audible:" << audible << "] encoded."; |
| + if (audible) { |
| + samples_cache_audible_.Add(token, samples); |
| + current_token_audible_ = token; |
| + // Force process transmits to pick up the new token. |
| + if (player_audible_->IsPlaying()) |
| + player_audible_->Stop(); |
| + } else { |
| + samples_cache_inaudible_.Add(token, samples); |
| + current_token_inaudible_ = token; |
| + // Force process transmits to pick up the new token. |
| + if (player_inaudible_->IsPlaying()) |
| + player_inaudible_->Stop(); |
| } |
| - if (record_duration > base::TimeDelta::FromSeconds(0)) |
| - RecordAudio(record_duration); |
| + ProcessNextTransmit(); |
| } |
| } // namespace copresence |