| 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..fcf1d8247c66c93a3cbe08e8d489b61f02eeba92 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,128 @@ 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::PlayAudio(
|
| - const scoped_refptr<media::AudioBusRefCounted>& samples,
|
| - base::TimeDelta duration) {
|
| - player_->Play(samples);
|
| - stop_playback_timer_.Start(
|
| - FROM_HERE, duration, this, &AudioDirectiveHandler::StopPlayback);
|
| -}
|
| +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);
|
|
|
| -void AudioDirectiveHandler::RecordAudio(base::TimeDelta duration) {
|
| - recorder_->Record();
|
| - stop_recording_timer_.Start(
|
| - FROM_HERE, duration, this, &AudioDirectiveHandler::StopRecording);
|
| + ProcessNextTransmit();
|
| + ProcessNextReceive();
|
| }
|
|
|
| // 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());
|
| +
|
| + 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
|
|
|