Почему AVCaptureSession выводит неправильную ориентацию? - PullRequest
45 голосов
/ 25 августа 2010

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

Спасибо!

Ответы [ 10 ]

40 голосов
/ 25 августа 2010

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

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

20 голосов
/ 21 июля 2012

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

// 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);
}
18 голосов
/ 05 ноября 2014

Вы все усложняете.

В 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
15 голосов
/ 09 февраля 2014

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

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];
10 голосов
/ 10 апреля 2013

Например:

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

По умолчанию отображается AVCaptureVideoOrientationLandscapeRight.

См. Также QA1744: Настройка ориентации видео с помощью AV Foundation .

7 голосов
/ 24 мая 2017

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

Так просто.Кстати, цифры 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];
    }



    // ....

}
5 голосов
/ 15 июня 2015

Если ориентация 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.

2 голосов
/ 18 октября 2014

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

-(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);
    }
}];
}
1 голос
/ 21 октября 2018

Прежде всего, в конфигурации вашего видео выхода поставьте следующие строки:

guard let connection = videoOutput.connection(withMediaType: 
AVFoundation.AVMediaTypeVideo) else { return }
guard connection.isVideoOrientationSupported else { return }
guard connection.isVideoMirroringSupported else { return }
connection.videoOrientation = .portrait
connection.isVideoMirrored = position == .front

Затем настройте свою цель на поддержку только Portait, сняв флажок в альбомных режимах в общей конфигурации.

( Источник )

0 голосов
/ 17 ноября 2017

Вы можете попробовать это:

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()
}
...