Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Пример iOS-приложения для аудиозвонков

Работа с кодом примера

Для разбора кода возьмем версию примера PhoneMin, которая доступена для скачивания в сборке 2.5.2.

Класс для основного вида приложения: ViewController (заголовочный файл ViewController.h; файл имплементации ViewController.m).

1. Импорт API. код

Code Block
languagebash
themeRDark
#import <FPWCSApi2/FPWCSApi2.h>

2. Подключение к серверу.

FPWCSApi2 createSession, FPWCSApi2Session connect код
В параметрах сессии указываются:

  • URL WCS-сервера
  • параметры SIP-аккаунта для совершения исходящих и приема входящих звонков
  • имя серверного приложения defaultApp
Code Block
languagebash
themeRDark
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
languagebash
themeRDark
- (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 код

...

languagebash
themeRDark

...

На скриншотах, приведенных ниже, отображается интерфейс приложения перед совершением звонка.

В поле ввода 'WCS URL' указан адрес демонстрационного WCS-сервера wcs5-eu.flashphoner.com

В полях ввода 'SIP...' указываются параметры регистрации на SIP-сервере.
В поле 'Callee', указано имя вызываемого абонента.

Поле 'Invite parameters' предназначено для ввода дополнительных параметров сообщения SIP INVITE.

Соединение с сервером устанавливается при нажатии на кнопку 'Connect'. Звонок устанавливается/завершается по нажатию Call/Hangup, вводится в режим удержания либо выводится из него кнопкой Hold/Unhold.

Image Added

Image Added

Работа с кодом примера

Для разбора кода возьмем версию примера PhoneMin, которая доступна для скачивания в сборке 2.5.2.

Класс для основного вида приложения: ViewController (заголовочный файл ViewController.h; файл имплементации ViewController.m).

1. Импорт API. код

Code Block
languagecpp
themeRDark
#import <FPWCSApi2/FPWCSApi2.h>


2. Подключение к серверу.

FPWCSApi2 createSession, FPWCSApi2Session connect код
В параметрах сессии указываются:

  • URL WCS-сервера
  • параметры SIP-аккаунта для совершения исходящих и приема входящих звонков
  • имя серверного приложения defaultApp
Code Block
languagecpp
themeRDark
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;
...
session = [FPWCSApi2 createSession:options error:&error];
...
[session connect];


3. Исходящий звонок.

FPWCSApi2Session createCall, FPWCSApi2Call call код

При создании звонка в метод createCall передаются параметры:

  • имя вызываемого SIP-аккаунта
  • дополнительные параметры SIP INVITE запроса, введенные пользователем
Code Block
languagecpp
themeRDark
- (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:[callparameters ondataUsingEncoding:kFPWCSCallStatusBusyNSUTF8StringEncoding] callbackoptions:^(FPWCSApi2Call *call){
0 error:&err];
        if [self changeCallStatus:call];
(err) {
          [self toCallState];
    }];
        
 NSLog(@"Error converting JSON Invite parameters to dictionary %@, JSON %@", err, parameters);
       [call on:kFPWCSCallStatusFailed callback:^(FPWCSApi2Call *call){
} else {
            options.inviteParameters [self changeCallStatus:call]= dictionary;
        [self toCallState];}
    }];
    options.callee = _callee.input.text;
  
  //used for [call on:kFPWCSCallStatusRing callback:^(FPWCSApi2Call *call){
only recv audio
//    options.localConstraints = [[FPWCSApi2MediaConstraints  [self changeCallStatus:callalloc] initWithAudio:NO video:NO];
//    options.remoteConstraints =   [[selfFPWCSApi2MediaConstraints toHangupStatealloc];
    }initWithAudio:YES video:NO];
    NSError *error;
   
 call  = [callsession on:kFPWCSCallStatusHold callback:^(FPWCSApi2Call *call){createCall:options error:&error];
    ...
    [selfcall changeCallStatus:call];
    return call;
}


4. Получение от сервера события, сигнализирующего о входящем звонке

FPWCSApi2Session onIncomingCallCallback код

Code Block
languagecpp
themeRDark
[session   [self changeViewState:_holdButton enabled:YES];
    }]onIncomingCallCallback:^(FPWCSApi2Call *rCall) {
    call = rCall;
        
    [call on:kFPWCSCallStatusEstablishedkFPWCSCallStatusBusy callback:^(FPWCSApi2Call *call){
        [self changeCallStatus:call];
        [self toHangupState];
        [self changeViewState:_holdButton enabled:YEStoCallState];
    }];
        
    [call on:kFPWCSCallStatusFinishkFPWCSCallStatusFailed callback:^(FPWCSApi2Call *call){
        [self changeCallStatus:call];
        [self toCallState];
    }];
   [self toCallState];
    
    [selfcall dismissViewControllerAnimatedon:YESkFPWCSCallStatusRing completion:nil];
    }];
    ...
}];

