Video capture and the settings and parameters ios camera operation

Outline

In broadcast applications, video capture are generally used AVFoundation framework, because we can use it to customize acquisition parameters of the video; also do switch the phone camera, take pictures, open the flashlight and some other camera operations; of course, the more important point that we can get to the original video data used for coding operations. This article describes our content as follows:

  • Introduction and video capture relevant key class
  • Describes the procedure for video capture
  • It describes how to change the parameters of the video capture, such as: resolution, frame rate, & zoom out of the preview layer, exposure and the like is provided.
  • Details of the camera operation, for example: camera, lens before and after switching, opening & closing operation of the flashlight and the like.

Code:

  • github
  • Welcome to fork & star

Video capture of key classes

AVCaptureDevice

It represents hardware devices, we can get the phone hardware camera, sound sensors from this class. When we need to change some properties of the hardware devices (for example: change the flash mode, the camera focus changes, etc.) must be called before changing the device properties lockForConfigurationfor the device lock, change the call after completion unlockForConfigurationmethod to unlock the device.

AVCaptureDeviceInput

The input device management object, may create corresponding AVCaptureDeviceInput AVCaptureDevice created object will be added to the management AVCaptureSession. It represents an input device that is configured hardware ports, there is the usual input device (microphone, camera, etc.).

AVCaptureOutput

Represents the output data, the output may be a picture (AVCaptureStillImageOutput) or video (AVCaptureMovieFileOutput)

AVCaptureSession

Media capture session, responsible for capturing audio and video data output to the output device. AVCaptureSession can have a plurality of input or output. It is the bridge connecting the AVCaptureInput and AVCaptureOutput that coordinates to transfer data between input output. It uses startRunning and stopRunning two ways to open and end the session.

Each session is called a session, i.e. if the need to change some of the configuration session (eg: switching the camera) the application is running, the open configuration need at this time, and then submit the configuration after the configuration.

AVCaptureConnection

AVCaptureConnection represents a connection between an AVCaptureInputPort or ports, and an AVCaptureOutput or AVCaptureVideoPreviewLayer present in an AVCaptureSession. That it is a connection that is between inputPort and output current preview image or the layers and the current session.

AVCaptureVideoPreviewPlayer

It is the image preview layer. How our photos and videos are displayed on the phone it? That is, on the object by adding to the layer of the UIView.

Step video capture

The following code is a video capture, a frame rate 30FPS, resolution is 1920*1080.

#import "MiVideoCollectVC.h"
#import <AVFoundation/AVFoundation.h>

@interface MiVideoCollectVC ()<AVCaptureVideoDataOutputSampleBufferDelegate>
@property (nonatomic,strong) AVCaptureVideoDataOutput *video_output;
@property (nonatomic,strong) AVCaptureSession  *m_session;

@property (weak, nonatomic) IBOutlet UIView *m_displayView;
@end

@implementation MiVideoCollectVC

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
     [self startCaptureSession];
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self startPreview];
}
- (IBAction)onpressedBtnDismiss:(id)sender {
    [self dismissViewControllerAnimated:YES completion:^{
        [self stopPreview];
    }];
}

