2010-08-24 2 views
41

Итак, я выполнил инструкции Apple, чтобы захватить видеосеанс, используя AVCaptureSession: http://developer.apple.com/iphone/library/qa/qa2010/qa1702.html. Одна из проблем, с которыми я сталкиваюсь, заключается в том, что даже если ориентация устройства камеры/iPhone вертикальна (и AVCaptureVideoPreviewLayer показывает вертикальный поток камеры), выходное изображение, похоже, находится в ландшафтном режиме. Я проверил ширину и высоту imageBuffer внутри imageFromSampleBuffer: образца кода, и я получил 640px и 480px соответственно. Кто-нибудь знает, почему так?Почему AVCaptureSession выводит неверную ориентацию?

Спасибо!

ответ

38

Посмотрите на заголовок AVCaptureSession.h. Существует определение для перечисления, называемого AVCaptureVideoOrientation, которое определяет различные ориентации видео. В объекте AVCaptureConnection имеется свойство videoOrientation, которое является AVCaptureVideoOrientation. Вы должны установить это, чтобы изменить ориентацию видео. Вероятно, вы хотите AVCaptureVideoOrientationLandscapeRight или AVCaptureVideoOrientationLandscapeLeft.

Вы можете найти AVCaptureConnections для сеанса, посмотрев выходы для сеанса. Выходы имеют свойство соединений, которое представляет собой массив соединений для этого вывода.

+8

'свойство«videoOrientation»не найден на объект типа«AVCaptureSession *»' – cheeesus

+1

права ответ, должен быть принят. – Rivera

+10

@cheesus videoOrientation находится на AVCaptureConnection, не AVCaptureSession – GabCas

19

Я сделал простую однострочную модификацию для imageFromSampleBuffer, чтобы исправить проблему ориентации (см. Мой комментарий в коде под «I modified ...»). Надеюсь, это поможет кому-то, потому что я потратил слишком много времени на это.

// Create a UIImage from sample buffer data 
- (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer { 
    // Get a CMSampleBuffer's Core Video image buffer for the media data 
    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); 

    // Create a device-dependent RGB color space 
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 

    // Create a bitmap graphics context with the sample buffer data 
    CGContextRef context1 = CGBitmapContextCreate(baseAddress, width, height, 8, 
               bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); 

    // Create a Quartz image from the pixel data in the bitmap graphics context 
    CGImageRef quartzImage = CGBitmapContextCreateImage(context1); 
    // Unlock the pixel buffer 
    CVPixelBufferUnlockBaseAddress(imageBuffer,0); 

    // Free up the context and color space 
    CGContextRelease(context1); 
    CGColorSpaceRelease(colorSpace); 

    // Create an image object from the Quartz image 
    //I modified this line: [UIImage imageWithCGImage:quartzImage]; to the following to correct the orientation: 
    UIImage *image = [UIImage imageWithCGImage:quartzImage scale:1.0 orientation:UIImageOrientationRight]; 

    // Release the Quartz image 
    CGImageRelease(quartzImage); 

    return (image); 
} 
+1

Спасибо, мне это помогает. примечание метод взят из: http://stackoverflow.com/questions/8924299/ios-capturing-image-using-avframework –

+0

Вы «да человек! Я не мог понять это, так как я не использую AVCaptureConnection ... это было трюк. Благодаря! –

+0

@ReidBelton вы не можете использовать AVCaptureConnection, его автоматически создавайте, когда у вас есть вход и выход, вы просто должны получить его и изменить ориентацию. – Pochi

8

Например:

AVCaptureConnection *captureConnection = <a capture connection>; 
if ([captureConnection isVideoOrientationSupported]) { 
    captureConnection.videoOrientation = AVCaptureVideoOrientationPortrait; 
} 

по умолчанию, как представляется AVCaptureVideoOrientationLandscapeRight.

См. Также QA1744: Setting the orientation of video with AV Foundation.

+0

это работает для меня, я добавил его в myAVCaptureConnection обработчик. Он повернул изображение так, как ожидалось, но изображение зеркально отражено. Я вижу, что isMirrored устарел, поэтому неясно, как исправить материал зеркалирования. B/c новые методы зеркалирования не имеют документации по использованию. –

