Adds low complexity audio mode for single core CPUs

BUG=webrtc:5538
R=tkchin@webrtc.org

Review URL: https://codereview.webrtc.org/1723163002 .

Cr-Commit-Position: refs/heads/master@{#11743}
This commit is contained in:
henrika
2016-02-24 14:27:09 +01:00
parent c2b785df5d
commit 3e60bf0ff3
3 changed files with 136 additions and 93 deletions

View File

@ -52,7 +52,10 @@ namespace webrtc {
// will be set to this value as well to avoid resampling the the audio unit's
// format converter. Note that, some devices, e.g. BT headsets, only supports
// 8000Hz as native sample rate.
const double kPreferredSampleRate = 48000.0;
const double kHighPerformanceSampleRate = 48000.0;
// A lower sample rate will be used for devices with only one core
// (e.g. iPhone 4). The goal is to reduce the CPU load of the application.
const double kLowComplexitySampleRate = 16000.0;
// Use a hardware I/O buffer size (unit is in seconds) that matches the 10ms
// size used by WebRTC. The exact actual size will differ between devices.
// Example: using 48kHz on iPhone 6 results in a native buffer size of
@ -61,7 +64,13 @@ const double kPreferredSampleRate = 48000.0;
// buffers used by WebRTC. It is beneficial for the performance if the native
// size is as close to 10ms as possible since it results in "clean" callback
// sequence without bursts of callbacks back to back.
const double kPreferredIOBufferDuration = 0.01;
const double kHighPerformanceIOBufferDuration = 0.01;
// Use a larger buffer size on devices with only one core (e.g. iPhone 4).
// It will result in a lower CPU consumption at the cost of a larger latency.
// The size of 60ms is based on instrumentation that shows a significant
// reduction in CPU load compared with 10ms on low-end devices.
// TODO(henrika): monitor this size and determine if it should be modified.
const double kLowComplexityIOBufferDuration = 0.06;
// Try to use mono to save resources. Also avoids channel format conversion
// in the I/O audio unit. Initial tests have shown that it is possible to use
// mono natively for built-in microphones and for BT headsets but not for
@ -84,9 +93,22 @@ const UInt16 kFixedRecordDelayEstimate = 30;
// initialization attempts.
const int kMaxNumberOfAudioUnitInitializeAttempts = 5;
using ios::CheckAndLogError;
// Return the preferred sample rate given number of CPU cores. Use highest
// possible if the CPU has more than one core.
static double GetPreferredSampleRate() {
return (ios::GetProcessorCount() > 1) ? kHighPerformanceSampleRate
: kLowComplexitySampleRate;
}
// Return the preferred I/O buffer size given number of CPU cores. Use smallest
// possible if the CPU has more than one core.
static double GetPreferredIOBufferDuration() {
return (ios::GetProcessorCount() > 1) ? kHighPerformanceIOBufferDuration
: kLowComplexityIOBufferDuration;
}
// Verifies that the current audio session supports input audio and that the
// required category and mode are enabled.
static bool VerifyAudioSession(RTCAudioSession* session) {
@ -152,12 +174,12 @@ static bool ActivateAudioSession(RTCAudioSession* session, bool activate) {
// to ensure that the I/O unit does not have to do sample rate conversion.
error = nil;
success =
[session setPreferredSampleRate:kPreferredSampleRate error:&error];
[session setPreferredSampleRate:GetPreferredSampleRate() error:&error];
RTC_DCHECK(CheckAndLogError(success, error));
// Set the preferred audio I/O buffer duration, in seconds.
error = nil;
success = [session setPreferredIOBufferDuration:kPreferredIOBufferDuration
success = [session setPreferredIOBufferDuration:GetPreferredIOBufferDuration()
error:&error];
RTC_DCHECK(CheckAndLogError(success, error));
@ -243,6 +265,11 @@ static void LogDeviceInfo() {
LOG(LS_INFO) << " system version: " << ios::GetSystemVersion();
LOG(LS_INFO) << " device type: " << ios::GetDeviceType();
LOG(LS_INFO) << " device name: " << ios::GetDeviceName();
LOG(LS_INFO) << " process name: " << ios::GetProcessName();
LOG(LS_INFO) << " process ID: " << ios::GetProcessID();
LOG(LS_INFO) << " OS version: " << ios::GetOSVersionString();
LOG(LS_INFO) << " processing cores: " << ios::GetProcessorCount();
LOG(LS_INFO) << " low power mode: " << ios::GetLowPowerModeEnabled();
}
}
#endif // !defined(NDEBUG)
@ -286,8 +313,10 @@ int32_t AudioDeviceIOS::Init() {
// here. They have not been set and confirmed yet since ActivateAudioSession()
// is not called until audio is about to start. However, it makes sense to
// store the parameters now and then verify at a later stage.
playout_parameters_.reset(kPreferredSampleRate, kPreferredNumberOfChannels);
record_parameters_.reset(kPreferredSampleRate, kPreferredNumberOfChannels);
playout_parameters_.reset(GetPreferredSampleRate(),
kPreferredNumberOfChannels);
record_parameters_.reset(GetPreferredSampleRate(),
kPreferredNumberOfChannels);
// Ensure that the audio device buffer (ADB) knows about the internal audio
// parameters. Note that, even if we are unable to get a mono audio session,
// we will always tell the I/O audio unit to do a channel format conversion
@ -641,7 +670,7 @@ void AudioDeviceIOS::SetupAudioBuffersForActiveAudioSession() {
// hardware sample rate but continue and use the non-ideal sample rate after
// reinitializing the audio parameters. Most BT headsets only support 8kHz or
// 16kHz.
if (session.sampleRate != kPreferredSampleRate) {
if (session.sampleRate != GetPreferredSampleRate()) {
LOG(LS_WARNING) << "Unable to set the preferred sample rate";
}

View File

@ -51,6 +51,23 @@ std::string GetDeviceType();
// Examples: "iPhone 5s (GSM)" and "iPhone 6 Plus".
std::string GetDeviceName();
// Returns the name of the process. Does not uniquely identify the process.
std::string GetProcessName();
// Returns the identifier of the process (often called process ID).
int GetProcessID();
// Returns a string containing the version of the operating system on which the
// process is executing. The string is string is human readable, localized, and
// is appropriate for displaying to the user.
std::string GetOSVersionString();
// Returns the number of processing cores available on the device.
int GetProcessorCount();
// Indicates whether Low Power Mode is enabled on the iOS device.
bool GetLowPowerModeEnabled();
} // namespace ios
} // namespace webrtc

View File

@ -23,6 +23,64 @@
namespace webrtc {
namespace ios {
// Internal helper method used by GetDeviceName() to return device name.
const char* LookUpRealName(const char* raw_name) {
// Lookup table which maps raw device names to real (human readable) names.
struct {
const char* raw_name;
const char* real_name;
} device_names[] = {
{"iPhone1,1", "iPhone 1G"},
{"iPhone1,2", "iPhone 3G"},
{"iPhone2,1", "iPhone 3GS"},
{"iPhone3,1", "iPhone 4"},
{"iPhone3,3", "Verizon iPhone 4"},
{"iPhone4,1", "iPhone 4S"},
{"iPhone5,1", "iPhone 5 (GSM)"},
{"iPhone5,2", "iPhone 5 (GSM+CDMA)"},
{"iPhone5,3", "iPhone 5c (GSM)"},
{"iPhone5,4", "iPhone 5c (GSM+CDMA)"},
{"iPhone6,1", "iPhone 5s (GSM)"},
{"iPhone6,2", "iPhone 5s (GSM+CDMA)"},
{"iPhone7,1", "iPhone 6 Plus"},
{"iPhone7,2", "iPhone 6"},
{"iPhone8,1", "iPhone 6s"},
{"iPhone8,2", "iPhone 6s Plus"},
{"iPod1,1", "iPod Touch 1G"},
{"iPod2,1", "iPod Touch 2G"},
{"iPod3,1", "iPod Touch 3G"},
{"iPod4,1", "iPod Touch 4G"},
{"iPod5,1", "iPod Touch 5G"},
{"iPad1,1", "iPad"},
{"iPad2,1", "iPad 2 (WiFi)"},
{"iPad2,2", "iPad 2 (GSM)"},
{"iPad2,3", "iPad 2 (CDMA)"},
{"iPad2,4", "iPad 2 (WiFi)"},
{"iPad2,5", "iPad Mini (WiFi)"},
{"iPad2,6", "iPad Mini (GSM)"},
{"iPad2,7", "iPad Mini (GSM+CDMA)"},
{"iPad3,1", "iPad 3 (WiFi)"},
{"iPad3,2", "iPad 3 (GSM+CDMA)"},
{"iPad3,3", "iPad 3 (GSM)"},
{"iPad3,4", "iPad 4 (WiFi)"},
{"iPad3,5", "iPad 4 (GSM)"},
{"iPad3,6", "iPad 4 (GSM+CDMA)"},
{"iPad4,1", "iPad Air (WiFi)"},
{"iPad4,2", "iPad Air (Cellular)"},
{"iPad4,4", "iPad mini 2G (WiFi)"},
{"iPad4,5", "iPad mini 2G (Cellular)"},
{"i386", "Simulator"},
{"x86_64", "Simulator"},
};
for (auto& d : device_names) {
if (strcmp(d.raw_name, raw_name) == 0)
return d.real_name;
}
LOG(LS_WARNING) << "Failed to find device name (" << raw_name << ")";
return "";
}
// TODO(henrika): move to shared location.
// See https://code.google.com/p/webrtc/issues/detail?id=4773 for details.
NSString* NSStringFromStdString(const std::string& stdString) {
@ -89,91 +147,30 @@ std::string GetDeviceName() {
rtc::scoped_ptr<char[]> machine;
machine.reset(new char[size]);
sysctlbyname("hw.machine", machine.get(), &size, NULL, 0);
std::string raw_name(machine.get());
if (!raw_name.compare("iPhone1,1"))
return std::string("iPhone 1G");
if (!raw_name.compare("iPhone1,2"))
return std::string("iPhone 3G");
if (!raw_name.compare("iPhone2,1"))
return std::string("iPhone 3GS");
if (!raw_name.compare("iPhone3,1"))
return std::string("iPhone 4");
if (!raw_name.compare("iPhone3,3"))
return std::string("Verizon iPhone 4");
if (!raw_name.compare("iPhone4,1"))
return std::string("iPhone 4S");
if (!raw_name.compare("iPhone5,1"))
return std::string("iPhone 5 (GSM)");
if (!raw_name.compare("iPhone5,2"))
return std::string("iPhone 5 (GSM+CDMA)");
if (!raw_name.compare("iPhone5,3"))
return std::string("iPhone 5c (GSM)");
if (!raw_name.compare("iPhone5,4"))
return std::string("iPhone 5c (GSM+CDMA)");
if (!raw_name.compare("iPhone6,1"))
return std::string("iPhone 5s (GSM)");
if (!raw_name.compare("iPhone6,2"))
return std::string("iPhone 5s (GSM+CDMA)");
if (!raw_name.compare("iPhone7,1"))
return std::string("iPhone 6 Plus");
if (!raw_name.compare("iPhone7,2"))
return std::string("iPhone 6");
if (!raw_name.compare("iPhone8,1"))
return std::string("iPhone 6s");
if (!raw_name.compare("iPhone8,2"))
return std::string("iPhone 6s Plus");
if (!raw_name.compare("iPod1,1"))
return std::string("iPod Touch 1G");
if (!raw_name.compare("iPod2,1"))
return std::string("iPod Touch 2G");
if (!raw_name.compare("iPod3,1"))
return std::string("iPod Touch 3G");
if (!raw_name.compare("iPod4,1"))
return std::string("iPod Touch 4G");
if (!raw_name.compare("iPod5,1"))
return std::string("iPod Touch 5G");
if (!raw_name.compare("iPad1,1"))
return std::string("iPad");
if (!raw_name.compare("iPad2,1"))
return std::string("iPad 2 (WiFi)");
if (!raw_name.compare("iPad2,2"))
return std::string("iPad 2 (GSM)");
if (!raw_name.compare("iPad2,3"))
return std::string("iPad 2 (CDMA)");
if (!raw_name.compare("iPad2,4"))
return std::string("iPad 2 (WiFi)");
if (!raw_name.compare("iPad2,5"))
return std::string("iPad Mini (WiFi)");
if (!raw_name.compare("iPad2,6"))
return std::string("iPad Mini (GSM)");
if (!raw_name.compare("iPad2,7"))
return std::string("iPad Mini (GSM+CDMA)");
if (!raw_name.compare("iPad3,1"))
return std::string("iPad 3 (WiFi)");
if (!raw_name.compare("iPad3,2"))
return std::string("iPad 3 (GSM+CDMA)");
if (!raw_name.compare("iPad3,3"))
return std::string("iPad 3 (GSM)");
if (!raw_name.compare("iPad3,4"))
return std::string("iPad 4 (WiFi)");
if (!raw_name.compare("iPad3,5"))
return std::string("iPad 4 (GSM)");
if (!raw_name.compare("iPad3,6"))
return std::string("iPad 4 (GSM+CDMA)");
if (!raw_name.compare("iPad4,1"))
return std::string("iPad Air (WiFi)");
if (!raw_name.compare("iPad4,2"))
return std::string("iPad Air (Cellular)");
if (!raw_name.compare("iPad4,4"))
return std::string("iPad mini 2G (WiFi)");
if (!raw_name.compare("iPad4,5"))
return std::string("iPad mini 2G (Cellular)");
if (!raw_name.compare("i386"))
return std::string("Simulator");
if (!raw_name.compare("x86_64"))
return std::string("Simulator");
LOG(LS_WARNING) << "Failed to find device name (" << raw_name << ")";
return raw_name;
return std::string(LookUpRealName(machine.get()));
}
std::string GetProcessName() {
NSString* processName = [NSProcessInfo processInfo].processName;
return StdStringFromNSString(processName);
}
int GetProcessID() {
return [NSProcessInfo processInfo].processIdentifier;
}
std::string GetOSVersionString() {
NSString* osVersion =
[NSProcessInfo processInfo].operatingSystemVersionString;
return StdStringFromNSString(osVersion);
}
int GetProcessorCount() {
return [NSProcessInfo processInfo].processorCount;
}
bool GetLowPowerModeEnabled() {
return [NSProcessInfo processInfo].lowPowerModeEnabled;
}
} // namespace ios