#include "media/base/audio_latency.h"
#include <stdint.h>
#include <algorithm>
#include <bit>
#include <cmath>
#include <numeric>
#include "base/check_op.h"
#include "base/logging.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "media/base/limits.h"
#include "media/media_buildflags.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/android_info.h"
#endif
#if BUILDFLAG(IS_FUCHSIA)
#include "base/fuchsia/scheduler.h"
#endif
namespace media {
bool AudioLatency::IsResamplingPassthroughSupported(Type type) {
#if BUILDFLAG(IS_CHROMEOS)
return true;
#elif BUILDFLAG(IS_FUCHSIA)
return true;
#elif BUILDFLAG(IS_ANDROID)
return type == Type::kPlayback &&
base::android::android_info::sdk_int() >=
base::android::android_info::SDK_VERSION_NOUGAT_MR1;
#else
return false;
#endif
}
int AudioLatency::GetHighLatencyBufferSize(int sample_rate,
int preferred_buffer_size) {
#if BUILDFLAG(USE_CRAS)
const double eighty_ms_size = 8.0 * sample_rate / 100;
const int high_latency_buffer_size =
std::bit_ceil(static_cast<uint32_t>(std::round(eighty_ms_size)));
#elif BUILDFLAG(IS_FUCHSIA)
constexpr base::TimeDelta period = base::Milliseconds(80);
static_assert(static_cast<int>(period / base::kAudioSchedulingPeriod) ==
period / base::kAudioSchedulingPeriod);
const int high_latency_buffer_size = period.InMilliseconds() * sample_rate /
base::Time::kMillisecondsPerSecond;
#elif BUILDFLAG(IS_WIN)
const double twenty_ms_size = 2.0 * sample_rate / 100;
preferred_buffer_size = std::max(preferred_buffer_size, 1);
const int high_latency_buffer_size =
std::ceil(twenty_ms_size / preferred_buffer_size) * preferred_buffer_size;
#else
const double twenty_ms_size = 2.0 * sample_rate / 100;
const int high_latency_buffer_size =
std::bit_ceil(static_cast<uint32_t>(std::round(twenty_ms_size)));
#endif
return std::max(preferred_buffer_size, high_latency_buffer_size);
}
int AudioLatency::GetRtcBufferSize(int sample_rate, int hardware_buffer_size) {
int frames_per_buffer = hardware_buffer_size;
if (!frames_per_buffer) {
frames_per_buffer = sample_rate / 100;
DVLOG(1) << "Using 10 ms sink output buffer size: " << frames_per_buffer;
return frames_per_buffer;
}
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_APPLE) || \
BUILDFLAG(IS_FUCHSIA)
frames_per_buffer = sample_rate / 100;
#elif BUILDFLAG(IS_ANDROID)
int frames_per_10ms = sample_rate / 100;
if (frames_per_buffer < 2 * frames_per_10ms) {
frames_per_buffer = 2 * frames_per_10ms;
DVLOG(1) << "Low-latency output detected on Android";
}
#endif
DVLOG(1) << "Using sink output buffer size: " << frames_per_buffer;
return frames_per_buffer;
}
int AudioLatency::GetInteractiveBufferSize(int hardware_buffer_size) {
CHECK_GT(hardware_buffer_size, 0);
#if BUILDFLAG(IS_ANDROID)
LOG(INFO) << "audioHardwareBufferSize = " << hardware_buffer_size;
const int kWebAudioRenderQuantumSize = 128;
if (hardware_buffer_size >= kWebAudioRenderQuantumSize)
return hardware_buffer_size;
int sensible_buffer_size =
std::min(std::lcm(hardware_buffer_size, kWebAudioRenderQuantumSize),
kWebAudioRenderQuantumSize * 4);
return sensible_buffer_size;
#else
return hardware_buffer_size;
#endif
}
int AudioLatency::GetExactBufferSize(base::TimeDelta duration,
int sample_rate,
int hardware_buffer_size,
int min_hardware_buffer_size,
int max_hardware_buffer_size,
int max_allowed_buffer_size) {
DCHECK_NE(0, hardware_buffer_size);
DCHECK_NE(0, max_allowed_buffer_size);
DCHECK_GE(hardware_buffer_size, min_hardware_buffer_size);
DCHECK_GE(max_hardware_buffer_size, min_hardware_buffer_size);
DCHECK(max_hardware_buffer_size == 0 ||
hardware_buffer_size <= max_hardware_buffer_size);
DCHECK_LE(hardware_buffer_size, max_allowed_buffer_size);
int requested_buffer_size = std::round(duration.InSecondsF() * sample_rate);
if (min_hardware_buffer_size &&
requested_buffer_size <= min_hardware_buffer_size) {
return min_hardware_buffer_size;
}
if (requested_buffer_size <= hardware_buffer_size)
return hardware_buffer_size;
#if BUILDFLAG(IS_WIN)
const int multiplier = hardware_buffer_size;
#else
const int multiplier = min_hardware_buffer_size > 0 ? min_hardware_buffer_size
: hardware_buffer_size;
#endif
int buffer_size =
std::ceil(requested_buffer_size / static_cast<double>(multiplier)) *
multiplier;
if (max_hardware_buffer_size && buffer_size >= max_hardware_buffer_size) {
buffer_size = std::ceil(requested_buffer_size /
static_cast<double>(max_hardware_buffer_size)) *
max_hardware_buffer_size;
}
const int platform_max_buffer_size =
(max_hardware_buffer_size &&
max_hardware_buffer_size <= max_allowed_buffer_size)
? (max_allowed_buffer_size / max_hardware_buffer_size) *
max_hardware_buffer_size
: (max_allowed_buffer_size / multiplier) * multiplier;
return std::min(buffer_size, platform_max_buffer_size);
}
const char* AudioLatency::ToString(Type type) {
switch (type) {
case Type::kExactMS:
return "LatencyExactMs";
case Type::kInteractive:
return "LatencyInteractive";
case Type::kRtc:
return "LatencyRtc";
case Type::kPlayback:
return "LatencyPlayback";
case Type::kUnknown:
return "LatencyUnknown";
}
}
}