+0

@Jim: Поскольку «зеркальный» в 'AVCaptureVideoPreviewLayer' устарел, вы должны использовать свойство' VideoMirrored' 'AVCaptureConnection'. Он документирован, по крайней мере, в файлах заголовков, где 'mirrored' и его друзья также явно ссылаются на« videoMirrored »и соответствующие методы подключения. –

+0

похоже, что эта функция недоступна в iOS печально –

10

Вот правая последовательность:

AVCaptureVideoDataOutput *videoCaptureOutput = [[AVCaptureVideoDataOutput alloc] init]; 

if([self.captureSession canAddOutput:self.videoCaptureOutput]){ 
    [self.captureSession addOutput:self.videoCaptureOutput]; 
}else{ 
    NSLog(@"cantAddOutput"); 
} 

// set portrait orientation 
AVCaptureConnection *conn = [self.videoCaptureOutput connectionWithMediaType:AVMediaTypeVideo]; 
[conn setVideoOrientation:AVCaptureVideoOrientationPortrait]; 
+1

Это сработало для меня. Как показано на этом коде, важно добавить вывод в сеанс захвата перед изменением этих свойств. AVCaptureVideoOrientationLandscapeRight с videoMirrored = true дал мне тот же эффект, который вы получаете от использования стандартного приложения камеры с фронтальной камерой. – spfursich

+0

Разве это не меняет ориентацию локального объекта 'conn', а не того, что принадлежит' self.videoCaptureOutput'? – mylogon

2

Ориентация проблема с передней камерой, поэтому проверьте тип устройства и создавать новый образ, это, безусловно, решить проблему ориентации:

-(void)capture:(void(^)(UIImage *))handler{ 

AVCaptureConnection *videoConnection = nil; 
for (AVCaptureConnection *connection in self.stillImageOutput.connections) 
{ 
    for (AVCaptureInputPort *port in [connection inputPorts]) 
    { 
     if ([[port mediaType] isEqual:AVMediaTypeVideo]) 
     { 
      videoConnection = connection; 
      break; 
     } 
    } 
    if (videoConnection) { break; } 
} 

[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) { 

    if (imageSampleBuffer != NULL) { 
     NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; 
     **UIImage *capturedImage = [UIImage imageWithData:imageData]; 
     if (self.captureDevice == [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo][1]) { 
      capturedImage = [[UIImage alloc] initWithCGImage:capturedImage.CGImage scale:1.0f orientation:UIImageOrientationLeftMirrored]; 
     }** 

     handler(capturedImage); 
    } 
}]; 
} 
15

Y «все затрудняют это.

В DidOutputSampleBuffer просто измените ориентацию, прежде чем захватить изображение. Это моно, но у вас есть

public class OutputRecorder : AVCaptureVideoDataOutputSampleBufferDelegate {  
     public override void DidOutputSampleBuffer (AVCaptureOutput captureOutput, CMSampleBuffer sampleBuffer, AVCaptureConnection connection) 
     { 
      try { 
       connection.videoOrientation = AVCaptureVideoOrientation.LandscapeLeft; 

в ObjC это этот метод

- (void) captureOutput: (AVCaptureOutput *) captureOutput 
    didOutputSampleBuffer: (CMSampleBufferRef) sampleBuffer 
     fromConnection: (AVCaptureConnection *) connection 
+0

Пробовал это, и я получаю «AVCaptureConnection» не имеет члена с именем «VideoOrientation» – drewish

+0

Это была проблема с капитализацией. Должна была «videoOrientation» так отредактировал ответ. – drewish

+0

ой, я пропустил ту часть, где вы сказали, что это моно ... я предположил, что это было быстро ... – drewish

5

Если AVCaptureVideoPreviewLayer ориентация правильно, вы можете просто установить ориентацию, прежде чем захватить изображение.

AVCaptureStillImageOutput *stillImageOutput; 
AVCaptureVideoPreviewLayer *previewLayer; 
NSData *capturedImageData; 

AVCaptureConnection *videoConnection = [stillImageOutput connectionWithMediaType:AVMediaTypeVideo]; 
if ([videoConnection isVideoOrientationSupported]) { 
    [videoConnection setVideoOrientation:previewLayer.connection.videoOrientation]; 
} 
[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) { 
    CFDictionaryRef exifAttachments = 
      CMGetAttachment(imageSampleBuffer, kCGImagePropertyExifDictionary, NULL); 
    if (exifAttachments) { 
     // Do something with the attachments. 
    } 
    // TODO need to manually add GPS data to the image captured 
    capturedImageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; 
    UIImage *image = [UIImage imageWithData:capturedImageData]; 
}]; 

Кроме того, важно отметить, что UIImageOrientation и AVCaptureVideoOrientation различны. UIImageOrientationUp относится к ландшафтному режиму с регуляторами громкости вниз к земле (не, если вы думаете об использовании регуляторов громкости в качестве кнопки спуска затвора).

Таким образом, ориентация на портрет с кнопкой питания, указывающей на небо (AVCaptureVideoOrientationPortrait), на самом деле UIImageOrientationLeft.

+0

Это должно быть отмечено как правильный ответ. Вы также можете использовать: connection.videoMirrored = true – user3344977

5

Для тех людей, которые должны работать с CIImage и ориентацией из буфера неправильно я использовал эту поправку.

Как легко, как это. BTW цифры 3,1,6,8 находятся здесь https://developer.apple.com/reference/imageio/kcgimagepropertyorientation

И не спрашивайте меня, почему 3,1,6,8 является правильной комбинацией. Я использовал метод грубой силы, чтобы найти его. Если вы знаете, почему дайте объяснение в комментарии, пожалуйста ...

- (void)captureOutput:(AVCaptureOutput *)captureOutput 
    didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer 
      fromConnection:(AVCaptureConnection *)connection 
{ 

    // common way to get CIImage 

    CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 

    CFDictionaryRef attachments = CMCopyDictionaryOfAttachments(kCFAllocatorDefault, sampleBuffer, kCMAttachmentMode_ShouldPropagate); 

    CIImage *ciImage = [[CIImage alloc] initWithCVPixelBuffer:pixelBuffer 
                 options:(__bridge NSDictionary *)attachments]; 

    if (attachments) { 
     CFRelease(attachments); 
    } 

    // fixing the orientation of the CIImage 

    UIInterfaceOrientation curOrientation = [[UIApplication sharedApplication] statusBarOrientation]; 

    if (curOrientation == UIInterfaceOrientationLandscapeLeft){ 
     ciImage = [ciImage imageByApplyingOrientation:3]; 
    } else if (curOrientation == UIInterfaceOrientationLandscapeRight){ 
     ciImage = [ciImage imageByApplyingOrientation:1]; 
    } else if (curOrientation == UIInterfaceOrientationPortrait){ 
     ciImage = [ciImage imageByApplyingOrientation:6]; 
    } else if (curOrientation == UIInterfaceOrientationPortraitUpsideDown){ 
     ciImage = [ciImage imageByApplyingOrientation:8]; 
    } 



    // .... 

} 
+0

Константы, которые вы ищете, определены в kCGImagePropertyorientation ... они получены из спецификации TIFF/EXIF: https://www.awaresystems.be/imaging/tiff/tifftags /orientation.html – ibisum

0

Просто попробуйте это;)

private func startLiveVideo() { 

    let captureSession = AVCaptureSession() 
    captureSession.sessionPreset = .photo 
    let captureDevice = AVCaptureDevice.default(for: .video) 

    let input = try! AVCaptureDeviceInput(device: captureDevice!) 
    let output = AVCaptureVideoDataOutput() 
    captureSession.addInput(input) 
    captureSession.addOutput(output) 

    output.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoQueue")) 
    output.connection(with: .video)?.videoOrientation = .portrait 

    let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) 
    previewLayer.frame = view.bounds 
    view.layer.addSublayer(previewLayer) 

    captureSession.startRunning() 
} 
+0

Можете ли вы объяснить, почему это сработает? Потому что, когда я «просто попробую это», он не –

Смежные вопросы