Liblinphone: IOS

Liblinphone 5.0.0

Functions
IOS
Portability:

Multitasking
Liblinphone for IOS natively supports multitasking assuming application follows multitasking guides provided by Apple. More...

## Functions
void | linphone_core_start_dtmf_stream (LinphoneCore *core)
void | linphone_core_stop_dtmf_stream (LinphoneCore *core)
void | linphone_core_activate_audio_session (LinphoneCore *core, bool_t actived)
| Special function to indicate if the audio session is activated. More...
void | linphone_core_configure_audio_session (LinphoneCore *core)
| Special function to configure audio session with default settings. More...
void | linphone_core_enable_callkit (LinphoneCore *core, bool_t enabled)
| Special function to enable the callkit. More...
MS2_DEPRECATED void | linphone_core_audio_route_changed (LinphoneCore *core)
| Special function to indicate if the audio route is changed. More...
bool_t | linphone_core_callkit_enabled (const LinphoneCore *core)
| Special function to check if the callkit is enabled, False by default. More...

## Detailed Description

Multitasking
Liblinphone for IOS natively supports multitasking assuming application follows multitasking guides provided by Apple.

First step is to declare application as multitasked. It means adding background mode for both audio and voip to Info.plist file.

<key>UIBackgroundModes</key>
<array>
<string>voip</string>
<string>audio</string>
</array>

- SIP socket
  Recommended mode is SIP over TCP, because UDP usually requires frequent keep alives for maintaining NAT association at the IP router level. This can be as frequent as one UDP packet every 15 seconds to maintain the NAT association accross NAT routers. Doing such drains the battery very fast, and furthermore the iOS keep-alive designed by Apple to handle this task can only be called with a minimum of 10 minutes interval.
  For TCP, liblinphone automatically configures SIP socket for voip (I.E kCFStreamNetworkServiceType set to kCFStreamNetworkServiceTypeVoIP).
  Since IOS > 4.1 Apple disabled voip mode for UDP sockets
- Entering background mode
  Before entering in background mode (through
  - (void)applicationDidEnterBackground:(UIApplication *)application
  ), the application must first refresh sip registration using function linphone_core_refresh_registers(); and register a keep-alive handler for periodically refreshing the registration. The speudo code below shows how to register a keep alive handler:
  //First refresh registration
  linphone_core_refresh_registers(theLinphoneCore);
  //wait for registration answer
  int i=0;
  while (!linphone_proxy_config_get_state(proxyCfg) == LinphoneRegistrationOk && i++<40 ) {
  linphone_core_iterate(theLinphoneCore);
  usleep(100000);
  }
  //register keepalive handler
  [[UIApplication sharedApplication] setKeepAliveTimeout:600/*minimal interval is 600 s*/
  handler:^{
  //refresh sip registration
  linphone_core_refresh_registers(theLinphoneCore);
  //make sure sip REGISTER is sent
  linphone_core_iterate(theLinphoneCore);
  }];
  linphone_core_iterate
  void linphone_core_iterate(LinphoneCore *core)
  Main loop function.
  linphone_core_refresh_registers
  void linphone_core_refresh_registers(LinphoneCore *core)
  force registration refresh to be initiated upon next iterate
  linphone_proxy_config_get_state
  LinphoneRegistrationState linphone_proxy_config_get_state(const LinphoneProxyConfig *proxy_config)
  Get the registration state of the given proxy config.
  LinphoneRegistrationOk
  @ LinphoneRegistrationOk
  Registration is successful.
  Definition: types.h:392