- (void)startCaptureSession
{
    NSError *error = nil;
    AVCaptureSession *session = [[AVCaptureSession alloc] init];
    if ([session canSetSessionPreset:AVCaptureSessionPreset1920x1080]) {
        session.sessionPreset = AVCaptureSessionPreset1920x1080;
    }else{
        session.sessionPreset = AVCaptureSessionPresetHigh;
    }
    
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
    if (error || !input) {
        NSLog(@"get input device error...");
        return;
    }
    [session addInput:input];
    
    _video_output = [[AVCaptureVideoDataOutput alloc] init];
    [session addOutput:_video_output];
    
    // Specify the pixel format
    _video_output.videoSettings = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange]
                                                              forKey:(id)kCVPixelBufferPixelFormatTypeKey];
    _video_output.alwaysDiscardsLateVideoFrames = NO;
    dispatch_queue_t video_queue = dispatch_queue_create("MIVideoQueue", NULL);
    [_video_output setSampleBufferDelegate:self queue:video_queue];
    
    CMTime frameDuration = CMTimeMake(1, 30);
    BOOL frameRateSupported = NO;
    
    for (AVFrameRateRange *range in [device.activeFormat videoSupportedFrameRateRanges]) {
        if (CMTIME_COMPARE_INLINE(frameDuration, >=, range.minFrameDuration) &&
            CMTIME_COMPARE_INLINE(frameDuration, <=, range.maxFrameDuration)) {
            frameRateSupported = YES;
        }
    }
    
    if (frameRateSupported && [device lockForConfiguration:&error]) {
        [device setActiveVideoMaxFrameDuration:frameDuration];
        [device setActiveVideoMinFrameDuration:frameDuration];
        [device unlockForConfiguration];
    }
    
    [self adjustVideoStabilization];
    _m_session = session;
    
    
    CALayer *previewViewLayer = [self.m_displayView layer];
    previewViewLayer.backgroundColor = [[UIColor blackColor] CGColor];
    
    AVCaptureVideoPreviewLayer *newPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_m_session];
    
    [newPreviewLayer setFrame:[UIApplication sharedApplication].keyWindow.bounds];
    
    [newPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
    //    [previewViewLayer insertSublayer:newPreviewLayer atIndex:2];
    [previewViewLayer insertSublayer:newPreviewLayer atIndex:0];
}

- (void)adjustVideoStabilization
{
    NSArray *devices = [AVCaptureDevice devices];
    for (AVCaptureDevice *device in devices) {
        if ([device hasMediaType:AVMediaTypeVideo]) {
            if ([device.activeFormat isVideoStabilizationModeSupported:AVCaptureVideoStabilizationModeAuto]) {
                for (AVCaptureConnection *connection in _video_output.connections) {
                    for (AVCaptureInputPort *port in [connection inputPorts]) {
                        if ([[port mediaType] isEqual:AVMediaTypeVideo]) {
                            if (connection.supportsVideoStabilization) {
                                connection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeStandard;
                                NSLog(@"now videoStabilizationMode = %ld",(long)connection.activeVideoStabilizationMode);
                            }else{
                                NSLog(@"connection does not support video stablization");
                            }
                        }
                    }
                }
            }else{
                NSLog(@"device does not support video stablization");
            }
        }
    }
}

- (void)startPreview
{
    if (![_m_session isRunning]) {
        [_m_session startRunning];
    }
}

- (void)stopPreview
{
    if ([_m_session isRunning]) {
        [_m_session stopRunning];
    }
}

#pragma mark -AVCaptureVideoDataOutputSampleBufferDelegate
- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
    NSLog(@"%s",__func__);
}

// 有丢帧时,此代理方法会触发
- (void)captureOutput:(AVCaptureOutput *)output didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
    NSLog(@"MediaIOS: 丢帧...");
}

@end

Video capture specific steps are summarized as follows:

  1. AVCaptureSession create a first object and adds the object for the input and output devices, and input and output devices to AVCaptrueSession object.
  2. Set video resolution is AVCaptureSession
  3. Set the frame rate video capture
  4. Creating video preview layer and the layer is inserted into view in

Change video acquisition parameters - resolution and frame rate

Let us not describe how to change the video resolution and frame rate, we first look at how to monitor these parameters in terms of video capture, because we are only able to monitor changes in these parameters in order to know these parameters we set for success.

Surveillance video resolution:

We can AVCaptureSession object sessionPresetdirectly to, it is a string, then we direct printing setup is complete click on it.

Surveillance video frame rate:

The video frame rate represents the number of frames captured per second video, we can start a timer (1s refresh), to print the current real-time video capture frame rate is. The following is a collection of video frames within the computing 1s Code:

