1174 lines
45 KiB
Objective-C
1174 lines
45 KiB
Objective-C
//
|
|
// MGMSIP.m
|
|
// VoiceBase
|
|
//
|
|
// Created by Mr. Gecko on 9/10/10.
|
|
// Copyright (c) 2011 Mr. Gecko's Media (James Coleman). http://mrgeckosmedia.com/
|
|
//
|
|
// Permission to use, copy, modify, and/or distribute this software for any purpose
|
|
// with or without fee is hereby granted, provided that the above copyright notice
|
|
// and this permission notice appear in all copies.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
|
|
// OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
// DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
|
// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
//
|
|
|
|
#if MGMSIPENABLED
|
|
#import "MGMSIP.h"
|
|
#import "MGMSIPAccount.h"
|
|
#import "MGMSIPCall.h"
|
|
#import "MGMSIPURL.h"
|
|
#import "MGMAddons.h"
|
|
#import <pjsua-lib/pjsua_internal.h>
|
|
#import <SystemConfiguration/SystemConfiguration.h>
|
|
#if !TARGET_OS_IPHONE
|
|
#import <CoreAudio/CoreAudio.h>
|
|
#endif
|
|
|
|
NSString * const MGMSIPCopyright = @"Copyright (c) 2011 Mr. Gecko's Media (James Coleman). http://mrgeckosmedia.com/";
|
|
|
|
const int MGMSIPMaxCalls = 8;
|
|
const int MGMSIPDefaultOutboundProxyPort = 5060;
|
|
const int MGMSIPDefaultOutboundPort = 5060;
|
|
const int MGMSIPDefaultSTUNPort = 3478;
|
|
|
|
NSString * const MGMSIPOutboundProxy = @"MGMSIPOutboundProxy";
|
|
NSString * const MGMSIPOutboundProxyPort = @"MGMSIPOutboundProxyPort";
|
|
NSString * const MGMSIPSTUN = @"MGMSIPSTUN";
|
|
NSString * const MGMSIPSTUNPort = @"MGMSIPSTUNPort";
|
|
NSString * const MGMSIPLogFile = @"MGMSIPLogFile";
|
|
NSString * const MGMSIPLogLevel = @"MGMSIPLogLevel";
|
|
NSString * const MGMSIPConsoleLogLevel = @"MGMSIPConsoleLogLevel";
|
|
NSString * const MGMSIPVoiceActivityDetection = @"MGMSIPVoiceActivityDetection";
|
|
NSString * const MGMSIPInteractiveConnectivityEstablishment = @"MGMSIPInteractiveConnectivityEstablishment";
|
|
NSString * const MGMSIPNameServersEnabled = @"MGMSIPNameServersEnabled";
|
|
NSString * const MGMSIPEchoCacnellationEnabled = @"MGMSIPEchoCacnellationEnabled";
|
|
NSString * const MGMSIPPort = @"MGMSIPPort";
|
|
NSString * const MGMSIPPublicAddress = @"MGMSIPPublicAddress";
|
|
NSString * const MGMSIPUserAgent = @"MGMSIPUserAgent";
|
|
NSString * const MGMSIPCodec = @"MGMSIPCodec";
|
|
|
|
NSString * const MGMNetworkConnectedNotification = @"MGMNetworkConnectedNotification";
|
|
NSString * const MGMNetworkDisconnectedNotification = @"MGMNetworkDisconnectedNotification";
|
|
|
|
NSString * const MGMSIPVolume = @"MGMSIPVolume";
|
|
NSString * const MGMSIPMicVolume = @"MGMSIPMicVolume";
|
|
NSString * const MGMSIPVolumeChangedNotification = @"MGMSIPVolumeChangedNotification";
|
|
NSString * const MGMSIPMicVolumeChangedNotification = @"MGMSIPMicVolumeChangedNotification";
|
|
|
|
NSString * const MGMSIPADeviceIdentifier = @"MGMSIPADeviceIdentifier";
|
|
NSString * const MGMSIPADeviceIndex = @"MGMSIPADeviceIndex";
|
|
NSString * const MGMSIPADeviceUID = @"MGMSIPADeviceUID";
|
|
NSString * const MGMSIPADeviceName = @"MGMSIPADeviceName";
|
|
NSString * const MGMSIPADeviceInputCount = @"MGMSIPADeviceInputCount";
|
|
NSString * const MGMSIPADeviceOutputCount = @"MGMSIPADeviceOutputCount";
|
|
NSString * const MGMSIPADeviceIsDefaultInput = @"MGMSIPADeviceIsDefaultInput";
|
|
NSString * const MGMSIPADeviceIsDefaultOutput = @"MGMSIPADeviceIsDefaultOutput";
|
|
|
|
NSString * const MGMSIPACurrentInputDevice = @"MGMSIPACurrentInputDevice";
|
|
NSString * const MGMSIPACurrentOutputDevice = @"MGMSIPACurrentOutputDevice";
|
|
NSString * const MGMSIPASystemDefault = @"System Default";
|
|
NSString * const MGMSIPAudioChangedNotification = @"MGMSIPAudioChangedNotification";
|
|
|
|
static MGMSIP *MGMSIPSingleton = nil;
|
|
|
|
#define THIS_FILE "MGMSIP.m"
|
|
|
|
static void MGMSIPIncomingCallReceived(pjsua_acc_id accountIdentifier, pjsua_call_id callIdentifier, pjsip_rx_data *messageData) {
|
|
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
|
PJ_LOG(3, (THIS_FILE, "Incoming call %d for account %d", callIdentifier, accountIdentifier));
|
|
|
|
if (accountIdentifier==[[MGMSIP sharedSIP] UDPAccount] || accountIdentifier==[[MGMSIP sharedSIP] TCPAccount]) {
|
|
pjsua_call_info callInfo;
|
|
pj_status_t status = pjsua_call_get_info(callIdentifier, &callInfo);
|
|
if (status==PJ_SUCCESS) {
|
|
MGMSIPURL *localURL = [MGMSIPURL URLWithSIPID:[NSString stringWithPJString:callInfo.local_info]];
|
|
accountIdentifier = [[MGMSIP sharedSIP] accountIDForUserName:[localURL userName]];
|
|
}
|
|
}
|
|
|
|
MGMSIPAccount *account = [[MGMSIP sharedSIP] accountWithIdentifier:accountIdentifier];
|
|
MGMSIPCall *call = [account addCallWithIdentifier:callIdentifier];
|
|
[[MGMSIP sharedSIP] receivedNewCall:call];
|
|
id<MGMSIPDelegate> delegate = [[MGMSIP sharedSIP] delegate];
|
|
if (delegate!=nil && [delegate respondsToSelector:@selector(gotNewCall:)]) [delegate gotNewCall:call];
|
|
if ([account delegate]!=nil && [[account delegate] respondsToSelector:@selector(receivedNewCall:)]) [[account delegate] receivedNewCall:call];
|
|
if ([account delegate]!=nil && [[account delegate] respondsToSelector:@selector(gotNewCall:)]) [[account delegate] gotNewCall:call];
|
|
[pool drain];
|
|
}
|
|
|
|
static void MGMSIPCallStateChanged(pjsua_call_id callIdentifier, pjsip_event *sipEvent) {
|
|
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
|
MGMSIPCall *call = [[MGMSIP sharedSIP] callWithIdentifier:callIdentifier];
|
|
pjsua_call_info callInfo;
|
|
pjsua_call_get_info(callIdentifier, &callInfo);
|
|
|
|
if (call==nil && callInfo.state==MGMSIPCallCallingState) {
|
|
MGMSIPAccount *account = [[MGMSIP sharedSIP] accountWithIdentifier:callInfo.acc_id];
|
|
MGMSIPCall *call = [account addCallWithIdentifier:callIdentifier];
|
|
[[MGMSIP sharedSIP] startingNewCall:call];
|
|
id<MGMSIPDelegate> delegate = [[MGMSIP sharedSIP] delegate];
|
|
if (delegate!=nil && [delegate respondsToSelector:@selector(gotNewCall:)]) [delegate gotNewCall:call];
|
|
if ([account delegate]!=nil && [[account delegate] respondsToSelector:@selector(startingNewCall:)]) [[account delegate] startingNewCall:call];
|
|
if ([account delegate]!=nil && [[account delegate] respondsToSelector:@selector(gotNewCall:)]) [[account delegate] gotNewCall:call];
|
|
} else {
|
|
[call setState:callInfo.state];
|
|
[call setStateText:[NSString stringWithPJString:callInfo.state_text]];
|
|
[call setLastStatus:callInfo.last_status];
|
|
[call setLastStatusText:[NSString stringWithPJString:callInfo.last_status_text]];
|
|
if ([call delegate]!=nil && [[call delegate] respondsToSelector:@selector(stateChanged:)]) [[call delegate] stateChanged:call];
|
|
}
|
|
|
|
if (callInfo.state==MGMSIPCallDisconnectedState) {
|
|
[call stopRingback];
|
|
NSLog(@"%@ Disconnected", call);
|
|
if ([call delegate]!=nil && [[call delegate] respondsToSelector:@selector(disconnected:)]) [[call delegate] disconnected:call];
|
|
[call setIdentifier:PJSUA_INVALID_ID];
|
|
[[call account] removeCall:call];
|
|
|
|
PJ_LOG(3, (THIS_FILE, "Call %d disconnected reason %d (%s)", callIdentifier, callInfo.last_status, callInfo.last_status_text.ptr));
|
|
} else if (callInfo.state==MGMSIPCallEarlyState) {
|
|
if (sipEvent->type!=PJSIP_EVENT_TSX_STATE) {
|
|
[pool drain];
|
|
return;
|
|
}
|
|
|
|
pjsip_msg *msg;
|
|
if (sipEvent->body.tsx_state.type==PJSIP_EVENT_RX_MSG)
|
|
msg = sipEvent->body.tsx_state.src.rdata->msg_info.msg;
|
|
else
|
|
msg = sipEvent->body.tsx_state.src.tdata->msg;
|
|
|
|
pj_str_t reason = msg->line.status.reason;
|
|
int code = msg->line.status.code;
|
|
|
|
if (callInfo.role==PJSIP_ROLE_UAC && code==180 && msg->body==NULL && callInfo.media_status==PJSUA_CALL_MEDIA_NONE)
|
|
[call startRingback];
|
|
|
|
PJ_LOG(3, (THIS_FILE, "Call %d state changed to %s %d (%.*s)", callIdentifier, callInfo.state_text.ptr, code, (int)reason.slen, reason.ptr));
|
|
|
|
if ([call delegate]!=nil && [[call delegate] respondsToSelector:@selector(early:code:reason:)]) [[call delegate] early:call code:code reason:[NSString stringWithPJString:reason]];
|
|
} else {
|
|
PJ_LOG(3, (THIS_FILE, "Call %d state changed to %s", callIdentifier, callInfo.state_text.ptr));
|
|
|
|
switch (callInfo.state) {
|
|
case MGMSIPCallCallingState:
|
|
if ([call delegate]!=nil && [[call delegate] respondsToSelector:@selector(calling:)]) [[call delegate] calling:call];
|
|
break;
|
|
case MGMSIPCallConnectingState:
|
|
if ([call delegate]!=nil && [[call delegate] respondsToSelector:@selector(connecting:)]) [[call delegate] connecting:call];
|
|
break;
|
|
case MGMSIPCallConfirmedState:
|
|
if ([call delegate]!=nil && [[call delegate] respondsToSelector:@selector(confirmed:)]) [[call delegate] confirmed:call];
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
[pool drain];
|
|
}
|
|
|
|
static void MGMSIPCallMediaStateChanged(pjsua_call_id callIdentifier) {
|
|
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
|
MGMSIPCall *call = [[MGMSIP sharedSIP] callWithIdentifier:callIdentifier];
|
|
[call stopRingback];
|
|
|
|
pjsua_call_info callInfo;
|
|
pjsua_call_get_info(callIdentifier, &callInfo);
|
|
if ([call delegate]!=nil && [[call delegate] respondsToSelector:@selector(mediaStateChanged:)]) [[call delegate] mediaStateChanged:call];
|
|
|
|
if (callInfo.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
|
|
pjsua_conf_connect(callInfo.conf_slot, 0);
|
|
pjsua_conf_connect(0, callInfo.conf_slot);
|
|
|
|
PJ_LOG(3, (THIS_FILE, "Media for call %d is active", callIdentifier));
|
|
|
|
if ([call delegate]!=nil && [[call delegate] respondsToSelector:@selector(becameActive:)]) [[call delegate] becameActive:call];
|
|
} else if (callInfo.media_status == PJSUA_CALL_MEDIA_LOCAL_HOLD) {
|
|
PJ_LOG(3, (THIS_FILE, "Media for call %d is placed on hold by local", callIdentifier));
|
|
|
|
if ([call delegate]!=nil && [[call delegate] respondsToSelector:@selector(localPlacedHold:)]) [[call delegate] becameActive:call];
|
|
} else if (callInfo.media_status == PJSUA_CALL_MEDIA_REMOTE_HOLD) {
|
|
PJ_LOG(3, (THIS_FILE, "Media for call %d is placed on hold by remote", callIdentifier));
|
|
|
|
if ([call delegate]!=nil && [[call delegate] respondsToSelector:@selector(remotePlacedHold:)]) [[call delegate] becameActive:call];
|
|
} else if (callInfo.media_status == PJSUA_CALL_MEDIA_ERROR) {
|
|
PJ_LOG(1, (THIS_FILE, "Media has reported error, disconnecting call"));
|
|
|
|
pj_str_t reason = pj_str("ICE negotiation failed");
|
|
pjsua_call_hangup(callIdentifier, 500, &reason, NULL);
|
|
} else {
|
|
PJ_LOG(3, (THIS_FILE, "Media for call %d is inactive", [call identifier]));
|
|
}
|
|
[pool drain];
|
|
}
|
|
|
|
static void MGMSIPCallTransferStatusChanged(pjsua_call_id callIdentifier, int statusCode, const pj_str_t *statusText, pj_bool_t isFinal, pj_bool_t *pCont) {
|
|
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
|
MGMSIPCall *call = [[MGMSIP sharedSIP] callWithIdentifier:callIdentifier];
|
|
[call setTransferStatus:statusCode];
|
|
[call setTransferStatusText:[NSString stringWithPJString:*statusText]];
|
|
if ([call delegate]!=nil && [[call delegate] respondsToSelector:@selector(transferStatusCahgned:)]) [[call delegate] transferStatusCahgned:call];
|
|
[pool drain];
|
|
}
|
|
|
|
static void MGMSIPAccountRegistrationStateChanged(pjsua_acc_id accountIdentifier) {
|
|
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
|
MGMSIPAccount *account = [[MGMSIP sharedSIP] accountWithIdentifier:accountIdentifier];
|
|
[account registrationStateChanged];
|
|
if ([account delegate]!=nil && [[account delegate] respondsToSelector:@selector(registrationChanged)]) [(NSObject *)[account delegate] performSelectorOnMainThread:@selector(registrationChanged) withObject:nil waitUntilDone:YES];
|
|
[pool drain];
|
|
}
|
|
|
|
static void MGMSIPDetectedNAT(const pj_stun_nat_detect_result *result) {
|
|
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
|
if (result->status!=PJ_SUCCESS) {
|
|
pjsua_perror(THIS_FILE, "NAT detection failed", result->status);
|
|
} else {
|
|
PJ_LOG(3, (THIS_FILE, "NAT detected as %s", result->nat_type_name));
|
|
[[MGMSIP sharedSIP] setNATType:result->nat_type];
|
|
}
|
|
[pool drain];
|
|
}
|
|
|
|
static void MGMSIPDTMFReceived(pjsua_call_id callIdentifier, int dtmfDigit) {
|
|
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
|
MGMSIPCall *call = [[MGMSIP sharedSIP] callWithIdentifier:callIdentifier];
|
|
[call receivedDTMFDigit:dtmfDigit];
|
|
PJ_LOG(3, (THIS_FILE, "Received DTMF on call %d: %c", callIdentifier, dtmfDigit));
|
|
[pool drain];
|
|
}
|
|
|
|
#if !TARGET_OS_IPHONE
|
|
static void MGMNetworkNotification(SCDynamicStoreRef store, NSArray *changedKeys, void *info) {
|
|
for (int i=0; i<[changedKeys count]; ++i) {
|
|
NSString *key = [changedKeys objectAtIndex:i];
|
|
if ([key isEqual:@"State:/Network/Global/IPv4"]) {
|
|
NSDictionary *value = (NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef)key);
|
|
if (value!=nil)
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:MGMNetworkConnectedNotification object:[value autorelease]];
|
|
else
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:MGMNetworkDisconnectedNotification object:nil];
|
|
}
|
|
}
|
|
}
|
|
|
|
static OSStatus MGMAudioDevicesChanged(AudioHardwarePropertyID propertyID, void *clientData) {
|
|
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
|
if (propertyID==kAudioHardwarePropertyDevices || propertyID==kAudioHardwarePropertyDefaultInputDevice || propertyID==kAudioHardwarePropertyDefaultOutputDevice) {
|
|
[NSObject cancelPreviousPerformRequestsWithTarget:(MGMSIP *)clientData selector:@selector(updateAudioDevices) object:nil];
|
|
[(MGMSIP *)clientData performSelector:@selector(updateAudioDevices) withObject:nil afterDelay:0.2];
|
|
}
|
|
[pool drain];
|
|
return noErr;
|
|
}
|
|
#endif
|
|
|
|
@interface MGMSIP (MGMPrivate)
|
|
|
|
@end
|
|
|
|
@implementation MGMSIP
|
|
+ (MGMSIP *)sharedSIP {
|
|
if (MGMSIPSingleton==nil)
|
|
MGMSIPSingleton = [MGMSIP new];
|
|
return MGMSIPSingleton;
|
|
}
|
|
- (id)init {
|
|
if ((self = [super init])) {
|
|
[self registerDefaults];
|
|
port = [[NSUserDefaults standardUserDefaults] integerForKey:MGMSIPPort];
|
|
lock = [NSLock new];
|
|
state = MGMSIPStoppedState;
|
|
NATType = MGMSIPNATUnknownType;
|
|
accounts = [NSMutableArray new];
|
|
#if !TARGET_OS_IPHONE
|
|
lastInputDevice = -1;
|
|
lastOutputDevice = -1;
|
|
#endif
|
|
shouldRestart = NO;
|
|
|
|
codecsInfo = [NSMutableDictionary new];
|
|
|
|
#if !TARGET_OS_IPHONE
|
|
store = SCDynamicStoreCreate(kCFAllocatorDefault, CFBundleGetIdentifier(CFBundleGetMainBundle()), (SCDynamicStoreCallBack)MGMNetworkNotification, NULL);
|
|
if (!store) {
|
|
NSLog(@"Unable to create store for system configuration %s", SCErrorString(SCError()));
|
|
} else {
|
|
NSArray *keys = [NSArray arrayWithObjects:@"State:/Network/Global/IPv4", @"State:/Network/Global/IPv6", nil];
|
|
if (!SCDynamicStoreSetNotificationKeys(store, (CFArrayRef)keys, NULL)) {
|
|
NSLog(@"Faild to set the store for notifications %s", SCErrorString(SCError()));
|
|
CFRelease(store);
|
|
store = NULL;
|
|
} else {
|
|
storeRunLoop = SCDynamicStoreCreateRunLoopSource(kCFAllocatorDefault, store, 0);
|
|
CFRunLoopAddSource(CFRunLoopGetCurrent(), storeRunLoop, kCFRunLoopDefaultMode);
|
|
CFRelease(storeRunLoop);
|
|
}
|
|
}
|
|
NSNotificationCenter *wsNotifications = [[NSWorkspace sharedWorkspace] notificationCenter];
|
|
[wsNotifications addObserver:self selector:@selector(computerSleep) name:NSWorkspaceWillSleepNotification object:nil];
|
|
[wsNotifications addObserver:self selector:@selector(computerWake) name:NSWorkspaceDidWakeNotification object:nil];
|
|
#endif
|
|
}
|
|
return self;
|
|
}
|
|
- (void)dealloc {
|
|
#if !TARGET_OS_IPHONE
|
|
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
|
|
[self stop];
|
|
if (storeRunLoop!=NULL)
|
|
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), storeRunLoop, kCFRunLoopDefaultMode);
|
|
if (store!=NULL)
|
|
CFRelease(store);
|
|
#endif
|
|
[lock release];
|
|
[codecsInfo release];
|
|
[accounts release];
|
|
[restartAccounts release];
|
|
[super dealloc];
|
|
}
|
|
|
|
- (void)registerDefaults {
|
|
NSMutableDictionary *defaults = [NSMutableDictionary dictionary];
|
|
[defaults setObject:[NSNumber numberWithInt:0] forKey:MGMSIPLogLevel];
|
|
[defaults setObject:[NSNumber numberWithInt:0] forKey:MGMSIPConsoleLogLevel];
|
|
[defaults setObject:[NSNumber numberWithBool:YES] forKey:MGMSIPVoiceActivityDetection];
|
|
[defaults setObject:[NSNumber numberWithBool:NO] forKey:MGMSIPInteractiveConnectivityEstablishment];
|
|
[defaults setObject:[NSNumber numberWithBool:NO] forKey:MGMSIPNameServersEnabled];
|
|
[defaults setObject:[NSNumber numberWithBool:YES] forKey:MGMSIPEchoCacnellationEnabled];
|
|
[defaults setObject:[NSNumber numberWithInt:0] forKey:MGMSIPPort];
|
|
[defaults setObject:@"speex/16000" forKey:MGMSIPCodec];
|
|
[defaults setObject:[NSNumber numberWithFloat:1.0] forKey:MGMSIPVolume];
|
|
[defaults setObject:[NSNumber numberWithFloat:1.0] forKey:MGMSIPMicVolume];
|
|
[defaults setObject:MGMSIPASystemDefault forKey:MGMSIPACurrentInputDevice];
|
|
[defaults setObject:MGMSIPASystemDefault forKey:MGMSIPACurrentOutputDevice];
|
|
[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
|
|
}
|
|
|
|
- (id<MGMSIPDelegate>)delegate {
|
|
return delegate;
|
|
}
|
|
- (void)setDelegate:(id)theDelegate {
|
|
delegate = theDelegate;
|
|
}
|
|
|
|
- (MGMSIPState)state {
|
|
return state;
|
|
}
|
|
- (BOOL)isStarted {
|
|
return (state==MGMSIPStartedState);
|
|
}
|
|
- (pj_pool_t *)PJPool {
|
|
return PJPool;
|
|
}
|
|
- (int)port {
|
|
return port;
|
|
}
|
|
- (void)setPort:(int)thePort {
|
|
port = thePort;
|
|
}
|
|
- (pjsua_media_config)mediaConfig {
|
|
return mediaConfig;
|
|
}
|
|
- (pjmedia_port *)ringbackPort {
|
|
return ringbackPort;
|
|
}
|
|
- (pjsua_conf_port_id)ringbackSlot {
|
|
return ringbackSlot;
|
|
}
|
|
- (pjsua_transport_id)UDPTransport {
|
|
return UDPTransport;
|
|
}
|
|
- (pjsua_acc_id)UDPAccount {
|
|
return UDPAccount;
|
|
}
|
|
- (pjsua_transport_id)TCPTransport {
|
|
return TCPTransport;
|
|
}
|
|
- (pjsua_acc_id)TCPAccount {
|
|
return TCPAccount;
|
|
}
|
|
- (MGMSIPNATType)NATType {
|
|
return NATType;
|
|
}
|
|
- (void)setNATType:(MGMSIPNATType)theNATType {
|
|
NATType = theNATType;
|
|
}
|
|
|
|
- (void)start {
|
|
if (state>MGMSIPStoppedState)
|
|
return;
|
|
|
|
[restartTimer invalidate];
|
|
[restartTimer release];
|
|
restartTimer = nil;
|
|
|
|
[NSThread detachNewThreadSelector:@selector(startBackground) toTarget:self withObject:nil];
|
|
}
|
|
- (void)startBackground {
|
|
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
|
[lock lock];
|
|
|
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
|
|
pj_status_t status;
|
|
|
|
state = MGMSIPStartingState;
|
|
status = pjsua_create();
|
|
if (status!=PJ_SUCCESS) {
|
|
NSLog(@"Unable to create PJSUA.");
|
|
state = MGMSIPNULLState;
|
|
[pool drain];
|
|
return;
|
|
}
|
|
|
|
PJPool = pjsua_pool_create("MGMSIP-pjsua", 1000, 1000);
|
|
|
|
pjsua_logging_config loggingConfig;
|
|
pjsua_logging_config_default(&loggingConfig);
|
|
if ([defaults objectForKey:MGMSIPLogFile]!=nil && ![[defaults objectForKey:MGMSIPLogFile] isEqual:@""])
|
|
loggingConfig.log_filename = [[[defaults objectForKey:MGMSIPLogFile] stringByExpandingTildeInPath] PJString];
|
|
loggingConfig.level = [defaults integerForKey:MGMSIPLogLevel];
|
|
loggingConfig.console_level = [defaults integerForKey:MGMSIPConsoleLogLevel];
|
|
|
|
pjsua_media_config_default(&mediaConfig);
|
|
mediaConfig.no_vad = ![defaults boolForKey:MGMSIPVoiceActivityDetection];
|
|
mediaConfig.enable_ice = [defaults boolForKey:MGMSIPInteractiveConnectivityEstablishment];
|
|
mediaConfig.snd_auto_close_time = 1;
|
|
if (![defaults boolForKey:MGMSIPEchoCacnellationEnabled])
|
|
mediaConfig.ec_tail_len = 0;
|
|
|
|
pjsua_config sipConfig;
|
|
pjsua_config_default(&sipConfig);
|
|
|
|
if ([defaults objectForKey:MGMSIPUserAgent]==nil || [[defaults objectForKey:MGMSIPUserAgent] isEqual:@""])
|
|
sipConfig.user_agent = [[NSString stringWithFormat:@"%@ %@", [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleNameKey], [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey]] PJString];
|
|
else
|
|
sipConfig.user_agent = [[defaults objectForKey:MGMSIPUserAgent] PJString];
|
|
|
|
sipConfig.cb.on_incoming_call = &MGMSIPIncomingCallReceived;
|
|
sipConfig.cb.on_call_media_state = &MGMSIPCallMediaStateChanged;
|
|
sipConfig.cb.on_call_state = &MGMSIPCallStateChanged;
|
|
sipConfig.cb.on_call_transfer_status = &MGMSIPCallTransferStatusChanged;
|
|
sipConfig.cb.on_reg_state = &MGMSIPAccountRegistrationStateChanged;
|
|
sipConfig.cb.on_nat_detect = &MGMSIPDetectedNAT;
|
|
sipConfig.cb.on_dtmf_digit = &MGMSIPDTMFReceived;
|
|
|
|
sipConfig.max_calls = MGMSIPMaxCalls;
|
|
|
|
#if !TARGET_OS_IPHONE
|
|
if ([defaults boolForKey:MGMSIPNameServersEnabled]) {
|
|
SCDynamicStoreRef dynamicStore = SCDynamicStoreCreate(NULL, CFBundleGetIdentifier(CFBundleGetMainBundle()), NULL, NULL);
|
|
CFPropertyListRef DNSSettings = SCDynamicStoreCopyValue(dynamicStore, CFSTR("State:/Network/Global/DNS"));
|
|
NSArray *nameServers = nil;
|
|
if (DNSSettings!=NULL) {
|
|
nameServers = [[[(NSDictionary *)DNSSettings objectForKey:@"ServerAddresses"] retain] autorelease];
|
|
CFRelease(DNSSettings);
|
|
}
|
|
CFRelease(dynamicStore);
|
|
|
|
if ([nameServers count]>0) {
|
|
sipConfig.nameserver_count = ([nameServers count]>4 ? 4 : [nameServers count]);
|
|
for (int i=0; i<[nameServers count] && i<4; i++)
|
|
sipConfig.nameserver[i] = [[nameServers objectAtIndex:i] PJString];
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ([defaults objectForKey:MGMSIPOutboundProxy]!=nil && ![[defaults objectForKey:MGMSIPOutboundProxy] isEqual:@""]) {
|
|
sipConfig.outbound_proxy_cnt = 1;
|
|
if ([defaults integerForKey:MGMSIPOutboundProxyPort]==0 || [defaults integerForKey:MGMSIPOutboundProxyPort]==MGMSIPDefaultOutboundProxyPort)
|
|
sipConfig.outbound_proxy[0] = [[NSString stringWithFormat:@"sip:%@", [defaults objectForKey:MGMSIPOutboundProxy]] PJString];
|
|
else
|
|
sipConfig.outbound_proxy[0] = [[NSString stringWithFormat:@"sip:%@:%d", [defaults objectForKey:MGMSIPOutboundProxy], [defaults integerForKey:MGMSIPOutboundProxyPort]] PJString];
|
|
}
|
|
|
|
|
|
if ([defaults objectForKey:MGMSIPSTUN]!=nil && ![[defaults objectForKey:MGMSIPSTUN] isEqual:@""]) {
|
|
int STUNPort = [defaults integerForKey:MGMSIPSTUNPort];
|
|
if (STUNPort==0) STUNPort = MGMSIPDefaultSTUNPort;
|
|
sipConfig.stun_host = [[NSString stringWithFormat:@"%@:%d", [defaults objectForKey:MGMSIPSTUN], STUNPort] PJString];
|
|
}
|
|
|
|
status = pjsua_init(&sipConfig, &loggingConfig, &mediaConfig);
|
|
if (status!=PJ_SUCCESS) {
|
|
NSLog(@"Error initializing PJSUA");
|
|
[self stop];
|
|
[lock unlock];
|
|
[pool drain];
|
|
return;
|
|
}
|
|
|
|
unsigned int samplesPerFrame = mediaConfig.audio_frame_ptime * mediaConfig.clock_rate * mediaConfig.channel_count / 1000;
|
|
pj_str_t name = pj_str("ringback");
|
|
status = pjmedia_tonegen_create2(PJPool, &name, mediaConfig.clock_rate, mediaConfig.channel_count, samplesPerFrame, 16, PJMEDIA_TONEGEN_LOOP, &ringbackPort);
|
|
if (status!=PJ_SUCCESS) {
|
|
NSLog(@"Error creating ringback tones");
|
|
[self stop];
|
|
[lock unlock];
|
|
[pool drain];
|
|
return;
|
|
}
|
|
|
|
pjmedia_tone_desc tone[1];
|
|
pj_bzero(&tone, sizeof(tone));
|
|
tone[0].freq1 = 440;
|
|
tone[0].freq2 = 480;
|
|
tone[0].on_msec = 2000;
|
|
tone[0].off_msec = 4000;
|
|
tone[0].off_msec = 4000;
|
|
|
|
pjmedia_tonegen_play(ringbackPort, 1, tone, PJMEDIA_TONEGEN_LOOP);
|
|
status = pjsua_conf_add_port(PJPool, ringbackPort, &ringbackSlot);
|
|
if (status!=PJ_SUCCESS) {
|
|
NSLog(@"Error adding ringback tone");
|
|
[self stop];
|
|
[lock unlock];
|
|
[pool drain];
|
|
return;
|
|
}
|
|
|
|
pjsua_transport_config transportConfig;
|
|
pjsua_transport_config_default(&transportConfig);
|
|
transportConfig.port = port;
|
|
if ([defaults objectForKey:MGMSIPPublicAddress]!=nil && ![[defaults objectForKey:MGMSIPPublicAddress] isEqual:@""])
|
|
transportConfig.public_addr = [[defaults objectForKey:MGMSIPPublicAddress] PJString];
|
|
status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &transportConfig, &UDPTransport);
|
|
if (status!=PJ_SUCCESS) {
|
|
NSLog(@"Error creating transport");
|
|
[self stop];
|
|
[lock unlock];
|
|
[pool drain];
|
|
return;
|
|
}
|
|
pjsua_acc_add_local(UDPTransport, PJ_TRUE, &UDPAccount);
|
|
if (port==0) {
|
|
pjsua_transport_info transportInfo;
|
|
status = pjsua_transport_get_info(UDPTransport, &transportInfo);
|
|
if (status!=PJ_SUCCESS)
|
|
NSLog(@"Unable to get transport info");
|
|
|
|
port = transportInfo.local_name.port;
|
|
transportConfig.port = port;
|
|
}
|
|
|
|
status = pjsua_transport_create(PJSIP_TRANSPORT_TCP, &transportConfig, &TCPTransport);
|
|
if (status!=PJ_SUCCESS) {
|
|
NSLog(@"Error creating tcp transport");
|
|
} else {
|
|
pjsua_acc_add_local(TCPTransport, PJ_TRUE, &TCPAccount);
|
|
pjsua_acc_set_online_status(pjsua_acc_get_default(), PJ_TRUE);
|
|
}
|
|
|
|
status = pjsua_start();
|
|
if (status!=PJ_SUCCESS) {
|
|
NSLog(@"Error starting PJSUA");
|
|
[self stop];
|
|
[lock unlock];
|
|
[pool drain];
|
|
return;
|
|
}
|
|
|
|
if (pjsua_var.med_endpt!=NULL) {
|
|
pjmedia_codec_info codecs[PJMEDIA_CODEC_MGR_MAX_CODECS];
|
|
unsigned int prio[PJMEDIA_CODEC_MGR_MAX_CODECS];
|
|
unsigned int count = PJ_ARRAY_SIZE(codecs);
|
|
status = pjmedia_codec_mgr_enum_codecs(pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt), &count, codecs, prio);
|
|
|
|
if (status==PJ_SUCCESS) {
|
|
for (int i=0; i<count; i++) {
|
|
[codecsInfo setObject:[NSNumber numberWithUnsignedInt:prio[i]] forKey:[NSString stringWithFormat:@"%@/%u", [NSString stringWithPJString:codecs[i].encoding_name], codecs[i].clock_rate]];
|
|
}
|
|
}
|
|
codecOriginalPriority = [[codecsInfo objectForKey:[defaults objectForKey:MGMSIPCodec]] unsignedIntValue];
|
|
[self setPriority:PJMEDIA_CODEC_PRIO_NEXT_HIGHER forCodec:[defaults objectForKey:MGMSIPCodec]];
|
|
} else {
|
|
NSLog(@"pjsua_var.med_endpt was NULL");
|
|
}
|
|
|
|
state = MGMSIPStartedState;
|
|
|
|
[accounts makeObjectsPerformSelector:@selector(login)];
|
|
|
|
if (delegate!=nil && [delegate respondsToSelector:@selector(SIPStarted)]) [delegate SIPStarted];
|
|
|
|
NSLog(@"MGMSIP Started");
|
|
|
|
[lock unlock];
|
|
|
|
pjsua_conf_adjust_tx_level(0, [defaults floatForKey:MGMSIPVolume]);
|
|
pjsua_conf_adjust_rx_level(0, [defaults floatForKey:MGMSIPMicVolume]);
|
|
#if !TARGET_OS_IPHONE
|
|
AudioHardwareAddPropertyListener(kAudioHardwarePropertyDevices, &MGMAudioDevicesChanged, self);
|
|
AudioHardwareAddPropertyListener(kAudioHardwarePropertyDefaultInputDevice, &MGMAudioDevicesChanged, self);
|
|
AudioHardwareAddPropertyListener(kAudioHardwarePropertyDefaultOutputDevice, &MGMAudioDevicesChanged, self);
|
|
[self updateAudioDevices];
|
|
#endif
|
|
|
|
[pool drain];
|
|
}
|
|
- (void)stop {
|
|
if (state==MGMSIPStoppingState || state==MGMSIPStoppedState)
|
|
return;
|
|
|
|
[NSThread detachNewThreadSelector:@selector(stopBackground) toTarget:self withObject:nil];
|
|
}
|
|
- (void)stopBackground {
|
|
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
|
[lock lock];
|
|
|
|
pj_status_t status;
|
|
|
|
state = MGMSIPStoppingState;
|
|
|
|
[accounts makeObjectsPerformSelector:@selector(logout)];
|
|
|
|
pj_thread_desc PJThreadDesc;
|
|
[self registerThread:&PJThreadDesc];
|
|
|
|
if (ringbackPort!=NULL && ringbackSlot!=PJSUA_INVALID_ID) {
|
|
pjsua_conf_remove_port(ringbackSlot);
|
|
ringbackSlot = PJSUA_INVALID_ID;
|
|
pjmedia_port_destroy(ringbackPort);
|
|
ringbackPort = NULL;
|
|
}
|
|
|
|
if (PJPool!=NULL) {
|
|
pj_pool_release(PJPool);
|
|
PJPool = NULL;
|
|
}
|
|
pjsua_transport_close(UDPTransport, PJ_FALSE);
|
|
pjsua_transport_close(TCPTransport, PJ_FALSE);
|
|
|
|
status = pjsua_destroy();
|
|
if (status!=PJ_SUCCESS)
|
|
NSLog(@"Error stopping SIP");
|
|
bzero(&PJThreadDesc, sizeof(pj_thread_desc));
|
|
|
|
state = MGMSIPStoppedState;
|
|
|
|
#if !TARGET_OS_IPHONE
|
|
AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, &MGMAudioDevicesChanged);
|
|
AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDefaultInputDevice, &MGMAudioDevicesChanged);
|
|
AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDefaultOutputDevice, &MGMAudioDevicesChanged);
|
|
#endif
|
|
|
|
if (delegate!=nil && [delegate respondsToSelector:@selector(SIPStopped)]) [delegate SIPStopped];
|
|
|
|
NSLog(@"MGMSIP Stopped");
|
|
|
|
if (shouldRestart) {
|
|
if (restartAccounts!=nil) {
|
|
for (int i=0; i<[restartAccounts count]; i++) {
|
|
if (![accounts containsObject:[restartAccounts objectAtIndex:i]])
|
|
[accounts addObject:[restartAccounts objectAtIndex:i]];
|
|
}
|
|
[restartAccounts release];
|
|
restartAccounts = nil;
|
|
}
|
|
|
|
shouldRestart = NO;
|
|
[self start];
|
|
//[self performSelectorOnMainThread:@selector(startRestartTimer) withObject:nil waitUntilDone:NO];
|
|
}
|
|
|
|
[lock unlock];
|
|
[pool drain];
|
|
}
|
|
- (void)startRestartTimer {
|
|
[restartTimer invalidate];
|
|
[restartTimer release];
|
|
restartTimer = nil;
|
|
restartTimer = [[NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(start) userInfo:nil repeats:NO] retain];
|
|
}
|
|
- (void)restart {
|
|
if (shouldRestart)
|
|
return;
|
|
restartAccounts = [accounts copy];
|
|
shouldRestart = YES;
|
|
[self stop];
|
|
}
|
|
|
|
- (void)computerSleep {
|
|
restartAccounts = [accounts copy];
|
|
[accounts makeObjectsPerformSelector:@selector(logout)];
|
|
#if !TARGET_OS_IPHONE
|
|
[self stopAudio];
|
|
#endif
|
|
}
|
|
- (void)computerWake {
|
|
#if !TARGET_OS_IPHONE
|
|
[self updateAudioDevices];
|
|
#endif
|
|
[restartAccounts makeObjectsPerformSelector:@selector(login)];
|
|
[restartAccounts release];
|
|
restartAccounts = nil;
|
|
}
|
|
|
|
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
|
|
- (void)keepAlive {
|
|
if (state==MGMSIPStartedState) {
|
|
pj_thread_desc PJThreadDesc;
|
|
[self registerThread:&PJThreadDesc];
|
|
|
|
for (int i=0; i<(int)pjsua_acc_get_count(); ++i) {
|
|
if (!pjsua_acc_is_valid(i))
|
|
continue;
|
|
pjsua_acc_set_registration(i, PJ_TRUE);
|
|
}
|
|
bzero(&PJThreadDesc, sizeof(pj_thread_desc));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
- (void)registerThread:(pj_thread_desc *)thePJThreadDesc {
|
|
if (!pj_thread_is_registered()) {
|
|
pj_thread_t *PJThread;
|
|
pj_status_t status = pj_thread_register(NULL, *thePJThreadDesc, &PJThread);
|
|
if (status!=PJ_SUCCESS)
|
|
NSLog(@"Error registering thread for PJSUA with status %d", status);
|
|
}
|
|
}
|
|
|
|
- (void)setTopCodec:(NSString *)theCodec {
|
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
[self setPriority:codecOriginalPriority forCodec:[defaults objectForKey:MGMSIPCodec]];
|
|
[defaults setObject:theCodec forKey:MGMSIPCodec];
|
|
codecOriginalPriority = [[codecsInfo objectForKey:theCodec] unsignedIntValue];
|
|
[self setPriority:PJMEDIA_CODEC_PRIO_NEXT_HIGHER forCodec:theCodec];
|
|
}
|
|
- (void)setPriority:(unsigned int)thePriority forCodec:(NSString *)theCodec {
|
|
if ([codecsInfo objectForKey:theCodec]!=nil) {
|
|
pj_str_t codec = [theCodec PJString];
|
|
pj_status_t status = pjmedia_codec_mgr_set_codec_priority(pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt), &codec, thePriority);
|
|
[codecsInfo setObject:[NSNumber numberWithUnsignedInt:thePriority] forKey:theCodec];
|
|
if (status!=PJ_SUCCESS)
|
|
NSLog(@"Error changing priority of codec %@ to %u: %d", theCodec, thePriority, status);
|
|
}
|
|
}
|
|
- (NSDictionary *)codecs {
|
|
return codecsInfo;
|
|
}
|
|
|
|
- (void)loginToAccount:(MGMSIPAccount *)theAccount {
|
|
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
|
if (![theAccount informationComplete]) {
|
|
[theAccount setLastError:@"The Account Information is not complete."];
|
|
[theAccount loginErrored];
|
|
NSLog(@"Error With Account %@: %@", theAccount, [theAccount lastError]);
|
|
[pool drain];
|
|
return;
|
|
}
|
|
if (![accounts containsObject:theAccount]) [accounts addObject:theAccount];
|
|
if (state!=MGMSIPStartedState) {
|
|
[pool drain];
|
|
return;
|
|
}
|
|
if ([theAccount identifier]!=PJSUA_INVALID_ID) {
|
|
[pool drain];
|
|
return;
|
|
}
|
|
|
|
pj_thread_desc PJThreadDesc;
|
|
[self registerThread:&PJThreadDesc];
|
|
|
|
pjsua_acc_config accountConfig;
|
|
pjsua_acc_config_default(&accountConfig);
|
|
|
|
accountConfig.cred_count = 1;
|
|
if ([theAccount domain]!=nil && ![[theAccount domain] isEqual:@""])
|
|
accountConfig.cred_info[0].realm = [[theAccount domain] PJString];
|
|
else
|
|
accountConfig.cred_info[0].realm = pj_str("*");
|
|
accountConfig.cred_info[0].scheme = pj_str("digest");
|
|
accountConfig.cred_info[0].username = [[theAccount userName] PJString];
|
|
accountConfig.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
|
|
accountConfig.cred_info[0].data = [[theAccount password] PJString];
|
|
|
|
if ([theAccount fullName]!=nil)
|
|
accountConfig.id = [[NSString stringWithFormat:@"%@ <sip:%@>", [theAccount fullName], [theAccount SIPAddress]] PJString];
|
|
else
|
|
accountConfig.id = [[NSString stringWithFormat:@"<sip:%@>", [theAccount SIPAddress]] PJString];
|
|
NSString *registerURI = [NSString stringWithFormat:@"sip:%@", [theAccount registrar]];
|
|
NSString *transport = @"";
|
|
if ([theAccount transport]==1)
|
|
transport = @";transport=tcp";
|
|
else if ([theAccount transport]==2)
|
|
transport = @";transport=udp";
|
|
accountConfig.reg_uri = [[registerURI stringByAppendingString:transport] PJString];
|
|
|
|
if ([theAccount proxy]!=nil && ![[theAccount proxy] isEqual:@""]) {
|
|
accountConfig.proxy_cnt = 1;
|
|
accountConfig.proxy[0] = [[NSString stringWithFormat:@"sip:%@:%d", [theAccount proxy], [theAccount proxyPort]] PJString];
|
|
}
|
|
|
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
if ([defaults boolForKey:MGMSIPInteractiveConnectivityEstablishment] && [defaults objectForKey:MGMSIPSTUN]!=nil && ![[defaults objectForKey:MGMSIPSTUN] isEqual:@""])
|
|
accountConfig.allow_contact_rewrite = PJ_TRUE;
|
|
else
|
|
accountConfig.allow_contact_rewrite = PJ_FALSE;
|
|
|
|
accountConfig.reg_timeout = [theAccount reregisterTimeout];
|
|
|
|
pjsua_acc_id identifier;
|
|
pj_status_t status = pjsua_acc_add(&accountConfig, PJ_FALSE, &identifier);
|
|
if (status!=PJ_SUCCESS) {
|
|
[theAccount setLastError:[NSString stringWithFormat:@"Unable to login with status %d.", status]];
|
|
[theAccount loginErrored];
|
|
NSLog(@"Error With Account %@: %@", theAccount, [theAccount lastError]);
|
|
[accounts removeObject:theAccount];
|
|
bzero(&PJThreadDesc, sizeof(pj_thread_desc));
|
|
[pool drain];
|
|
return;
|
|
}
|
|
[theAccount setIdentifier:identifier];
|
|
[theAccount setOnline:YES];
|
|
bzero(&PJThreadDesc, sizeof(pj_thread_desc));
|
|
if (delegate!=nil && [delegate respondsToSelector:@selector(accountLoggedIn:)]) [delegate accountLoggedIn:theAccount];
|
|
if ([theAccount delegate]!=nil && [[theAccount delegate] respondsToSelector:@selector(loggedIn)]) [[theAccount delegate] loggedIn];
|
|
[pool drain];
|
|
}
|
|
- (void)logoutOfAccount:(MGMSIPAccount *)theAccount {
|
|
if (state<MGMSIPStartedState) return;
|
|
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
|
if ([theAccount identifier]==PJSUA_INVALID_ID) {
|
|
[theAccount setLastError:@"Unable to logout due to missing information."];
|
|
[theAccount logoutErrored];
|
|
NSLog(@"Error With Account %@: %@", theAccount, [theAccount lastError]);
|
|
[pool drain];
|
|
return;
|
|
}
|
|
|
|
pj_thread_desc PJThreadDesc;
|
|
[self registerThread:&PJThreadDesc];
|
|
|
|
[theAccount setOnline:NO];
|
|
|
|
pj_status_t status = pjsua_acc_del([theAccount identifier]);
|
|
if (status!=PJ_SUCCESS) {
|
|
[theAccount setLastError:[NSString stringWithFormat:@"Unable to logout with status %d.", status]];
|
|
[theAccount logoutErrored];
|
|
NSLog(@"Error With Account %@: %@", theAccount, [theAccount lastError]);
|
|
[pool drain];
|
|
return;
|
|
}
|
|
[theAccount setIdentifier:PJSUA_INVALID_ID];
|
|
//if ([theAccount delegate]!=nil && [[theAccount delegate] respondsToSelector:@selector(loggedOut)]) [[theAccount delegate] loggedOut];
|
|
if (delegate!=nil && [delegate respondsToSelector:@selector(accountLoggedOut:)]) [delegate accountLoggedOut:theAccount];
|
|
[accounts removeObject:theAccount];
|
|
bzero(&PJThreadDesc, sizeof(pj_thread_desc));
|
|
[pool drain];
|
|
}
|
|
|
|
- (NSArray *)accounts {
|
|
return accounts;
|
|
}
|
|
- (pjsua_acc_id)accountIDForUserName:(NSString *)theUserName {
|
|
for (int i=0; i<[accounts count]; i++) {
|
|
MGMSIPAccount *account = [accounts objectAtIndex:i];
|
|
if ([[[account userName] lowercaseString] isEqual:[theUserName lowercaseString]])
|
|
return [account identifier];
|
|
|
|
if ([[account userName] isPhone] && [[[account userName] phoneFormat] isEqual:[theUserName phoneFormat]])
|
|
return [account identifier];
|
|
|
|
pjsua_acc_info info;
|
|
pj_status_t status = pjsua_acc_get_info([account identifier], &info);
|
|
if (status==PJ_SUCCESS) {
|
|
MGMSIPURL *url = [MGMSIPURL URLWithSIPID:[NSString stringWithPJString:info.acc_uri]];
|
|
if ([[[url userName] lowercaseString] isEqual:[theUserName lowercaseString]])
|
|
return [account identifier];
|
|
|
|
if ([[url userName] isPhone] && [[[url userName] phoneFormat] isEqual:[theUserName phoneFormat]])
|
|
return [account identifier];
|
|
}
|
|
}
|
|
if ([accounts count]<=1)
|
|
return [(MGMSIPAccount *)[accounts objectAtIndex:0] identifier];
|
|
return PJSUA_INVALID_ID;
|
|
}
|
|
- (MGMSIPAccount *)accountWithIdentifier:(pjsua_acc_id)theIdentifier {
|
|
for (int i=0; i<[accounts count]; i++) {
|
|
if ([(MGMSIPAccount *)[accounts objectAtIndex:i] identifier]==theIdentifier)
|
|
return [accounts objectAtIndex:i];
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
- (int)ringbackCount {
|
|
return ringbackCount;
|
|
}
|
|
- (void)setRingbackCount:(int)theRingbackCount {
|
|
ringbackCount = theRingbackCount;
|
|
}
|
|
|
|
- (void)hangUpAllCalls {
|
|
pj_thread_desc PJThreadDesc;
|
|
[self registerThread:&PJThreadDesc];
|
|
|
|
pjsua_call_hangup_all();
|
|
bzero(&PJThreadDesc, sizeof(pj_thread_desc));
|
|
}
|
|
|
|
- (float)volume {
|
|
return [[NSUserDefaults standardUserDefaults] floatForKey:MGMSIPVolume];
|
|
}
|
|
- (void)setVolume:(float)theVolume {
|
|
pj_thread_desc PJThreadDesc;
|
|
[self registerThread:&PJThreadDesc];
|
|
|
|
[[NSUserDefaults standardUserDefaults] setFloat:theVolume forKey:MGMSIPVolume];
|
|
pjsua_conf_adjust_tx_level(0, theVolume);
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:MGMSIPVolumeChangedNotification object:[NSNumber numberWithFloat:theVolume]];
|
|
bzero(&PJThreadDesc, sizeof(pj_thread_desc));
|
|
}
|
|
- (float)micVolume {
|
|
return [[NSUserDefaults standardUserDefaults] floatForKey:MGMSIPMicVolume];
|
|
}
|
|
- (void)setMicVolume:(float)theVolume {
|
|
pj_thread_desc PJThreadDesc;
|
|
[self registerThread:&PJThreadDesc];
|
|
|
|
[[NSUserDefaults standardUserDefaults] setFloat:theVolume forKey:MGMSIPMicVolume];
|
|
pjsua_conf_adjust_rx_level(0, theVolume);
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:MGMSIPMicVolumeChangedNotification object:[NSNumber numberWithFloat:theVolume]];
|
|
bzero(&PJThreadDesc, sizeof(pj_thread_desc));
|
|
}
|
|
|
|
#if !TARGET_OS_IPHONE
|
|
- (BOOL)setInputSoundDevice:(int)theInputDevice outputSoundDevice:(int)theOutputDevice {
|
|
if (state!=MGMSIPStartedState)
|
|
return NO;
|
|
|
|
NSString *inputDeviceUID = nil;
|
|
if (theInputDevice==-1) {
|
|
inputDeviceUID = MGMSIPASystemDefault;
|
|
for (int i=0; i<[audioDevices count]; i++) {
|
|
if ([[[audioDevices objectAtIndex:i] objectForKey:MGMSIPADeviceIsDefaultInput] boolValue]) {
|
|
theInputDevice = [[[audioDevices objectAtIndex:i] objectForKey:MGMSIPADeviceIndex] intValue];
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
for (int i=0; i<[audioDevices count]; i++) {
|
|
if ([[[audioDevices objectAtIndex:i] objectForKey:MGMSIPADeviceIndex] intValue]==theInputDevice) {
|
|
inputDeviceUID = [[audioDevices objectAtIndex:i] objectForKey:MGMSIPADeviceUID];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
NSString *outputDeviceUID = nil;
|
|
if (theOutputDevice==-1) {
|
|
outputDeviceUID = MGMSIPASystemDefault;
|
|
for (int i=0; i<[audioDevices count]; i++) {
|
|
if ([[[audioDevices objectAtIndex:i] objectForKey:MGMSIPADeviceIsDefaultOutput] boolValue]) {
|
|
theOutputDevice = [[[audioDevices objectAtIndex:i] objectForKey:MGMSIPADeviceIndex] intValue];
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
for (int i=0; i<[audioDevices count]; i++) {
|
|
if ([[[audioDevices objectAtIndex:i] objectForKey:MGMSIPADeviceIndex] intValue]==theOutputDevice) {
|
|
outputDeviceUID = [[audioDevices objectAtIndex:i] objectForKey:MGMSIPADeviceUID];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
[defaults setObject:inputDeviceUID forKey:MGMSIPACurrentInputDevice];
|
|
[defaults setObject:outputDeviceUID forKey:MGMSIPACurrentOutputDevice];
|
|
|
|
pj_thread_desc PJThreadDesc;
|
|
[self registerThread:&PJThreadDesc];
|
|
|
|
lastInputDevice = theInputDevice;
|
|
lastOutputDevice = theOutputDevice;
|
|
|
|
pjsua_set_null_snd_dev();
|
|
pj_status_t status = pjsua_set_snd_dev(theInputDevice, theOutputDevice);
|
|
bzero(&PJThreadDesc, sizeof(pj_thread_desc));
|
|
return (status==PJ_SUCCESS);
|
|
}
|
|
- (BOOL)stopAudio {
|
|
if (state!=MGMSIPStartedState)
|
|
return NO;
|
|
|
|
pj_thread_desc PJThreadDesc;
|
|
[self registerThread:&PJThreadDesc];
|
|
|
|
pj_status_t status = pjsua_set_null_snd_dev();
|
|
bzero(&PJThreadDesc, sizeof(pj_thread_desc));
|
|
return (status==PJ_SUCCESS);
|
|
}
|
|
- (void)updateAudioDevices {
|
|
if (state!=MGMSIPStartedState)
|
|
return;
|
|
|
|
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
|
[lock lock];
|
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
|
|
OSStatus error = noErr;
|
|
UInt32 size = 0;
|
|
AudioBufferList *bufferList = NULL;
|
|
|
|
NSMutableArray *devicesArray = [NSMutableArray array];
|
|
AudioDeviceID *devices = NULL;
|
|
int deviceCount = 0;
|
|
Boolean writable;
|
|
|
|
error = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, &writable);
|
|
if (error!=noErr) {
|
|
[pool drain];
|
|
[lock unlock];
|
|
return;
|
|
}
|
|
|
|
deviceCount = size / sizeof(AudioDeviceID);
|
|
if (deviceCount>=1) {
|
|
devices = malloc(size);
|
|
memset(devices, 0, size);
|
|
|
|
AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, (void *)devices);
|
|
}
|
|
size = 0;
|
|
|
|
size = sizeof(AudioDeviceID);
|
|
AudioDeviceID defaultInput = 0;
|
|
AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &size, &defaultInput);
|
|
size = sizeof(AudioDeviceID);
|
|
AudioDeviceID defaultOutput = 0;
|
|
AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &defaultOutput);
|
|
|
|
NSMutableDictionary *defaultDevice = [NSMutableDictionary dictionary];
|
|
[defaultDevice setObject:[NSNumber numberWithUnsignedInt:0] forKey:MGMSIPADeviceIdentifier];
|
|
[defaultDevice setObject:[NSNumber numberWithUnsignedInt:-1] forKey:MGMSIPADeviceIndex];
|
|
[defaultDevice setObject:MGMSIPASystemDefault forKey:MGMSIPADeviceName];
|
|
[defaultDevice setObject:MGMSIPASystemDefault forKey:MGMSIPADeviceUID];
|
|
[devicesArray addObject:defaultDevice];
|
|
|
|
int currentInput = -1;
|
|
int currentOutput = -1;
|
|
int defaultInputIndex = -1;
|
|
int defaultOutputIndex = -1;
|
|
|
|
for (int d=0; d<deviceCount; d++) {
|
|
NSMutableDictionary *deviceInfo = [NSMutableDictionary dictionary];
|
|
[deviceInfo setObject:[NSNumber numberWithUnsignedLong:devices[d]] forKey:MGMSIPADeviceIdentifier];
|
|
[deviceInfo setObject:[NSNumber numberWithInt:d] forKey:MGMSIPADeviceIndex];
|
|
|
|
if (devices[d]==defaultInput) {
|
|
defaultInputIndex = d;
|
|
[deviceInfo setObject:[NSNumber numberWithBool:YES] forKey:MGMSIPADeviceIsDefaultInput];
|
|
}
|
|
if (devices[d]==defaultOutput) {
|
|
defaultOutputIndex = d;
|
|
[deviceInfo setObject:[NSNumber numberWithBool:YES] forKey:MGMSIPADeviceIsDefaultOutput];
|
|
}
|
|
|
|
CFStringRef UIDString = NULL;
|
|
size = sizeof(CFStringRef);
|
|
error = AudioDeviceGetProperty(devices[d], 0, 0, kAudioDevicePropertyDeviceUID, &size, &UIDString);
|
|
if (error==noErr && UIDString!=NULL)
|
|
[deviceInfo setObject:[(NSString *)UIDString autorelease] forKey:MGMSIPADeviceUID];
|
|
|
|
CFStringRef nameString = NULL;
|
|
size = sizeof(CFStringRef);
|
|
error = AudioDeviceGetProperty(devices[d], 0, 0, kAudioDevicePropertyDeviceNameCFString, &size, &nameString);
|
|
if (error==noErr && nameString!=NULL)
|
|
[deviceInfo setObject:[(NSString *)nameString autorelease] forKey:MGMSIPADeviceName];
|
|
|
|
size = 0;
|
|
unsigned int inputChannelCount = 0;
|
|
error = AudioDeviceGetPropertyInfo(devices[d], 0, 1, kAudioDevicePropertyStreamConfiguration, &size, NULL);
|
|
if (error==noErr && size!=0) {
|
|
bufferList = malloc(size);
|
|
if (bufferList!=NULL) {
|
|
error = AudioDeviceGetProperty(devices[d], 0, 1, kAudioDevicePropertyStreamConfiguration, &size, bufferList);
|
|
if (error==noErr) {
|
|
for (unsigned int i=0; i<bufferList->mNumberBuffers; i++)
|
|
inputChannelCount += bufferList->mBuffers[i].mNumberChannels;
|
|
}
|
|
free(bufferList);
|
|
[deviceInfo setObject:[NSNumber numberWithUnsignedInt:inputChannelCount] forKey:MGMSIPADeviceInputCount];
|
|
}
|
|
}
|
|
|
|
size = 0;
|
|
unsigned int outputChannelCount = 0;
|
|
error = AudioDeviceGetPropertyInfo(devices[d], 0, 0, kAudioDevicePropertyStreamConfiguration, &size, NULL);
|
|
if(error==noErr && size!=0) {
|
|
bufferList = malloc(size);
|
|
if (bufferList!=NULL) {
|
|
error = AudioDeviceGetProperty(devices[d], 0, 0, kAudioDevicePropertyStreamConfiguration, &size, bufferList);
|
|
if (error==noErr) {
|
|
for (unsigned int i = 0; i < bufferList->mNumberBuffers; ++i)
|
|
outputChannelCount += bufferList->mBuffers[i].mNumberChannels;
|
|
}
|
|
free(bufferList);
|
|
[deviceInfo setObject:[NSNumber numberWithUnsignedInt:outputChannelCount] forKey:MGMSIPADeviceOutputCount];
|
|
}
|
|
}
|
|
|
|
if ([[deviceInfo objectForKey:MGMSIPADeviceInputCount] unsignedIntValue]!=0 && [[deviceInfo objectForKey:MGMSIPADeviceUID] isEqual:[defaults objectForKey:MGMSIPACurrentInputDevice]])
|
|
currentInput = d;
|
|
if ([[deviceInfo objectForKey:MGMSIPADeviceOutputCount] unsignedIntValue]!=0 && [[deviceInfo objectForKey:MGMSIPADeviceUID] isEqual:[defaults objectForKey:MGMSIPACurrentOutputDevice]])
|
|
currentOutput = d;
|
|
|
|
if ([[deviceInfo objectForKey:MGMSIPADeviceInputCount] unsignedIntValue]!=0 || [[deviceInfo objectForKey:MGMSIPADeviceOutputCount] unsignedIntValue]!=0)
|
|
[devicesArray addObject:deviceInfo];
|
|
}
|
|
free(devices);
|
|
|
|
[audioDevices release];
|
|
audioDevices = [devicesArray copy];
|
|
|
|
//if ((currentInput==-1 ? defaultInputIndex!=lastInputDevice : currentInput!=lastInputDevice) && (currentOutput==-1 ? defaultOutputIndex!=lastOutputDevice : currentOutput!=lastOutputDevice)) {
|
|
pj_thread_desc PJThreadDesc;
|
|
[self registerThread:&PJThreadDesc];
|
|
|
|
pjsua_set_null_snd_dev();
|
|
pjmedia_snd_deinit();
|
|
pjmedia_snd_init(pjsua_get_pool_factory());
|
|
|
|
[self setInputSoundDevice:currentInput outputSoundDevice:currentOutput];
|
|
bzero(&PJThreadDesc, sizeof(pj_thread_desc));
|
|
//}
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:MGMSIPAudioChangedNotification object:audioDevices];
|
|
|
|
[lock unlock];
|
|
[pool drain];
|
|
}
|
|
- (NSArray *)audioDevices {
|
|
return audioDevices;
|
|
}
|
|
#endif
|
|
|
|
- (void)receivedNewCall:(MGMSIPCall *)theCall {
|
|
if (delegate!=nil && [delegate respondsToSelector:@selector(receivedNewCall:)]) [delegate receivedNewCall:theCall];
|
|
}
|
|
- (void)startingNewCall:(MGMSIPCall *)theCall {
|
|
if (delegate!=nil && [delegate respondsToSelector:@selector(startingNewCall:)]) [delegate startingNewCall:theCall];
|
|
}
|
|
- (NSArray *)calls {
|
|
NSMutableArray *calls = [NSMutableArray array];
|
|
for (int i=0; i<[accounts count]; i++)
|
|
[calls addObjectsFromArray:[[accounts objectAtIndex:i] calls]];
|
|
return calls;
|
|
}
|
|
- (MGMSIPCall *)callWithIdentifier:(pjsua_call_id)theIdentifier {
|
|
for (int i=0; i<[accounts count]; i++) {
|
|
MGMSIPCall *call = [[accounts objectAtIndex:i] callWithIdentifier:theIdentifier];
|
|
if (call!=nil)
|
|
return call;
|
|
}
|
|
return nil;
|
|
}
|
|
@end
|
|
#endif |