美文网首页MacOS开发
ios/macOS 视频采集-方向(5)

ios/macOS 视频采集-方向(5)

作者: yxibng | 来源:发表于2021-04-08 10:49 被阅读0次

    参考demo AppleVideoCapturer

    采集视频的方向

    iOS摄像头采集的视频方向带有一个旋转角度。一般会对采集的视频方向进行修正。

    1. 对采集后的数据进行旋转,使其处于正确的方向
    2. 通过修改对应connection. videoOrientation的方向,使其和UIDeviceOrientation方向一致。参考AVCam: Building a Camera App
    3. 通过修改对应connection. videoOrientation的方向,使其和statusBarOrientation方向一致,参考声网的实现

    关于 videoOrientation

    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.

    总结一下就是:

    1. 检查connection 是否支持旋转, supportsVideoOrientation == YES
    2. 设置videoOrientation不一定会导致video buffers 发生物理旋转
      • AVCaptureMovieFileOutput 通过 QuickTime track matrix 来修正connection 的方向
      • AVCaptureStillImageOutput 通过Exif tags来处理connection的方向
    3. 对于AVCaptureVideoDataOutput,可以通过设置 connection 的方向,在回调里面得到旋转后的video buffer
    4. 对于AVCaptureVideoDataOutput,可以通过设置connection 的 mirror 属性,得到镜像的 video buffer
    5. 物理旋转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;
            }
        });
    }
    

    相关文章

      网友评论

        本文标题:ios/macOS 视频采集-方向(5)

        本文链接:https://www.haomeiwen.com/subject/gqbmkltx.html