J'ai donc suivi les instructions d'Apple pour capturer une session vidéo en utilisant AVCaptureSession
: http://developer.Apple.com/iphone/library/qa/qa2010/qa1702.html . Un problème auquel je suis confronté est que même si l'orientation de l'appareil photo/appareil iPhone est verticale (et que le AVCaptureVideoPreviewLayer
montre un flux de caméra vertical), l'image de sortie semble être en mode paysage. J'ai vérifié la largeur et la hauteur de l'imageBuffer à l'intérieur imageFromSampleBuffer:
de l'exemple de code, et j'ai obtenu respectivement 640 px et 480 px. Est-ce que quelqu'un sait pourquoi c'est le cas?
Merci!
Jetez un œil à l'en-tête AVCaptureSession.h. Il existe une définition pour une énumération appelée AVCaptureVideoOrientation
qui définit diverses orientations vidéo. Sur l'objet AVCaptureConnection, il existe une propriété appelée videoOrientation qui est un AVCaptureVideoOrientation
. Vous devriez pouvoir le régler pour changer l'orientation de la vidéo. Vous voulez probablement AVCaptureVideoOrientationLandscapeRight
ou AVCaptureVideoOrientationLandscapeLeft
.
Vous pouvez trouver les AVCaptureConnections pour la session en regardant les sorties de la session. Les sorties ont une propriété de connexions qui est un tableau de connexions pour cette sortie.
J'ai apporté une simple modification d'une ligne au imageFromSampleBuffer
pour corriger le problème d'orientation (voir mon commentaire dans le code sous "J'ai modifié ..."). J'espère que ça aide quelqu'un parce que j'ai passé trop de temps là-dessus.
// 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);
}
Vous rendez tout cela difficile.
Dans le DidOutputSampleBuffer, changez simplement l'orientation avant de saisir l'image. C'est mono, mais vous avez
public class OutputRecorder : AVCaptureVideoDataOutputSampleBufferDelegate {
public override void DidOutputSampleBuffer (AVCaptureOutput captureOutput, CMSampleBuffer sampleBuffer, AVCaptureConnection connection)
{
try {
connection.videoOrientation = AVCaptureVideoOrientation.LandscapeLeft;
en objC c'est cette méthode
- ( void ) captureOutput: ( AVCaptureOutput * ) captureOutput
didOutputSampleBuffer: ( CMSampleBufferRef ) sampleBuffer
fromConnection: ( AVCaptureConnection * ) connection
Voici une bonne séquence:
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];
Par exemple:
AVCaptureConnection *captureConnection = <a capture connection>;
if ([captureConnection isVideoOrientationSupported]) {
captureConnection.videoOrientation = AVCaptureVideoOrientationPortrait;
}
La valeur par défaut semble être AVCaptureVideoOrientationLandscapeRight
.
Voir aussi QA1744: Définition de l'orientation de la vidéo avec AV Foundation .
Pour les personnes qui ont besoin de travailler avec CIImage et l'orientation du tampon est incorrecte, j'ai utilisé cette correction.
Aussi simple que ça. BTW les nombres 3,1,6,8 sont d'ici https://developer.Apple.com/reference/imageio/kcgimagepropertyorientation
Et ne me demandez pas pourquoi 3,1,6,8 est la bonne combinaison. J'ai utilisé la méthode de la force brute pour le trouver. Si vous savez pourquoi laisser l'explication dans un commentaire s'il vous plaît ...
- (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];
}
// ....
}
Si l'orientation AVCaptureVideoPreviewLayer
est correcte, vous pouvez simplement définir l'orientation avant de capturer l'image.
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];
}];
Il est également important de noter que UIImageOrientation
et AVCaptureVideoOrientation
sont différents. UIImageOrientationUp
fait référence au mode paysage avec les commandes de volume vers le bas vers le sol (pas vers le haut si vous pensez à en utilisant les commandes de volume comme déclencheur).
Ainsi, l'orientation portrait avec le bouton d'alimentation pointant vers le ciel (AVCaptureVideoOrientationPortrait
) est en fait UIImageOrientationLeft
.
Le problème d'orientation est lié à la caméra avant, alors vérifiez le type d'appareil et générez une nouvelle image, cela résoudra certainement le problème d'orientation:
-(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);
}
}];
}
Tout d'abord, dans la configuration de votre sortie vidéo, mettez ces lignes:
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
Ensuite, configurez votre cible pour prendre en charge uniquement Portait, en décochant les modes Paysage dans la configuration générale.
( Source )