- Incoming call notification while in background mode
  Assuming application using liblinphone is well configured for multitasking, incoming calls arriving while liblinphone is in background mode will simply wakeup liblinphone thread but not resume GUI. To wakeup GUI, it is recommended to send a Local Notification to the user from the LinphoneCoreCallStateChangedCb. Here under a speudo code for this operation:
  if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive) {
  // Create a new notification
  UILocalNotification* notif = [[[UILocalNotification alloc] init] autorelease];
  if (notif) {
  notif.repeatInterval = 0;
  notif.alertBody =@"New incoming call";
  notif.alertAction = @"Answer";
  notif.soundName = @"oldphone-mono-30s.caf";
  [[UIApplication sharedApplication] presentLocalNotificationNow:notif];
  }

Networking

- WWAN connection
  Liblinphone relies on iOS's standard BSD socket layer for sip/rtp networking. On IOS, WWAN connection is supposed to automatically bring up on any networking resquest issued by an application. At least on iPhone OS 3.x, BSD sockets do not implement this behavior. So it is recomended to add a special code to make sure the WWAN connection is properly setup. Pseudo code below describes a way to force WWAN connection by setting up a dummy TCP connection.
  /*start a new thread to avoid blocking the main ui in case of peer host failure*/
  [NSThread detachNewThreadSelector:@selector(runNetworkConnection) toTarget:self withObject:nil];
  -(void) runNetworkConnection {
  CFWriteStreamRef writeStream;
  //create a dummy socket
  CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)@"192.168.0.200", 15000, nil, &writeStream);
  CFWriteStreamOpen (writeStream);
  const char* buff="hello";
  //try to write on this socket
  CFWriteStreamWrite (writeStream,(const UInt8*)buff,strlen(buff));
  CFWriteStreamClose (writeStream);
  }
  It is recommanded to perform this task each time the application is woken up, including keep alive handler.
- Managing IP connection state
  Liblinphone for IOS relies on the application to be informed of network connectivity changes. Network state changes when the IP connection moves from DOWN to UP, or from WIFI to WWAN. Applications using liblinphone must inform libliblinphone of this changes using function linphone_core_set_network_reachable(). Usually this method is called from the IOS NetworkReachability callback. Here under a sample code:
  //typical reachability callback
  void networkReachabilityCallBack(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void * info) {
  if ((flags == 0) | (flags & (kSCNetworkReachabilityFlagsConnectionRequired |kSCNetworkReachabilityFlagsConnectionOnTraffic))) {
  //network state is off
  linphone_core_set_network_reachable(lc,false);
  ((LinphoneManager*)info).connectivity = none;
  } else {
  Connectivity newConnectivity = flags & kSCNetworkReachabilityFlagsIsWWAN ? wwan:wifi;
  if (lLinphoneMgr.connectivity == none) {
  //notify new network state
  linphone_core_set_network_reachable(lc,true);
  } else if (lLinphoneMgr.connectivity != newConnectivity) {
  // connectivity has changed
  linphone_core_set_network_reachable(lc,false);
  linphone_core_set_network_reachable(lc,true);
  }
  //store new connectivity status
  lLinphoneMgr.connectivity=newConnectivity;
  }
  }
  }
  linphone_core_set_network_reachable
  void linphone_core_set_network_reachable(LinphoneCore *core, bool_t reachable)
  This method is called by the application to notify the linphone core library when network is reachabl...

Sound cards
Since IOS 5.0, liblinphone supports 2 sound cards. AU: Audio Unit Receiver based on IO units for voice calls plus AQ: Audio Queue Device dedicated to rings. Here under the recommended settings (I.E default one)

linphone_core_set_playback_device(lc, "AU: Audio Unit Receiver");
linphone_core_set_ringer_device(lc, "AQ: Audio Queue Device");
linphone_core_set_capture_device(lc, "AU: Audio Unit Receiver");
linphone_core_set_ringer_device
LinphoneStatus linphone_core_set_ringer_device(LinphoneCore *core, const char *devid)
Sets the sound device used for ringing.
linphone_core_set_capture_device
LinphoneStatus linphone_core_set_capture_device(LinphoneCore *core, const char *devid)
Sets the sound device used for capture.
linphone_core_set_playback_device
LinphoneStatus linphone_core_set_playback_device(LinphoneCore *core, const char *devid)
Sets the sound device used for playback.

GSM call interaction
To ensure gentle interaction with GSM calls, it is recommended to register an AudioSession delegate. This allows the application to be notified when its audio session is interrupted/resumed (presumably by a GSM call).

