Пример iOS-приложения для видеоконференции
Работа с кодом примера
Для разбора кода возьмем версию примера Conference, которая доступена для скачивания в сборке 2.5.2.
Класс для основного вида приложения: ViewController (заголовочный файл ViewController.h; файл имплементации ViewController.m).
1. Импорт API. код
Code Block | ||||
---|---|---|---|---|
| ||||
#import <FPWCSApi2/FPWCSApi2.h> |
2. Подключение к серверу.
FPWCSApi2 createRoomManager код
В параметрах сессии указываются:
- URL WCS-сервера
- имя пользователя чат-комнаты
...
language | bash |
---|---|
theme | RDark |
...
Данный пример может использоваться для участия в видеоконференции для трех пользователей на Web Call Server и позволяет публиковать WebRTC-поток.
На скриншоте ниже представлен пример с конференцией, к которой присоединились два других участника.
Поля ввода, необходимые для установления соединения и присоединения к конференции
- 'WCS URL' - адрес WCS-сервера
- 'Login' - имя пользователя
- 'Room' - имя "комнаты" конференции
На скриншоте вопроизводятся три видео
- нижнее - видео с камеры данного участника
- два верхних - видео от других двух участников
Работа с кодом примера
Для разбора кода возьмем версию примера Conference, которая доступна для скачивания в сборке 2.5.2.
Класс для основного вида приложения: ViewController (заголовочный файл ViewController.h; файл имплементации ViewController.m).
1. Импорт API. код
Code Block | ||||
---|---|---|---|---|
| ||||
#import <FPWCSApi2/FPWCSApi2.h> |
2. Подключение к серверу.
FPWCSApi2 createRoomManager код
В параметрах сессии указываются:
- URL WCS-сервера
- имя пользователя чат-комнаты
Code Block | ||||
---|---|---|---|---|
| ||||
- (void)connect { FPWCSApi2RoomManagerOptions *options = [[FPWCSApi2RoomManagerOptions alloc] init]; options.urlServer = _connectUrl.text; options.username = _connectLogin.input.text; NSError *error; roomManager = [FPWCSApi2 createRoomManager:options error:&error]; if (!roomManager) { 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]; } [roomManager on:kFPWCSRoomManagerEventConnected callback:^(FPWCSApi2RoomManager *rManager){ [self changeConnectionStatus:kFPWCSRoomManagerEventConnected]; [self onConnected:rManager]; }]; [roomManager on:kFPWCSRoomManagerEventFailed callback:^(FPWCSApi2RoomManager *rManager){ [self changeConnectionStatus:kFPWCSRoomManagerEventDisconnected]; [self onUnpublished]; [self onLeaved]; [self onDisconnected]; }]; [roomManager on:kFPWCSRoomManagerEventDisconnected callback:^(FPWCSApi2RoomManager *rManager){ [self changeConnectionStatus:kFPWCSRoomManagerEventDisconnected]; [self onUnpublished]; [self onLeaved]; [self onDisconnected]; }]; } |
3. Присоединение к конференции.
FPWCSApi2RoomManager join код
Методу передаются параметры:
- имя чат-комнаты
Code Block | ||||
---|---|---|---|---|
| ||||
FPWCSApi2RoomOptions * options = [[FPWCSApi2RoomOptions alloc] init];
options.name = _joinRoomName.input.text;
room = [roomManager join:options]; |
...
FPWCSApi2Room onStateCallback код
При получении данного события количество и состав других участников определяется с помощью метода FPWCSApi2Room getParticipants. Если количество участников более 3, текущий участник выходит из комнаты.
Если текущий участник остается в комнате, запускается проигрывание потока от других участников при помощи метода FPWCSApi2RoomParticipant play
Code Block | ||||
---|---|---|---|---|
| ||||
[room onStateCallback:^(FPWCSApi2Room *room) {
NSDictionary *participants = [room getParticipants];
if ([participants count] >= 3) {
[room leave:nil];
_joinStatus.text = @"Room is full";
[self changeViewState:_joinButton enabled:YES];
return;
}
NSString *chatState = @"participants: ";
for (NSString* key in participants) {
FPWCSApi2RoomParticipant *participant = [participants valueForKey:key];
ParticipantView *pv = [freeViews pop];
[busyViews setValue:pv forKey:[participant getName]];
[participant play:pv.display];
pv.login.text = [participant getName];
chatState = [NSString stringWithFormat:@"%@%@, ", chatState, [participant getName]];
}
_joinStatus.text = @"JOINED";
[self changeViewState:_joinButton enabled:YES];
[_joinButton setTitle:@"LEAVE" forState:UIControlStateNormal];
[self changeViewState:_publishButton enabled:YES];
[self changeViewState:_sendButton enabled:YES];
if ([participants count] == 0) {
_messageHistory.text = [NSString stringWithFormat:@"%@\n%@ - %@", _messageHistory.text, @"chat", @"room is empty"];
} else {
_messageHistory.text = [NSString stringWithFormat:@"%@\n%@ - %@", _messageHistory.text, @"chat", [chatState substringToIndex:MAX((int)[chatState length]-2, 0)]];
}
}]; |
5. Публикация видеопотока.
FPWCSApi2Room publish код
Методу передаются параметры:
- вид для локального отображения публикуемого потока
Code Block | ||||
---|---|---|---|---|
| ||||
- (void)publishButton:(UIButton *)button {
[self changeViewState:button enabled:NO];
if ([button.titleLabel.text isEqualToString:@"STOP"]) {
[room unpublish];
} else {
publishStream = [room publish:_localDisplay];
[publishStream on:kFPWCSStreamStatusPublishing callback:^(FPWCSApi2Stream *rStream){
[self changeViewState:_publishButton enabled:YES];
[self changeLocalStatus:rStream];
[_publishButton setTitle:@"STOP" forState:UIControlStateNormal];
[self changeViewState:_muteAudio enabled:YES];
[self changeViewState:_muteVideo enabled:YES];
}];
[publishStream on:kFPWCSStreamStatusUnpublished callback:^(FPWCSApi2Stream *rStream){
[self onUnpublished];
[self changeLocalStatus:rStream];
}];
[publishStream on:kFPWCSStreamStatusFailed callback:^(FPWCSApi2Stream *rStream){
[self onUnpublished];
[self changeLocalStatus:rStream];
}];
}
} |
6. Получение от сервера события, сигнализирующего о присоединении к конференции другого участника
FPWCSApi2Room kFPWCSRoomParticipantEventJoined participantCallback код
Code Block | ||||
---|---|---|---|---|
| ||||
[room on:kFPWCSRoomParticipantEventJoined participantCallback:^(FPWCSApi2Room *room, FPWCSApi2RoomParticipant *participant) {
ParticipantView *pv = [freeViews pop];
if (pv) {
pv.login.text = [participant getName];
_messageHistory.text = [NSString stringWithFormat:@"%@\n%@ - %@", _messageHistory.text, participant.getName, @"joined"];
[busyViews setValue:pv forKey:[participant getName]];
}
}]; |
7. Получение от сервера события, сигнализирующего о публикации видеопотока другим участником, и воспроизведение видеопотока.
FPWCSApi2Room kFPWCSRoomParticipantEventPublished participantCallback, FPWCSApi2RoomParticipant play код
Code Block | ||||
---|---|---|---|---|
| ||||
FPWCSApi2Room kFPWCSRoomParticipantEventPublished participantCallback, FPWCSApi2RoomParticipant play код [room on:kFPWCSRoomParticipantEventPublished participantCallback:^(FPWCSApi2Room *room, FPWCSApi2RoomParticipant *participant) { ... } |
3. Присоединение к конференции.
FPWCSApi2RoomManager join код
Методу передаются параметры:
- имя чат-комнаты
Code Block | ||||
---|---|---|---|---|
| ||||
FPWCSApi2RoomOptions * options = [[FPWCSApi2RoomOptions alloc] init];
options.name = _joinRoomName.input.text;
room = [roomManager join:options]; |
4. Получение от сервера события, подтверждающего успешное присоединение к конференции
FPWCSApi2Room onStateCallback код
При получении данного события количество и состав других участников определяется с помощью метода FPWCSApi2Room getParticipants. Если количество участников более 3, текущий участник выходит из комнаты.
Если текущий участник остается в комнате, запускается проигрывание потока от других участников при помощи метода FPWCSApi2RoomParticipant play
Code Block | ||||
---|---|---|---|---|
| ||||
[room onStateCallback:^(FPWCSApi2Room *room) { NSDictionary *participants = [room getParticipants]; if ([participants count] >= 3) { [room leave:nil]; _joinStatus.text = @"Room is full"; [self changeViewState:_joinButton enabled:YES]; return; } NSString *chatState = @"participants: "; for (NSString* key in participants) { FPWCSApi2RoomParticipant *participant = [participants valueForKey:key]; ParticipantView *pv = [busyViews valueForKey:[participant getName]]; freeViews pop]; if[busyViews (pv) {setValue:pv forKey:[participant getName]]; [participant play:pv.display]; } }]; |
8. Получение от сервера события, сигнализирующего о получении сообщения от другого участника.
FPWCSApi2Room onMessageCallback код
Code Block | ||||
---|---|---|---|---|
| ||||
[room onMessageCallback:^(FPWCSApi2Room *room, FPWCSApi2RoomMessage *message) { _messageHistory.text pv.login.text = [participant getName]; chatState = [NSString stringWithFormat:@"%@\n%@ - %@%@%@, ", _messageHistory.text, message.from, message.text];chatState, [participant getName]]; } ... }]; |
9. Отправка текстового сообщения5. Публикация видеопотока.
FPWCSApi2RoomParticipant sendMessage FPWCSApi2Room publish код
Методу передается текст сообщения.передаются параметры:
- вид для локального отображения публикуемого потока
Code Block | ||||
---|---|---|---|---|
| ||||
- (void)sendButtonpublishButton:(UIButton *)button { for (NSString *name in [room getParticipants]) { self changeViewState:button enabled:NO]; FPWCSApi2RoomParticipant *participantif = [room getParticipants][name];([button.titleLabel.text isEqualToString:@"STOP"]) { [participant sendMessage:_messageBody.textroom unpublish]; } else { _messageHistory.text = [NSString stringWithFormat:@"%@\n%@ - %@", _messageHistory.text, _connectLogin.input.text, _messageBody.textpublishStream = [room publish:_localDisplay]; _messageBody.text = @""; } |
10. Включение/выключение аудио и видео для публикуемого потока.
...
...
}
} |
6. Получение от сервера события, сигнализирующего о присоединении к конференции другого участника
FPWCSApi2Room kFPWCSRoomParticipantEventJoined participantCallback код
Code Block | ||||
---|---|---|---|---|
| ||||
- (void)muteAudioChanged:(id)sender[room on:kFPWCSRoomParticipantEventJoined participantCallback:^(FPWCSApi2Room *room, FPWCSApi2RoomParticipant *participant) { if (publishStream) { ParticipantView *pv = [freeViews pop]; if (_muteAudio.control.isOnpv) { pv.login.text = [publishStreamparticipant muteAudiogetName]; } else { [publishStream unmuteAudio_messageHistory.text = [NSString stringWithFormat:@"%@\n%@ - %@", _messageHistory.text, participant.getName, @"joined"]; } } } - (void)muteVideoChanged:(id)sender {[busyViews setValue:pv forKey:[participant getName]]; if (publishStream) { if (_muteVideo.control.isOn} }]; |
7. Получение от сервера события, сигнализирующего о публикации видеопотока другим участником, и воспроизведение видеопотока.
FPWCSApi2Room kFPWCSRoomParticipantEventPublished participantCallback, FPWCSApi2RoomParticipant play код
Code Block | ||||
---|---|---|---|---|
| ||||
FPWCSApi2Room kFPWCSRoomParticipantEventPublished participantCallback, FPWCSApi2RoomParticipant play код [room on:kFPWCSRoomParticipantEventPublished participantCallback:^(FPWCSApi2Room *room, FPWCSApi2RoomParticipant *participant) { ParticipantView *pv = [publishStream muteVideo[busyViews valueForKey:[participant getName]]; if (pv) { } else { [participant play:pv.display]; } }]; |
8. Получение от сервера события, сигнализирующего о получении сообщения от другого участника.
FPWCSApi2Room onMessageCallback код
Code Block | ||||
---|---|---|---|---|
| ||||
[room onMessageCallback:^(FPWCSApi2Room *room, FPWCSApi2RoomMessage [publishStream unmuteVideo];*message) { _messageHistory.text = [NSString } } } |
11. Остановка публикации видеопотока. (код)
...
stringWithFormat:@"%@\n%@ - %@", _messageHistory.text, message.from, message.text];
}]; |
9. Отправка текстового сообщения.
FPWCSApi2RoomParticipant sendMessage код
Методу передается текст сообщения.
Code Block | ||||
---|---|---|---|---|
| ||||
- (void)publishButtonsendButton:(UIButton *)button { [self changeViewState:button enabled:NO]; if ([button.titleLabel.text isEqualToString:@"STOP"])*)button { for (NSString *name in [room unpublishgetParticipants]; } else ) { FPWCSApi2StreamOptionsFPWCSApi2RoomParticipant *participant options = [[FPWCSApi2StreamOptionsroom alloc] init]; options.record = [_record.control isOngetParticipants][name]; publishStream = [roomparticipant publishsendMessage:_localDisplay withOptions:optionsmessageBody.text]; } _messageHistory.text = [publishStreamNSString on:kFPWCSStreamStatusPublishing callback:^(FPWCSApi2Stream *rStream){stringWithFormat:@"%@\n%@ - %@", _messageHistory.text, _connectLogin.input.text, _messageBody.text]; _messageBody.text [self changeViewState:_publishButton enabled:YES];= @""; } |
10. Включение/выключение аудио и видео для публикуемого потока.
FPWCSApi2Stream unmuteAudio, muteAudio, unmuteVideo, muteVideo код
Code Block | ||||
---|---|---|---|---|
| ||||
- (void)muteAudioChanged:(id)sender { if [self changeLocalStatus:rStream];(publishStream) { if [_publishButton setTitle:@"STOP" forState:UIControlStateNormal];(_muteAudio.control.isOn) { [selfpublishStream changeViewState:_muteAudio enabled:YES]; } [self changeViewState:_muteVideo enabled:YES];else { [self changeViewState:_record enabled:NO]; publishStream unmuteAudio]; } } }]; - (void)muteVideoChanged:(id)sender { if (publishStream) { [publishStream on:kFPWCSStreamStatusUnpublished callback:^(FPWCSApi2Stream *rStream)if (_muteVideo.control.isOn) { [selfpublishStream onUnpublishedmuteVideo]; } else { [self changeLocalStatus:rStream]; [publishStream }unmuteVideo]; } [publishStream on:kFPWCSStreamStatusFailed callback:^(FPWCSApi2Stream *rStream)} } |
11. Остановка публикации видеопотока.
FPWCSApi2Room unpublish код
Code Block | ||||
---|---|---|---|---|
| ||||
- (void)publishButton:(UIButton *)button { [self changeViewState:button enabled:NO]; if [self onUnpublished]; ([button.titleLabel.text isEqualToString:@"STOP"]) { [selfroom changeLocalStatus:rStreamunpublish]; } else { }]; ... } } |
12. Выход из комнаты конференции.
...
Методу передается хэндлер для обработки ответа REST-приложения WCS-сервера.
Code Block | ||||
---|---|---|---|---|
| ||||
if ([button.titleLabel.text isEqualToString:@"LEAVE"]) { if (room) { FPWCSApi2DataHandler *handler = [[FPWCSApi2DataHandler alloc] init]; handler.onAccepted = ^(FPWCSApi2Session *session, FPWCSApi2Data *data){ [self onUnpublished]; [self onLeaved]; }; handler.onRejected = ^(FPWCSApi2Session *session, FPWCSApi2Data *data){ [self onUnpublished]; [self onLeaved]; }; [room leave:handler]; room = nil; } } |
...
FPWCSApi2RoomManager disconnect код
Code Block | ||||
---|---|---|---|---|
| ||||
- (void)connectButton:(UIButton *)button { [self changeViewState:button enabled:NO]; if ([button.titleLabel.text isEqualToString:@"DISCONNECT"]) { if (roomManager) { [roomManager disconnect]; } } else]) { //todo check url is not empty if (roomManager) { [self changeViewState:_connectUrl enabled:NOroomManager disconnect]; [self changeViewState:_connectLogin.input enabled:NO];} [self connect];... } } |