Index: components/copresence/mediums/audio/audio_manager_impl.cc |
diff --git a/components/copresence/mediums/audio/audio_manager_impl.cc b/components/copresence/mediums/audio/audio_manager_impl.cc |
index 387eb54d33901ec0f65a2b9734945bdcf4c0d8be..d1cd7d96a5f8d3d2a74e902f63ea486a68a490a9 100644 |
--- a/components/copresence/mediums/audio/audio_manager_impl.cc |
+++ b/components/copresence/mediums/audio/audio_manager_impl.cc |
@@ -5,14 +5,19 @@ |
#include "components/copresence/mediums/audio/audio_manager_impl.h" |
#include <algorithm> |
+#include <limits> |
#include <vector> |
#include "base/bind.h" |
#include "base/bind_helpers.h" |
+#include "base/command_line.h" |
#include "base/logging.h" |
#include "base/run_loop.h" |
#include "base/strings/string_util.h" |
+#include "base/strings/stringprintf.h" |
+#include "base/strings/sys_string_conversions.h" |
#include "base/time/time.h" |
+#include "components/copresence/copresence_switches.h" |
#include "components/copresence/mediums/audio/audio_player_impl.h" |
#include "components/copresence/mediums/audio/audio_recorder_impl.h" |
#include "components/copresence/public/copresence_constants.h" |
@@ -21,27 +26,59 @@ |
#include "media/audio/audio_manager.h" |
#include "media/audio/audio_manager_base.h" |
#include "media/base/audio_bus.h" |
+#include "third_party/webrtc/common_audio/wav_file.h" |
namespace copresence { |
namespace { |
+const int kSampleExpiryTimeMs = 60 * 60 * 1000; // 60 minutes. |
+const int kMaxSamples = 10000; |
+const int kTokenTimeoutMs = 2000; |
+const int kMonoChannelCount = 1; |
+ |
// UrlSafe is defined as: |
// '/' represented by a '_' and '+' represented by a '-' |
-// TODO(rkc): Move this processing to the whispernet wrapper. |
+// TODO(ckehoe): Move this to a central place. |
std::string FromUrlSafe(std::string token) { |
base::ReplaceChars(token, "-", "+", &token); |
base::ReplaceChars(token, "_", "/", &token); |
return token; |
} |
+std::string ToUrlSafe(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; |
-const int kTokenTimeoutMs = 2000; |
+// TODO(ckehoe): Move this to a central place. |
+std::string AudioTypeToString(AudioType audio_type) { |
+ if (audio_type == AUDIBLE) |
+ return "audible"; |
+ if (audio_type == INAUDIBLE) |
+ return "inaudible"; |
+ |
+ NOTREACHED() << "Got unexpected token type " << audio_type; |
+ return std::string(); |
+} |
+ |
+bool ReadBooleanFlag(const std::string& flag, bool default_value) { |
+ const std::string flag_value = base::StringToLowerASCII( |
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(flag)); |
+ if (flag_value == "true" || flag_value == "1") |
+ return true; |
+ if (flag_value == "false" || flag_value == "0") |
+ return false; |
+ LOG_IF(ERROR, !flag_value.empty()) |
+ << "Unrecognized value \"" << flag_value << " for flag " |
+ << flag << ". Defaulting to " << default_value; |
+ return default_value; |
+} |
} // namespace |
-// Public methods. |
+ |
+// Public functions. |
AudioManagerImpl::AudioManagerImpl() |
: whispernet_client_(nullptr), recorder_(nullptr) { |
@@ -51,6 +88,10 @@ AudioManagerImpl::AudioManagerImpl() |
should_be_recording_[AUDIBLE] = false; |
should_be_recording_[INAUDIBLE] = false; |
+ player_enabled_[AUDIBLE] = ReadBooleanFlag( |
+ switches::kCopresenceEnableAudibleBroadcast, true); |
+ player_enabled_[INAUDIBLE] = ReadBooleanFlag( |
+ switches::kCopresenceEnableInaudibleBroadcast, true); |
player_[AUDIBLE] = nullptr; |
player_[INAUDIBLE] = nullptr; |
token_length_[0] = 0; |
@@ -88,6 +129,9 @@ void AudioManagerImpl::Initialize(WhispernetClient* whispernet_client, |
if (!recorder_) |
recorder_ = new AudioRecorderImpl(); |
recorder_->Initialize(decode_cancelable_cb_.callback()); |
+ |
+ dump_tokens_dir_ = base::FilePath(base::CommandLine::ForCurrentProcess() |
+ ->GetSwitchValueNative(switches::kCopresenceDumpTokensToDir)); |
} |
AudioManagerImpl::~AudioManagerImpl() { |
@@ -113,10 +157,16 @@ void AudioManagerImpl::StartPlaying(AudioType type) { |
// will call this code again (if we're still supposed to be playing). |
if (samples_cache_[type]->HasKey(playing_token_[type])) { |
DCHECK(!playing_token_[type].empty()); |
- started_playing_[type] = base::Time::Now(); |
- player_[type]->Play(samples_cache_[type]->GetValue(playing_token_[type])); |
- // If we're playing, we always record to hear what we are playing. |
- recorder_->Record(); |
+ if (player_enabled_[type]) { |
+ started_playing_[type] = base::Time::Now(); |
+ player_[type]->Play(samples_cache_[type]->GetValue(playing_token_[type])); |
+ |
+ // If we're playing, we always record to hear what we are playing. |
+ recorder_->Record(); |
+ } else { |
+ DVLOG(3) << "Skipping playback for disabled " << AudioTypeToString(type) |
+ << " player."; |
+ } |
} |
} |
@@ -173,13 +223,15 @@ void AudioManagerImpl::SetTokenLength(AudioType type, size_t token_length) { |
token_length_[type] = token_length; |
} |
-// Private methods. |
+ |
+// Private functions. |
void AudioManagerImpl::OnTokenEncoded( |
AudioType type, |
const std::string& token, |
const scoped_refptr<media::AudioBusRefCounted>& samples) { |
samples_cache_[type]->Add(token, samples); |
+ DumpToken(type, token, samples.get()); |
UpdateToken(type, token); |
} |
@@ -222,11 +274,8 @@ void AudioManagerImpl::RestartPlaying(AudioType type) { |
// in the cache. |
DCHECK(samples_cache_[type]->HasKey(playing_token_[type])); |
- started_playing_[type] = base::Time::Now(); |
player_[type]->Stop(); |
- player_[type]->Play(samples_cache_[type]->GetValue(playing_token_[type])); |
- // If we're playing, we always record to hear what we are playing. |
- recorder_->Record(); |
+ StartPlaying(type); |
} |
void AudioManagerImpl::DecodeSamplesConnector(const std::string& samples) { |
@@ -250,4 +299,37 @@ void AudioManagerImpl::DecodeSamplesConnector(const std::string& samples) { |
} |
} |
+void AudioManagerImpl::DumpToken(AudioType audio_type, |
+ const std::string& token, |
+ const media::AudioBus* samples) { |
+ if (dump_tokens_dir_.empty()) |
+ return; |
+ |
+ // Convert the samples to 16-bit integers. |
+ std::vector<int16_t> int_samples; |
+ int_samples.reserve(samples->frames()); |
+ for (int i = 0; i < samples->frames(); i++) { |
+ int_samples.push_back(round( |
+ samples->channel(0)[i] * std::numeric_limits<int16_t>::max())); |
+ } |
+ DCHECK_EQ(static_cast<int>(int_samples.size()), samples->frames()); |
+ DCHECK_EQ(kMonoChannelCount, samples->channels()); |
+ |
+ const std::string filename = base::StringPrintf("%s %s.wav", |
+ AudioTypeToString(audio_type).c_str(), ToUrlSafe(token).c_str()); |
+ DVLOG(3) << "Dumping token " << filename; |
+ |
+ std::string file_str; |
+#if defined(OS_WIN) |
+ base::FilePath file_path = dump_tokens_dir_.Append( |
+ base::SysNativeMBToWide(filename)); |
+ file_str = base::SysWideToNativeMB(file_path.value()); |
+#else |
+ file_str = dump_tokens_dir_.Append(filename).value(); |
+#endif |
+ |
+ webrtc::WavWriter writer(file_str, kDefaultSampleRate, kMonoChannelCount); |
+ writer.WriteSamples(int_samples.data(), int_samples.size()); |
+} |
+ |
} // namespace copresence |