IPhone SDK 4 AVFoundation - Как правильно использовать captureStillImageAsynchronouslyFromConnection?
Я пытаюсь использовать новый AVFoundation framework
для снятия неподвижных изображений с iPhone.
При нажатии кнопки этот метод вызывается. Я слышу звук затвора, но я не вижу выход журнала. Если я вызову этот метод несколько раз, просмотр камеры заморозится.
Есть ли там учебник, как использовать captureStillImageAsynchronouslyFromConnection
?
[[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:
[[self stillImageOutput].connections objectAtIndex:0]
completionHandler:^(CMSampleBufferRef imageDataSampleBuffer,
NSError *error) {
NSLog(@"inside");
}];
- (void)initCapture {
AVCaptureDeviceInput *captureInput = [AVCaptureDeviceInput
deviceInputWithDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]
error:nil];
AVCaptureVideoDataOutput *captureOutput = [[AVCaptureVideoDataOutput alloc] init];
captureOutput.alwaysDiscardsLateVideoFrames = YES;
dispatch_queue_t queue;
queue = dispatch_queue_create("cameraQueue", NULL);
[captureOutput setSampleBufferDelegate:self queue:queue];
dispatch_release(queue);
NSString* key = (NSString*)kCVPixelBufferPixelFormatTypeKey;
NSNumber* value = [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA];
NSDictionary* videoSettings = [NSDictionary dictionaryWithObject:value forKey:key];
[captureOutput setVideoSettings:videoSettings];
self.captureSession = [[AVCaptureSession alloc] init];
self.captureSession.sessionPreset = AVCaptureSessionPresetLow;
[self.captureSession addInput:captureInput];
[self.captureSession addOutput:captureOutput];
self.prevLayer = [AVCaptureVideoPreviewLayer layerWithSession: self.captureSession];
[self.prevLayer setOrientation:AVCaptureVideoOrientationLandscapeLeft];
self.prevLayer.frame = CGRectMake(0.0, 0.0, 480.0, 320.0);
self.prevLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
[self.view.layer addSublayer: self.prevLayer];
// Setup the default file outputs
AVCaptureStillImageOutput *_stillImageOutput = [[[AVCaptureStillImageOutput alloc] init] autorelease];
NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:
AVVideoCodecJPEG, AVVideoCodecKey,
nil];
[_stillImageOutput setOutputSettings:outputSettings];
[outputSettings release];
[self setStillImageOutput:_stillImageOutput];
if ([self.captureSession canAddOutput:stillImageOutput]) {
[self.captureSession addOutput:stillImageOutput];
}
[self.captureSession commitConfiguration];
[self.captureSession startRunning];
}
Ответы
Ответ 1
У нас была эта проблема, когда 4.0 все еще находилась в бета-версии. Я попробовал честную кучу вещей. Здесь:
- AVCaptureStillImageOutput и AVCaptureVideoDataOutput не кажутся хорошо играющими друг с другом. Если выход видеосигнала запущен, выход изображения никогда не будет завершен (пока вы не приостановите сеанс, поставив телефон в режим сна, а затем вы получите одно изображение).
- AVCaptureStillImageOutput работает только с AVCaptureSessionPresetPhoto; в противном случае вы эффективно получаете видеофрагменты, закодированные в формате JPEG. Возможно также использовать более качественные рамки BGRA (кстати, выход на выходе камеры выглядит как BGRA, он не имеет цветовой подвыборки 2vuy/420v).
- Видео (все, что не является фото) и предустановками фотографий кажется принципиально иным; вы никогда не получаете видеокадры, если сеанс находится в режиме фото (вы также не получаете ошибку). Возможно, они изменили это...
- Кажется, у вас нет двух сеансов захвата (один с предустановленным видео и видеовыходом, один с предустановкой фото и выходом изображения). Возможно, они исправили это.
- Вы можете остановить сеанс, изменить пресет на фотографию, начать сеанс, сделать снимок, а когда фото завершено, остановить, изменить предустановку назад и начать заново. Это занимает некоторое время, и уровень предварительного просмотра видеороликов выглядит ужасно (он корректирует уровни экспозиции). Это также иногда затормозило в бета-версии (после вызова -stopRunning, session.running по-прежнему был ДА).
- Возможно, вы сможете отключить AVCaptureConnection (он должен работать). Я помню эту тупик; они, возможно, исправили это.
В итоге я просто захватил видеокадры. Кнопка "сфотографировать" просто устанавливает флаг; в обратном вызове видеофрагмента, если флаг установлен, он возвращает видеокадр вместо UIImage *. Этого было достаточно для наших потребностей в обработке изображений; "делать снимок" существует во многом таким образом, что пользователь может получить отрицательный ответ (и возможность представить отчет об ошибке); мы фактически не хотим 2/3/5 мегапиксельных изображений, так как они берут возраст для обработки.
Если видеокадры не достаточно хороши (т.е. вы хотите захватить рамки видоискателя между захватами изображений высокого разрешения), я бы сначала увидел, исправлены ли они с использованием нескольких сеансов AVCapture, так как это единственный способ, которым вы можете установить как пресеты.
Вероятно, стоит записать ошибку. Я подал ошибку вокруг запуска 4.0 GM; Apple спросила меня о некотором примерном коде, но к тому времени я решил использовать обходное решение для видеокадра и выпустил релиз.
Кроме того, "низкий" пресет имеет очень низкое разрешение (и обеспечивает предварительный просмотр видео с низким разрешением, с низким разрешением). Я поеду на 640x480, если таковые имеются, отступив на Medium, если нет.
Ответ 2
После большого количества проб и ошибок я разработал, как это сделать.
Подсказка: официальные документы Apple - просто - неправильные. Код, который они вам дают, на самом деле не работает.
Я написал его здесь с пошаговыми инструкциями:
http://red-glasses.com/index.php/tutorials/ios4-take-photos-with-live-video-preview-using-avfoundation/
Множество кода в ссылке, но в целом:
-(void) viewDidAppear:(BOOL)animated
{
AVCaptureSession *session = [[AVCaptureSession alloc] init];
session.sessionPreset = AVCaptureSessionPresetMedium;
CALayer *viewLayer = self.vImagePreview.layer;
NSLog(@"viewLayer = %@", viewLayer);
AVCaptureVideoPreviewLayer *captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];
captureVideoPreviewLayer.frame = self.vImagePreview.bounds;
[self.vImagePreview.layer addSublayer:captureVideoPreviewLayer];
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *error = nil;
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (!input) {
// Handle the error appropriately.
NSLog(@"ERROR: trying to open camera: %@", error);
}
[session addInput:input];
stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys: AVVideoCodecJPEG, AVVideoCodecKey, nil];
[stillImageOutput setOutputSettings:outputSettings];
[session addOutput:stillImageOutput];
[session startRunning];
}
-(IBAction) captureNow
{
AVCaptureConnection *videoConnection = nil;
for (AVCaptureConnection *connection in stillImageOutput.connections)
{
for (AVCaptureInputPort *port in [connection inputPorts])
{
if ([[port mediaType] isEqual:AVMediaTypeVideo] )
{
videoConnection = connection;
break;
}
}
if (videoConnection) { break; }
}
NSLog(@"about to request a capture from: %@", stillImageOutput);
[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error)
{
CFDictionaryRef exifAttachments = CMGetAttachment( imageSampleBuffer, kCGImagePropertyExifDictionary, NULL);
if (exifAttachments)
{
// Do something with the attachments.
NSLog(@"attachements: %@", exifAttachments);
}
else
NSLog(@"no attachments");
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
UIImage *image = [[UIImage alloc] initWithData:imageData];
self.vImage.image = image;
}];
}
Ответ 3
Это была огромная помощь - я застрял в сорняках довольно долго, пытаясь следовать примеру AVCam.
Вот полный рабочий проект с моими комментариями, которые объясняют, что происходит.
Это иллюстрирует, как вы можете использовать менеджер захвата с несколькими выходами.
В этом примере есть два выхода.
Первый - это вывод неподвижного изображения в примере выше.
Второе обеспечивает обратный доступ к видео, выходящему из камеры.
Вы можете добавить дополнительный код, чтобы сделать что-то интересное с фреймами, если хотите.
В этом примере я просто обновляю счетчик кадров на экране изнутри обратного вызова делегата.
https://github.com/tdsltm/iphoneStubs/tree/master/VideoCamCaptureExample-RedGlassesBlog/VideoCamCaptureExample-RedGlassesBlog
Ответ 4
В Apple есть несколько заметок и пример кода:
Техническое Q & A QA1702: Как захватить видеокадры с камеры в виде изображений с помощью AV Foundation
Ответ 5
Вы должны использовать ответ Адам, но если вы используете Swift (как и большинство из вас, вероятно, сейчас), здесь находится Swift 1.2 порт его кода:
- Убедитесь, что вы
import ImageIO
- Добавить свойство
private var stillImageOutput: AVCaptureStillImageOutput!
- Выполнить
stillImageOutput
перед captureSession.startRunning()
:
Вот так:
stillImageOutput = AVCaptureStillImageOutput()
stillImageOutput.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
captureSession.addOutput(stillImageOutput)
Затем используйте этот код для захвата изображения:
private func captureImage() {
var videoConnection: AVCaptureConnection?
for connection in stillImageOutput.connections as! [AVCaptureConnection] {
for port in connection.inputPorts {
if port.mediaType == AVMediaTypeVideo {
videoConnection = connection
break
}
}
if videoConnection != nil {
break
}
}
print("about to request a capture from: \(stillImageOutput)")
stillImageOutput.captureStillImageAsynchronouslyFromConnection(videoConnection) { (imageSampleBuffer: CMSampleBuffer!, error: NSError!) -> Void in
let exifAttachments = CMGetAttachment(imageSampleBuffer, kCGImagePropertyExifDictionary, nil)
if let attachments = exifAttachments {
// Do something with the attachments
print("attachments: \(attachments)")
} else {
print("no attachments")
}
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageSampleBuffer)
let image = UIImage(data: imageData)
// Do something with the image
}
}
Все это предполагает, что у вас уже есть настройка AVCaptureSession
, и вам просто нужно взять с нее все еще, как и я.