// 计算每秒钟采集视频多少帧
static int captureVideoFPS;
+ (void)calculatorCaptureFPS
{
    static int count = 0;
    static float lastTime = 0;
    CMClockRef hostClockRef = CMClockGetHostTimeClock();
    CMTime hostTime = CMClockGetTime(hostClockRef);
    float nowTime = CMTimeGetSeconds(hostTime);
    if(nowTime - lastTime >= 1)
    {
        captureVideoFPS = count;
        lastTime = nowTime;
        count = 0;
    }
    else
    {
        count ++;
    }
}

// 获取视频帧率
+ (int)getCaptureVideoFPS
{
    return captureVideoFPS;
}

Changing the resolution

/**
 *  Reset resolution
 *
 *  @param m_session     AVCaptureSession instance
 *  @param resolution
 */
+ (void)resetSessionPreset:(AVCaptureSession *)m_session resolution:(int)resolution
{
    [m_session beginConfiguration];
    switch (resolution) {
        case 1080:
            m_session.sessionPreset = [m_session canSetSessionPreset:AVCaptureSessionPreset1920x1080] ? AVCaptureSessionPreset1920x1080 : AVCaptureSessionPresetHigh;
            break;
        case 720:
            m_session.sessionPreset = [m_session canSetSessionPreset:AVCaptureSessionPreset1280x720] ? AVCaptureSessionPreset1280x720 : AVCaptureSessionPresetMedium;
            break;
        case 480:
            m_session.sessionPreset = [m_session canSetSessionPreset:AVCaptureSessionPreset640x480] ? AVCaptureSessionPreset640x480 : AVCaptureSessionPresetMedium;
            break;
        case 360:
            m_session.sessionPreset = AVCaptureSessionPresetMedium;
            break;
            
        default:
            break;
    }
    [m_session commitConfiguration];
}


Change video frame rate

+ (void)settingFrameRate:(int)frameRate
{
    AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    [captureDevice lockForConfiguration:NULL];
    @try {
        [captureDevice setActiveVideoMinFrameDuration:CMTimeMake(1, frameRate)];
        [captureDevice setActiveVideoMaxFrameDuration:CMTimeMake(1, frameRate)];
    } @catch (NSException *exception) {
        NSLog(@"MediaIOS, 设备不支持所设置的分辨率,错误信息:%@",exception.description);
    } @finally {
        
    }
    
    [captureDevice unlockForConfiguration];
}

Add a pinch gesture layer video preview

When potential with both hands, can zoom the video preview.

#define MiMaxZoomFactor 5.0f
#define MiPrinchVelocityDividerFactor 20.0f

+ (void)zoomCapture:(UIPinchGestureRecognizer *)recognizer
{
    
    AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    [videoDevice formats];
    if ([recognizer state] == UIGestureRecognizerStateChanged) {
        NSError *error = nil;
        if ([videoDevice lockForConfiguration:&error]) {
            CGFloat desiredZoomFactor = videoDevice.videoZoomFactor + atan2f(recognizer.velocity, MiPrinchVelocityDividerFactor);
            videoDevice.videoZoomFactor = desiredZoomFactor <= MiMaxZoomFactor ? MAX(1.0, MIN(desiredZoomFactor, videoDevice.activeFormat.videoMaxZoomFactor)) : MiMaxZoomFactor ;
            [videoDevice unlockForConfiguration];
        } else {
            NSLog(@"error: %@", error);
        }
    }
    
}

Camera operation

When video capture, possibly accompanied by switching around the lens, open & closed flash, camera and other operations.

Switching back and forth lens camera

Here after switching lens, I put the default setting for the resolution 720p, because for some devices may not support front camera 1080p, so I am here to set a fixed 720p, if in a real project, this value should be that you previously set value, if the front camera does not support the corresponding support policies.