// declare a class handling the AVAudioSessionDelegate protocol
@interface MyClass : NSObject <AVAudioSessionDelegate> { [...] }
// implement 2 methods : here's an example implementation
-(void) beginInterruption {
LinphoneCall* c = linphone_core_get_current_call(theLinphoneCore);
ms_message("Sound interruption detected!");
if (c) {
linphone_core_pause_call(theLinphoneCore, c);
}
}
-(void) endInterruption {
ms_message("Sound interruption ended!");
const MSList* c = linphone_core_get_calls(theLinphoneCore);
if (c) {
ms_message("Auto resuming call");
linphone_core_resume_call(theLinphoneCore, (LinphoneCall*) c->data);
}
}
linphone_core_get_current_call
LinphoneCall * linphone_core_get_current_call(const LinphoneCore *core)
Gets the current call.
linphone_core_pause_call
MS2_DEPRECATED LinphoneStatus linphone_core_pause_call(LinphoneCore *core, LinphoneCall *call)
Pauses the call.
linphone_core_get_calls
const bctbx_list_t * linphone_core_get_calls(LinphoneCore *core)
Gets the current list of calls.
linphone_core_resume_call
MS2_DEPRECATED LinphoneStatus linphone_core_resume_call(LinphoneCore *core, LinphoneCall *call)
Resumes a call.
LinphoneCall
struct _LinphoneCall LinphoneCall
This object represents a call issued or received by the LinphoneCore.
Definition: c-types.h:219

See also
  http://developer.apple.com/library/ios/#documentation/AVFoundation/Reference/AVAudioSessionDelegate_ProtocolReference/Reference/Reference.html

Declare an instance of your class as AudioSession's delegate :

[audioSession setDelegate:myClassInstance];

See also
  http://developer.apple.com/library/ios/#documentation/AVFoundation/Reference/AVAudioSession_ClassReference/Reference/Reference.html

Video
Since 3.5 video support has been added to liblinphone for IOS. It requires the application to provide liblinphone with pointers to IOS's views hosting video display and video previous.
These 2 UIView objects must be passed to the core using functions linphone_core_set_native_video_window_id() and linphone_core_set_native_preview_window_id(). here under speudo code:

UIView* display = [[UIView alloc] init];
UIView* preview = [[UIView alloc] init];
linphone_core_set_native_video_window_id(lc,(unsigned long)display);
linphone_core_set_native_preview_window_id(lc,(unsigned long)preview);
linphone_core_set_native_video_window_id
void linphone_core_set_native_video_window_id(LinphoneCore *core, void *window_id)
Set the native video window id where the video is to be displayed.
linphone_core_set_native_preview_window_id
void linphone_core_set_native_preview_window_id(LinphoneCore *core, void *window_id)
Set the native window id where the preview video (local camera) is to be displayed.

Screen rotations are also handled by liblinphone. 2 positions are currently supported, namely UIInterfaceOrientationPortrait and UIInterfaceOrientationLandscapeRight. Applications may invoke linphone_core_set_device_rotation() followed by linphone_core_update_call() to notify liblinphone of an orientation change. Here under a speudo code to handle orientation changes

-(void) configureOrientation:(UIInterfaceOrientation) oritentation {
int oldLinphoneOrientation = linphone_core_get_device_rotation(lc);
if (oritentation == UIInterfaceOrientationPortrait ) {
linphone_core_set_native_video_window_id(lc,(unsigned long)display-portrait);
linphone_core_set_native_preview_window_id(lc,(unsigned long)preview-portrait);
linphone_core_set_device_rotation(lc, 0);
} else if (oritentation == UIInterfaceOrientationLandscapeRight ) {
linphone_core_set_native_video_window_id(lc,(unsigned long)display-landscape);
linphone_core_set_native_preview_window_id(lc,(unsigned long)preview-landscape);
linphone_core_set_device_rotation(lc, 270);
}
if ((oldLinphoneOrientation != linphone_core_get_device_rotation(lc))
&& linphone_core_get_current_call(lc)) {
//Orientation has changed, must call update call
linphone_core_update_call(lc, linphone_core_get_current_call(lc), NULL);
}
}
linphone_core_update_call
MS2_DEPRECATED LinphoneStatus linphone_core_update_call(LinphoneCore *core, LinphoneCall *call, const LinphoneCallParams *params)
Updates a running call according to supplied call parameters or parameters changed in the LinphoneCor...
linphone_core_get_device_rotation
int linphone_core_get_device_rotation(LinphoneCore *core)
Gets the current device orientation.
linphone_core_set_device_rotation
void linphone_core_set_device_rotation(LinphoneCore *core, int rotation)
Tells the core the device current orientation.

DTMF feebacks
Liblinphone provides functions to play dtmf to the local user. Usually this is used to play a sound when the user presses a digit, inside or outside of any call. On IOS, libLinphone relies on AudioUnits for interfacing with the audio system. Unfortunately the Audio Unit initialization is a quite long operation that may trigger a bad user experience if performed each time a DTMF is played, the sound being delayed half a second after the press. To solve this issue and thus insure real-time precision, liblinphone introduces 2 functions for preloading and unloading the underlying audio graph responsible for playing DTMFs.
For an application using function linphone_core_play_dtmf(), it is recommanded to call linphone_core_start_dtmf_stream() when entering in foreground and linphone_core_stop_dtmf_stream() upon entering background mode.

