Commit 4f0acbe3 authored by ckehoe's avatar ckehoe Committed by Commit bot

Adding some flags to make audio debugging easier. Now we can separate audible...

Adding some flags to make audio debugging easier. Now we can separate audible audio from inaudible, and dump the tokens in their original encoded form.

rkc@ for copresence review.
andrew@webrtc for wav_file review.

Review URL: https://codereview.chromium.org/892423002

Cr-Commit-Position: refs/heads/master@{#314772}
parent 77752c3e
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
'../media/media.gyp:media', '../media/media.gyp:media',
'../media/media.gyp:shared_memory_support', '../media/media.gyp:shared_memory_support',
'../net/net.gyp:net', '../net/net.gyp:net',
'../third_party/webrtc/common_audio/common_audio.gyp:common_audio',
'copresence_proto', 'copresence_proto',
], ],
'include_dirs': [ 'include_dirs': [
......
...@@ -53,5 +53,6 @@ static_library("copresence") { ...@@ -53,5 +53,6 @@ static_library("copresence") {
"//content", "//content",
"//media", "//media",
"//net", "//net",
"//third_party/webrtc/common_audio",
] ]
} }
...@@ -6,5 +6,6 @@ include_rules = [ ...@@ -6,5 +6,6 @@ include_rules = [
"+google_apis", "+google_apis",
"+media", "+media",
"+net", "+net",
"+third_party/webrtc/common_audio",
"+url", "+url",
] ]
...@@ -4,8 +4,23 @@ ...@@ -4,8 +4,23 @@
#include "components/copresence/copresence_switches.h" #include "components/copresence/copresence_switches.h"
// TODO(ckehoe): Move these flags to the chrome://copresence page.
namespace switches { namespace switches {
// Directory to dump encoded tokens to, for debugging.
// If empty (the default), tokens are not dumped.
// If invalid (not a writable directory), Chrome will crash!
const char kCopresenceDumpTokensToDir[] = "copresence-dump-tokens-to-dir";
// Allow broadcast of audible audio tokens. Defaults to true.
const char kCopresenceEnableAudibleBroadcast[] =
"copresence-enable-audible-broadcast";
// Allow broadcast of inaudible audio tokens. Defaults to true.
const char kCopresenceEnableInaudibleBroadcast[] =
"copresence-enable-inaudible-broadcast";
// Address for calls to the Copresence server (via Apiary). // Address for calls to the Copresence server (via Apiary).
// Defaults to https://www.googleapis.com/copresence/v2/copresence. // Defaults to https://www.googleapis.com/copresence/v2/copresence.
const char kCopresenceServer[] = "copresence-server"; const char kCopresenceServer[] = "copresence-server";
......
...@@ -9,6 +9,9 @@ namespace switches { ...@@ -9,6 +9,9 @@ namespace switches {
// All switches in alphabetical order. The switches should be documented // All switches in alphabetical order. The switches should be documented
// alongside the definition of their values in the .cc file. // alongside the definition of their values in the .cc file.
extern const char kCopresenceDumpTokensToDir[];
extern const char kCopresenceEnableAudibleBroadcast[];
extern const char kCopresenceEnableInaudibleBroadcast[];
extern const char kCopresenceServer[]; extern const char kCopresenceServer[];
extern const char kCopresenceTracingToken[]; extern const char kCopresenceTracingToken[];
......
...@@ -5,14 +5,19 @@ ...@@ -5,14 +5,19 @@
#include "components/copresence/mediums/audio/audio_manager_impl.h" #include "components/copresence/mediums/audio/audio_manager_impl.h"
#include <algorithm> #include <algorithm>
#include <limits>
#include <vector> #include <vector>
#include "base/bind.h" #include "base/bind.h"
#include "base/bind_helpers.h" #include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/strings/string_util.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 "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_player_impl.h"
#include "components/copresence/mediums/audio/audio_recorder_impl.h" #include "components/copresence/mediums/audio/audio_recorder_impl.h"
#include "components/copresence/public/copresence_constants.h" #include "components/copresence/public/copresence_constants.h"
...@@ -21,27 +26,59 @@ ...@@ -21,27 +26,59 @@
#include "media/audio/audio_manager.h" #include "media/audio/audio_manager.h"
#include "media/audio/audio_manager_base.h" #include "media/audio/audio_manager_base.h"
#include "media/base/audio_bus.h" #include "media/base/audio_bus.h"
#include "third_party/webrtc/common_audio/wav_file.h"
namespace copresence { namespace copresence {
namespace { 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: // UrlSafe is defined as:
// '/' represented by a '_' and '+' represented by a '-' // '/' 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) { std::string FromUrlSafe(std::string token) {
base::ReplaceChars(token, "-", "+", &token); base::ReplaceChars(token, "-", "+", &token);
base::ReplaceChars(token, "_", "/", &token); base::ReplaceChars(token, "_", "/", &token);
return 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. // TODO(ckehoe): Move this to a central place.
const int kMaxSamples = 10000; std::string AudioTypeToString(AudioType audio_type) {
const int kTokenTimeoutMs = 2000; 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 } // namespace
// Public methods.
// Public functions.
AudioManagerImpl::AudioManagerImpl() AudioManagerImpl::AudioManagerImpl()
: whispernet_client_(nullptr), recorder_(nullptr) { : whispernet_client_(nullptr), recorder_(nullptr) {
...@@ -51,6 +88,10 @@ AudioManagerImpl::AudioManagerImpl() ...@@ -51,6 +88,10 @@ AudioManagerImpl::AudioManagerImpl()
should_be_recording_[AUDIBLE] = false; should_be_recording_[AUDIBLE] = false;
should_be_recording_[INAUDIBLE] = false; should_be_recording_[INAUDIBLE] = false;
player_enabled_[AUDIBLE] = ReadBooleanFlag(
switches::kCopresenceEnableAudibleBroadcast, true);
player_enabled_[INAUDIBLE] = ReadBooleanFlag(
switches::kCopresenceEnableInaudibleBroadcast, true);
player_[AUDIBLE] = nullptr; player_[AUDIBLE] = nullptr;
player_[INAUDIBLE] = nullptr; player_[INAUDIBLE] = nullptr;
token_length_[0] = 0; token_length_[0] = 0;
...@@ -88,6 +129,9 @@ void AudioManagerImpl::Initialize(WhispernetClient* whispernet_client, ...@@ -88,6 +129,9 @@ void AudioManagerImpl::Initialize(WhispernetClient* whispernet_client,
if (!recorder_) if (!recorder_)
recorder_ = new AudioRecorderImpl(); recorder_ = new AudioRecorderImpl();
recorder_->Initialize(decode_cancelable_cb_.callback()); recorder_->Initialize(decode_cancelable_cb_.callback());
dump_tokens_dir_ = base::FilePath(base::CommandLine::ForCurrentProcess()
->GetSwitchValueNative(switches::kCopresenceDumpTokensToDir));
} }
AudioManagerImpl::~AudioManagerImpl() { AudioManagerImpl::~AudioManagerImpl() {
...@@ -113,10 +157,16 @@ void AudioManagerImpl::StartPlaying(AudioType type) { ...@@ -113,10 +157,16 @@ void AudioManagerImpl::StartPlaying(AudioType type) {
// will call this code again (if we're still supposed to be playing). // will call this code again (if we're still supposed to be playing).
if (samples_cache_[type]->HasKey(playing_token_[type])) { if (samples_cache_[type]->HasKey(playing_token_[type])) {
DCHECK(!playing_token_[type].empty()); DCHECK(!playing_token_[type].empty());
started_playing_[type] = base::Time::Now(); if (player_enabled_[type]) {
player_[type]->Play(samples_cache_[type]->GetValue(playing_token_[type])); started_playing_[type] = base::Time::Now();
// If we're playing, we always record to hear what we are playing. player_[type]->Play(samples_cache_[type]->GetValue(playing_token_[type]));
recorder_->Record();
// 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) { ...@@ -173,13 +223,15 @@ void AudioManagerImpl::SetTokenLength(AudioType type, size_t token_length) {
token_length_[type] = token_length; token_length_[type] = token_length;
} }
// Private methods.
// Private functions.
void AudioManagerImpl::OnTokenEncoded( void AudioManagerImpl::OnTokenEncoded(
AudioType type, AudioType type,
const std::string& token, const std::string& token,
const scoped_refptr<media::AudioBusRefCounted>& samples) { const scoped_refptr<media::AudioBusRefCounted>& samples) {
samples_cache_[type]->Add(token, samples); samples_cache_[type]->Add(token, samples);
DumpToken(type, token, samples.get());
UpdateToken(type, token); UpdateToken(type, token);
} }
...@@ -222,11 +274,8 @@ void AudioManagerImpl::RestartPlaying(AudioType type) { ...@@ -222,11 +274,8 @@ void AudioManagerImpl::RestartPlaying(AudioType type) {
// in the cache. // in the cache.
DCHECK(samples_cache_[type]->HasKey(playing_token_[type])); DCHECK(samples_cache_[type]->HasKey(playing_token_[type]));
started_playing_[type] = base::Time::Now();
player_[type]->Stop(); player_[type]->Stop();
player_[type]->Play(samples_cache_[type]->GetValue(playing_token_[type])); StartPlaying(type);
// If we're playing, we always record to hear what we are playing.
recorder_->Record();
} }
void AudioManagerImpl::DecodeSamplesConnector(const std::string& samples) { void AudioManagerImpl::DecodeSamplesConnector(const std::string& samples) {
...@@ -250,4 +299,37 @@ 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 } // namespace copresence
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/callback.h" #include "base/callback.h"
#include "base/cancelable_callback.h" #include "base/cancelable_callback.h"
#include "base/files/file_path.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/scoped_vector.h" #include "base/memory/scoped_vector.h"
...@@ -21,6 +22,10 @@ namespace base { ...@@ -21,6 +22,10 @@ namespace base {
class Time; class Time;
} }
namespace media {
class AudioBus;
}
namespace copresence { namespace copresence {
class AudioPlayer; class AudioPlayer;
...@@ -76,6 +81,10 @@ class AudioManagerImpl final : public AudioManager { ...@@ -76,6 +81,10 @@ class AudioManagerImpl final : public AudioManager {
void DecodeSamplesConnector(const std::string& samples); void DecodeSamplesConnector(const std::string& samples);
void DumpToken(AudioType audio_type,
const std::string& token,
const media::AudioBus* samples);
WhispernetClient* whispernet_client_; WhispernetClient* whispernet_client_;
// Callbacks to send tokens back to the CopresenceManager. // Callbacks to send tokens back to the CopresenceManager.
...@@ -91,6 +100,7 @@ class AudioManagerImpl final : public AudioManager { ...@@ -91,6 +100,7 @@ class AudioManagerImpl final : public AudioManager {
static_assert(INAUDIBLE == 1, "AudioType::INAUDIBLE should be 1."); static_assert(INAUDIBLE == 1, "AudioType::INAUDIBLE should be 1.");
// Indexed using enum AudioType. // Indexed using enum AudioType.
bool player_enabled_[2];
bool should_be_playing_[2]; bool should_be_playing_[2];
bool should_be_recording_[2]; bool should_be_recording_[2];
...@@ -102,6 +112,7 @@ class AudioManagerImpl final : public AudioManager { ...@@ -102,6 +112,7 @@ class AudioManagerImpl final : public AudioManager {
// Indexed using enum AudioType. // Indexed using enum AudioType.
std::string playing_token_[2]; std::string playing_token_[2];
size_t token_length_[2];
base::Time started_playing_[2]; base::Time started_playing_[2];
base::Time heard_own_token_[2]; base::Time heard_own_token_[2];
...@@ -110,7 +121,7 @@ class AudioManagerImpl final : public AudioManager { ...@@ -110,7 +121,7 @@ class AudioManagerImpl final : public AudioManager {
// Indexed using enum AudioType. // Indexed using enum AudioType.
ScopedVector<SamplesMap> samples_cache_; ScopedVector<SamplesMap> samples_cache_;
size_t token_length_[2]; base::FilePath dump_tokens_dir_;
DISALLOW_COPY_AND_ASSIGN(AudioManagerImpl); DISALLOW_COPY_AND_ASSIGN(AudioManagerImpl);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment