diff --git a/webrtc/modules/audio_device/ios/audio_device_ios.mm b/webrtc/modules/audio_device/ios/audio_device_ios.mm index a0ea2a0f45..2551dad76d 100644 --- a/webrtc/modules/audio_device/ios/audio_device_ios.mm +++ b/webrtc/modules/audio_device/ios/audio_device_ios.mm @@ -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"; } diff --git a/webrtc/modules/utility/include/helpers_ios.h b/webrtc/modules/utility/include/helpers_ios.h index a5a07ace17..d3f788fec3 100644 --- a/webrtc/modules/utility/include/helpers_ios.h +++ b/webrtc/modules/utility/include/helpers_ios.h @@ -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 diff --git a/webrtc/modules/utility/source/helpers_ios.mm b/webrtc/modules/utility/source/helpers_ios.mm index 2d0ac098c1..4b28cee3dc 100644 --- a/webrtc/modules/utility/source/helpers_ios.mm +++ b/webrtc/modules/utility/source/helpers_ios.mm @@ -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 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