5. Ответ на входящий звонок.

FPWCSApi2Call answer код

Code Block
languagebash
themeRDark
alert = [UIAlertController
      callback:^(FPWCSApi2Call *call){
        [self changeCallStatus:call];
        [self toHangupState];
    }];
        
    [call on:kFPWCSCallStatusHold callback:^(FPWCSApi2Call *call){
        alertControllerWithTitle:[NSStringself stringWithFormat:@"Incoming call from '%@'", [rCall getCallee]]changeCallStatus:call];
        [self changeViewState:_holdButton enabled:YES];
    }];
        
    [call on:kFPWCSCallStatusEstablished callback:^(FPWCSApi2Call *call){
        [self  message:error.localizedDescription
changeCallStatus:call];
        [self toHangupState];
        [self changeViewState:_holdButton enabled:YES];
    }];
        preferredStyle:UIAlertControllerStyleAlert];
    [call on:kFPWCSCallStatusFinish   
UIAlertAction* answerButton = [UIAlertAction
callback:^(FPWCSApi2Call *call){
        [self changeCallStatus:call];
        [self toCallState];
        [self dismissViewControllerAnimated:YES completion:nil];
    actionWithTitle:@"Answer"}];
    ...
}];


5. Ответ на входящий звонок.

FPWCSApi2Call answer код

Code Block
languagecpp
themeRDark
alert = [UIAlertController
                         style:UIAlertActionStyleDefault
    alertControllerWithTitle:[NSString stringWithFormat:@"Incoming call from '%@'", [rCall getCallee]]
                     handler:^(UIAlertAction * action) {
     message:error.localizedDescription
                              [call answerpreferredStyle:UIAlertControllerStyleAlert];
        
UIAlertAction* answerButton =  [UIAlertAction
                   }];
        
[alert addAction:answerButton];
UIAlertAction* hangupButton = [UIAlertAction actionWithTitle:@"Answer"
                               actionWithTitle:@"Hangup"style:UIAlertActionStyleDefault
                               style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
                                 handler:^(UIAlertAction * action) {
[call answer];
                               }];
        
[callalert hangupaddAction:answerButton];
UIAlertAction* hangupButton = [UIAlertAction
                               }];
   actionWithTitle:@"Hangup"
     
[alert addAction:hangupButton];
[self presentViewController:alert animated:YES completion:nil];

6. Удержание звонка.

FPWCSApi2Call hold, unhold код

Code Block
languagebash
themeRDark
- (void)holdButton:(UIButton *)button {
    [self  changeViewState:button enabled:NO];
    if ([button.titleLabel.text isEqualToString:@"UNHOLD"]) {
     style:UIAlertActionStyleDefault
   if (call) {
            [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
languagebash
themeRDark
- (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
languagebashcpp
themeRDark
- (void)callButtonholdButton:(UIButton *)button {
    [self changeViewState:button enabled:NO];
    if ([button.titleLabel.text isEqualToString:@"HANGUPUNHOLD"]) {
        if ([FPWCSApi2 getSessions].countcall) {
            [call hangup unhold];
            [_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
languagecpp
themeRDark
- (void)dtmfButton:(UIButton *)button {
     } elseif (call) {
        [call    [self toCallState];
        }sendDTMF:_dtmf.input.text type:kFPWCSCallDTMFRFC2833];
    }
}


98. Завершение входящего исходящего звонка.

FPWCSApi2Call hangup код

UIAlertAction* hangupButton = [UIAlertAction
Code Block
languagebashcpp
themeRDark
- (void)callButton:(UIButton *)button {
    [self changeViewState:button enabled:NO];
    if ([button.titleLabel.text isEqualToString:@"HANGUP"]) {
               actionWithTitle:@"Hangup"
     if ([FPWCSApi2 getSessions].count) {
            [call hangup];
        } else {
   style:UIAlertActionStyleDefault
         [self toCallState];
        }
        ...
     handler:^(UIAlertAction * action) {
}
}


9. Завершение входящего звонка.

FPWCSApi2Call hangup код

Code Block
languagecpp
themeRDark
UIAlertAction* hangupButton = [UIAlertAction
                               actionWithTitle:@"Hangup"
     [call hangup];
                         style:UIAlertActionStyleDefault
      }];
        
[alert addAction:hangupButton];

10. Закрытие соединения.

FPWCSApi2Session disconnect код

Code Block
languagebash
themeRDark
- (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 addAction:hangupButton];


10. Закрытие соединения.

FPWCSApi2Session disconnect код

Code Block
languagecpp
themeRDark
-  } else(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];...
    }
}