// 切换摄像头
- (void)switchCamera
{
    [_m_session beginConfiguration];
    if ([[_video_input device] position] == AVCaptureDevicePositionBack) {
        NSArray * devices = [AVCaptureDevice devices];
        for(AVCaptureDevice * device in devices) {
            if([device hasMediaType:AVMediaTypeVideo]) {
                if([device position] == AVCaptureDevicePositionFront) {
                    [self rePreviewWithCameraType:MiCameraType_Front device:device];
                    break;
                }
            }
        }
    }else{
        NSArray * devices = [AVCaptureDevice devices];
        for(AVCaptureDevice * device in devices) {
            if([device hasMediaType:AVMediaTypeVideo]) {
                if([device position] == AVCaptureDevicePositionBack) {
                    [self rePreviewWithCameraType:MiCameraType_Back device:device];
                    break;
                }
            }
        }
    }
    [_m_session commitConfiguration];
}

- (void)rePreviewWithCameraType:(MiCameraType)cameraType device:(AVCaptureDevice *)device {
    NSError *error = nil;
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device
                                                                        error:&error];
    if (!input) return;
    
    [_m_session removeInput:_video_input];
    _m_session.sessionPreset = AVCaptureSessionPresetLow;
    if ([_m_session canAddInput:input])  {
        [_m_session addInput:input];
    }else {
        return;
    }
    _video_input      = input;
    _m_cameraType    = cameraType;
    NSString *preset = AVCaptureSessionPreset1280x720;
    if([device supportsAVCaptureSessionPreset:preset] && [_m_session canSetSessionPreset:preset]) {
        _m_session.sessionPreset = preset;
    }else {
        NSString *sesssionPreset = AVCaptureSessionPreset1280x720;
        if(![sesssionPreset isEqualToString:preset]) {
            _m_session.sessionPreset = sesssionPreset;
        }
    }
}

Open the flash turned off

// 打开关闭闪光灯
-(void)switchTorch
{
    [_m_session beginConfiguration];
    [[_video_input device] lockForConfiguration:NULL];
    
    self.m_torchMode = [_video_input device].torchMode == AVCaptureTorchModeOn ? AVCaptureTorchModeOff : AVCaptureTorchModeOn;
    
    if ([[_video_input device] isTorchModeSupported:_m_torchMode ]) {
        [_video_input device].torchMode = self.m_torchMode;
    }
    [[_video_input device] unlockForConfiguration];
    [_m_session commitConfiguration];
}

Take pictures and save to album

Specific programs are:

  • Setting a flag, the flag in the proxy method to monitor video capture, when the camera is triggered action to change the value of the flag
  • The method of video capture agent determines the value of the flag whether the need to take pictures of the filling, if the current frame is converted CMSampleBufferRefto UIImage, and then stored in the album UIImage

Note: The following code only when the specified pixel RGB format, in order to successfully saved a color photo to the album.

- (UIImage *)convertSameBufferToUIImage:(CMSampleBufferRef)sampleBuffer
{
    // 为媒体数据设置一个CMSampleBuffer的Core Video图像缓存对象
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    // 锁定pixel buffer的基地址
    CVPixelBufferLockBaseAddress(imageBuffer, 0);
    // 得到pixel buffer的基地址
    void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
    // 得到pixel buffer的行字节数
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
    // 得到pixel buffer的宽和高
    size_t width = CVPixelBufferGetWidth(imageBuffer);
    size_t height = CVPixelBufferGetHeight(imageBuffer);
    // 创建一个依赖于设备的RGB颜色空间
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    // 用抽样缓存的数据创建一个位图格式的图形上下文(graphics context)对象
    CGContextRef context = CGBitmapContextCreate(baseAddress, width, height, 8,bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
    // 根据这个位图context中的像素数据创建一个Quartz image对象
    CGImageRef quartzImage = CGBitmapContextCreateImage(context);
    // 解锁pixel buffer
    CVPixelBufferUnlockBaseAddress(imageBuffer,0);
    // 释放context和颜色空间
    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);
    // 用Quartz image创建一个UIImage对象image
    UIImage *image = [UIImage imageWithCGImage:quartzImage];
    // 释放Quartz image对象
    CGImageRelease(quartzImage);
    return (image);
}

