Пример iOS-приложения для аудиозвонков
Работа с кодом примера
Для разбора кода возьмем версию примера PhoneMin, которая доступена для скачивания в сборке 2.5.2.
...
1. Импорт API. код
Code Block | ||||
---|---|---|---|---|
| ||||
#import <FPWCSApi2/FPWCSApi2.h> |
...
- URL WCS-сервера
- параметры SIP-аккаунта для совершения исходящих и приема входящих звонков
- имя серверного приложения defaultApp
Code Block | ||||
---|---|---|---|---|
| ||||
FPWCSApi2SessionOptions *options = [[FPWCSApi2SessionOptions alloc] init]; options.urlServer = _connectUrl.text; options.sipRegisterRequired = _sipRegRequired.control.isOn; options.sipLogin = _sipLogin.input.text; options.sipAuthenticationName = _sipAuthName.input.text; options.sipPassword = _sipPassword.input.text; options.sipDomain = _sipDomain.input.text; options.sipOutboundProxy = _sipOutboundProxy.input.text; options.sipPort = [NSNumber numberWithInteger: [_sipPort.input.text integerValue]]; options.appKey = @"defaultApp"; NSError *error; if (!options..sipLogin.length || !options.sipAuthenticationName.length || !options.sipPassword.length || !options.sipDomain.length || !options.sipOutboundProxy.length || options.sipPort.integerValue == 0) { UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"All Sip Credentials is required" message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction* okButton = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self onDisconnected]; }]; [alert addAction:okButton]; [self presentViewController:alert animated:YES completion:nil]; return nil; } session = [FPWCSApi2 createSession:options error:&error]; if (error) { UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"Failed to connect" message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction* okButton = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self onDisconnected]; }]; [alert addAction:okButton]; [self presentViewController:alert animated:YES completion:nil]; return nil; } ... [session connect]; |
...
FPWCSApi2Session createCall, FPWCSApi2Call call код
При создании звонка в метод createCall передаются параметры:
- имя вызываемого SIP-аккаунта
- дополнительные параметры SIP INVITE запроса, введенные пользователем
Code Block | ||||
---|---|---|---|---|
| ||||
- (FPWCSApi2Call *)call {
FPWCSApi2Session *session = [FPWCSApi2 getSessions][0];
FPWCSApi2CallOptions *options = [[FPWCSApi2CallOptions alloc] init];
NSString *parameters = _inviteParameters.input.text;
if (parameters && [parameters length] > 0) {
NSError* err = nil;
parameters = [parameters stringByReplacingOccurrencesOfString:@""" withString:@"\""];
NSMutableDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:[parameters dataUsingEncoding:NSUTF8StringEncoding] options:0 error:&err];
if (err) {
NSLog(@"Error converting JSON Invite parameters to dictionary %@, JSON %@", err, parameters);
} else {
options.inviteParameters = dictionary;
}
}
options.callee = _callee.input.text;
//used for only recv audio
// options.localConstraints = [[FPWCSApi2MediaConstraints alloc] initWithAudio:NO video:NO];
// options.remoteConstraints = [[FPWCSApi2MediaConstraints alloc] initWithAudio:YES video:NO];
NSError *error;
call = [session createCall:options error:&error];
if (!call) {
UIAlertController * alert = [UIAlertController
alertControllerWithTitle:@"Failed to create call"
message:error.localizedDescription
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* okButton = [UIAlertAction
actionWithTitle:@"Ok"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self toCallState];
}];
[alert addAction:okButton];
[self presentViewController:alert animated:YES completion:nil];
return nil;
}
[call on:kFPWCSCallStatusBusy callback:^(FPWCSApi2Call *call){
[self changeCallStatus:call];
[self toCallState];
}];
[call on:kFPWCSCallStatusFailed callback:^(FPWCSApi2Call *call){
[self changeCallStatus:call];
[self toCallState];
}];
[call on:kFPWCSCallStatusRing callback:^(FPWCSApi2Call *call){
[self changeCallStatus:call];
[self toHangupState];
}];
[call on:kFPWCSCallStatusHold callback:^(FPWCSApi2Call *call){
[self changeCallStatus:call];
[self changeViewState:_holdButton enabled:YES];
}];
[call on:kFPWCSCallStatusEstablished callback:^(FPWCSApi2Call *call){
[self changeCallStatus:call];
[self toHangupState];
[self changeViewState:_holdButton enabled:YES];
}];
[call on:kFPWCSCallStatusFinish callback:^(FPWCSApi2Call *call){
[self changeCallStatus:call];
[self toCallState];
}];
[call call];
return call;
} |
4. Получение от сервера события, сигнализирующего о входящем звонке
FPWCSApi2Session onIncomingCallCallback код
Code Block | ||||
---|---|---|---|---|
| ||||
[session onIncomingCallCallback:^(FPWCSApi2Call *rCall) { call = rCall; [call on:kFPWCSCallStatusBusy callback:^(FPWCSApi2Call *call){ [self changeCallStatus:call]; [self toCallState]; }] session = [FPWCSApi2 createSession:options error:&error]; ... [session connect]; |
3. Исходящий звонок.
FPWCSApi2Session createCall, FPWCSApi2Call call код
При создании звонка в метод createCall передаются параметры:
- имя вызываемого SIP-аккаунта
- дополнительные параметры SIP INVITE запроса, введенные пользователем
Code Block | ||||
---|---|---|---|---|
| ||||
- (FPWCSApi2Call *)call { FPWCSApi2Session *session = [FPWCSApi2 getSessions][0]; FPWCSApi2CallOptions *options = [[FPWCSApi2CallOptions alloc] init]; NSString *parameters = _inviteParameters.input.text; if (parameters && [parameters length] > 0) { NSError* err = nil; parameters = [callparameters on:kFPWCSCallStatusFailed callback:^(FPWCSApi2Call *call){ stringByReplacingOccurrencesOfString:@""" withString:@"\""]; NSMutableDictionary *dictionary = [NSJSONSerialization [self changeCallStatus:callJSONObjectWithData:[parameters dataUsingEncoding:NSUTF8StringEncoding] options:0 error:&err]; if [self toCallState]; (err) { }]; NSLog(@"Error converting JSON Invite parameters to [call on:kFPWCSCallStatusRing callback:^(FPWCSApi2Call *call){dictionary %@, JSON %@", err, parameters); [self changeCallStatus:call]; } else { [self toHangupState]; options.inviteParameters = }]dictionary; } } [call on:kFPWCSCallStatusHold callback:^(FPWCSApi2Call *call){ options.callee = _callee.input.text; //used for only [self changeCallStatus:call]; recv audio // options.localConstraints = [[FPWCSApi2MediaConstraints [self changeViewState:_holdButton enabled:YESalloc] initWithAudio:NO video:NO]; // }]; [call on:kFPWCSCallStatusEstablished callback:^(FPWCSApi2Call *call){options.remoteConstraints = [[FPWCSApi2MediaConstraints alloc] initWithAudio:YES video:NO]; NSError *error; call = [session [self changeCallStatus:callcreateCall:options error:&error]; ... [selfcall toHangupStatecall]; return call; } |
4. Получение от сервера события, сигнализирующего о входящем звонке
FPWCSApi2Session onIncomingCallCallback код
Code Block | ||||
---|---|---|---|---|
| ||||
[session onIncomingCallCallback:^(FPWCSApi2Call [self changeViewState:_holdButton enabled:YES]; }]*rCall) { call = rCall; [call on:kFPWCSCallStatusFinishkFPWCSCallStatusBusy callback:^(FPWCSApi2Call *call){ [self changeCallStatus:call]; [self toCallState]; }]; [call [self toCallState];on:kFPWCSCallStatusFailed callback:^(FPWCSApi2Call *call){ [self dismissViewControllerAnimated:YES completion:nilchangeCallStatus:call]; }]; ... }]; |
5. Ответ на входящий звонок.
FPWCSApi2Call answer код
Code Block | ||||
---|---|---|---|---|
| ||||
alert = [UIAlertControllerself toCallState]; }]; [call on:kFPWCSCallStatusRing callback:^(FPWCSApi2Call *call){ [self alertControllerWithTitle:[NSString stringWithFormat:@"Incoming call from '%@'", [rCall getCallee]] changeCallStatus:call]; [self toHangupState]; }]; [call on:kFPWCSCallStatusHold callback:^(FPWCSApi2Call *call){ [self changeCallStatus:call]; message:error.localizedDescription[self changeViewState:_holdButton enabled:YES]; }]; [call on:kFPWCSCallStatusEstablished callback:^(FPWCSApi2Call *call){ [self preferredStylechangeCallStatus:UIAlertControllerStyleAlertcall]; [self toHangupState]; UIAlertAction* answerButton = [UIAlertAction [self changeViewState:_holdButton enabled:YES]; }]; [call on:kFPWCSCallStatusFinish callback:^(FPWCSApi2Call *call){ actionWithTitle:@"Answer" [self changeCallStatus:call]; [self toCallState]; [self dismissViewControllerAnimated:YES completion:nil]; }]; style:UIAlertActionStyleDefault ... }]; |
5. Ответ на входящий звонок.
FPWCSApi2Call answer код
Code Block | ||||
---|---|---|---|---|
| ||||
alert = [UIAlertController handler:^(UIAlertAction * action) { alertControllerWithTitle:[NSString stringWithFormat:@"Incoming call from '%@'", [rCall getCallee]] [call answer];message:error.localizedDescription }preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:answerButton]; UIAlertAction* hangupButtonanswerButton = [UIAlertAction actionWithTitle:@"HangupAnswer" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [call hangupanswer]; }]; [alert addAction:hangupButtonanswerButton]; [self presentViewController:alert animated:YES completion:nil]; |
6. Удержание звонка.
FPWCSApi2Call hold, unhold код
Code Block | ||||
---|---|---|---|---|
| ||||
- (void)holdButton:(UIButton *)button { [self changeViewState:button enabled:NO]; if ([button.titleLabel.text isEqualToString:@"UNHOLD"]) { UIAlertAction* hangupButton = [UIAlertAction actionWithTitle:@"Hangup" if (call) { style:UIAlertActionStyleDefault [call unhold]; [_holdButton setTitle:@"HOLD" forState:UIControlStateNormal];handler:^(UIAlertAction * action) { } } else { if (call) { [call holdhangup]; [_holdButton setTitle:@"UNHOLD" forState:UIControlStateNormal]; } } } |
7. Отправка тонального сигнала
FPWCSApi2Call sendDTMF код
Code Block | ||||
---|---|---|---|---|
| ||||
- (void)dtmfButton:(UIButton *)button { if (call) { }]; [callalert sendDTMF:_dtmf.input.text type:kFPWCSCallDTMFRFC2833addAction:hangupButton]; [self presentViewController:alert } } |
...
animated:YES completion:nil]; |
6. Удержание звонка.
FPWCSApi2Call hangup hold, unhold код
Code Block | ||||
---|---|---|---|---|
| ||||
- (void)callButtonholdButton:(UIButton *)button { [self changeViewState:button enabled:NO]; if ([button.titleLabel.text isEqualToString:@"HANGUP"]UNHOLD"]) { if (call) { if ([FPWCSApi2call getSessions].count) {unhold]; [call hangup_holdButton setTitle:@"HOLD" forState:UIControlStateNormal]; } } else { if (call) { [self toCallState]; } [call hold]; } else { if ([FPWCSApi2 getSessions].count) {[_holdButton setTitle:@"UNHOLD" forState:UIControlStateNormal]; } [self call]; } } |
7. Отправка тонального сигнала
FPWCSApi2Call sendDTMF код
Code Block | ||||
---|---|---|---|---|
| ||||
- (void)dtmfButton:(UIButton *)button { } elseif (call) { [call [self toCallState]; }sendDTMF:_dtmf.input.text type:kFPWCSCallDTMFRFC2833]; } } |
98. Завершение входящего исходящего звонка.
FPWCSApi2Call hangup код
Code Block | ||||
---|---|---|---|---|
| ||||
UIAlertAction* hangupButton = [UIAlertAction - (void)callButton:(UIButton *)button { [self changeViewState:button enabled:NO]; if ([button.titleLabel.text isEqualToString:@"HANGUP"]) { if ([FPWCSApi2 actionWithTitle:@"Hangup"getSessions].count) { [call hangup]; } else { style:UIAlertActionStyleDefault [self toCallState]; } ... } } |
9. Завершение входящего звонка.
FPWCSApi2Call hangup код
Code Block | ||||
---|---|---|---|---|
| ||||
UIAlertAction* hangupButton = handler:^([UIAlertAction * action) { actionWithTitle:@"Hangup" [call hangup]; style:UIAlertActionStyleDefault }]; [alert addAction:hangupButton]; |
10. Закрытие соединения.
FPWCSApi2Session disconnect код
Code Block | ||||
---|---|---|---|---|
| ||||
- (void)connectButton:(UIButton *)button { [self changeViewState:button enabled:NO]; if ([button.titleLabel.text isEqualToString:@"DISCONNECT"]handler:^(UIAlertAction * action) { if ([FPWCSApi2 getSessions].count) { FPWCSApi2Session *session = [FPWCSApi2 getSessions][0call hangup]; NSLog(@"Disconnect session with server %@", [session getServerUrl]); [session disconnect}]; [alert } elseaddAction:hangupButton]; |
10. Закрытие соединения.
FPWCSApi2Session disconnect код
Code Block | ||||
---|---|---|---|---|
| ||||
- (void)connectButton:(UIButton *)button { [self changeViewState:button enabled:NO]; if NSLog(@"Nothing to disconnect"); [self onDisconnected]; ([button.titleLabel.text isEqualToString:@"DISCONNECT"]) { } if } else([FPWCSApi2 getSessions].count) { //todo check url is notFPWCSApi2Session empty *session = [self changeViewState:_connectUrl enabled:NO[FPWCSApi2 getSessions][0]; [self changeViewState:_sipLogin.input enabled:NO]; NSLog(@"Disconnect session with server %@", [self changeViewState:_sipAuthName.input enabled:NO]; session getServerUrl]); [self changeViewState:_sipPassword.input enabled:NOsession disconnect]; } else [self changeViewState:_sipDomain.input enabled:NO]; { [self changeViewState:_sipOutboundProxy.input enabled:NO]; NSLog(@"Nothing to disconnect"); [self changeViewState:_sipRegRequired.control enabled:NOonDisconnected]; [self changeViewState:_sipPort.input enabled:NO];} [self connect];... } } |