美文网首页iOS学习笔记
AVFoundation拍照,合成gif动图。。。

AVFoundation拍照,合成gif动图。。。

作者: ChooseYourself | 来源:发表于2017-01-17 11:25 被阅读167次

    工作之余简单整理了下,关于使用系统框架 AVFoudation自定义拍照功能的一些代码,还有依赖MobileCoreServices框架制作gif的一些知识,话不多说,直接上代码,不对的地方,欢迎指正。




    1.PhotoCameraViewController.h

    #import <UIKit/UIKit.h>
    @interface PhotoCameraViewController : UIViewController 
    /*  * 图片回调  **/ 
    @property (nonatomic, copy) void(^CameraBlock)(NSURL *fileURL); 
    /*  * photoCount大于1可生成gif,最大为5,默认为1  **/
     @property (nonatomic, assign) NSInteger photoCount; 
    @end 
    

    2.PhotoCameraViewController.m 相关框架的导入和属性的声明

    #import "PhotoCameraViewController.h"
    #import <AVFoundation/AVFoundation.h>
    #import <ImageIO/ImageIO.h>
    #import <MobileCoreServices/MobileCoreServices.h>
    #define SOUNDID   1108 
    typedef void(^PropertyChangeBlock)(AVCaptureDevice *captureDevice); 
    @interface PhotoCameraViewController () 
    {     
         NSTimer *cameraTimer;
     } 
    @property (nonatomic, strong) AVCaptureSession *captureSession;//负责输入和输出设备之间的数据传递 
    @property (nonatomic, strong) AVCaptureDeviceInput *captureDeviceInput;//负责从AVCaptureDevice获得输入数据 
    @property (nonatomic, strong) AVCaptureStillImageOutput *captureStillImageOutput;//照片输出流 @property (nonatomic, strong) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;//相机拍摄预览图层 
    @property (nonatomic, strong) UIView      *viewContainer; //摄像头预览的背景视图
    @property (nonatomic, strong) UIView      *flashView; //闪光灯选择视图
    @property (nonatomic, strong) UIImageView *focusCursor; //聚焦视图 
    @property (nonatomic, strong) UIImageView *previewImage; //预览视图 
    @property (nonatomic, strong) UIButton    *backButton; //返回按钮 
    @property (nonatomic, strong) UIButton    *toggleButton; //切换前后摄像头 
    @property (nonatomic, strong) UIButton    *flashButton; //切换闪光灯模式 
    @property (nonatomic, strong) UIButton    *takeButton; // 拍照按钮 
    @property (nonatomic, strong) UIButton    *resetButton; //重拍 
    @property (nonatomic, strong) UIButton    *sureButton; //使用合成后的图片 
    @property (nonatomic, strong) NSMutableArray *imageArray; //存放图片的数组 
    @property (nonatomic, strong) NSURL *gifUrl; // 合成的图片的地址 
    @end 
    

    3.初始化视图界面, 这里用的代码布局,因为是自己写的小demo,没有导入第三方框架,也没有适配,求勿喷, 以4.7寸屏为示例。

    #pragma mark - layoutView 
    - (void)initSubViews {          
         self.backButton = [self creatButtonWithFrame:CGRectMake(0, 20, 40, 40) title:nil imageName:@"Back" action:@selector(handleBacK:)]; 
         self.toggleButton = [self creatButtonWithFrame:CGRectMake(kScreenWidth / 2 - 20, 20, 40, 40) title:nil imageName:@"CameraToggle" action:@selector(toggleBtnClick:)];
         self.flashButton = [self creatButtonWithFrame:CGRectMake(kScreenWidth - 40, 20, 40, 40) title:nil imageName:@"CameraFlashOn" action:@selector(flashClick:)];
         self.flashView = [[UIView alloc] initWithFrame:CGRectMake(CGRectGetMinX(self.flashButton.frame), CGRectGetMaxY(self.flashButton.frame), 40, 120)];
         [self.flashView addSubview:[self creatButtonWithFrame:CGRectMake(0, 0, 40, 40) title:nil imageName:@"CameraFlashOn" action:@selector(flashOnClick:)]];
         [self.flashView addSubview:[self creatButtonWithFrame:CGRectMake(0, 40, 40, 40) title:nil imageName:@"CameraFlashOff" action:@selector(flashOffClick:)]]; 
         [self.flashView addSubview:[self creatButtonWithFrame:CGRectMake(0, 80, 40, 40) title:nil imageName:@"CameraFlashAuto" action:@selector(flashAutoClick:)]];
         self.flashView.hidden = YES; 
         self.takeButton = [self creatButtonWithFrame:CGRectMake(kScreenWidth / 2 - 30, kScreenheight - 110, 60, 110) title:nil imageName:@"CameraTake" action:@selector(takeBtnClick:)]; 
         self.resetButton = [self creatButtonWithFrame:CGRectMake(20, kScreenheight - 45, 60, 25) title:@"Retake" imageName:nil action:@selector(resetBtnClick:)];
         self.resetButton.hidden = YES;          
         self.sureButton = [self creatButtonWithFrame:CGRectMake(kScreenWidth - 80, kScreenheight - 45, 60, 25) title:@"Use gif" imageName:nil action:@selector(useBtnClick:)];
         self.sureButton.hidden = YES;          
         self.viewContainer = [[UIView alloc] initWithFrame:CGRectMake(0, 64, kScreenWidth, kScreenheight - 174)];    
         [self.viewContainer addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]];          
          
    }
     #pragma mark - Creat button 
    - (UIButton *)creatButtonWithFrame:(CGRect)frame title:(NSString *)title imageName:(NSString *)imageName action:(SEL)action {
         UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
         button.frame = frame;
         if (imageName) [button setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];
         if (title) [button setTitle:title forState:UIControlStateNormal];
         [button addTarget:self action:action forControlEvents:UIControlEventTouchUpInside];
         return button; 
    } 
    

    4.初始化摄像头的一些相关配置

    #pragma mark - captureSession 
    - (void)configureSession {
        _captureSession = [[AVCaptureSession alloc] init];
        if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720])
        {
            [_captureSession setSessionPreset:AVCaptureSessionPreset1280x720];
        }
        AVCaptureDevice *captureDevice = [self getCameraDeviceWithPosition:AVCaptureDevicePositionBack];
        if (!captureDevice) {
            NSLog(@"取得后置摄像头时出现问题.");
            return;
        }
        NSError *error = nil;
        _captureDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:captureDevice error:&error];
        if (error) {
            NSLog(@"取得设备输入对象时出错,错误原因:%@",error.localizedDescription);
            return;
        }
        _captureStillImageOutput = [[AVCaptureStillImageOutput alloc] init];
        NSDictionary *outputSetting = @{AVVideoCodecKey : AVVideoCodecJPEG};
        [_captureStillImageOutput setOutputSettings:outputSetting];
        
        if ([_captureSession canAddInput:_captureDeviceInput])
        {
            [_captureSession addInput:_captureDeviceInput];
        }
        if ([_captureSession canAddOutput:_captureStillImageOutput])
        {
            [_captureSession addOutput:_captureStillImageOutput];
        }
        
        _captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];
      
        _captureVideoPreviewLayer.frame = self.viewContainer.layer.bounds;
        _captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
        [self.viewContainer.layer insertSublayer:_captureVideoPreviewLayer atIndex:0];
        [self.captureSession commitConfiguration];
        [self.captureSession startRunning];
    }
    
    

    5.设置聚焦视图和聚焦,曝光,闪光灯模式。。

    #pragma mark 聚焦框
    - (void)setFocusCursorWithPoint:(CGPoint)point
    {
        _focusCursor.center = point;
        _focusCursor.transform = CGAffineTransformMakeScale(1.5, 1.5);
        _focusCursor.alpha = 1.0;
        [UIView animateWithDuration:1.0 animations:^{
            self.focusCursor.transform = CGAffineTransformIdentity;
        } completion:^(BOOL finished) {
            self.focusCursor.alpha = 0;
            
        }];
    }
    #pragma mark 设置聚焦点
    - (void)focusWithMode:(AVCaptureFocusMode)focusMode exposureMode:(AVCaptureExposureMode)exposureMode atPoint:(CGPoint)point
    {
        [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
            if ([captureDevice isFocusModeSupported:focusMode])
            {
                [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
            }
            if ([captureDevice isFocusPointOfInterestSupported])
            {
                [captureDevice setFocusPointOfInterest:point];
            }
            if ([captureDevice isExposureModeSupported:exposureMode])
            {
                [captureDevice setExposureMode:AVCaptureExposureModeAutoExpose];
            }
            if ([captureDevice isExposurePointOfInterestSupported])
            {
                [captureDevice setExposurePointOfInterest:point];
            }
        }];
    }
    
    #pragma mark 设置聚焦模式
    - (void)setFocusMode:(AVCaptureFocusMode)focusMode
    {
        [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
            if ([captureDevice isFocusModeSupported:focusMode])
            {
                [captureDevice setFocusMode:focusMode];
            }
        }];
    }
    #pragma mark 设置曝光模式
    - (void)setExposureMode:(AVCaptureExposureMode)exposureMode
    {
        [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
            if ([captureDevice isExposureModeSupported:exposureMode])
            {
                [captureDevice setExposureMode:exposureMode];
            }
        }];
    }
    #pragma mark 设置闪光灯模式
    - (void)setFlashMode:(AVCaptureFlashMode)flashMode
    {
        switch (flashMode) {
            case AVCaptureFlashModeOff:
                [_flashButton setImage:[UIImage imageNamed:@"CameraFlashOff"] forState:UIControlStateNormal];
                break;
            case AVCaptureFlashModeOn:
                [_flashButton setImage:[UIImage imageNamed:@"CameraFlashOn"] forState:UIControlStateNormal];
                break;
            case AVCaptureFlashModeAuto:
                [_flashButton setImage:[UIImage imageNamed:@"CameraFlashAuto"] forState:UIControlStateNormal];
                break;
            default:
                break;
        }
        [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
            if ([captureDevice isFlashModeSupported:flashMode])
            {
                [captureDevice setFlashMode:flashMode];
            }
        }];
    }
    
    - (void)changeDeviceProperty:(PropertyChangeBlock)propertyChange
    {
        AVCaptureDevice *captureDevice = [_captureDeviceInput device];
        NSError *error = nil;
        // 注意改变设备属性前先加锁,调用完解锁
        if ([captureDevice lockForConfiguration:&error])
        {
            propertyChange(captureDevice);
            [captureDevice unlockForConfiguration];
        }
        else NSLog(@"changeDevicePropertyError:%@",error.localizedDescription);
    }
    

    6.切换摄像头,聚焦视图的显示。。

    //切换摄像头
    - (void)toggleBtnClick:(UIButton *)sender {
        AVCaptureDevice *currentDevice = [_captureDeviceInput device];
        AVCaptureDevicePosition currentPosition = [currentDevice position];
        AVCaptureDevice *toChangeDevice;
        AVCaptureDevicePosition toChangePosition = AVCaptureDevicePositionFront;
        if (currentPosition == AVCaptureDevicePositionUnspecified || currentPosition == AVCaptureDevicePositionFront)
        {
            toChangePosition = AVCaptureDevicePositionBack;
        }
        toChangeDevice = [self getCameraDeviceWithPosition:toChangePosition];
        // 获得要调整的设备输入对象
        AVCaptureDeviceInput *toChangeDeviceInput = [[AVCaptureDeviceInput alloc]initWithDevice:toChangeDevice error:nil];
        
        // 改变会话的配置前一定要先开启配置,配置完成后提交配置改变
        [self.captureSession beginConfiguration];
        // 移除原有输入对象
        [self.captureSession removeInput:self.captureDeviceInput];
        // 添加新的输入对象
        if ([self.captureSession canAddInput:toChangeDeviceInput])
        {
            [self.captureSession addInput:toChangeDeviceInput];
            self.captureDeviceInput = toChangeDeviceInput;
        }
        // 提交会话配置
        [self.captureSession commitConfiguration];
        [self setFlashMode:AVCaptureFlashModeAuto];
    
    }
    #pragma mark - Tap Action
    - (void)handleTap:(UITapGestureRecognizer *)gesture {
        CGPoint point = [gesture locationInView:self.viewContainer];
        // 将UI坐标转化为摄像头坐标
        CGPoint cameraPoint= [self.captureVideoPreviewLayer captureDevicePointOfInterestForPoint:point];
        [self setFocusCursorWithPoint:point];
        [self focusWithMode:AVCaptureFocusModeAutoFocus exposureMode:AVCaptureExposureModeAutoExpose atPoint:cameraPoint];
    
    }
    

    7.拍照,生成gif。。

    //拍照
    - (void)takeBtnClick:(UIButton *)sender {
     
        cameraTimer = [NSTimer scheduledTimerWithTimeInterval:1.2 target:self selector:@selector(takePhotos) userInfo:nil repeats:YES];
    }
    - (void)takePhotos
    {
        __weak PhotoCameraViewController *weakSelf = self;
        AudioServicesPlaySystemSound(SOUNDID);
        // 根据设备输出获得连接
        AVCaptureConnection *captureConnection = [_captureStillImageOutput connectionWithMediaType:AVMediaTypeVideo];
        // 根据连接取得设备输出的数据
        [_captureStillImageOutput captureStillImageAsynchronouslyFromConnection:captureConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error)
         {
             if (imageDataSampleBuffer)
             {
                 NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
                 UIImage *image = [UIImage imageWithData:imageData];
                 UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
                 UIImage *lastImg = [UIImage squareImage:image scaledToSize:kScreenWidth];
                 _previewImage.image = image;
                 _previewImage.hidden = NO;
                 [weakSelf.imageArray addObject:lastImg];
                 if (_imageArray.count == _photoCount) {
                     [cameraTimer invalidate];
                     [self takePhotoFinished];
                 } else {
                     _takeButton.hidden = YES;
                     _backButton.hidden = YES;
                 }
             
               
                 
             }
         }];
    }
    #pragma mark 完成拍摄条件
    
    
    - (void)takePhotoFinished
    {
        _backButton.hidden = YES;
        _toggleButton.hidden = YES;
        _takeButton.hidden = YES;
        _resetButton.hidden = NO;
        _sureButton.hidden = NO;
        _flashButton.hidden = YES;
        _previewImage.hidden = NO;
        [self makeGif];
    }
    #pragma mark 生成gif
    - (void)makeGif
    {
        NSDictionary *fileProperties = @{(__bridge id)kCGImagePropertyGIFDictionary:@{(__bridge id)kCGImagePropertyGIFLoopCount:@0,}};
        NSDictionary *frameProperties = @{(__bridge id)kCGImagePropertyGIFDictionary:@{(__bridge id)kCGImagePropertyGIFDelayTime:@0.5f,}};
        NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil];
        NSURL *fileURL = [documentsDirectoryURL URLByAppendingPathComponent:@"animated.gif"];
        CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)fileURL, kUTTypeGIF, self.imageArray.count, NULL);
        CGImageDestinationSetProperties(destination, (__bridge CFDictionaryRef)fileProperties);
        
        for (NSUInteger i = 0; i < self.imageArray.count; i++)
        {
            @autoreleasepool
            {
                CGImageDestinationAddImage(destination, ((UIImage *)self.imageArray[i]).CGImage, (__bridge CFDictionaryRef)frameProperties);
            }
        }
        
        if (!CGImageDestinationFinalize(destination))
        {
            NSLog(@"failed to finalize image destination");
        }
        CFRelease(destination);
        self.gifUrl = fileURL;
    
     
    }
    
    

    8.其他的一些按钮事件和方法。。。

    //重拍
    - (void)resetBtnClick:(UIButton *)sender {
        if (_imageArray)
        {
            [_imageArray removeAllObjects];
        }
        _backButton.hidden = NO;
        _toggleButton.hidden = NO;
        _takeButton.hidden = NO;
        _resetButton.hidden = YES;
        _sureButton.hidden = YES;
        _flashView.hidden = YES;
        _previewImage.hidden = YES;
    
    }
    //确认
    - (void)useBtnClick:(UIButton *)sender {
        if(self.CameraBlock)
        {
            self.CameraBlock(self.gifUrl);
        }
        [self handleBacK:_backButton];
    }
    //打开闪光灯
    - (void)flashOnClick:(UIButton *)sender {
        [self setFlashMode:AVCaptureFlashModeOn];
        _flashView.hidden = YES;
    }
    //关闭
    - (void)flashOffClick:(UIButton *)sender {
        [self setFlashMode:AVCaptureFlashModeOff];
        _flashView.hidden = YES;
    }
    //自动
    - (void)flashAutoClick:(UIButton *)sender {
        [self setFlashMode:AVCaptureFlashModeAuto];
        _flashView.hidden = YES;
    }
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    #pragma mark - 
    - (AVCaptureDevice *)getCameraDeviceWithPosition:(AVCaptureDevicePosition)position
    {
        NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
        for (AVCaptureDevice *camera in devices)
        {
            if ([camera position] == position)
            {
                return camera;
            }
        }
        return nil;
    }
    //闪关灯模式
    - (void)flashClick:(UIButton *)sender {
        _flashView.hidden = NO;
    
    }
    //返回
    - (void)handleBacK:(UIButton *)sender {
        [self dismissViewControllerAnimated:YES completion:nil];
    }
    //初始化
    -(instancetype)init {
        self = [super init];
        if (self) {
            self.photoCount = 1;
        }
        return self;
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.view.backgroundColor = [UIColor blackColor];
        [self initSubViews];
        [self configureSession];
        // Do any additional setup after loading the view.
    }
    #pragma mark - 懒加载
    - (NSMutableArray *)imageArray {
        if (!_imageArray) {
            _imageArray = [NSMutableArray array];
        }
        return _imageArray;
    }
    - (UIImageView *)previewImage {
        if (!_previewImage) {
            _previewImage = [[UIImageView alloc] initWithFrame:self.view.bounds];
            _previewImage.hidden = YES;
        }
        return _previewImage;
    }
    

    9.因为我是单独写的demo,这个类是单独写的,是从根视图view controller push过来的。下面附上view controller 的代码,这里用到了SDWebImage来加载gif图片。

    
    #import "ViewController.h"
    #import "PhotoCameraViewController.h"
    #import "UIImage+GIF.h"
    @interface ViewController ()
    
    @property (nonatomic, strong) UIImageView *photoView;
    
    @property (nonatomic, strong) UIButton    *presentButton;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        [self.view addSubview:self.photoView];
        [self.view addSubview:self.presentButton];
        
        // Do any additional setup after loading the view, typically from a nib.
    }
    
    - (UIImageView *)photoView {
        if (!_photoView) {
            _photoView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 120, 120)];
            _photoView.layer.borderColor = [UIColor orangeColor].CGColor;
            _photoView.layer.borderWidth = 2.f;
            _photoView.layer.cornerRadius = 60;
            _photoView.layer.masksToBounds = YES;
            _photoView.center = CGPointMake(kScreenWidth / 2, 130);
        }
        return _photoView;
    }
    - (UIButton *)presentButton {
        if (!_presentButton) {
            _presentButton = [UIButton buttonWithType:UIButtonTypeCustom];
            _presentButton.frame = CGRectMake(0, 0, 120, 40);
            _presentButton.center = CGPointMake(kScreenWidth / 2, kScreenheight / 2);
            _presentButton.layer.cornerRadius = 20;
            _presentButton.layer.masksToBounds = YES;
            _presentButton.backgroundColor = [UIColor colorWithRed:24 / 255.0 green:180/255.0 blue:237 / 255.0 alpha:1.0];
            [_presentButton addTarget:self action:@selector(handleAction:) forControlEvents:UIControlEventTouchUpInside];
            [_presentButton setTitle:@"点击拍照" forState:UIControlStateNormal];
        }
        return _presentButton;
    }
    - (void)handleAction:(UIButton *)sender {
        PhotoCameraViewController *photoVC = [[PhotoCameraViewController alloc] init];
        photoVC.photoCount = 3;
        photoVC.CameraBlock = ^(NSURL *fileURL){
            _photoView.image = [UIImage sd_animatedGIFWithData:[NSData dataWithContentsOfURL:fileURL]];
        };
        [self presentViewController:photoVC animated:YES completion:nil];
    }
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    
    @end
    

    先写到这里,还有很多不完善的地方,文章有写的不对地方还望指出,demo还在完善中。

    相关文章

      网友评论

      • Feroo_J:真详细,公司项目正好用到,只看这个就搞定了,太感谢了

      本文标题:AVFoundation拍照,合成gif动图。。。

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