Пример iOS-приложения с плеером и стримером
Данное приложение может использоваться для публикации WebRTC-видеопотока и воспроизведения любого из следующих типов потоков с Web Call Server:
...
Слева отображается видео с камеры, справа воспроизводится другой поток.
Работа с кодом примера
Для разбора кода возьмем версию примера TwoWayStreaming, которая доступена для скачивания в соответствующей сборке 2.5.2.
...
1. Импорт API. код
Code Block | ||||
---|---|---|---|---|
| ||||
#import <FPWCSApi2/FPWCSApi2.h> |
...
- URL WCS-сервера
- имя серверного приложения defaultApp
Code Block | ||||
---|---|---|---|---|
| ||||
- (FPWCSApi2Session *)connect { FPWCSApi2SessionOptions *options = [[FPWCSApi2SessionOptions alloc] init]; options.urlServer = _connectUrl.text; options.appKey = @"defaultApp"; NSError *error; FPWCSApi2Session *session = [FPWCSApi2 createSession:options error:&error]; if (!session) {... [session connect]; UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"Failed to connect" return session; } |
3. Публикация видеопотока.
FPWCSApi2Session createStream, FPWCSApi2Stream publish код
Методу createStream передаются параметры:
- имя публикуемого потока
- вид для локального отображения
- параметр record = true для записи потока при публикации
- размеры и FPS публикуемого видео при публикации с iPad
Code Block | ||||
---|---|---|---|---|
| ||||
- (FPWCSApi2Stream *)publishStream { FPWCSApi2Session *session = [FPWCSApi2 getSessions][0]; FPWCSApi2StreamOptions *options = [[FPWCSApi2StreamOptions alloc] init]; options.name = _localStreamName.text; options.display = _localDisplay; if ( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ) { options.constraints = [[FPWCSApi2MediaConstraints alloc] message:error.localizedDescriptioninitWithAudio:YES videoWidth:640 videoHeight:480 videoFps:15]; } NSError *error; FPWCSApi2Stream *stream = [session createStream:options error:&error]; ... if(![stream publish:&error]) { preferredStyle:UIAlertControllerStyleAlert]; UIAlertController * alert = [UIAlertController UIAlertAction* okButton = [UIAlertAction actionWithTitlealertControllerWithTitle:@"OkFailed to publish" stylemessage:UIAlertActionStyleDefaulterror.localizedDescription handler:^(UIAlertAction * action) { preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction* okButton = [UIAlertAction [self onDisconnected]; actionWithTitle:@"Ok" }]; [alert addAction:okButton];style:UIAlertActionStyleDefault [self presentViewController:alert animated:YES completion:nil]; return nil; } [session on:kFPWCSSessionStatusEstablished callbackhandler:^(FPWCSApi2SessionUIAlertAction *rSession action) { [self changeConnectionStatus:[rSession getStatus]]; [self onConnected:rSessiononUnpublished]; }]; [session on:kFPWCSSessionStatusDisconnected callback:^(FPWCSApi2Session *rSession){ [self changeConnectionStatus:[rSession getStatus]]; [self onDisconnected]; }]; [session on:kFPWCSSessionStatusFailed callback:^(FPWCSApi2Session *rSession){ [selfalert changeConnectionStatus:[rSession getStatus]]addAction:okButton]; [self onDisconnected]; }presentViewController:alert animated:YES completion:nil]; [session connect];} return sessionstream; } |
3. Публикация видеопотока.
FPWCSApi2Session createStream, FPWCSApi2Stream publish код
Методу createStream передаются параметры:
- имя публикуемого потока
- вид для локального отображения
- параметр record = true для записи потока при публикации
- размеры и FPS публикуемого видео при публикации с iPad
4. Переключение камеры во время публикации потока
FPWCSApi2Stream switchCamera код
Code Block | ||||
---|---|---|---|---|
| ||||
- (FPWCSApi2Streamvoid)switchCameraButton:(UIButton *)publishStreambutton { FPWCSApi2Session *session = if ([FPWCSApi2 getSessions][0];.count) { FPWCSApi2StreamOptions *options FPWCSApi2Session *session = [[FPWCSApi2StreamOptionsFPWCSApi2 allocgetSessions] init[0]; options.name = _localStreamName.text; NSArray *streams options.display = _localDisplay; = [session getStreams]; if for ( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPadFPWCSApi2Stream *stream in streams ) { options.constraints = [[FPWCSApi2MediaConstraints alloc] initWithAudio:YES videoWidth:640 videoHeight:480 videoFps:15]; if ([stream isPublished]) { } NSError *error; NSLog(@"Found FPWCSApi2Streampublished *stream, = [session createStream:options error:&error]switching camera"); if (!stream) { UIAlertController * alert = [UIAlertController[stream switchCamera]; } } } else { alertControllerWithTitle:NSLog(@"FailedNo active tosessions publishfound"); message:error.localizedDescription} } |
5. Воспроизведение видеопотока.
FPWCSApi2Session createStream, FPWCSApi2Stream play код
Методу createStream передаются параметры:
- имя воспроизводимого потока
- вид для отображения потока
Code Block | ||||
---|---|---|---|---|
| ||||
- (FPWCSApi2Stream *)playStream { FPWCSApi2Session *session = preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction* okButton = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self onUnpublished]; }]; [alert addAction:okButton]; [self presentViewController:alert animated:YES completion:nil]; return nil; } [stream on:kFPWCSStreamStatusPublishing callback:^(FPWCSApi2Stream *rStream){ [self changeStreamStatus:rStream]; [self onPublishing:rStream]; }]; [stream on:kFPWCSStreamStatusUnpublished callback:^(FPWCSApi2Stream *rStream){ [self changeStreamStatus:rStream]; [self onUnpublished]; }]; [stream on:kFPWCSStreamStatusFailed callback:^(FPWCSApi2Stream *rStream){ [self changeStreamStatus:rStream]; [self onUnpublished]; }]; if(![stream publish:&error]) { UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"Failed to publish" message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction* okButton = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self onUnpublished]; }]; [alert addAction:okButton]; [self presentViewController:alert animated:YES completion:nil]; } return stream; } |
4. Переключение камеры во время публикации потока
FPWCSApi2Stream switchCamera код
Code Block | ||||
---|---|---|---|---|
| ||||
- (void)switchCameraButton:(UIButton *)button {
if ([FPWCSApi2 getSessions].count) {
FPWCSApi2Session *session = [FPWCSApi2 getSessions][0];
NSArray *streams = [session getStreams];
for (FPWCSApi2Stream *stream in streams ) {
if ([stream isPublished]) {
NSLog(@"Found published stream, switching camera");
[stream switchCamera];
}
}
} else {
NSLog(@"No active sessions found");
}
} |
5. Воспроизведение видеопотока.
FPWCSApi2Session createStream, FPWCSApi2Stream play код
Методу createStream передаются параметры:
- имя воспроизводимого потока
- вид для отображения потока
Code Block | ||||
---|---|---|---|---|
| ||||
- (FPWCSApi2Stream *)playStream { FPWCSApi2Session *session = [FPWCSApi2 getSessions][0]; FPWCSApi2StreamOptions *options = [[FPWCSApi2StreamOptions alloc] init]; options.name = _remoteStreamName.text; options.display = _remoteDisplay; NSError *error; FPWCSApi2Stream *stream = [session createStream:options error:nil]; if (!stream) { UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"Failed to play" message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction* okButton = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self onStopped]; }]; [alert addAction:okButton]; [self presentViewController:alert animated:YES completion:nil]; return nil; } [stream on:kFPWCSStreamStatusPlaying callback:^(FPWCSApi2Stream *rStream){ [self changeStreamStatus:rStream]; [self onPlaying:rStream]; }]; [stream on:kFPWCSStreamStatusNotEnoughtBandwidth callback:^(FPWCSApi2Stream *rStream){ NSLog(@"Not enough bandwidth stream %@, consider using lower video resolution or bitrate. Bandwidth %ld bitrate %ld", [rStream getName], [rStream getNetworkBandwidth] / 1000, [rStream getRemoteBitrate] / 1000); [self changeStreamStatus:rStream]; }]; [stream on:kFPWCSStreamStatusStopped callback:^(FPWCSApi2Stream *rStream){ [self changeStreamStatus:rStream]; [self onStopped]; }]; [stream on:kFPWCSStreamStatusFailed callback:^(FPWCSApi2Stream *rStream){ [self changeStreamStatus:rStream]; [self onStopped]; }]; if(![stream play:&error]) { UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"Failed to play" message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction* okButton = [UIAlertAction [FPWCSApi2 getSessions][0]; FPWCSApi2StreamOptions *options = [[FPWCSApi2StreamOptions alloc] init]; options.name = _remoteStreamName.text; options.display = _remoteDisplay; NSError *error; FPWCSApi2Stream *stream = [session createStream:options error:nil]; ... if(![stream play:&error]) { UIAlertController * alert = [UIAlertController actionWithTitle:@"Ok" alertControllerWithTitle:@"Failed to play" style:UIAlertActionStyleDefault message:error.localizedDescription handler:^(UIAlertAction * action) { preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction* okButton = [UIAlertAction }]; actionWithTitle:@"Ok" [alert addAction:okButton]; [self presentViewController:alert animated:YES completion:nil]; } return stream; } |
6. Остановка воспроизведения видеопотока.
FPWCSApi2Stream stop код
Code Block | ||||
---|---|---|---|---|
| ||||
- (void)playButton:(UIButton *)button { [self changeViewState:button enabledstyle:NO];UIAlertActionStyleDefault if ([button.titleLabel.text isEqualToString:@"STOP"]) { if ([FPWCSApi2 getSessions].counthandler:^(UIAlertAction * action) { FPWCSApi2Stream *stream; for (FPWCSApi2Stream *s in [[FPWCSApi2 getSessions][0] getStreams]) { if ([[s getName] isEqualToString:_remoteStreamName.text]) { stream = s}]; break[alert addAction:okButton]; [self presentViewController:alert animated:YES completion:nil]; } return stream; } |
6. Остановка воспроизведения видеопотока.
FPWCSApi2Stream stop код
Code Block | ||||
---|---|---|---|---|
| ||||
- (void)playButton:(UIButton }*)button { [self changeViewState:button enabled:NO]; if (!stream[button.titleLabel.text isEqualToString:@"STOP"]) { if ([FPWCSApi2 getSessions].count) { NSLog(@"Stop playing, nothing to stop"); FPWCSApi2Stream *stream; [self onStopped]; for (FPWCSApi2Stream *s in [[FPWCSApi2 getSessions][0] getStreams]) { return; } if ([[s getName] isEqualToString:_remoteStreamName.text]) { NSError *error; stream [stream stop:&error]; } else { = s; NSLog(@"Stop playing, no session") break; [self onStopped]; } } } else { if ([FPWCSApi2 getSessions].count!stream) { [self changeViewState:_remoteStreamName enabled:NO]; NSLog(@"Stop playing, nothing to stop"); [self playStream]; } else {[self onStopped]; NSLog(@"Start playing, no session") return; [self onStopped]; } } NSError } } |
7. Остановка публикации видеопотока. ViewController.m, line 274
FPWCSApi2Stream stop код
Code Block | ||||
---|---|---|---|---|
| ||||
- (void)publishButton:(UIButton *)button { *error; [selfstream changeViewState:button enabled:NOstop:&error]; if ([button.titleLabel.text isEqualToString:@"STOP"]) } else { if ([FPWCSApi2 getSessions].count) {NSLog(@"Stop playing, no session"); FPWCSApi2Stream *stream[self onStopped]; } for (FPWCSApi2Stream *s in [[FPWCSApi2 getSessions][0] getStreams])... } } |
7. Остановка публикации видеопотока. ViewController.m, line 274
FPWCSApi2Stream stop код
Code Block | ||||
---|---|---|---|---|
| ||||
- (void)publishButton:(UIButton *)button { [self changeViewState:button enabled:NO]; if ([[s getName] isEqualToString:_localStreamName.text]) { stream = s; button.titleLabel.text isEqualToString:@"STOP"]) { if ([FPWCSApi2 getSessions].count) { FPWCSApi2Stream break*stream; for (FPWCSApi2Stream *s in } [[FPWCSApi2 getSessions][0] getStreams]) { } if ([[s if (!streamgetName] isEqualToString:_localStreamName.text]) { NSLog(@"Stop publishing, nothing to stop") stream = s; [self onUnpublished] break; return;} } NSError *error; [stream stop:&error]; } else { if (!stream) { NSLog(@"Stop publishing, nonothing to sessionstop"); [self onUnpublished]; } } else {return; if ([FPWCSApi2 getSessions].count) {} [self changeViewState:_localStreamName enabled:NO]NSError *error; [self publishStreamstream stop:&error]; } else { NSLog(@"StartStop publishing, no session"); [self onUnpublished]; } }... } } |
8. Закрытие соединения.
FPWCSApi2Session disconnect код
Code Block | ||||
---|---|---|---|---|
| ||||
- (void)connectButton:(UIButton *)button { [self changeViewState:button enabled:NO]; if ([button.titleLabel.text isEqualToString:@"DISCONNECT"]) { if ([FPWCSApi2 getSessions].count) { FPWCSApi2Session *session = [FPWCSApi2 getSessions][0]; NSLog(@"Disconnect session with server %@", [session getServerUrl]); [session disconnect]; } else { NSLog(@"Nothing to disconnect"); [self onDisconnected]; } } else { //todo check url is not empty [self changeViewState:_connectUrl enabled:NO]; [self connect]; ... } } |