+ (void)saveImageToSysphotos:(UIImage *)image
{
    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
    [library writeImageToSavedPhotosAlbum:image.CGImage metadata:nil completionBlock:^(NSURL *assetURL, NSError *error) {
        if (error) {
            NSLog(@"MediaIos, save photo to photos error, error info: %@",error.description);
        }else{
            NSLog(@"MediaIos, save photo success...");
        }
    }];
}

Setting the AF

// 设置为自动对焦
- (void)mifocus:(UITapGestureRecognizer *)sender
{
    CGPoint point = [sender locationInView:self.m_displayView];
    [self miAutoFocusWithPoint:point];
    NSLog(@"MediaIos, auto focus complete...");
}

- (void)miAutoFocusWithPoint:(CGPoint)touchPoint{
    AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    if ([captureDevice isFocusPointOfInterestSupported] && [captureDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
        NSError *error;
        if ([captureDevice lockForConfiguration:&error]) {
            // 设置曝光点
            [captureDevice setExposurePointOfInterest:touchPoint];
            [captureDevice setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
            
            // 设置对焦点
            [captureDevice setFocusPointOfInterest:touchPoint];
            [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
            [captureDevice unlockForConfiguration];
        }
    }
}

Exposure adjustment

// 曝光调节
- (void)changeExposure:(id)sender
{
    UISlider *slider = (UISlider *)sender;
    [self michangeExposure:slider.value];
    
}

- (void)michangeExposure:(CGFloat)value{
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    NSError *error;
    if ([device lockForConfiguration:&error]) {
        [device setExposureTargetBias:value completionHandler:nil];
        [device unlockForConfiguration];
    }
}

White balance settings

- (AVCaptureWhiteBalanceGains)recalcGains:(AVCaptureWhiteBalanceGains)gains
                                 minValue:(CGFloat)minValue
                                 maxValue:(CGFloat)maxValue
{
    AVCaptureWhiteBalanceGains tmpGains = gains;
    tmpGains.blueGain   = MAX(MIN(tmpGains.blueGain , maxValue), minValue);
    tmpGains.redGain    = MAX(MIN(tmpGains.redGain  , maxValue), minValue);
    tmpGains.greenGain  = MAX(MIN(tmpGains.greenGain, maxValue), minValue);
    return tmpGains;
}

-(void)setWhiteBlanceUseTemperature:(CGFloat)temperature{
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    if ([device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeLocked]) {
        [device lockForConfiguration:nil];
        AVCaptureWhiteBalanceGains currentGains = device.deviceWhiteBalanceGains;
        CGFloat currentTint = [device temperatureAndTintValuesForDeviceWhiteBalanceGains:currentGains].tint;
        AVCaptureWhiteBalanceTemperatureAndTintValues tempAndTintValues = {
            .temperature = temperature,
            .tint        = currentTint,
        };
        
        AVCaptureWhiteBalanceGains gains = [device deviceWhiteBalanceGainsForTemperatureAndTintValues:tempAndTintValues];
        CGFloat maxWhiteBalanceGain = device.maxWhiteBalanceGain;
        gains = [self recalcGains:gains minValue:1 maxValue:maxWhiteBalanceGain];
        
        [device setWhiteBalanceModeLockedWithDeviceWhiteBalanceGains:gains completionHandler:nil];
        [device unlockForConfiguration];
    }
}

// 黑白平衡调节
- (void)whiteBlanceChange:(id)sender
{
    UISlider *slider = (UISlider *)sender;
    [self setWhiteBlanceUseTemperature:slider.value];
}

Reproduced in: https: //www.jianshu.com/p/a0e2d7b3b8a7

Guess you like

Origin blog.csdn.net/weixin_33816946/article/details/91188674