Plugins
On iOS, plugins are built as static libraries so Liblinphone will not be able to load them at runtime dynamically. Instead, you should declare their prototypes:

extern void libmsamr_init(MSFactory *factory);
extern void libmsx264_init(MSFactory *factory);
extern void libmsopenh264_init(MSFactory *factory);
extern void libmssilk_init(MSFactory *factory);
extern void libmsbcg729_init(MSFactory *factory);
extern void libmswebrtc_init(MSFactory *factory);

Then you should register them after the instantiation of LinphoneCore:

theLinphoneCore = linphone_core_new_with_config(/* options ... */);
// Load plugins if available in the linphone SDK - otherwise these calls will do nothing
MSFactory *f = linphone_core_get_ms_factory(theLinphoneCore);
libmssilk_init(f);
libmsamr_init(f);
libmsx264_init(f);
libmsopenh264_init(f);
libmsbcg729_init(f);
libmswebrtc_init(f);
linphone_core_reload_ms_plugins(theLinphoneCore, NULL);
linphone_core_reload_ms_plugins
void linphone_core_reload_ms_plugins(LinphoneCore *core, const char *path)
Reload mediastreamer2 plugins from specified directory.
linphone_core_new_with_config
MS2_DEPRECATED LinphoneCore * linphone_core_new_with_config(const LinphoneCoreVTable *vtable, LinphoneConfig *config, void *userdata)
Instantiates a LinphoneCore object with a given LpConfig.

If the plugin has not been enabled at compilation time, a stubbed library will be generated with only libplugin_init method declared, doing nothing. You should see these trace in logs, if plugin is stubbed:

I/lib/Could not find encoder for SILK
I/lib/Could not find decoder for SILK

## Function Documentation

## ◆ linphone_core_activate_audio_session()

void linphone_core_activate_audio_session | ( | LinphoneCore * | core,
|  | bool_t | actived
| ) |  |

Special function to indicate if the audio session is activated.

Must be called when ProviderDelegate of the callkit notifies that the audio session is activated or deactivated.

Parameters

  core | The LinphoneCore object.
  activated | TRUE to activate the audio session, FALSE to disable it.

## ◆ linphone_core_audio_route_changed()

MS2_DEPRECATED void linphone_core_audio_route_changed | ( | LinphoneCore * | core | ) |

Special function to indicate if the audio route is changed.

Must be called in the callback of AVAudioSessionRouteChangeNotification.

Parameters

  core | The LinphoneCore object.

Deprecated:
  07/01/2020 now handled in the linphone SDK directly

## ◆ linphone_core_callkit_enabled()

bool_t linphone_core_callkit_enabled | ( | const LinphoneCore * | core | ) |

Special function to check if the callkit is enabled, False by default.

Parameters

  core | The LinphoneCore object.

Returns
  TRUE if callkit is enabled, FALSE otherwise.

## ◆ linphone_core_configure_audio_session()

void linphone_core_configure_audio_session | ( | LinphoneCore * | core | ) |

Special function to configure audio session with default settings.

Must be called in ProviderDelegate's callbacks when answer an incoming call and start an outgoing call.

Parameters

  core | The LinphoneCore object.

## ◆ linphone_core_enable_callkit()

void linphone_core_enable_callkit | ( | LinphoneCore * | core,
|  | bool_t | enabled
| ) |  |

Special function to enable the callkit.

Parameters

  core | The LinphoneCore object.
  enabled | TRUE to enable callkit, FALSE to disable it.

## ◆ linphone_core_start_dtmf_stream()

void linphone_core_start_dtmf_stream | ( | LinphoneCore * | core | ) |

Parameters

  core | The LinphoneCore object. Special function to warm up dtmf feeback stream. linphone_core_stop_dtmf_stream() must be called before entering FG mode

## ◆ linphone_core_stop_dtmf_stream()

void linphone_core_stop_dtmf_stream | ( | LinphoneCore * | core | ) |

Parameters

  core | The LinphoneCore object. Special function to stop dtmf feed back function. Must be called before entering BG mode