iOS GPUImageDemo¶
Пример приложения с захватом видео с использованием библиотеки GPUImage¶
Данное приложение демонстрирует возможность захвата видео из кастомного источника. В качестве источника используется фильтр-бьютификатор на основе библиотеки GPUImage. Приложение работает только с версией SDK 2.6.1 и новее.
На скриншоте представлен пример публикации потока с фильтром бьютификации
Поля ввода
WCS URL
, гдеdemo.flashphoner.com
- адрес WCS-сервераPublish Stream
- для имени публикуемого потокаPlay Stream
- для имени воспроизводимого потока
Кнопка Beautify Enable/Disable
включает и отключает фильтр бьютификации (на скриншоте фильтр включен)
Работа с кодом примера¶
Для разбора кода возьмем версию примера GPUImageDemo, которая доступна здесь.
Класс для основного вида приложения: ViewController (заголовочный файл ViewController.h; файл имплементации ViewController.m).
1. Импорт API¶
2. Создание сессии и подключение к серверу¶
FPWCSApi2.createSession
, FPWCSApi2Session.connect
В параметрах сессии указываются:
- URL WCS-сервера
- имя серверного приложения
defaultApp
- (FPWCSApi2Session *)connect {
FPWCSApi2SessionOptions *options = [[FPWCSApi2SessionOptions alloc] init];
options.urlServer = _connectUrl.text;
options.appKey = @"defaultApp";
NSError *error;
FPWCSApi2Session *session = [FPWCSApi2 createSession:options error:&error];
...
[session connect];
return session;
}
3. Подготовка захвата из кастомного источника¶
Источник инициализируется один раз при первом входе в функцию publishStream
- (FPWCSApi2Stream *)publishStream {
FPWCSApi2Session *session = [FPWCSApi2 getSessions][0];
FPWCSApi2StreamOptions *options = [[FPWCSApi2StreamOptions alloc] init];
options.name = _localStreamName.text;
if (!self.videoCamera) {
self.videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionFront];
self.videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
self.videoCamera.horizontallyMirrorFrontFacingCamera = YES;
self.rawDataOutput = [[GPUImageRawDataOutput alloc] initWithImageSize:CGSizeMake(480, 640) resultsInBGRAFormat:YES];
self.videoCapturer = [[GPUImageVideoCapturer alloc] init];
[self.rawDataOutput setNewFrameAvailableBlock:^{
[_videoCapturer processNewFrame:_rawDataOutput];
}];
[self.videoCamera addTarget:_rawDataOutput];
[self.videoCamera addTarget:_localDisplay];
}
...
}
4. Публикация потока¶
FPWCSApi2Session.createStream
, FPWCSApi2Stream.publish
Методу createStream
передаются параметры:
- имя публикуемого потока
- вид для локального отображения
- источник захвата видео
Если вызов publish
был успешным, запускается захват видео при помощи функции startCameraCapture
- (FPWCSApi2Stream *)publishStream {
...
options.display = _localNativeDisplay;
options.constraints = [[FPWCSApi2MediaConstraints alloc] initWithAudio:YES videoCapturer:self.videoCapturer];
NSError *error;
FPWCSApi2Stream *stream = [session createStream:options error:&error];
...
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];
}
[self.videoCamera startCameraCapture];
return stream;
}
5. Включение и отключение фильтра бьютификации¶
- (void)beautify:(UIButton *)button {
if (self.beautifyEnabled) {
self.beautifyEnabled = NO;
[_beautyButton setTitle:@"Beautify Enable" forState:UIControlStateNormal];
[self.videoCamera removeAllTargets];
[self.videoCamera addTarget:_rawDataOutput];
[self.videoCamera addTarget:_localDisplay];
}
else {
self.beautifyEnabled = YES;
[_beautyButton setTitle:@"Beautify Disable" forState:UIControlStateNormal];
[self.videoCamera removeAllTargets];
GPUImageBeautifyFilter *beautifyFilter = [[GPUImageBeautifyFilter alloc] init];
[self.videoCamera addTarget:beautifyFilter];
[beautifyFilter addTarget:_localDisplay];
[beautifyFilter addTarget:_rawDataOutput];
}
}
6. Воспроизведение видеопотока¶
FPWCSApi2Session.createStream
, FPWCSApi2Stream.play
Методу createStream
передаются параметры:
- имя воспроизводимого потока
- вид для отображения потока
- (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 play:&error]) {
UIAlertController * alert = [UIAlertController
alertControllerWithTitle:@"Failed to play"
message:error.localizedDescription
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* okButton = [UIAlertAction
actionWithTitle:@"Ok"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
}];
[alert addAction:okButton];
[self presentViewController:alert animated:YES completion:nil];
}
return stream;
}
7. Остановка воспроизведения видеопотока¶
FPWCSApi2Stream.stop
- (void)playButton:(UIButton *)button {
[self changeViewState:button enabled:NO];
if ([button.titleLabel.text isEqualToString:@"STOP"]) {
if ([FPWCSApi2 getSessions].count) {
FPWCSApi2Stream *stream;
for (FPWCSApi2Stream *s in [[FPWCSApi2 getSessions][0] getStreams]) {
if ([[s getName] isEqualToString:_remoteStreamName.text]) {
stream = s;
break;
}
}
if (!stream) {
NSLog(@"Stop playing, nothing to stop");
[self onStopped];
return;
}
NSError *error;
[stream stop:&error];
} else {
NSLog(@"Stop playing, no session");
[self onStopped];
}
...
}
}
8. Остановка публикации видеопотока¶
FPWCSApi2Stream.stop
- (void)publishButton:(UIButton *)button {
[self changeViewState:button enabled:NO];
if ([button.titleLabel.text isEqualToString:@"STOP"]) {
if ([FPWCSApi2 getSessions].count) {
FPWCSApi2Stream *stream;
for (FPWCSApi2Stream *s in [[FPWCSApi2 getSessions][0] getStreams]) {
if ([[s getName] isEqualToString:_localStreamName.text]) {
stream = s;
break;
}
}
if (!stream) {
NSLog(@"Stop publishing, nothing to stop");
[self onUnpublished];
return;
}
NSError *error;
[stream stop:&error];
} else {
NSLog(@"Stop publishing, no session");
[self onUnpublished];
}
...
}
}
9. Закрытие соединения¶
FPWCSApi2Session.disconnect
- (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];
}
...
}
}