Почему AVCaptureSession выводит неверную ориентацию?
Итак, я последовал инструкциям Apple для захвата видеосеанса с помощью AVCaptureSession
: http://developer.apple.com/iphone/library/qa/qa2010/qa1702.html. Одна из проблем, с которыми я сталкиваюсь, заключается в том, что даже если ориентация устройства камеры /iPhone вертикальна (и AVCaptureVideoPreviewLayer
показывает вертикальный поток камеры), выходное изображение, похоже, находится в ландшафтном режиме. Я проверил ширину и высоту imageBuffer внутри imageFromSampleBuffer:
образцового кода, и я получил 640px и 480px соответственно. Кто-нибудь знает, почему это так?
Спасибо!
Ответы
Ответ 1
Взгляните на заголовок AVCaptureSession.h. Существует определение для перечисления, называемого AVCaptureVideoOrientation
, которое определяет различные ориентации видео. В объекте AVCaptureConnection имеется свойство videoOrientation, которое является AVCaptureVideoOrientation
. Вы должны установить это, чтобы изменить ориентацию видео. Вероятно, вы хотите AVCaptureVideoOrientationLandscapeRight
или AVCaptureVideoOrientationLandscapeLeft
.
Вы можете найти AVCaptureConnections для сеанса, просмотрев результаты для сеанса. Выходы имеют свойство соединений, которое представляет собой массив соединений для этого вывода.
Ответ 2
Я сделал простую однострочную модификацию для imageFromSampleBuffer
, чтобы исправить проблему ориентации (см. мой комментарий в коде под "I modified..." ). Надеюсь, это поможет кому-то, потому что я потратил слишком много времени на это.
// Create a UIImage from sample buffer data
- (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer {
// Get a CMSampleBuffer 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);
}
Ответ 3
Я делаю это трудным.
В 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
Ответ 4
Вот правая последовательность:
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];
Ответ 5
Например:
AVCaptureConnection *captureConnection = <a capture connection>;
if ([captureConnection isVideoOrientationSupported]) {
captureConnection.videoOrientation = AVCaptureVideoOrientationPortrait;
}
По умолчанию используется AVCaptureVideoOrientationLandscapeRight
.
См. также QA1744: настройка ориентации видео с помощью AV Foundation.
Ответ 6
Если ориентация 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
.
Ответ 7
Для тех людей, которые должны работать с 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];
}
// ....
}
Ответ 8
Проблема с ориентацией с передней камерой, поэтому проверьте тип устройства и создайте новое изображение, оно определенно решит проблему ориентации:
-(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);
}
}];
}
Ответ 9
Просто попробуйте это;)
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()
}