Apple постоянно держит разработчиков в тонусе.
Фронтальные камеры на iPad и iPhone родили новый виток идей у создателей сиюминутных приложений. Я тоже провел небольшое исследование двухкамерных телефонов и приглашаю нажать на кнопку, кому это интересно.
Видеозахват в iOS 4.3+ стал простым, как апельсин.
Четыре вызова – и Вы обладатель пикселей, полученных с любой из двух камер iPhone.
Чуть-чуть кода, дизайнерам не читать
Заводим наш HabrahabrView
@class CaptureSessionManager;
@interface HabrahabrView : UIView <AVCaptureVideoDataOutputSampleBufferDelegate> {
CaptureSessionManager *captureFront;
CaptureSessionManager *captureBack;
UIImageView *face;
}
В теле класса вводим обязательную функцию captureOutput, куда будет приходить 20 раз в секунду картинки с видеокамеры.
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
// Lock the base address of the pixel buffer
CVPixelBufferLockBaseAddress(imageBuffer, 0);
// Get the number of bytes per row for the pixel buffer
// void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
// Get the number of bytes per row for the pixel buffer
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
// Get the pixel buffer width and height
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
unsigned char* pixel = (unsigned char *)CVPixelBufferGetBaseAddress(imageBuffer);
CGColorSpaceRef colorSpace=CGColorSpaceCreateDeviceRGB();
CGContextRef context=CGBitmapContextCreate(pixel, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little|kCGImageAlphaPremultipliedFirst);
CGImageRef image=CGBitmapContextCreateImage(context);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
UIImage *resultUIImage=[UIImage imageWithCGImage:image];
CGImageRelease(image);
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
[resultUIImage retain];
[self performSelectorOnMainThread:@selector(cameraCaptureGotFrame:)
withObject:resultUIImage waitUntilDone:NO];
}
- (void) cameraCaptureGotFrame:(UIImage*)image
{
face.image = [self fixOrientation:image];
// decrement ref count
[image release];
}
Теперь, когда вся предварительная работа закончена, заводим картинку на экране, куда будет выводится наше видео
face = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 58, 70)];
[self addSubview:face];
Вот так подключаем заднюю камеру
[self setCaptureBack:[[[CaptureSessionManager alloc] init] autorelease]];
[[self captureBack] addVideoInput:2 PView:self];
[[self captureBack] addVideoPreviewLayer];
[[[self captureBack] captureSession] setSessionPreset:AVCaptureSessionPresetLow];
А вот так переднюю
[self setCaptureFront:[[[CaptureSessionManager alloc] init] autorelease]];
[[self captureFront] addVideoInput:1 PView:self];
[[self captureFront] addVideoPreviewLayer];
[[[self captureFront] captureSession] setSessionPreset:AVCaptureSessionPresetLow];
Оформим все функции в отдельный класс.
#import "CaptureSessionManager.h"
@implementation CaptureSessionManager
@synthesize captureSession;
@synthesize previewLayer;
- (id)init {
if ((self = [super init])) {
[self setCaptureSession:[[AVCaptureSession alloc] init]];
}
return self;
}
- (void)addVideoPreviewLayer {
[self setPreviewLayer:[[[AVCaptureVideoPreviewLayer alloc] initWithSession:[self captureSession]] autorelease]];
[[self previewLayer] setVideoGravity:AVLayerVideoGravityResizeAspectFill];
}
- (void)addVideoInput:(int)camType PView:(HabrahabrView*) habraview {
NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
AVCaptureDevice *videoDevice = nil;
NSInteger side = (camType==1) ? AVCaptureDevicePositionFront : AVCaptureDevicePositionBack;
for (AVCaptureDevice *device in videoDevices)
{
if (device.position == side)
{
videoDevice = device;
break;
}
}
if (videoDevice == nil) {
videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
}
if (videoDevice) {
NSError *error;
AVCaptureDeviceInput *videoIn = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error];
if (!error) {
if ([[self captureSession] canAddInput:videoIn])
[[self captureSession] addInput:videoIn];
else
NSLog(@"Couldn't add video input");
// Set the output
AVCaptureVideoDataOutput* videoOutput = [[AVCaptureVideoDataOutput alloc] init];
// create a queue to run the capture on
dispatch_queue_t captureQueue=dispatch_queue_create("catpureQueue", NULL);
// setup our delegate
[videoOutput setSampleBufferDelegate:habraview queue:captureQueue];
// configure the pixel format
videoOutput.videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], (id)kCVPixelBufferPixelFormatTypeKey,
nil];
if ([[self captureSession] canAddOutput:videoOutput])
[[self captureSession] addOutput:videoOutput];
else
NSLog(@"Couldn't add video ouput");
}
else
NSLog(@"Couldn't create video input");
}
else
NSLog(@"Couldn't create video capture device");
}
- (void)dealloc {
[[self captureSession] stopRunning];
[previewLayer release], previewLayer = nil;
[captureSession release], captureSession = nil;
[super dealloc];
}
@end
Готово.
Работа с камерами
Включаем переднюю камеру
[[captureFront captureSession] startRunning];
Все работает. Как видно из кода видео можно дергать в трех разрешениях. Я выбираю для скорости самое маленькое AVCaptureSessionPresetLow (примерно 144 на 192 пикселя ). Для наших нужд этого хватает, заодно и фильтр картинки происходит бесплатный.
Как теперь включить заднюю камеру? Остановить переднюю и включить заднюю
[[captureFront captureSession] stopRunning];
[[captureBack captureSession] startRunning];
Сразу захотелось включить обе. Увы. Невозможно. Я попытался быстро переключать камеры, но существует задержка примерно в треть секунды, которая ничего, кроме раздражения не вызывает.
Мечту о наложении двух изображении онлайн пришлось убить.
Фруктожорка
Но не убить мечту о каком-нибудь дурацком приложении. Я решил быстро сотворить фруктожорку — изображение с фронтальной камеры реальное, апельсины — виртуальные.
Апельсины по очереди падают на экран. Их надо захватить ртом и съесть. Кто быстрее съест 7 апельсинов — тому приз.
Осталось написать процедуру распознавания рта.
Распознать рот — занятие примитивное, даже я, человек впадающий в тоску от слов ООП, ОпенСиВи, сделал эту функцию быстро. Не используя ни ООП, ни ОпенСиВи.
Подсказка — Вы знаете положение апельсина и пляшете от него.
Ага, воскликнет внимательный читатель — процедура распознавания отлажена исключительно на авторе при освещении его рабочего места. Вы правы, но судя по фотографиям пользователей, приложение успешно работает от Китая до Бразилии, от офисов до квартир.
Конечно, был велик соблазн стащить фотографию пользователя в момент поедания им 7-ого апельсина. Я всегда поддаюсь соблазнам, ведь они могут не повториться. Спокойно! Для любителей нравственности я добавил кнопку — Разрешить отправлять свое фото. По умолчанию — выключена.
Я отправляю небольшую картинку игрока размером 58 на 70 и тайком любуюсь. Попадаются очень забавные фотки. Ржу порой минуты по 3. Есть и симпатичные особы.
Ради Бога, не болтайте про фото, храните корпоративные тайны.
Совсем забыл. Мой рекорд — 12 секунд. Готовлюсь к Олимпиаде.
До встречи в Лондоне, друзья.
Автор: PapaBubaDiop