参考demo AppleVideoCapturer
采集视频的方向
iOS摄像头采集的视频方向带有一个旋转角度。一般会对采集的视频方向进行修正。
- 对采集后的数据进行旋转,使其处于正确的方向
- 通过修改对应
connection. videoOrientation
的方向,使其和UIDeviceOrientation
方向一致。参考AVCam: Building a Camera App - 通过修改对应
connection. videoOrientation
的方向,使其和statusBarOrientation
方向一致,参考声网的实现
This property is only applicable to connections involving video.
If the value of supportsVideoOrientation is YES, you can set videoOrientation to rotate the video buffers consumed by the connection’s output. Setting videoOrientation doesn’t necessarily result in a physical rotation of video buffers. For example, a video connection to an AVCaptureMovieFileOutput object handles orientation using a QuickTime track matrix. A video connection to an AVCaptureStillImageOutput object handles orientation using Exif tags.
AVCaptureVideoDataOutput clients may receive physically rotated pixel buffers in their captureOutput:didOutputSampleBuffer:fromConnection: delegate callback. The AVCaptureVideoDataOutput hardware accelerates the rotation operation and supports all four AVCaptureVideoOrientation modes. A client sets videoOrientation or videoMirrored on the video data output’s video AVCaptureConnection to request physical buffer rotation.
Important
Physically rotating buffers comes with a performance cost, so only request rotation when necessary. If you want to write rotated video to a movie file using AVAssetWriter, set the transform property on the AVAssetWriterInput instead.
总结一下就是:
- 检查connection 是否支持旋转,
supportsVideoOrientation == YES
- 设置videoOrientation不一定会导致video buffers 发生物理旋转
- AVCaptureMovieFileOutput 通过
QuickTime track matrix
来修正connection 的方向 - AVCaptureStillImageOutput 通过
Exif tags
来处理connection的方向
- AVCaptureMovieFileOutput 通过
- 对于AVCaptureVideoDataOutput,可以通过设置 connection 的方向,在回调里面得到旋转后的video buffer
- 对于AVCaptureVideoDataOutput,可以通过设置connection 的 mirror 属性,得到镜像的 video buffer
- 物理旋转video buffer 会有性能损耗,所以需要的时候再使用。如果是通过AVAssetWriter写文件的时候,可以通过设置transform属性达到相同的目的。
预览的方向
Note</br>
If your app supports multiple interface orientations, use the preview layer’s connection to the capture session to set a videoOrientation matching that of your UI.
如果你的app支持多个旋转方向,可以通过修改 AVCaptureVideoPreviewLayer. connection. videoOrientation
来决定预览的方向。
//preview
@class AVCaptureSession;
@interface AVCamPreviewView : UIView
@property (nonatomic, readonly) AVCaptureVideoPreviewLayer *videoPreviewLayer;
@property (nonatomic) AVCaptureSession *session;
@end
@implementation AVCamPreviewView
+ (Class)layerClass
{
return [AVCaptureVideoPreviewLayer class];
}
- (AVCaptureVideoPreviewLayer*) videoPreviewLayer
{
return (AVCaptureVideoPreviewLayer *)self.layer;
}
- (AVCaptureSession*) session
{
return self.videoPreviewLayer.session;
}
- (void)setSession:(AVCaptureSession*) session
{
self.videoPreviewLayer.session = session;
}
@end
//view controller, handle orientation change
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAll;
}
- (void) viewWillTransitionToSize:(CGSize)size
withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if (UIDeviceOrientationIsPortrait(deviceOrientation) || UIDeviceOrientationIsLandscape(deviceOrientation)) {
self.previewView.videoPreviewLayer.connection.videoOrientation = (AVCaptureVideoOrientation)deviceOrientation;
}
}
视频方向
- (AVCaptureVideoOrientation)videoOrientation
{
#if TARGET_OS_IOS
UIInterfaceOrientation statusBarOrientation = UIApplication.sharedApplication.statusBarOrientation;
if (statusBarOrientation == UIInterfaceOrientationUnknown) {
return AVCaptureVideoOrientationPortrait;
}
return (AVCaptureVideoOrientation)statusBarOrientation;
#endif
return AVCaptureVideoOrientationPortrait;
}
- (AVCaptureDevice *)videoCaptureDeviceWithPosition:(AVCaptureDevicePosition)position
{
AVCaptureDevice *videoDevice;
#if TARGET_OS_IOS
if (@available(iOS 11.1, *)) {
NSArray<AVCaptureDeviceType> *deviceTypes = @[ AVCaptureDeviceTypeBuiltInWideAngleCamera,
AVCaptureDeviceTypeBuiltInDualCamera,
AVCaptureDeviceTypeBuiltInTrueDepthCamera ];
AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:deviceTypes
mediaType:AVMediaTypeVideo
position:position];
for (AVCaptureDevice *device in session.devices) {
if (device.position == position) {
videoDevice = device;
break;
}
}
} else if (@available(iOS 10.0, *)) {
videoDevice = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera mediaType:AVMediaTypeVideo position:position];
} else {
NSArray *cameras = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *device in cameras) {
if (device.position == position) {
videoDevice = device;
break;
}
}
}
#elif TARGET_OS_OSX
videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
#endif
return videoDevice;
}
//config data output orientation
- (void)configSession {
dispatch_async(self.sessionQueue, ^{
#if TARGET_OS_IOS
self.currentDevice = [self videoCaptureDeviceWithPosition:AVCaptureDevicePositionFront];
//获取视频方向,需要在主线程获取
dispatch_async(dispatch_get_main_queue(), ^{
self.captureVideoOrientation = [self videoOrientation];
dispatch_semaphore_signal(self.semaphore);
});
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
#elif TARGET_OS_OSX
self.currentDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
self.captureVideoOrientation = AVCaptureVideoOrientationPortrait;
#endif
[self.session beginConfiguration];
//添加input
NSError *error = nil;
AVCaptureDeviceInput *videoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:self.currentDevice error:&error];
if (!videoDeviceInput) {
self.setupResult = AVCamSetupResultSessionConfigurationFailed;
[self.session commitConfiguration];
return;
}
NSArray *inputs = self.session.inputs;
if (inputs.count > 0) {
//remove old inputs
for (AVCaptureDeviceInput *input in inputs) {
[self.session removeInput:input];
}
}
if ([self.session canAddInput:videoDeviceInput]) {
[self.session addInput:videoDeviceInput];
self.videoInput = videoDeviceInput;
} else {
self.setupResult = AVCamSetupResultSessionConfigurationFailed;
[self.session commitConfiguration];
return;
}
//添加output
AVCaptureVideoDataOutput *output = [[AVCaptureVideoDataOutput alloc] init];
#if TARGET_OS_OSX
//https://stackoverflow.com/questions/15608931/mac-osx-avfoundation-video-capture
NSDictionary *settings = @{(NSString *)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange),
(NSString *)kCVPixelBufferWidthKey : @(self.videoConfig.dimension.width),
(NSString *)kCVPixelBufferHeightKey : @(self.videoConfig.dimension.height)
};
#else
NSDictionary *settings = @{(NSString *)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) };
#endif
output.videoSettings = settings;
if ([self.session canAddOutput:output]) {
[self.session addOutput:output];
[output setSampleBufferDelegate:self queue:self.sampleBufferCallbackQueue];
self.videoOutput = output;
} else {
self.setupResult = AVCamSetupResultSessionConfigurationFailed;
[self.session commitConfiguration];
return;
}
//设置视频的方向
AVCaptureConnection *connect = [self.videoOutput connectionWithMediaType:AVMediaTypeVideo];
connect.videoOrientation = self.captureVideoOrientation;
self.setupResult = AVCamSetupResultSuccess;
[self.session commitConfiguration];
});
}
响应屏幕旋转
//监听UIApplicationDidChangeStatusBarOrientationNotification
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarOrientationDidChange:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
//更新视频方向
- (void)statusBarOrientationDidChange:(NSNotification *)notification
{
AVCaptureConnection *connection = [self.videoOutput connectionWithMediaType:AVMediaTypeVideo];
if ([NSThread isMainThread]) {
AVCaptureVideoOrientation orientation = [self videoOrientation];
self.captureVideoOrientation = orientation;
if (connection.videoOrientation != orientation) {
connection.videoOrientation = orientation;
}
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
AVCaptureVideoOrientation orientation = [self videoOrientation];
self.captureVideoOrientation = orientation;
if (connection.videoOrientation != orientation) {
connection.videoOrientation = orientation;
}
});
}
网友评论