Commit 033d506979f7e6248286c5792d106e0b3d90276a

Authored by 梁星国
1 parent fe93196e

提交相机

Showing 28 changed files with 3301 additions and 12 deletions
CNLiveAVCamera.podspec
@@ -8,8 +8,8 @@ @@ -8,8 +8,8 @@
8 8
9 Pod::Spec.new do |s| 9 Pod::Spec.new do |s|
10 s.name = 'CNLiveAVCamera' 10 s.name = 'CNLiveAVCamera'
11 - s.version = '0.1.0'  
12 - s.summary = 'A short description of CNLiveAVCamera.' 11 + s.version = '0.1.2'
  12 + s.summary = '自定义相机'
13 13
14 # This description is used to generate tags and improve search results. 14 # This description is used to generate tags and improve search results.
15 # * Think: What does it do? Why did you write it? What is the focus? 15 # * Think: What does it do? Why did you write it? What is the focus?
@@ -18,25 +18,31 @@ Pod::Spec.new do |s| @@ -18,25 +18,31 @@ Pod::Spec.new do |s|
18 # * Finally, don't worry about the indent, CocoaPods strips it! 18 # * Finally, don't worry about the indent, CocoaPods strips it!
19 19
20 s.description = <<-DESC 20 s.description = <<-DESC
21 -TODO: Add long description of the pod here. 21 + 自定义相
22 DESC 22 DESC
23 23
24 - s.homepage = 'https://github.com/jyyjkt/CNLiveAVCamera' 24 + s.homepage = 'http://bj.gitlab.cnlive.com/ios-team/CNLiveAVCamera'
25 # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' 25 # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
26 s.license = { :type => 'MIT', :file => 'LICENSE' } 26 s.license = { :type => 'MIT', :file => 'LICENSE' }
27 - s.author = { 'jyyjkt' => 'jyyjkt@qq.com' }  
28 - s.source = { :git => 'https://github.com/jyyjkt/CNLiveAVCamera.git', :tag => s.version.to_s } 27 + s.author = { '梁星国' => 'liangxingguo@cnlive.com' }
  28 + s.source = { :git => 'http://bj.gitlab.cnlive.com/ios-team/CNLiveAVCamera.git', :tag => s.version.to_s }
29 # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>' 29 # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
30 30
31 - s.ios.deployment_target = '8.0' 31 + s.ios.deployment_target = '9.0'
32 32
33 s.source_files = 'CNLiveAVCamera/Classes/**/*' 33 s.source_files = 'CNLiveAVCamera/Classes/**/*'
34 34
  35 + s.resources = 'CNLiveAVCamera/Assets/*'
35 # s.resource_bundles = { 36 # s.resource_bundles = {
36 # 'CNLiveAVCamera' => ['CNLiveAVCamera/Assets/*.png'] 37 # 'CNLiveAVCamera' => ['CNLiveAVCamera/Assets/*.png']
37 # } 38 # }
38 39
39 # s.public_header_files = 'Pod/Classes/**/*.h' 40 # s.public_header_files = 'Pod/Classes/**/*.h'
40 - # s.frameworks = 'UIKit', 'MapKit'  
41 - # s.dependency 'AFNetworking', '~> 2.3' 41 + s.frameworks = 'UIKit', 'Photos'
  42 + s.dependency 'Masonry', '~> 1.0.2'
  43 + s.dependency 'CNLiveBaseKit'
  44 + s.dependency 'QMUIKit', '~> 3.1.7'
  45 + s.dependency 'CNLiveBusinessTools'
  46 + s.dependency 'CNLiveCommonCategory'
  47 +
42 end 48 end
CNLiveAVCamera/Assets/CNLiveAVCamera.bundle/Root.plist 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  3 +<plist version="1.0">
  4 +<dict>
  5 + <key>StringsTable</key>
  6 + <string>Root</string>
  7 + <key>PreferenceSpecifiers</key>
  8 + <array>
  9 + <dict>
  10 + <key>Type</key>
  11 + <string>PSGroupSpecifier</string>
  12 + <key>Title</key>
  13 + <string>Group</string>
  14 + </dict>
  15 + <dict>
  16 + <key>Type</key>
  17 + <string>PSTextFieldSpecifier</string>
  18 + <key>Title</key>
  19 + <string>Name</string>
  20 + <key>Key</key>
  21 + <string>name_preference</string>
  22 + <key>DefaultValue</key>
  23 + <string></string>
  24 + <key>IsSecure</key>
  25 + <false/>
  26 + <key>KeyboardType</key>
  27 + <string>Alphabet</string>
  28 + <key>AutocapitalizationType</key>
  29 + <string>None</string>
  30 + <key>AutocorrectionType</key>
  31 + <string>No</string>
  32 + </dict>
  33 + <dict>
  34 + <key>Type</key>
  35 + <string>PSToggleSwitchSpecifier</string>
  36 + <key>Title</key>
  37 + <string>Enabled</string>
  38 + <key>Key</key>
  39 + <string>enabled_preference</string>
  40 + <key>DefaultValue</key>
  41 + <true/>
  42 + </dict>
  43 + <dict>
  44 + <key>Type</key>
  45 + <string>PSSliderSpecifier</string>
  46 + <key>Key</key>
  47 + <string>slider_preference</string>
  48 + <key>DefaultValue</key>
  49 + <real>0.5</real>
  50 + <key>MinimumValue</key>
  51 + <integer>0</integer>
  52 + <key>MaximumValue</key>
  53 + <integer>1</integer>
  54 + <key>MinimumValueImage</key>
  55 + <string></string>
  56 + <key>MaximumValueImage</key>
  57 + <string></string>
  58 + </dict>
  59 + </array>
  60 + <key>ApplicationGroupContainerIdentifier</key>
  61 + <string></string>
  62 +</dict>
  63 +</plist>
CNLiveAVCamera/Assets/CNLiveAVCamera.bundle/en.lproj/Root.strings 0 → 100644
1 Binary files /dev/null and b/CNLiveAVCamera/Assets/CNLiveAVCamera.bundle/en.lproj/Root.strings differ 1 Binary files /dev/null and b/CNLiveAVCamera/Assets/CNLiveAVCamera.bundle/en.lproj/Root.strings differ
CNLiveAVCamera/Assets/CNLiveAVCamera.bundle/fb_chongpai@2x.png 0 → 100644

3.38 KB

CNLiveAVCamera/Assets/CNLiveAVCamera.bundle/fb_chongpai@3x.png 0 → 100644

4.87 KB

CNLiveAVCamera/Assets/CNLiveAVCamera.bundle/fb_quxiaoxia@2x.png 0 → 100644

1.68 KB

CNLiveAVCamera/Assets/CNLiveAVCamera.bundle/fb_quxiaoxia@3x.png 0 → 100644

1.96 KB

CNLiveAVCamera/Assets/CNLiveAVCamera.bundle/fb_wancheng@2x.png 0 → 100644

3.46 KB

CNLiveAVCamera/Assets/CNLiveAVCamera.bundle/fb_wancheng@3x.png 0 → 100644

5.08 KB

CNLiveAVCamera/Assets/CNLiveAVCamera.bundle/fb_xiangji@2x.png 0 → 100644

1.68 KB

CNLiveAVCamera/Assets/CNLiveAVCamera.bundle/fb_xiangji@3x.png 0 → 100644

2.27 KB

CNLiveAVCamera/Assets/CNLiveAVCamera.bundle/gz_shoudian2@2x.png 0 → 100644

1.34 KB

CNLiveAVCamera/Assets/CNLiveAVCamera.bundle/gz_shoudian2@3x.png 0 → 100644

1.52 KB

CNLiveAVCamera/Assets/CNLiveAVCamera.bundle/gz_shoudian@2x.png 0 → 100644

1.29 KB

CNLiveAVCamera/Assets/CNLiveAVCamera.bundle/gz_shoudian@3x.png 0 → 100644

1.41 KB

CNLiveAVCamera/Assets/CNLiveAVCamera.bundle/sight_video_focus@2x.png 0 → 100644

3.02 KB

CNLiveAVCamera/Assets/CNLiveAVCamera.bundle/sight_video_focus@3x.png 0 → 100644

3.36 KB

CNLiveAVCamera/Classes/ReplaceMe.m deleted 100644 → 0
CNLiveAVCamera/Classes/XFCameraButton.h 0 → 100644
  1 +//
  2 +// XFCameraButton.h
  3 +//
  4 +//
  5 +// Created by xf-ling on 2017/6/1.
  6 +// Copyright © 2017年 LXF. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +
  11 +typedef void(^TapEventBlock)(UITapGestureRecognizer *tapGestureRecognizer);
  12 +typedef void(^LongPressEventBlock)(UILongPressGestureRecognizer *longPressGestureRecognizer);
  13 +
  14 +@interface XFCameraButton : UIView
  15 +
  16 +/**
  17 + * 设置进度条的录制视频时长百分比 = 当前录制时间 / 最大录制时间
  18 + */
  19 +@property (nonatomic, assign) CGFloat progressPercentage;
  20 +
  21 ++ (instancetype)defaultCameraButton;
  22 +
  23 +/**
  24 + * 配置点击事件
  25 + */
  26 +- (void)configureTapCameraButtonEventWithBlock:(TapEventBlock)tapEventBlock;
  27 +
  28 +/**
  29 + * 配置按压事件
  30 + */
  31 +- (void)configureLongPressCameraButtonEventWithBlock:(LongPressEventBlock)longPressEventBlock;
  32 +
  33 +/**
  34 + * 开始录制前的准备动画
  35 + */
  36 +- (void)startShootAnimationWithDuration:(NSTimeInterval)duration;
  37 +
  38 +/**
  39 + * 结束摄影动画
  40 + */
  41 +- (void)stopShootAnimation;
  42 +
  43 +@end
CNLiveAVCamera/Classes/XFCameraButton.m 0 → 100644
  1 +//
  2 +// XFCameraButton.m
  3 +//
  4 +//
  5 +// Created by xf-ling on 2017/6/1.
  6 +// Copyright © 2017年 LXF. All rights reserved.
  7 +//
  8 +
  9 +#import "XFCameraButton.h"
  10 +
  11 +// 默认按钮大小
  12 +#define CAMERABUTTONWIDTH 75
  13 +#define TOUCHVIEWWIDTH 55
  14 +
  15 +// 录制时按钮的缩放比
  16 +#define SHOOTCAMERABUTTONSCALE 1.6f
  17 +#define SHOOTTOUCHVIEWSCALE 0.5f
  18 +
  19 +// 录制按钮动画轨道宽度
  20 +#define PROGRESSLINEWIDTH 3
  21 +
  22 +#ifndef RGB
  23 +#define RGB(r, g, b, a) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a]
  24 +#endif
  25 +
  26 +#define kUIColorMake(r, g, b) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1]
  27 +#define kUIColorMakeWithRGBA(r, g, b, a) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a/1.0]
  28 +
  29 +@interface XFCameraButton ()
  30 +
  31 +@property (weak, nonatomic) UIView *touchView;
  32 +@property (strong, nonatomic) CAShapeLayer *trackLayer;
  33 +@property (strong, nonatomic) CAShapeLayer *progressLayer;
  34 +
  35 +@property (copy, nonatomic) TapEventBlock tapEventBlock;
  36 +@property (copy, nonatomic) LongPressEventBlock longPressEventBlock;
  37 +
  38 +@end
  39 +
  40 +@implementation XFCameraButton
  41 +
  42 +#pragma mark - 工厂方法
  43 +
  44 ++ (instancetype)defaultCameraButton
  45 +{
  46 + // 设置camera view
  47 + XFCameraButton *cameraButton = [[XFCameraButton alloc] initWithFrame:CGRectMake(0, 0, CAMERABUTTONWIDTH, CAMERABUTTONWIDTH)];
  48 + [cameraButton.layer setCornerRadius:(CAMERABUTTONWIDTH / 2)];
  49 + cameraButton.backgroundColor = [UIColor whiteColor];//[UIColor colorWithRed:225/255.0f green:225/255.0f blue:230/255.0f alpha:1.0f];
  50 +
  51 + // 设置camera view的点击按钮
  52 + CGFloat touchViewX = (CAMERABUTTONWIDTH - TOUCHVIEWWIDTH) / 2;
  53 + CGFloat touchViewY = (CAMERABUTTONWIDTH - TOUCHVIEWWIDTH) / 2;
  54 + UIView *touchView = [[UIView alloc] initWithFrame:CGRectMake(touchViewX, touchViewY, TOUCHVIEWWIDTH, TOUCHVIEWWIDTH)];
  55 + cameraButton.touchView = touchView;
  56 + [cameraButton addSubview:touchView];
  57 + [cameraButton.touchView.layer setCornerRadius:(cameraButton.touchView.bounds.size.width / 2)];
  58 + touchView.backgroundColor = kUIColorMake(255, 163, 0);
  59 +
  60 + [cameraButton initCircleAnimationLayer];
  61 +
  62 + return cameraButton;
  63 +}
  64 +
  65 +- (void)dealloc {
  66 + NSLog(@"XFCameraButton走dealloc");
  67 +}
  68 +
  69 +#pragma mark - 点击事件与长按事件
  70 +
  71 +/**
  72 + * 配置点击事件
  73 + */
  74 +- (void)configureTapCameraButtonEventWithBlock:(TapEventBlock)tapEventBlock
  75 +{
  76 + self.tapEventBlock = tapEventBlock;
  77 +
  78 + UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapCameraButtonEvent:)];
  79 +
  80 + [self.touchView addGestureRecognizer:tapGestureRecognizer];
  81 +}
  82 +
  83 +- (void)tapCameraButtonEvent:(UITapGestureRecognizer *)tapGestureRecognizer
  84 +{
  85 + if (self.tapEventBlock)
  86 + {
  87 + self.tapEventBlock(tapGestureRecognizer);
  88 + }
  89 +}
  90 +
  91 +/**
  92 + * 配置按压事件
  93 + */
  94 +- (void)configureLongPressCameraButtonEventWithBlock:(LongPressEventBlock)longPressEventBlock
  95 +{
  96 + self.longPressEventBlock = longPressEventBlock;
  97 +
  98 + UILongPressGestureRecognizer *longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressCameraButtonEvent:)];
  99 +
  100 + [self.touchView addGestureRecognizer:longPressGestureRecognizer];
  101 +}
  102 +
  103 +- (void)longPressCameraButtonEvent:(UILongPressGestureRecognizer *)longPressGestureRecognizer
  104 +{
  105 + if (self.longPressEventBlock)
  106 + {
  107 + self.longPressEventBlock(longPressGestureRecognizer);
  108 + }
  109 +}
  110 +
  111 +#pragma mark - 录制视频按钮动画
  112 +
  113 +// 初始化按钮路径
  114 +- (void)initCircleAnimationLayer
  115 +{
  116 + float centerX = self.bounds.size.width / 2.0;
  117 + float centerY = self.bounds.size.height / 2.0;
  118 + //半径
  119 + float radius = (self.bounds.size.width - PROGRESSLINEWIDTH) / 2.0;
  120 +
  121 + //创建贝塞尔路径
  122 + UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(centerX, centerY) radius:radius startAngle:(-0.5f * M_PI) endAngle:(1.5f * M_PI) clockwise:YES];
  123 +
  124 + //添加背景圆环
  125 + CAShapeLayer *backLayer = [CAShapeLayer layer];
  126 + backLayer.frame = self.bounds;
  127 + backLayer.fillColor = [[UIColor clearColor] CGColor];
  128 + backLayer.strokeColor = kUIColorMake(242, 242, 242).CGColor;//[RGB(225, 225, 230, 1.0f) CGColor];
  129 + backLayer.lineWidth = PROGRESSLINEWIDTH;
  130 + backLayer.path = [path CGPath];
  131 + backLayer.strokeEnd = 1;
  132 + [self.layer addSublayer:backLayer];
  133 +
  134 + //创建进度layer
  135 + _progressLayer = [CAShapeLayer layer];
  136 + _progressLayer.frame = self.bounds;
  137 + _progressLayer.fillColor = [[UIColor clearColor] CGColor];
  138 + //指定path的渲染颜色
  139 + _progressLayer.strokeColor = [[UIColor blackColor] CGColor];
  140 + _progressLayer.lineCap = kCALineCapSquare;//kCALineCapRound;
  141 + _progressLayer.lineWidth = PROGRESSLINEWIDTH;
  142 + _progressLayer.path = [path CGPath];
  143 + _progressLayer.strokeEnd = 0;
  144 +
  145 + //设置渐变颜色
  146 + CAGradientLayer *gradientLayer = [CAGradientLayer layer];
  147 + gradientLayer.frame = self.bounds;
  148 +
  149 + // 渐变颜色
  150 + [gradientLayer setColors:[NSArray arrayWithObjects:(id)[kUIColorMake(255, 163, 0) CGColor], (id)[kUIColorMake(255, 163, 0) CGColor], nil]];//RGB(76, 192, 29, 1.0f) [RGB(76, 192, 29, 1.0f) CGColor]
  151 +// [gradientLayer setColors:[NSArray arrayWithObjects:(id)[RGB(28, 178, 29, 1.0f) CGColor], (id)[RGB(255, 203, 0, 1.0f) CGColor], nil]];
  152 +
  153 + gradientLayer.startPoint = CGPointMake(0, 0);
  154 + gradientLayer.endPoint = CGPointMake(0, 1);
  155 + [gradientLayer setMask:_progressLayer]; //用progressLayer来截取渐变层
  156 + [self.layer addSublayer:gradientLayer];
  157 +
  158 +}
  159 +
  160 +// 设置按钮百分比
  161 +- (void)setProgressPercentage:(CGFloat)progressPercentage
  162 +{
  163 + _progressPercentage = progressPercentage;
  164 + _progressLayer.strokeEnd = progressPercentage;
  165 + [_progressLayer removeAllAnimations];
  166 +}
  167 +
  168 +/**
  169 + * 开始录制前的准备动画
  170 + */
  171 +- (void)startShootAnimationWithDuration:(NSTimeInterval)duration
  172 +{
  173 + __weak typeof(self) weakSelf = self;
  174 +
  175 + [UIView animateWithDuration:duration animations:^{
  176 +
  177 + weakSelf.transform = CGAffineTransformMakeScale(SHOOTCAMERABUTTONSCALE, SHOOTCAMERABUTTONSCALE);
  178 + weakSelf.touchView.transform = CGAffineTransformMakeScale(SHOOTTOUCHVIEWSCALE, SHOOTTOUCHVIEWSCALE);
  179 +
  180 + } completion:^(BOOL finished) {
  181 + // nothing
  182 + }];
  183 +}
  184 +
  185 +/**
  186 + * 结束摄影动画
  187 + */
  188 +- (void)stopShootAnimation
  189 +{
  190 + self.transform = CGAffineTransformIdentity;
  191 + self.touchView.transform = CGAffineTransformIdentity;
  192 +}
  193 +
  194 +
  195 +@end
  196 +
  197 +
  198 +
  199 +
  200 +
  201 +
  202 +
  203 +
  204 +
  205 +
CNLiveAVCamera/Classes/XFCameraController.h 0 → 100644
  1 +//
  2 +// QXCameraController.h
  3 +//
  4 +//
  5 +// Created by xf-ling on 2017/6/1.
  6 +// Copyright © 2017年 LXF. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +#import <Photos/Photos.h>
  11 +
  12 +
  13 +typedef void(^TakePhotosCompletionBlock)(UIImage *image, NSError *error);
  14 +typedef void(^ShootCompletionBlock)(NSURL *videoUrl, CGFloat videoTimeLength, UIImage *thumbnailImage, NSError *error);
  15 +//zx
  16 +typedef void(^ZXShootCompletionBlock)(PHAsset *asset,NSURL *videoUrl, CGFloat videoTimeLength, UIImage *thumbnailImage, NSError *error);
  17 +
  18 +/**
  19 + Provides extensions for `CALayer`.
  20 + */
  21 +@interface CALayer (XFCameraAdd)
  22 +
  23 +@property (nonatomic) CGFloat xf_centerY; ///< Shortcut for center.y
  24 +
  25 +@end
  26 +
  27 +@interface NSBundle (XFCamera)
  28 +
  29 ++ (NSBundle *)xf_cameraBundle;
  30 +
  31 +@end
  32 +
  33 +@interface UIImage (XFCamera)
  34 ++ (UIImage *)imageNamedFromMyBundle:(NSString *)name;
  35 +@end
  36 +
  37 +
  38 +@interface XFCameraController : UIViewController
  39 +
  40 +/**
  41 + * 拍照完成后的Block回调
  42 + */
  43 +@property (copy, nonatomic) TakePhotosCompletionBlock takePhotosCompletionBlock;
  44 +
  45 +/**
  46 + * 拍摄完成后的Block回调
  47 + */
  48 +@property (copy, nonatomic) ShootCompletionBlock shootCompletionBlock;
  49 +
  50 +/**
  51 + * zx拍摄完成后的Block回调
  52 + */
  53 +@property (copy, nonatomic) ZXShootCompletionBlock zxshootCompletionBlock;
  54 +
  55 +
  56 +/**
  57 + * 自定义APP相册名字,如果为空则默认为APP的名字
  58 + */
  59 +@property (strong, nonatomic) NSString *assetCollectionName;
  60 +
  61 +/**
  62 + * 视频文件保存文件夹,如果没有定义,默认在document/video文件夹下面
  63 + */
  64 +@property (strong, nonatomic) NSString *videoFilePath;
  65 +
  66 +
  67 +//zx 当前是否可以拍摄视频 BOOL
  68 +@property (nonatomic ,assign) BOOL isCanShootVideo;
  69 +
  70 +@property (nonatomic, assign) BOOL isNoCanTakePhoto;// 当前是否可以拍照
  71 +
  72 +//zl__会话ID,用于存储拍摄的视频
  73 +@property (nonatomic ,strong) NSString *conversationId;
  74 +
  75 +@end
CNLiveAVCamera/Classes/XFCameraController.m 0 → 100644
  1 +//
  2 +// QXCameraController.m
  3 +//
  4 +//
  5 +// Created by xf-ling on 2017/6/1.
  6 +// Copyright © 2017年 LXF. All rights reserved.
  7 +//
  8 +
  9 +#import "XFCameraController.h"
  10 +#import <AVFoundation/AVFoundation.h>
  11 +#import <AssetsLibrary/AssetsLibrary.h>
  12 +#import "XFCameraButton.h"
  13 +#import "XFPhotoLibraryManager.h"
  14 +#import <Photos/Photos.h>
  15 +#import <CoreMotion/CoreMotion.h>
  16 +#import <CNLiveBaseKit/CNLiveBaseKit.h>
  17 +#import <Masonry/Masonry.h>
  18 +#import <QMUIKit/QMUIKit.h>
  19 +#import <CNLiveBusinessTools/CNLiveBusinessTools.h>
  20 +#import <CNLiveCommonCategory/CNLiveCommonCategory.h>
  21 +
  22 +
  23 +#define kCNVideoOrAudioEnterNotification @"CNVideoOrAudioEnterNotification" //进入音视频通知
  24 +#define kCNVideoOrAudioOutNotification @"CNVideoOrAudioOutNotification" //退出音视频
  25 +
  26 +#ifndef k_ScreenWidth
  27 +#define k_ScreenWidth [UIScreen mainScreen].bounds.size.width
  28 +#endif
  29 +
  30 +#ifndef k_ScreenHeight
  31 +#define k_ScreenHeight [UIScreen mainScreen].bounds.size.height
  32 +#endif
  33 +
  34 +#define k_SafeViewBottomHeight (k_ScreenHeight == 812.0 ? 34.0 : 0.0)
  35 +
  36 +#define VIDEO_FILEPATH @"video"
  37 +#define TIMER_INTERVAL 0.01f //定时器记录视频间隔
  38 +#define VIDEO_RECORDER_MAX_TIME 15.0f //视频最大时长 (单位/秒)
  39 +#define VIDEO_RECORDER_MIN_TIME 3.0f //最短视频时长 (单位/秒)
  40 +#define START_VIDEO_ANIMATION_DURATION 0.3f //录制视频前的动画时间
  41 +
  42 +@implementation CALayer (XFCameraAdd)
  43 +
  44 +- (CGFloat)xf_centerY {
  45 + return self.frame.origin.y + self.frame.size.height * 0.5;
  46 +}
  47 +
  48 +- (void)setXf_centerY:(CGFloat)xf_centerY {
  49 + CGRect frame = self.frame;
  50 + frame.origin.y = xf_centerY - frame.size.height * 0.5;
  51 + self.frame = frame;
  52 +}
  53 +
  54 +
  55 +@end
  56 +
  57 +@implementation NSBundle (XFCamera)
  58 +
  59 ++ (NSBundle *)xf_cameraBundle {
  60 + NSBundle *bundle = [NSBundle bundleForClass:[XFCameraController class]];
  61 + NSURL *url = [bundle URLForResource:@"CNLiveAVCamera" withExtension:@"bundle"];
  62 + bundle = [NSBundle bundleWithURL:url];
  63 + return bundle;
  64 +}
  65 +
  66 +@end
  67 +
  68 +@implementation UIImage (XFCamera)
  69 +
  70 ++ (UIImage *)imageNamedFromMyBundle:(NSString *)name {
  71 + NSBundle *imageBundle = [NSBundle xf_cameraBundle];
  72 + name = [name stringByAppendingString:@"@2x"];
  73 + NSString *imagePath = [imageBundle pathForResource:name ofType:@"png"];
  74 + UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
  75 + if (!image) {
  76 + // 兼容业务方自己设置图片的方式
  77 + name = [name stringByAppendingString:@"@3x"];
  78 + image = [UIImage imageNamed:name];
  79 + if (!image) {
  80 + image = [UIImage new];
  81 + }
  82 + }
  83 + return image;
  84 +}
  85 +
  86 +@end
  87 +
  88 +typedef void(^PropertyChangeBlock)(AVCaptureDevice *captureDevice);
  89 +
  90 +@interface XFCameraController() <AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAudioDataOutputSampleBufferDelegate, UIGestureRecognizerDelegate>
  91 +{
  92 + BOOL _isPicOrVideo;//已经拍照或者录制视频标识,进入后台时不dismiss
  93 +}
  94 +@property (nonatomic ,assign) BOOL isAlertShow;
  95 +@property (nonatomic, strong) dispatch_queue_t videoQueue;
  96 +
  97 +@property (strong, nonatomic) AVCaptureSession *captureSession; //负责输入和输出设备之间的数据传递
  98 +
  99 +@property (nonatomic, strong) AVCaptureDeviceInput *videoInput; //视频输入
  100 +@property (nonatomic, strong) AVCaptureDeviceInput *audioInput; //声音输入
  101 +@property (nonatomic, strong) AVCaptureVideoDataOutput *videoOutput;
  102 +@property (nonatomic, strong) AVCaptureAudioDataOutput *audioOutput;
  103 +
  104 +@property (strong, nonatomic) AVCaptureStillImageOutput *captureStillImageOutput; //照片输出流
  105 +
  106 +@property (nonatomic, strong) AVAssetWriter *assetWriter;
  107 +@property (nonatomic, strong) AVAssetWriterInput *assetWriterVideoInput;
  108 +@property (nonatomic, strong) AVAssetWriterInput *assetWriterAudioInput;
  109 +@property (nonatomic, strong) NSDictionary *videoCompressionSettings;
  110 +@property (nonatomic, strong) NSDictionary *audioCompressionSettings;
  111 +@property (nonatomic, assign) BOOL canWrite;
  112 +
  113 +@property (strong, nonatomic) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer; //预览图层
  114 +
  115 +@property (nonatomic, strong) NSTimer *timer; //记录录制时间
  116 +
  117 +@property (nonatomic, strong) UIButton *rotateCameraButton;
  118 +@property (nonatomic, strong) UIButton *flashButton;
  119 +@property (nonatomic, strong) UIButton *takeButton; //拍摄按钮
  120 +@property (nonatomic, strong) UIButton *closeButton;
  121 +@property (nonatomic, strong) UILabel *tipLabel;
  122 +@property (strong, nonatomic) XFCameraButton *cameraButton; //拍摄按钮
  123 +
  124 +@property (nonatomic, strong) UIImageView *focusImageView; //聚焦视图
  125 +@property (assign, nonatomic) Boolean isFocusing; //镜头正在聚焦
  126 +@property (assign, nonatomic) Boolean isTakingPhoto; //正在拍摄照片 lxg
  127 +@property (assign, nonatomic) Boolean isShooting; //正在拍摄
  128 +@property (assign, nonatomic) Boolean isRotatingCamera; //正在旋转摄像头
  129 +
  130 +//捏合缩放摄像头
  131 +@property (nonatomic,assign) CGFloat beginGestureScale; //记录开始的缩放比例
  132 +@property (nonatomic,assign) CGFloat effectiveScale; //最后的缩放比例
  133 +
  134 +// 拍照摄像后的预览模块
  135 +@property (nonatomic, strong) UIButton *cancelButton;
  136 +@property (nonatomic, strong) UIButton *confirmButton;
  137 +@property (strong, nonatomic) UIView *photoPreviewContainerView; //相片预览ContainerView
  138 +@property (strong, nonatomic) UIImageView *photoPreviewImageView; //相片预览ImageView
  139 +@property (strong, nonatomic) UIView *videoPreviewContainerView; //视频预览View
  140 +@property (strong, nonatomic) NSURL *videoURL; //视频文件地址
  141 +@property (strong, nonatomic) AVPlayerLayer *playerLayer;
  142 +@property (strong, nonatomic) AVPlayer *player;
  143 +@property (strong, nonatomic) AVPlayerItem *playerItem;
  144 +@property (assign, nonatomic) CGFloat currentVideoTimeLength; //当前小视频总时长
  145 +
  146 +@property (assign, nonatomic) UIDeviceOrientation shootingOrientation; //拍摄中的手机方向
  147 +@property (strong, nonatomic) CMMotionManager *motionManager;
  148 +
  149 +/*** 音频是否播放 ***/
  150 +@property (assign, nonatomic) BOOL isAudioPlaying;
  151 +
  152 +@end
  153 +
  154 +@implementation XFCameraController{
  155 +
  156 + CGFloat timeLength; //时间长度
  157 +
  158 +}
  159 +
  160 +#pragma mark - 控制器方法
  161 +
  162 +- (void)viewDidLoad
  163 +{
  164 + [super viewDidLoad];
  165 + self.view.backgroundColor = [UIColor blackColor];
  166 +
  167 + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(enterBackgroundNotification) name:UIApplicationDidEnterBackgroundNotification object:nil];
  168 +
  169 + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
  170 +
  171 +#pragma mark - TODO: 音频导致崩溃
  172 + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioEnter:) name:kCNVideoOrAudioEnterNotification object:nil];
  173 + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioOut:) name:kCNVideoOrAudioOutNotification object:nil];
  174 +
  175 +
  176 + // 隐藏状态栏
  177 + // [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];
  178 + [UIApplication sharedApplication].statusBarHidden = YES;
  179 +
  180 + _isFocusing = NO;
  181 + _isTakingPhoto = NO;
  182 + _isShooting = NO;
  183 + _isRotatingCamera = NO;
  184 + _canWrite = NO;
  185 + _beginGestureScale = 1.0f;
  186 + _effectiveScale = 1.0f;
  187 + [self.view addSubview:self.rotateCameraButton];
  188 + [self.view addSubview:self.flashButton];
  189 + [self.view addSubview:self.takeButton];
  190 + [self.view addSubview:self.closeButton];
  191 + [self.view addSubview:self.tipLabel];
  192 + [self.view addSubview:self.focusImageView];
  193 + [self.view addSubview:self.cancelButton];
  194 + [self.view addSubview:self.confirmButton];
  195 + [self setUpViewLayout];
  196 +
  197 +
  198 +}
  199 +
  200 +#pragma mark -布局masony
  201 +-(void)setUpViewLayout {
  202 + __weak typeof(self) weakSelf = self;
  203 + CGFloat rotateOffest = IS_IPhoneX_All ? kNavigationBarHeight : 20 ;
  204 + [self.rotateCameraButton mas_makeConstraints:^(MASConstraintMaker *make) {
  205 + make.width.mas_equalTo(50);
  206 + make.height.mas_equalTo(30);
  207 + make.top.mas_equalTo(weakSelf.view.mas_top).mas_offset(rotateOffest);
  208 + make.right.mas_equalTo(weakSelf.view.mas_right).mas_offset(-15);
  209 + }];
  210 +
  211 + [self.flashButton mas_makeConstraints:^(MASConstraintMaker *make) {
  212 + make.width.mas_equalTo(50);
  213 + make.height.mas_equalTo(30);
  214 + make.top.mas_equalTo(weakSelf.view.mas_top).mas_offset(rotateOffest);
  215 + make.right.mas_equalTo(weakSelf.rotateCameraButton.mas_left);
  216 + }];
  217 +
  218 + //拍摄按钮
  219 + CGFloat bottom = IS_IPhoneX_All ? -55-kVerticalBottomSafeHeight : -20;
  220 + [self.takeButton mas_makeConstraints:^(MASConstraintMaker *make) {
  221 + make.width.mas_equalTo(120);
  222 + make.height.mas_equalTo(120);
  223 + make.centerX.mas_equalTo(weakSelf.view.mas_centerX);
  224 + make.bottom.mas_equalTo(weakSelf.view.mas_bottom).mas_offset(bottom);
  225 + }];
  226 +
  227 + [self.closeButton mas_makeConstraints:^(MASConstraintMaker *make) {
  228 + make.width.mas_equalTo(60);
  229 + make.height.mas_equalTo(30);
  230 + make.centerY.mas_equalTo(weakSelf.takeButton.mas_centerY);
  231 + make.right.mas_equalTo(weakSelf.takeButton.mas_left).mas_offset(-20);
  232 + }];
  233 +
  234 + [self.tipLabel mas_makeConstraints:^(MASConstraintMaker *make) {
  235 + make.centerX.mas_equalTo(weakSelf.view.mas_centerX);
  236 + make.bottom.mas_equalTo(weakSelf.takeButton.mas_top).mas_offset(-20);
  237 + }];
  238 +
  239 + [self.focusImageView mas_makeConstraints:^(MASConstraintMaker *make) {
  240 + make.width.mas_equalTo(60);
  241 + make.height.mas_equalTo(60);
  242 + make.centerX.mas_equalTo(weakSelf.view.mas_centerX);
  243 + make.centerY.mas_equalTo(weakSelf.view.mas_centerY);
  244 + }];
  245 +
  246 + [self.cancelButton mas_makeConstraints:^(MASConstraintMaker *make) {
  247 + make.width.mas_equalTo(60);
  248 + make.height.mas_equalTo(60);
  249 + make.left.mas_equalTo(weakSelf.view.mas_left).mas_offset(40);
  250 + make.bottom.mas_equalTo(weakSelf.view.mas_bottom).mas_offset(-60-kVerticalBottomSafeHeight);
  251 + }];
  252 +
  253 + [self.confirmButton mas_makeConstraints:^(MASConstraintMaker *make) {
  254 + make.width.mas_equalTo(60);
  255 + make.height.mas_equalTo(60);
  256 + make.right.mas_equalTo(weakSelf.view.mas_right).mas_offset(-40);
  257 + make.bottom.mas_equalTo(weakSelf.cancelButton.mas_bottom);
  258 + }];
  259 +}
  260 +- (void)viewWillAppear:(BOOL)animated
  261 +{
  262 + [super viewWillAppear:animated];
  263 +
  264 + AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
  265 + if (authStatus == AVAuthorizationStatusRestricted || authStatus == AVAuthorizationStatusDenied)
  266 + {
  267 + [self requestAuthorizationForVideo];
  268 + }
  269 +
  270 + //判断用户是否允许访问麦克风权限
  271 + authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
  272 + if (authStatus == AVAuthorizationStatusRestricted || authStatus == AVAuthorizationStatusDenied)
  273 + {
  274 + [self requestAuthorizationForVideo];
  275 + }
  276 +
  277 +#pragma mark - TODO: 在音频播放中导致崩溃
  278 +// self.isAudioPlaying = [CNAudioPlayerManager sharedInstance].isPlaying;
  279 +// if (self.isAudioPlaying) {
  280 +// [[CNAudioPlayerManager sharedInstance] pausePlaying];
  281 +// }
  282 +
  283 +
  284 + [self requestAuthorizationForPhotoLibrary];
  285 +
  286 + [self initAVCaptureSession];
  287 +
  288 + [self configDefaultUIDisplay];
  289 +
  290 + [self addTapGenstureRecognizerForCamera];
  291 +
  292 +}
  293 +
  294 +- (void)viewDidAppear:(BOOL)animated
  295 +{
  296 + [super viewDidAppear:animated];
  297 +
  298 + [self startSession];
  299 +
  300 + [self setFocusCursorWithPoint:self.view.center];
  301 +
  302 + if (self.isCanShootVideo)
  303 + {
  304 + if (self.isNoCanTakePhoto) {
  305 + self.tipLabel.text = @"长按摄像,不可拍照";
  306 + }else {
  307 + self.tipLabel.text = @"点击拍照,长按摄像";
  308 + }
  309 + }else
  310 + {
  311 + self.tipLabel.text = @"点击拍照,不可摄像";
  312 + }
  313 +// if (self.isNoCanTakePhoto) {
  314 +//
  315 +// }else {
  316 +// self.tipLabel.text = @"点击拍照,长按摄像";
  317 +// }
  318 +
  319 + [self tipLabelAnimation];
  320 +
  321 +}
  322 +
  323 +- (void)viewWillDisappear:(BOOL)animated
  324 +{
  325 + [super viewWillDisappear:animated];
  326 +
  327 + [self setCaptureVideoPreviewLayerTransformWithScale:1.0f];
  328 +
  329 + // 显示状态栏
  330 + // [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
  331 + [UIApplication sharedApplication].statusBarHidden = YES;
  332 +
  333 +#pragma mark - TODO: 在音频播放中导致崩溃
  334 +// if (self.isAudioPlaying) {
  335 +// [[CNAudioPlayerManager sharedInstance] resumePlaying];
  336 +// }
  337 +
  338 +
  339 +}
  340 +
  341 +- (BOOL)prefersStatusBarHidden
  342 +{
  343 + return YES;
  344 +}
  345 +
  346 +- (void)viewDidDisappear:(BOOL)animated
  347 +{
  348 + [super viewDidDisappear:animated];
  349 +
  350 + [self stopSession];
  351 + if (_motionManager) {
  352 + [self stopUpdateAccelerometer];
  353 + }
  354 +
  355 +}
  356 +
  357 +#pragma mark - 释放
  358 +- (void)dealloc
  359 +{
  360 + if (_timer) {
  361 + [_timer invalidate];
  362 + _timer = nil;
  363 + }
  364 +
  365 + if (_captureSession) {
  366 + _captureSession = nil;
  367 + }
  368 +
  369 + if (_motionManager) {
  370 + [self stopUpdateAccelerometer];
  371 + }
  372 +
  373 + NSLog(@"XFCameraController 释放了");
  374 + [[NSNotificationCenter defaultCenter] removeObserver:self];
  375 +}
  376 +
  377 +- (void)didReceiveMemoryWarning
  378 +{
  379 + [super didReceiveMemoryWarning];
  380 +
  381 + NSLog(@"输出内存警告");
  382 +
  383 +
  384 +}
  385 +
  386 +#pragma mark - 控件方法
  387 +
  388 +/**
  389 + * 关闭当前界面
  390 + */
  391 +- (void)closeBtnFunc
  392 +{
  393 + [self removePlayerItemNotification];
  394 + [self dismissViewControllerAnimated:YES completion:nil];
  395 +}
  396 +
  397 +/**
  398 + * 切换前后摄像头
  399 + */
  400 +- (void)rotateCameraBtnFunc:(UIButton *)btn
  401 +{
  402 + btn.selected = !btn.selected;
  403 + if (btn.selected) {
  404 + self.flashButton.hidden = YES;
  405 + self.flashButton.selected = NO;
  406 + } else {
  407 + self.flashButton.hidden = NO;
  408 + }
  409 + _isRotatingCamera = YES;
  410 +
  411 + AVCaptureDevice *currentDevice = [self.videoInput device];
  412 + AVCaptureDevicePosition currentPosition = [currentDevice position];
  413 +
  414 + AVCaptureDevice *toChangeDevice;
  415 + AVCaptureDevicePosition toChangePosition = AVCaptureDevicePositionFront;
  416 + if (currentPosition == AVCaptureDevicePositionUnspecified || currentPosition == AVCaptureDevicePositionFront)
  417 + {
  418 + toChangePosition = AVCaptureDevicePositionBack;
  419 + }
  420 + toChangeDevice = [self getCameraDeviceWithPosition:toChangePosition];
  421 +
  422 + //获得要调整的设备输入对象
  423 + AVCaptureDeviceInput *toChangeDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:toChangeDevice error:nil];
  424 +
  425 + //改变会话的配置前一定要先开启配置,配置完成后提交配置改变
  426 + [self.captureSession beginConfiguration];
  427 + //移除原有输入对象
  428 + [self.captureSession removeInput:self.videoInput];
  429 + //添加新的输入对象
  430 + if ([self.captureSession canAddInput:toChangeDeviceInput])
  431 + {
  432 + [self.captureSession addInput:toChangeDeviceInput];
  433 + self.videoInput = toChangeDeviceInput;
  434 + }
  435 +
  436 + //提交会话配置
  437 + [self.captureSession commitConfiguration];
  438 +
  439 + _isRotatingCamera = NO;
  440 +}
  441 +
  442 +- (void)flashBtnFunc:(UIButton *)btn
  443 +{
  444 + btn.selected = !btn.selected;
  445 +
  446 + if (btn.selected)
  447 + {
  448 + [self openFlashlight];
  449 + }else
  450 + {
  451 + [self closeFlashlight];
  452 + }
  453 +}
  454 +
  455 +- (void)cancelBtnfunc
  456 +{
  457 + _isTakingPhoto = NO;
  458 + [self removePlayerItemNotification];
  459 +
  460 + [self startAnimationGroup];
  461 +
  462 +}
  463 +
  464 +/**
  465 + * 确认按钮并返回代理
  466 + */
  467 +- (void)confirmBtnFunc
  468 +{
  469 +
  470 + if (self.playerLayer) {
  471 + self.playerLayer.hidden = YES;
  472 + }
  473 +
  474 + if (self.captureVideoPreviewLayer) {
  475 + self.captureVideoPreviewLayer.hidden = NO;
  476 + }
  477 +
  478 + __weak typeof(self) weakSelf = self;
  479 + if (self.photoPreviewImageView)
  480 + {
  481 + UIImage *finalImage = [self cutImageWithView:self.photoPreviewImageView];
  482 +
  483 + // [XFPhotoLibraryManager savePhotoWithImage:finalImage andAssetCollectionName:self.assetCollectionName withCompletion:^(UIImage *image, NSError *error) {
  484 + //
  485 + if (self.takePhotosCompletionBlock)
  486 + {
  487 + // if (error)
  488 + // {
  489 + // NSLog(@"保存照片失败!");
  490 + // weakSelf.takePhotosCompletionBlock(nil, error);
  491 + // }
  492 + // else
  493 + // {
  494 + // NSLog(@"保存照片成功!");
  495 + weakSelf.takePhotosCompletionBlock(finalImage, nil);
  496 + // }
  497 + }
  498 +
  499 + // }];
  500 +
  501 + self.confirmButton.userInteractionEnabled = NO;
  502 +
  503 + }
  504 + else
  505 + {
  506 + [self.player.currentItem cancelPendingSeeks];
  507 + [self.player.currentItem.asset cancelLoading];
  508 + [self.player pause];
  509 +
  510 + _playerItem = nil;
  511 + [self.player replaceCurrentItemWithPlayerItem:nil];
  512 + self.player = nil;
  513 +
  514 + [_playerLayer removeFromSuperlayer];
  515 + _playerLayer = nil;
  516 +
  517 + [weakSelf cropWithVideoUrlStr:weakSelf.videoURL start:0 end:weakSelf.currentVideoTimeLength completion:^(NSURL *outputURL, Float64 videoDuration, BOOL isSuccess) {
  518 +
  519 + if (isSuccess)
  520 + {
  521 +
  522 + [XFPhotoLibraryManager zx_saveVideoWithVideoUrl:outputURL andAssetCollectionName:nil withCompletion:^(PHAsset *asset, NSURL *videoUrl, NSError *error) {
  523 + if (weakSelf.zxshootCompletionBlock)
  524 + {
  525 + if (error)
  526 + {
  527 + NSLog(@"保存视频失败!");
  528 + weakSelf.zxshootCompletionBlock(asset,nil, 0, nil, error);
  529 + }
  530 + else
  531 + {
  532 + NSLog(@"保存视频成功!");
  533 +
  534 + // 获取视频的第一帧图片
  535 + UIImage *image = [weakSelf thumbnailImageRequestWithVideoUrl:videoUrl andTime:0.01f];
  536 +
  537 +
  538 + weakSelf.zxshootCompletionBlock(asset,videoUrl, videoDuration, image, nil);
  539 +
  540 + [[NSFileManager defaultManager] removeItemAtURL:weakSelf.videoURL error:nil];
  541 + weakSelf.videoURL = nil;
  542 + }
  543 + }
  544 +
  545 + weakSelf.confirmButton.userInteractionEnabled = NO;
  546 + }];
  547 +
  548 + // [XFPhotoLibraryManager saveVideoWithVideoUrl:outputURL andAssetCollectionName:nil withCompletion:^(NSURL *videoUrl, NSError *error) {
  549 + //
  550 + // if (self.shootCompletionBlock)
  551 + // {
  552 + // if (error)
  553 + // {
  554 + // NSLog(@"保存视频失败!");
  555 + // weakSelf.shootCompletionBlock(nil, 0, nil, error);
  556 + // }
  557 + // else
  558 + // {
  559 + // NSLog(@"保存视频成功!");
  560 + //
  561 + // // 获取视频的第一帧图片
  562 + // UIImage *image = [weakSelf thumbnailImageRequestWithVideoUrl:videoUrl andTime:0.01f];
  563 + //
  564 + // weakSelf.shootCompletionBlock(videoUrl, videoDuration, image, nil);
  565 + //
  566 + // [[NSFileManager defaultManager] removeItemAtURL:weakSelf.videoURL error:nil];
  567 + // weakSelf.videoURL = nil;
  568 + // }
  569 + // }
  570 + //
  571 + // weakSelf.confirmButton.userInteractionEnabled = NO;
  572 + //
  573 + // }];
  574 + }
  575 + else
  576 + {
  577 + NSLog(@"保存视频失败!");
  578 + [[NSFileManager defaultManager] removeItemAtURL:weakSelf.videoURL error:nil];
  579 + weakSelf.videoURL = nil;
  580 + [[NSFileManager defaultManager] removeItemAtURL:outputURL error:nil];
  581 + }
  582 + }];
  583 + }
  584 +}
  585 +#pragma mark - 懒加载
  586 +- (AVCaptureSession *)captureSession
  587 +{
  588 + if (_captureSession == nil)
  589 + {
  590 + _captureSession = [[AVCaptureSession alloc] init];
  591 +
  592 + if ([_captureSession canSetSessionPreset:AVCaptureSessionPresetHigh])
  593 + {
  594 + _captureSession.sessionPreset = AVCaptureSessionPresetHigh;
  595 + }
  596 + }
  597 +
  598 + return _captureSession;
  599 +}
  600 +
  601 +- (dispatch_queue_t)videoQueue
  602 +{
  603 + if (!_videoQueue)
  604 + {
  605 + _videoQueue = dispatch_get_main_queue();
  606 + }
  607 +
  608 + return _videoQueue;
  609 +}
  610 +
  611 +- (CMMotionManager *)motionManager
  612 +{
  613 + if (!_motionManager)
  614 + {
  615 + _motionManager = [[CMMotionManager alloc] init];
  616 + }
  617 + return _motionManager;
  618 +}
  619 +
  620 +- (UIButton *)rotateCameraButton
  621 +{
  622 + if (!_rotateCameraButton)
  623 + {
  624 + _rotateCameraButton = [[UIButton alloc] init];
  625 + [_rotateCameraButton setImage:[UIImage imageNamedFromMyBundle:@"fb_xiangji"] forState:UIControlStateNormal];
  626 + [_rotateCameraButton addTarget:self action:@selector(rotateCameraBtnFunc:) forControlEvents:UIControlEventTouchUpInside];
  627 + }
  628 + return _rotateCameraButton;
  629 +}
  630 +
  631 +- (UIButton *)flashButton
  632 +{
  633 + if (!_flashButton)
  634 + {
  635 + _flashButton = [[UIButton alloc] init];
  636 + [_flashButton setImage:[UIImage imageNamedFromMyBundle:@"gz_shoudian"] forState:UIControlStateNormal];
  637 + [_flashButton setImage:[UIImage imageNamedFromMyBundle:@"gz_shoudian2"] forState:UIControlStateSelected];
  638 + [_flashButton addTarget:self action:@selector(flashBtnFunc:) forControlEvents:UIControlEventTouchUpInside];
  639 + }
  640 + return _flashButton;
  641 +}
  642 +
  643 +//拍摄按钮
  644 +- (UIButton *)takeButton
  645 +{
  646 + if (!_takeButton)
  647 + {
  648 + _takeButton = [[UIButton alloc] init];
  649 + }
  650 +
  651 + return _takeButton;
  652 +}
  653 +
  654 +- (UIButton *)closeButton
  655 +{
  656 + if (!_closeButton)
  657 + {
  658 + _closeButton = [[UIButton alloc] init];
  659 + [_closeButton setImage:[UIImage imageNamedFromMyBundle:@"fb_quxiaoxia"] forState:UIControlStateNormal];
  660 + [_closeButton addTarget:self action:@selector(closeBtnFunc) forControlEvents:UIControlEventTouchUpInside];
  661 + }
  662 + return _closeButton;
  663 +}
  664 +
  665 +- (UILabel *)tipLabel
  666 +{
  667 + if (!_tipLabel)
  668 + {
  669 + _tipLabel = [[UILabel alloc] init];
  670 + _tipLabel.font = UIFontMake(14);
  671 + _tipLabel.textColor = kWhiteColor;
  672 + }
  673 + return _tipLabel;
  674 +}
  675 +
  676 +- (UIImageView *)focusImageView
  677 +{
  678 + if (!_focusImageView)
  679 + {
  680 + _focusImageView = [[UIImageView alloc] init];
  681 + _focusImageView.image = [UIImage imageNamedFromMyBundle:@"sight_video_focus"];
  682 + }
  683 + return _focusImageView;
  684 +}
  685 +#pragma mark -拍摄后的预览模块的懒加载
  686 +// 拍照摄像后的预览模块
  687 +- (UIButton *)cancelButton
  688 +{
  689 + if (!_cancelButton)
  690 + {
  691 + _cancelButton = [[UIButton alloc] init];
  692 + [_cancelButton setImage:[UIImage imageNamedFromMyBundle:@"fb_chongpai"] forState:UIControlStateNormal];
  693 + [_cancelButton addTarget:self action:@selector(cancelBtnfunc) forControlEvents:UIControlEventTouchUpInside];
  694 + }
  695 + return _cancelButton;
  696 +}
  697 +
  698 +// 拍照摄像后的预览模块
  699 +- (UIButton *)confirmButton
  700 +{
  701 + if (!_confirmButton)
  702 + {
  703 + _confirmButton = [[UIButton alloc] init];
  704 + [_confirmButton setImage:[UIImage imageNamedFromMyBundle:@"fb_wancheng"] forState:UIControlStateNormal];
  705 + [_confirmButton addTarget:self action:@selector(confirmBtnFunc) forControlEvents:UIControlEventTouchUpInside];
  706 + _confirmButton.cs_acceptEventInterval = 2.0f;
  707 +
  708 + }
  709 + return _confirmButton;
  710 +}
  711 +
  712 +
  713 +
  714 +#pragma mark - 私有方法
  715 +
  716 +/** 打开手电筒 */
  717 +- (void)openFlashlight {
  718 + AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
  719 + NSError *error = nil;
  720 + if ([captureDevice hasTorch]) {
  721 + BOOL locked = [captureDevice lockForConfiguration:&error];
  722 + if (locked) {
  723 + captureDevice.torchMode = AVCaptureTorchModeOn;
  724 + [captureDevice unlockForConfiguration];
  725 + }
  726 + }
  727 +}
  728 +/** 关闭手电筒 */
  729 +- (void)closeFlashlight {
  730 + AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
  731 + if ([device hasTorch]) {
  732 + [device lockForConfiguration:nil];
  733 + [device setTorchMode: AVCaptureTorchModeOff];
  734 + [device unlockForConfiguration];
  735 + }
  736 +}
  737 +
  738 +
  739 +/**
  740 + * 初始化AVCapture会话
  741 + */
  742 +- (void)initAVCaptureSession
  743 +{
  744 + //1、添加 "视频" 与 "音频" 输入流到session
  745 + [self setupVideo];
  746 +
  747 + [self setupAudio];
  748 +
  749 + //2、添加图片,movie输出流到session
  750 + [self setupCaptureStillImageOutput];
  751 +
  752 + //3、创建视频预览层,用于实时展示摄像头状态
  753 + [self setupCaptureVideoPreviewLayer];
  754 +
  755 + //设置静音状态也可播放声音
  756 + AVAudioSession *audioSession = [AVAudioSession sharedInstance];
  757 + [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
  758 +}
  759 +
  760 +/**
  761 + * 设置视频输入
  762 + */
  763 +- (void)setupVideo
  764 +{
  765 + AVCaptureDevice *captureDevice = [self getCameraDeviceWithPosition:AVCaptureDevicePositionBack];
  766 + if (!captureDevice)
  767 + {
  768 + NSLog(@"取得后置摄像头时出现问题.");
  769 +
  770 + return;
  771 + }
  772 +
  773 + NSError *error = nil;
  774 + self.videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:captureDevice error:&error];
  775 + if (error)
  776 + {
  777 + NSLog(@"取得设备输入videoInput对象时出错,错误原因:%@", error);
  778 +
  779 + return;
  780 + }
  781 +
  782 + //3、将设备输出添加到会话中
  783 + if ([self.captureSession canAddInput:self.videoInput])
  784 + {
  785 + [self.captureSession addInput:self.videoInput];
  786 + }
  787 +
  788 + self.videoOutput = [[AVCaptureVideoDataOutput alloc] init];
  789 + self.videoOutput.alwaysDiscardsLateVideoFrames = YES; //立即丢弃旧帧,节省内存,默认YES
  790 + [self.videoOutput setSampleBufferDelegate:self queue:self.videoQueue];
  791 + if ([self.captureSession canAddOutput:self.videoOutput])
  792 + {
  793 + [self.captureSession addOutput:self.videoOutput];
  794 + }
  795 +}
  796 +
  797 +/**
  798 + * 设置音频录入
  799 + */
  800 +- (void)setupAudio
  801 +{
  802 + NSError *error = nil;
  803 + self.audioInput = [[AVCaptureDeviceInput alloc] initWithDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio] error:&error];
  804 + if (error)
  805 + {
  806 + NSLog(@"取得设备输入audioInput对象时出错,错误原因:%@", error);
  807 +
  808 + return;
  809 + }
  810 + if ([self.captureSession canAddInput:self.audioInput])
  811 + {
  812 + [self.captureSession addInput:self.audioInput];
  813 + }
  814 +
  815 + self.audioOutput = [[AVCaptureAudioDataOutput alloc] init];
  816 + [self.audioOutput setSampleBufferDelegate:self queue:self.videoQueue];
  817 + if([self.captureSession canAddOutput:self.audioOutput])
  818 + {
  819 + [self.captureSession addOutput:self.audioOutput];
  820 + }
  821 +}
  822 +
  823 +/**
  824 + * 设置图片输出
  825 + */
  826 +- (void)setupCaptureStillImageOutput
  827 +{
  828 + self.captureStillImageOutput = [[AVCaptureStillImageOutput alloc] init];
  829 + NSDictionary *outputSettings = @{
  830 + AVVideoCodecKey:AVVideoCodecJPEG
  831 + };
  832 + [_captureStillImageOutput setOutputSettings:outputSettings];
  833 +
  834 + if ([self.captureSession canAddOutput:_captureStillImageOutput])
  835 + {
  836 + [self.captureSession addOutput:_captureStillImageOutput];
  837 + }
  838 +}
  839 +
  840 +/**
  841 + * 设置预览layer
  842 + */
  843 +- (void)setupCaptureVideoPreviewLayer
  844 +{
  845 + _captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
  846 +
  847 + CALayer *layer = self.view.layer;
  848 +
  849 + _captureVideoPreviewLayer.frame = CGRectMake(0, 0, k_ScreenWidth, KScreenHeight);
  850 + _captureVideoPreviewLayer.xf_centerY = self.view.centerY;
  851 +
  852 + _captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspect; //填充模式
  853 +
  854 + [layer addSublayer:_captureVideoPreviewLayer];
  855 +}
  856 +
  857 +/**
  858 + * 开启会话
  859 + */
  860 +- (void)startSession
  861 +{
  862 + if (![self.captureSession isRunning])
  863 + {
  864 + [self.captureSession startRunning];
  865 + }
  866 +}
  867 +
  868 +/**
  869 + * 停止会话
  870 + */
  871 +- (void)stopSession
  872 +{
  873 + if ([self.captureSession isRunning])
  874 + {
  875 + [self.captureSession stopRunning];
  876 + }
  877 +}
  878 +
  879 +/**
  880 + * 开始拍照录像动画组合
  881 + */
  882 +- (void)startAnimationGroup
  883 +{
  884 + [self configDefaultUIDisplay];
  885 +
  886 + [self setFocusCursorWithPoint:self.view.center];
  887 +
  888 +
  889 +
  890 + [self tipLabelAnimation];
  891 +}
  892 +
  893 +/**
  894 + * 配置默认UI信息
  895 + */
  896 +- (void)configDefaultUIDisplay
  897 +{
  898 + _isPicOrVideo = NO;
  899 + if (self.photoPreviewImageView)//图片设置置空
  900 + {
  901 + [self.photoPreviewImageView removeFromSuperview];
  902 + [self.photoPreviewContainerView removeFromSuperview];
  903 + self.photoPreviewImageView = nil;
  904 + self.photoPreviewContainerView = nil;
  905 + }
  906 + if (self.playerLayer)//视频设置置空
  907 + {
  908 + [self.player pause];
  909 + self.player = nil;
  910 + self.playerItem = nil;
  911 + self.playerLayer.hidden = YES;
  912 + [self.playerLayer removeFromSuperlayer];
  913 + self.playerLayer = nil;
  914 + self.cameraButton.progressPercentage = 0.0f;
  915 + self.videoPreviewContainerView = nil;
  916 + [[NSFileManager defaultManager] removeItemAtURL:self.videoURL error:nil];
  917 + self.videoURL = nil;
  918 + }
  919 + if (self.captureVideoPreviewLayer) {
  920 + self.captureVideoPreviewLayer.hidden = NO;
  921 + }
  922 +
  923 +
  924 + [self.view.layer qmui_bringSublayerToFront:_captureVideoPreviewLayer];
  925 + [self.view bringSubviewToFront:self.rotateCameraButton];
  926 + [self.view bringSubviewToFront:self.flashButton];
  927 + [self.view bringSubviewToFront:self.closeButton];
  928 + [self.rotateCameraButton setHidden:NO];
  929 + [self.flashButton setHidden:NO];
  930 + [self.closeButton setHidden:NO];
  931 +
  932 +
  933 + [self.view bringSubviewToFront:self.tipLabel];
  934 + [self.tipLabel setAlpha:0];
  935 + [self.cancelButton setHidden:YES];
  936 + [self.confirmButton setHidden:YES];
  937 +
  938 + // 设置拍照按钮
  939 + if (_cameraButton == nil)
  940 + {
  941 + XFCameraButton *cameraButton = [XFCameraButton defaultCameraButton];
  942 + self.cameraButton = cameraButton;
  943 +
  944 + [self.view addSubview:cameraButton];
  945 + CGFloat cameraBtnX = (k_ScreenWidth - cameraButton.bounds.size.width) / 2;
  946 +
  947 + CGFloat bottom = IS_IPhoneX_All ? 90 + k_SafeViewBottomHeight : 20+30;
  948 +
  949 + CGFloat cameraBtnY = k_ScreenHeight - cameraButton.bounds.size.height - bottom; //距离底部60
  950 + cameraButton.frame = CGRectMake(cameraBtnX, cameraBtnY, cameraButton.bounds.size.width, cameraButton.bounds.size.height);
  951 + [self.view bringSubviewToFront:cameraButton];
  952 +
  953 + // 设置拍照按钮点击事件
  954 + __weak typeof(self) weakSelf = self;
  955 + // 配置拍照方法
  956 + if (!self.isNoCanTakePhoto) {
  957 + [self.cameraButton configureTapCameraButtonEventWithBlock:^(UITapGestureRecognizer *tapGestureRecognizer) {
  958 + if (weakSelf.isTakingPhoto) {
  959 + return;
  960 + }
  961 + /*** 关闭散光灯 ***/
  962 + weakSelf.flashButton.selected = NO;
  963 + [weakSelf closeFlashlight];
  964 + [weakSelf requestAuthCamera];
  965 + [weakSelf takePhotos:tapGestureRecognizer];
  966 +
  967 +
  968 + }];
  969 + }
  970 +
  971 + if (self.isCanShootVideo) {
  972 + // 配置拍摄方法
  973 + [self.cameraButton configureLongPressCameraButtonEventWithBlock:^(UILongPressGestureRecognizer *longPressGestureRecognizer) {
  974 +
  975 + [weakSelf requestAuthorizationForVideo];
  976 + [weakSelf longPressCameraButtonFunc:longPressGestureRecognizer];
  977 +
  978 + }];
  979 + }
  980 + }
  981 + [self.cameraButton setHidden:NO];
  982 + [self.view bringSubviewToFront:self.cameraButton];
  983 +
  984 + [self setCaptureVideoPreviewLayerTransformWithScale:1.0f];
  985 +
  986 + // 对焦imageView
  987 + [self.view bringSubviewToFront:self.focusImageView];
  988 + [self.focusImageView setAlpha:0];
  989 +
  990 + // 监听屏幕方向
  991 + [self startUpdateAccelerometer];
  992 +}
  993 +
  994 +/**
  995 + * 提示语动画
  996 + */
  997 +- (void)tipLabelAnimation
  998 +{
  999 + [self.view bringSubviewToFront:self.tipLabel];
  1000 +
  1001 + __weak typeof(self) weakSelf = self;
  1002 + [UIView animateWithDuration:1.0f delay:0.5f options:UIViewAnimationOptionCurveEaseInOut animations:^{
  1003 +
  1004 + [weakSelf.tipLabel setAlpha:1];
  1005 +
  1006 + } completion:^(BOOL finished) {
  1007 +
  1008 + [UIView animateWithDuration:1.0f delay:3.0f options:UIViewAnimationOptionCurveEaseInOut animations:^{
  1009 +
  1010 + [weakSelf.tipLabel setAlpha:0];
  1011 +
  1012 + } completion:nil];
  1013 +
  1014 + }];
  1015 +}
  1016 +
  1017 +/**
  1018 + * 取得指定位置的摄像头
  1019 + *
  1020 + * @param position 摄像头位置
  1021 + *
  1022 + * @return 摄像头设备
  1023 + */
  1024 +- (AVCaptureDevice *)getCameraDeviceWithPosition:(AVCaptureDevicePosition )position
  1025 +{
  1026 + NSArray *cameras = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
  1027 + for (AVCaptureDevice *camera in cameras)
  1028 + {
  1029 + if ([camera position] == position)
  1030 + {
  1031 + return camera;
  1032 + }
  1033 + }
  1034 + return nil;
  1035 +}
  1036 +
  1037 +/**
  1038 + * 改变设备属性的统一操作方法
  1039 + *
  1040 + * @param propertyChange 属性改变操作
  1041 + */
  1042 +- (void)changeDeviceProperty:(PropertyChangeBlock)propertyChange
  1043 +{
  1044 + AVCaptureDevice *captureDevice = [self.videoInput device];
  1045 + NSError *error;
  1046 +
  1047 + //注意改变设备属性前一定要首先调用lockForConfiguration:调用完之后使用unlockForConfiguration方法解锁
  1048 + if ([captureDevice lockForConfiguration:&error])
  1049 + {
  1050 + propertyChange(captureDevice);
  1051 + [captureDevice unlockForConfiguration];
  1052 + }
  1053 + else
  1054 + {
  1055 + NSLog(@"设置设备属性过程发生错误,错误信息:%@",error.localizedDescription);
  1056 + }
  1057 +}
  1058 +
  1059 +#pragma mark - 拍照功能
  1060 +
  1061 +/**
  1062 + * 拍照方法
  1063 + */
  1064 +- (void)takePhotos:(UITapGestureRecognizer *)tapGestureRecognizer
  1065 +{
  1066 +
  1067 + __weak typeof(self) weakSelf = self;
  1068 + // 请求相机权限
  1069 + AVAuthorizationStatus videoAuthStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
  1070 + if (videoAuthStatus != AVAuthorizationStatusAuthorized)
  1071 + {
  1072 + NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
  1073 +
  1074 + NSString *appName = [infoDictionary objectForKey:@"CFBundleDisplayName"];
  1075 + if (appName == nil)
  1076 + {
  1077 + appName = @"APP";
  1078 + }
  1079 + NSString *message = [NSString stringWithFormat:@"请在iPhone的“设置-隐私”选项中,允许%@访问你的相册,相机和麦克风。", appName];
  1080 +
  1081 + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];//无法拍摄
  1082 +
  1083 + UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  1084 + [weakSelf dismissViewControllerAnimated:YES completion:nil];
  1085 + }];
  1086 +
  1087 + [alertController addAction:okAction];
  1088 + [self presentViewController:alertController animated:YES completion:nil];
  1089 +
  1090 + return;
  1091 + }
  1092 +
  1093 + // 请求麦克风权限
  1094 + AVAuthorizationStatus audioAuthStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
  1095 + if (audioAuthStatus != AVAuthorizationStatusAuthorized)
  1096 + {
  1097 + NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
  1098 +
  1099 + NSString *appName = [infoDictionary objectForKey:@"CFBundleDisplayName"];
  1100 + if (appName == nil)
  1101 + {
  1102 + appName = @"APP";
  1103 + }
  1104 + NSString *message = [NSString stringWithFormat:@"请在iPhone的“设置-隐私“选项中,允许%@访问你的相册,相机和麦克风。", appName];//[NSString stringWithFormat:@"请在iPhone的““设置-隐私””选项中, 允许%@访问你的麦克风?", appName];
  1105 +
  1106 + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];
  1107 +
  1108 + UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  1109 + [weakSelf dismissViewControllerAnimated:YES completion:nil];
  1110 + }];
  1111 +
  1112 + [alertController addAction:okAction];
  1113 +
  1114 + [self presentViewController:alertController animated:YES completion:nil];
  1115 +
  1116 + return;
  1117 + }
  1118 +
  1119 + _isTakingPhoto = YES;
  1120 + //根据设备输出获得连接
  1121 + AVCaptureConnection *captureConnection = [self.captureStillImageOutput connectionWithMediaType:AVMediaTypeVideo];
  1122 +
  1123 + [captureConnection setVideoScaleAndCropFactor:self.effectiveScale];
  1124 +
  1125 + //根据连接取得设备输出的数据
  1126 + [self.captureStillImageOutput captureStillImageAsynchronouslyFromConnection:captureConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
  1127 + if (imageDataSampleBuffer)
  1128 + {
  1129 + NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
  1130 + UIImage *image = [UIImage imageWithData:imageData];
  1131 +
  1132 + [self previewPhotoWithImage:image];
  1133 +// self.isSelectPhoto = YES;
  1134 + weakSelf.isTakingPhoto = YES;
  1135 + }else{
  1136 + weakSelf.isTakingPhoto = NO;
  1137 + }
  1138 +
  1139 + }];
  1140 +}
  1141 +
  1142 +/**
  1143 + * 预览图片
  1144 + */
  1145 +- (void)previewPhotoWithImage:(UIImage *)image
  1146 +{
  1147 + [self stopUpdateAccelerometer];
  1148 +
  1149 + [self.cameraButton setHidden:YES];
  1150 + [self.closeButton setHidden:YES];
  1151 + [self.rotateCameraButton setHidden:YES];
  1152 + [self.flashButton setHidden:YES];
  1153 +
  1154 + UIImage *finalImage = nil;
  1155 + if (self.shootingOrientation == UIDeviceOrientationLandscapeRight)
  1156 + {
  1157 + finalImage = [self rotateImage:image withOrientation:UIImageOrientationDown];
  1158 + }
  1159 + else if (self.shootingOrientation == UIDeviceOrientationLandscapeLeft)
  1160 + {
  1161 + finalImage = [self rotateImage:image withOrientation:UIImageOrientationUp];
  1162 + }
  1163 + else if (self.shootingOrientation == UIDeviceOrientationPortraitUpsideDown)
  1164 + {
  1165 + finalImage = [self rotateImage:image withOrientation:UIImageOrientationLeft];
  1166 + }
  1167 + else
  1168 + {
  1169 + finalImage = [self rotateImage:image withOrientation:UIImageOrientationRight];
  1170 + }
  1171 +
  1172 + self.photoPreviewImageView = [[UIImageView alloc] init];
  1173 + float videoRatio = finalImage.size.width / finalImage.size.height;
  1174 + if (self.shootingOrientation == UIDeviceOrientationLandscapeRight || self.shootingOrientation == UIDeviceOrientationLandscapeLeft)
  1175 + {
  1176 + CGFloat height = k_ScreenWidth * videoRatio;
  1177 + CGFloat y = (k_ScreenHeight - height) / 2;
  1178 + [self.photoPreviewImageView setFrame:CGRectMake(0, y, k_ScreenWidth, height)];
  1179 + }
  1180 + else
  1181 + {
  1182 + if (IS_IPhoneX_All) {
  1183 + [self.photoPreviewImageView setFrame:CGRectMake(0, 0, k_ScreenWidth, 667)];
  1184 + self.photoPreviewImageView.centerY = self.view.centerY;
  1185 + }else
  1186 + {
  1187 + [self.photoPreviewImageView setFrame:CGRectMake(0, 0, k_ScreenWidth, k_ScreenHeight)];
  1188 + }
  1189 + }
  1190 + self.photoPreviewImageView.image = finalImage;
  1191 + self.photoPreviewContainerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, k_ScreenWidth, k_ScreenHeight)];
  1192 + self.photoPreviewContainerView.backgroundColor = [UIColor blackColor];
  1193 + [self.photoPreviewContainerView addSubview:self.photoPreviewImageView];
  1194 + [self.view addSubview:self.photoPreviewContainerView];
  1195 + [self.view bringSubviewToFront:self.photoPreviewImageView];
  1196 + [self.view bringSubviewToFront:self.cancelButton];
  1197 + [self.view bringSubviewToFront:self.confirmButton];
  1198 + [self.cancelButton setHidden:NO];
  1199 + [self.confirmButton setHidden:NO];
  1200 + _isPicOrVideo = YES;
  1201 +}
  1202 +
  1203 +- (UIImage *)cutImageWithView:(UIView *)view
  1204 +{
  1205 + UIGraphicsBeginImageContextWithOptions(view.frame.size, NO, 0);
  1206 + [view.layer renderInContext:UIGraphicsGetCurrentContext()];
  1207 + UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  1208 + UIGraphicsEndImageContext();
  1209 +
  1210 + return image;
  1211 +}
  1212 +
  1213 +#pragma mark - 视频录制
  1214 +
  1215 +/**
  1216 + * 录制视频方法
  1217 + */
  1218 +- (void)longPressCameraButtonFunc:(UILongPressGestureRecognizer *)sender
  1219 +{
  1220 + AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
  1221 + if (authStatus == AVAuthorizationStatusRestricted || authStatus ==AVAuthorizationStatusDenied)
  1222 + {
  1223 + return;
  1224 + }
  1225 +
  1226 + //判断用户是否允许访问麦克风权限
  1227 + authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
  1228 + if (authStatus == AVAuthorizationStatusRestricted || authStatus ==AVAuthorizationStatusDenied)
  1229 + {
  1230 + return;
  1231 + }
  1232 +
  1233 + switch (sender.state) {
  1234 + case UIGestureRecognizerStateBegan:
  1235 + [self startVideoRecorder];
  1236 + break;
  1237 + case UIGestureRecognizerStateCancelled:
  1238 + [self stopVideoRecorder];
  1239 + break;
  1240 + case UIGestureRecognizerStateEnded:
  1241 + [self stopVideoRecorder];
  1242 + break;
  1243 + case UIGestureRecognizerStateFailed:
  1244 + [self stopVideoRecorder];
  1245 + break;
  1246 + default:
  1247 + break;
  1248 + }
  1249 +
  1250 +}
  1251 +
  1252 +/**
  1253 + * 开始录制视频
  1254 + */
  1255 +- (void)startVideoRecorder
  1256 +{
  1257 + _isShooting = YES;
  1258 +
  1259 + [self stopUpdateAccelerometer];
  1260 +
  1261 + [self setCaptureVideoPreviewLayerTransformWithScale:1.0f];
  1262 +
  1263 + [self.cameraButton startShootAnimationWithDuration:START_VIDEO_ANIMATION_DURATION];
  1264 +
  1265 + __weak typeof(self) weakSelf = self;
  1266 + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(START_VIDEO_ANIMATION_DURATION * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  1267 +
  1268 + NSURL *url = [NSURL fileURLWithPath:[weakSelf createVideoFilePath]];
  1269 + self.videoURL = url;
  1270 +
  1271 + [self setUpWriter];
  1272 +
  1273 + [weakSelf timerFired];
  1274 +
  1275 + });
  1276 +}
  1277 +
  1278 +/**
  1279 + * 结束录制视频
  1280 + */
  1281 +- (void)stopVideoRecorder
  1282 +{
  1283 + if (_isShooting)
  1284 + {
  1285 + _isShooting = NO;
  1286 + self.cameraButton.progressPercentage = 0.0f;
  1287 + [self.cameraButton stopShootAnimation];
  1288 + [self timerStop];
  1289 +
  1290 + __weak __typeof(self)weakSelf = self;
  1291 + if(_assetWriter && _assetWriter.status == AVAssetWriterStatusWriting)
  1292 + {
  1293 + // dispatch_async(self.videoQueue, ^{
  1294 + [_assetWriter finishWritingWithCompletionHandler:^{
  1295 + weakSelf.canWrite = NO;
  1296 + weakSelf.assetWriter = nil;
  1297 + weakSelf.assetWriterAudioInput = nil;
  1298 + weakSelf.assetWriterVideoInput = nil;
  1299 + }];
  1300 + // });
  1301 + }
  1302 +
  1303 + if (timeLength < VIDEO_RECORDER_MIN_TIME)
  1304 + {
  1305 + NSString *message = [NSString stringWithFormat:@"拍摄时长需要大于3秒"];
  1306 + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];//无法拍摄
  1307 +
  1308 + UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  1309 +
  1310 + }];
  1311 +
  1312 + [alertController addAction:okAction];
  1313 + [self presentViewController:alertController animated:YES completion:nil];
  1314 +
  1315 +
  1316 + return;
  1317 + }
  1318 +
  1319 + [self.confirmButton setHidden:NO];
  1320 + [self.cancelButton setHidden:NO];
  1321 + [self.cameraButton setHidden:YES];
  1322 +
  1323 + /*** 关闭散光灯 ***/
  1324 + self.flashButton.selected = NO;
  1325 + [self closeFlashlight];
  1326 +
  1327 + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  1328 +
  1329 + [weakSelf previewVideoAfterShoot];
  1330 +
  1331 + });
  1332 + }
  1333 + else
  1334 + {
  1335 + // nothing
  1336 + }
  1337 +}
  1338 +
  1339 +/**
  1340 + * 设置写入视频属性
  1341 + */
  1342 +- (void)setUpWriter
  1343 +{
  1344 + if (self.videoURL == nil)
  1345 + {
  1346 + return;
  1347 + }
  1348 +
  1349 + self.assetWriter = [AVAssetWriter assetWriterWithURL:self.videoURL fileType:AVFileTypeMPEG4 error:nil];
  1350 + //写入视频大小
  1351 + NSInteger numPixels = k_ScreenWidth * k_ScreenHeight;
  1352 + //每像素比特
  1353 + CGFloat bitsPerPixel = 16.0;
  1354 + NSInteger bitsPerSecond = numPixels * bitsPerPixel;
  1355 +
  1356 + // 码率和帧率设置
  1357 + NSDictionary *compressionProperties = @{ AVVideoAverageBitRateKey : @(bitsPerSecond),
  1358 + AVVideoExpectedSourceFrameRateKey : @(30),
  1359 + AVVideoMaxKeyFrameIntervalKey : @(30),
  1360 + AVVideoProfileLevelKey : AVVideoProfileLevelH264BaselineAutoLevel };
  1361 +
  1362 + //视频属性
  1363 + CGFloat videoWidthKey = k_ScreenHeight;
  1364 + if (IS_IPhoneX_All) {
  1365 + videoWidthKey = 667;
  1366 + }
  1367 + self.videoCompressionSettings = @{ AVVideoCodecKey : AVVideoCodecH264,
  1368 + AVVideoScalingModeKey : AVVideoScalingModeResizeAspectFill,
  1369 + AVVideoWidthKey : @(videoWidthKey * 2),
  1370 + AVVideoHeightKey : @(k_ScreenWidth * 2),
  1371 + AVVideoCompressionPropertiesKey : compressionProperties };
  1372 + _assetWriterVideoInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:self.videoCompressionSettings];
  1373 + //expectsMediaDataInRealTime 必须设为yes,需要从capture session 实时获取数据
  1374 + _assetWriterVideoInput.expectsMediaDataInRealTime = YES;
  1375 +
  1376 + if (self.shootingOrientation == UIDeviceOrientationLandscapeRight)
  1377 + {
  1378 + _assetWriterVideoInput.transform = CGAffineTransformMakeRotation(M_PI);
  1379 + }
  1380 + else if (self.shootingOrientation == UIDeviceOrientationLandscapeLeft)
  1381 + {
  1382 + _assetWriterVideoInput.transform = CGAffineTransformMakeRotation(0);
  1383 + }
  1384 + else if (self.shootingOrientation == UIDeviceOrientationPortraitUpsideDown)
  1385 + {
  1386 + _assetWriterVideoInput.transform = CGAffineTransformMakeRotation(M_PI + (M_PI / 2.0));
  1387 + }
  1388 + else
  1389 + {
  1390 + _assetWriterVideoInput.transform = CGAffineTransformMakeRotation(M_PI / 2.0);
  1391 + }
  1392 +
  1393 + // 音频设置
  1394 + self.audioCompressionSettings = @{ AVEncoderBitRatePerChannelKey : @(28000),
  1395 + AVFormatIDKey : @(kAudioFormatMPEG4AAC),
  1396 + AVNumberOfChannelsKey : @(1),
  1397 + AVSampleRateKey : @(22050) };
  1398 +
  1399 + _assetWriterAudioInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings:self.audioCompressionSettings];
  1400 + _assetWriterAudioInput.expectsMediaDataInRealTime = YES;
  1401 +
  1402 + if ([_assetWriter canAddInput:_assetWriterVideoInput])
  1403 + {
  1404 + [_assetWriter addInput:_assetWriterVideoInput];
  1405 + }
  1406 + else
  1407 + {
  1408 + NSLog(@"AssetWriter videoInput append Failed");
  1409 + }
  1410 +
  1411 + if ([_assetWriter canAddInput:_assetWriterAudioInput])
  1412 + {
  1413 + [_assetWriter addInput:_assetWriterAudioInput];
  1414 + }
  1415 + else
  1416 + {
  1417 + NSLog(@"AssetWriter audioInput Append Failed");
  1418 + }
  1419 +
  1420 + _canWrite = NO;
  1421 +}
  1422 +
  1423 +- (NSString *)createVideoFilePath
  1424 +{
  1425 + // 创建视频文件的存储路径
  1426 + NSString *filePath = [self createVideoFolderPath];
  1427 + if (filePath == nil)
  1428 + {
  1429 + return nil;
  1430 + }
  1431 +
  1432 + NSString *videoType = @".mp4";
  1433 + NSString *videoDestDateString = [self createFileNamePrefix];
  1434 + NSString *videoFileName = [videoDestDateString stringByAppendingString:videoType];
  1435 +
  1436 + NSUInteger idx = 1;
  1437 + /*We only allow 10000 same file name*/
  1438 + NSString *finalPath = [NSString stringWithFormat:@"%@/fileType=videoType_%@", filePath, videoFileName];
  1439 +
  1440 + while (idx % 10000 && [[NSFileManager defaultManager] fileExistsAtPath:finalPath])
  1441 + {
  1442 + finalPath = [NSString stringWithFormat:@"%@/%@_(%lu)%@", filePath, videoDestDateString, (unsigned long)idx++, videoType];
  1443 + }
  1444 +
  1445 + return finalPath;
  1446 +}
  1447 +
  1448 +- (NSString *)createVideoFolderPath
  1449 +{
  1450 +
  1451 +#pragma mark - TODO: 存储路径
  1452 +
  1453 +// if (self.conversationId) {
  1454 +// return [CNLiveBusinessTools newChatFilePathByUserId:self.conversationId];
  1455 +// }
  1456 +// return [CNLiveBusinessTools chatFilePathByUserId:[IMAPlatform sharedInstance].host.userId];
  1457 +
  1458 + return [self chatFilePathByName:@"相册"];
  1459 +
  1460 +}
  1461 +
  1462 +//用户ID
  1463 +- (nullable NSString *)chatFilePathByName:(NSString *_Nonnull)name{
  1464 +
  1465 + // NSHomeDirectory():应用程序目录, @"tmp/temp":在tmp文件夹下创建temp 文件夹
  1466 + NSString *tempName = [NSString stringWithFormat:@"%@/",name];
  1467 +
  1468 + NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:tempName];
  1469 +
  1470 + NSFileManager *fileManager = [NSFileManager defaultManager];
  1471 +
  1472 + BOOL isDir = NO;
  1473 +
  1474 + // fileExistsAtPath 判断一个文件或目录是否有效,isDirectory判断是否一个目录
  1475 + BOOL existed = [fileManager fileExistsAtPath:filePath isDirectory:&isDir];
  1476 +
  1477 + if ( !(isDir == YES && existed == YES) ) {
  1478 +
  1479 + // 在 temp 目录下创建一临时文件
  1480 + [fileManager createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:nil];
  1481 + }
  1482 +
  1483 + return filePath;
  1484 +}
  1485 +
  1486 +
  1487 +/**
  1488 + * 创建文件名
  1489 + *
  1490 + * 文件名类型
  1491 + */
  1492 +- (NSString *)createFileNamePrefix
  1493 +{
  1494 + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
  1495 + [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss zzz"];
  1496 +
  1497 + NSString *destDateString = [dateFormatter stringFromDate:[NSDate date]];
  1498 + destDateString = [destDateString stringByReplacingOccurrencesOfString:@" " withString:@"-"];
  1499 + destDateString = [destDateString stringByReplacingOccurrencesOfString:@"+" withString:@"-"];
  1500 + destDateString = [destDateString stringByReplacingOccurrencesOfString:@":" withString:@"-"];
  1501 +
  1502 + return destDateString;
  1503 +}
  1504 +
  1505 +/**
  1506 + * 开启定时器
  1507 + */
  1508 +- (void)timerFired
  1509 +{
  1510 + timeLength = 0;
  1511 + self.timer = [NSTimer scheduledTimerWithTimeInterval:TIMER_INTERVAL target:self selector:@selector(timerRecord) userInfo:nil repeats:YES];
  1512 +}
  1513 +
  1514 +/**
  1515 + * 绿色转圈百分比计算
  1516 + */
  1517 +- (void)timerRecord
  1518 +{
  1519 + if (!_isShooting)
  1520 + {
  1521 + [self timerStop];
  1522 + return ;
  1523 + }
  1524 +
  1525 + // 时间大于VIDEO_RECORDER_MAX_TIME则停止录制
  1526 + if (timeLength > VIDEO_RECORDER_MAX_TIME)
  1527 + {
  1528 + [self stopVideoRecorder];
  1529 + }
  1530 +
  1531 + timeLength += TIMER_INTERVAL;
  1532 +
  1533 + // NSLog(@"%lf", timeLength / VIDEO_RECORDER_MAX_TIME);
  1534 +
  1535 + self.cameraButton.progressPercentage = timeLength / VIDEO_RECORDER_MAX_TIME;
  1536 +
  1537 +}
  1538 +
  1539 +/**
  1540 + * 停止定时器
  1541 + */
  1542 +- (void)timerStop
  1543 +{
  1544 + if ([self.timer isValid])
  1545 + {
  1546 + [self.timer invalidate];
  1547 + self.timer = nil;
  1548 + }
  1549 +}
  1550 +
  1551 +/**
  1552 + * 预览录制的视频
  1553 + */
  1554 +- (void)previewVideoAfterShoot
  1555 +{
  1556 + if (self.videoURL == nil || self.playerLayer != nil)
  1557 + {
  1558 + return;
  1559 + }
  1560 +
  1561 + _isPicOrVideo = YES;
  1562 +
  1563 + AVURLAsset *asset = [AVURLAsset assetWithURL:self.videoURL];
  1564 +
  1565 + //获取视频总时长
  1566 + Float64 duration = CMTimeGetSeconds(asset.duration);
  1567 +
  1568 + self.currentVideoTimeLength = duration;
  1569 +
  1570 +// // 初始化AVPlayer
  1571 +// self.videoPreviewContainerView = [[UIView alloc] init];
  1572 +//
  1573 +// self.videoPreviewContainerView.frame = CGRectMake(0, 0, kScreenWidth, kScreenHeight);
  1574 +// self.videoPreviewContainerView.backgroundColor = [UIColor blackColor];
  1575 +
  1576 + self.playerItem = [AVPlayerItem playerItemWithAsset:asset];
  1577 + self.player = [[AVPlayer alloc] initWithPlayerItem:_playerItem];
  1578 + self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
  1579 + self.playerLayer.frame = CGRectMake(0, 0, k_ScreenWidth, k_ScreenHeight);
  1580 + self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
  1581 + self.playerLayer.hidden = NO;
  1582 +
  1583 + [self.view.layer addSublayer:self.playerLayer];
  1584 +
  1585 + // 其余UI布局设置
  1586 +// [self.view addSubview:self.videoPreviewContainerView];
  1587 +// [self.view bringSubviewToFront:self.videoPreviewContainerView];
  1588 +
  1589 + [self.view.layer qmui_bringSublayerToFront:self.playerLayer];
  1590 + self.captureVideoPreviewLayer.hidden = YES;
  1591 + [self.view bringSubviewToFront:self.cancelButton];
  1592 + [self.view bringSubviewToFront:self.confirmButton];
  1593 + [self.cameraButton setHidden:YES];
  1594 + [self.closeButton setHidden:YES];
  1595 + [self.rotateCameraButton setHidden:YES];
  1596 + [self.flashButton setHidden:YES];
  1597 + [self.confirmButton setHidden:NO];
  1598 + [self.cancelButton setHidden:NO];
  1599 + self.captureVideoPreviewLayer.hidden = YES;
  1600 +
  1601 + // 重复播放预览视频
  1602 + [self addNotificationWithPlayerItem];
  1603 +
  1604 + // 开始播放
  1605 + [self.player play];
  1606 +}
  1607 +
  1608 +/**
  1609 + * 截取指定时间的视频缩略图
  1610 + *
  1611 + * @param timeBySecond 时间点,单位:s
  1612 + */
  1613 +- (UIImage *)thumbnailImageRequestWithVideoUrl:(NSURL *)videoUrl andTime:(CGFloat)timeBySecond
  1614 +{
  1615 + if (self.videoURL == nil)
  1616 + {
  1617 + return nil;
  1618 + }
  1619 +
  1620 + AVURLAsset *urlAsset = [AVURLAsset assetWithURL:videoUrl];
  1621 +
  1622 + //根据AVURLAsset创建AVAssetImageGenerator
  1623 + AVAssetImageGenerator *imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:urlAsset];
  1624 + /*截图
  1625 + * requestTime:缩略图创建时间
  1626 + * actualTime:缩略图实际生成的时间
  1627 + */
  1628 + NSError *error = nil;
  1629 + CMTime requestTime = CMTimeMakeWithSeconds(timeBySecond, 10); //CMTime是表示电影时间信息的结构体,第一个参数表示是视频第几秒,第二个参数表示每秒帧数.(如果要活的某一秒的第几帧可以使用CMTimeMake方法)
  1630 + CMTime actualTime;
  1631 + CGImageRef cgImage = [imageGenerator copyCGImageAtTime:requestTime actualTime:&actualTime error:&error];
  1632 + if(error)
  1633 + {
  1634 + NSLog(@"截取视频缩略图时发生错误,错误信息:%@", error.localizedDescription);
  1635 + return nil;
  1636 + }
  1637 +
  1638 + CMTimeShow(actualTime);
  1639 + UIImage *image = [UIImage imageWithCGImage:cgImage];
  1640 + CGImageRelease(cgImage);
  1641 +
  1642 + UIImage *finalImage = nil;
  1643 + if (self.shootingOrientation == UIDeviceOrientationLandscapeRight)
  1644 + {
  1645 + finalImage = [self rotateImage:image withOrientation:UIImageOrientationDown];
  1646 + }
  1647 + else if (self.shootingOrientation == UIDeviceOrientationLandscapeLeft)
  1648 + {
  1649 + finalImage = [self rotateImage:image withOrientation:UIImageOrientationUp];
  1650 + }
  1651 + else if (self.shootingOrientation == UIDeviceOrientationPortraitUpsideDown)
  1652 + {
  1653 + finalImage = [self rotateImage:image withOrientation:UIImageOrientationLeft];
  1654 + }
  1655 + else
  1656 + {
  1657 + finalImage = [self rotateImage:image withOrientation:UIImageOrientationRight];
  1658 + }
  1659 +
  1660 + return finalImage;
  1661 +}
  1662 +
  1663 +/**
  1664 + * 图片旋转
  1665 + */
  1666 +- (UIImage *)rotateImage:(UIImage *)image withOrientation:(UIImageOrientation)orientation
  1667 +{
  1668 + long double rotate = 0.0;
  1669 + CGRect rect;
  1670 + float translateX = 0;
  1671 + float translateY = 0;
  1672 + float scaleX = 1.0;
  1673 + float scaleY = 1.0;
  1674 +
  1675 + switch (orientation)
  1676 + {
  1677 + case UIImageOrientationLeft:
  1678 + rotate = M_PI_2;
  1679 + rect = CGRectMake(0, 0, image.size.height, image.size.width);
  1680 + translateX = 0;
  1681 + translateY = -rect.size.width;
  1682 + scaleY = rect.size.width/rect.size.height;
  1683 + scaleX = rect.size.height/rect.size.width;
  1684 + break;
  1685 + case UIImageOrientationRight:
  1686 + rotate = 3 * M_PI_2;
  1687 + rect = CGRectMake(0, 0, image.size.height, image.size.width);
  1688 + translateX = -rect.size.height;
  1689 + translateY = 0;
  1690 + scaleY = rect.size.width/rect.size.height;
  1691 + scaleX = rect.size.height/rect.size.width;
  1692 + break;
  1693 + case UIImageOrientationDown:
  1694 + rotate = M_PI;
  1695 + rect = CGRectMake(0, 0, image.size.width, image.size.height);
  1696 + translateX = -rect.size.width;
  1697 + translateY = -rect.size.height;
  1698 + break;
  1699 + default:
  1700 + rotate = 0.0;
  1701 + rect = CGRectMake(0, 0, image.size.width, image.size.height);
  1702 + translateX = 0;
  1703 + translateY = 0;
  1704 + break;
  1705 + }
  1706 +
  1707 + UIGraphicsBeginImageContext(rect.size);
  1708 + CGContextRef context = UIGraphicsGetCurrentContext();
  1709 + //做CTM变换
  1710 + CGContextTranslateCTM(context, 0.0, rect.size.height);
  1711 + CGContextScaleCTM(context, 1.0, -1.0);
  1712 + CGContextRotateCTM(context, rotate);
  1713 + CGContextTranslateCTM(context, translateX, translateY);
  1714 +
  1715 + CGContextScaleCTM(context, scaleX, scaleY);
  1716 + //绘制图片
  1717 + CGContextDrawImage(context, CGRectMake(0, 0, rect.size.width, rect.size.height), image.CGImage);
  1718 + UIImage *newPic = UIGraphicsGetImageFromCurrentImageContext();
  1719 + UIGraphicsEndImageContext();
  1720 + return newPic;
  1721 +}
  1722 +
  1723 +#pragma mark - 截取视频方法
  1724 +
  1725 +- (void)cropWithVideoUrlStr:(NSURL *)videoUrl start:(CGFloat)startTime end:(CGFloat)endTime completion:(void (^)(NSURL *outputURL, Float64 videoDuration, BOOL isSuccess))completionHandle
  1726 +{
  1727 + AVURLAsset *asset =[[AVURLAsset alloc] initWithURL:videoUrl options:nil];
  1728 +
  1729 + //获取视频总时长
  1730 + Float64 duration = CMTimeGetSeconds(asset.duration);
  1731 +
  1732 + if (duration > VIDEO_RECORDER_MAX_TIME)
  1733 + {
  1734 + duration = VIDEO_RECORDER_MAX_TIME;
  1735 + }
  1736 +
  1737 + startTime = 0;
  1738 + endTime = duration;
  1739 +
  1740 + NSString *outputFilePath = [self createVideoFilePath];
  1741 + NSURL *outputFileUrl = [NSURL fileURLWithPath:outputFilePath];
  1742 +
  1743 + NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:asset];
  1744 + if ([compatiblePresets containsObject:AVAssetExportPresetMediumQuality])
  1745 + {
  1746 +
  1747 + AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]
  1748 + initWithAsset:asset presetName:AVAssetExportPresetPassthrough];
  1749 +
  1750 + NSURL *outputURL = outputFileUrl;
  1751 +
  1752 + exportSession.outputURL = outputURL;
  1753 + exportSession.outputFileType = AVFileTypeMPEG4;
  1754 + exportSession.shouldOptimizeForNetworkUse = YES;
  1755 +
  1756 + CMTime start = CMTimeMakeWithSeconds(startTime, asset.duration.timescale);
  1757 + CMTime duration = CMTimeMakeWithSeconds(endTime - startTime,asset.duration.timescale);
  1758 + CMTimeRange range = CMTimeRangeMake(start, duration);
  1759 + exportSession.timeRange = range;
  1760 +
  1761 + [exportSession exportAsynchronouslyWithCompletionHandler:^{
  1762 + switch ([exportSession status]) {
  1763 + case AVAssetExportSessionStatusFailed:
  1764 + {
  1765 + NSLog(@"合成失败:%@", [[exportSession error] description]);
  1766 + completionHandle(outputURL, endTime, NO);
  1767 + }
  1768 + break;
  1769 + case AVAssetExportSessionStatusCancelled:
  1770 + {
  1771 + completionHandle(outputURL, endTime, NO);
  1772 + }
  1773 + break;
  1774 + case AVAssetExportSessionStatusCompleted:
  1775 + {
  1776 + completionHandle(outputURL, endTime, YES);
  1777 + }
  1778 + break;
  1779 + default:
  1780 + {
  1781 + completionHandle(outputURL, endTime, NO);
  1782 + } break;
  1783 + }
  1784 + }];
  1785 + }
  1786 +}
  1787 +
  1788 +#pragma mark - 预览视频通知
  1789 +/**
  1790 + * 添加播放器通知
  1791 + */
  1792 +-(void)addNotificationWithPlayerItem
  1793 +{
  1794 + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playVideoFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem];
  1795 +}
  1796 +
  1797 +-(void)removePlayerItemNotification
  1798 +{
  1799 + [[NSNotificationCenter defaultCenter] removeObserver:self];
  1800 +}
  1801 +
  1802 +/**
  1803 + * 播放完成通知
  1804 + *
  1805 + * @param notification 通知对象
  1806 + */
  1807 +-(void)playVideoFinished:(NSNotification *)notification
  1808 +{
  1809 + // NSLog(@"视频播放完成.");
  1810 +
  1811 + // 播放完成后重复播放
  1812 + // 跳到最新的时间点开始播放
  1813 + [self.player seekToTime:CMTimeMake(0, 1)];
  1814 + [self.player play];
  1815 +}
  1816 +
  1817 +#pragma mark - AVCaptureVideoDataOutputSampleBufferDelegate AVCaptureAudioDataOutputSampleBufferDelegate
  1818 +
  1819 +- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
  1820 +{
  1821 + if (_isRotatingCamera)
  1822 + {
  1823 + return;
  1824 + }
  1825 +
  1826 + @autoreleasepool
  1827 + {
  1828 + //视频
  1829 + if (connection == [self.videoOutput connectionWithMediaType:AVMediaTypeVideo])
  1830 + {
  1831 + @synchronized(self)
  1832 + {
  1833 + if (_isShooting)
  1834 + {
  1835 + [self appendSampleBuffer:sampleBuffer ofMediaType:AVMediaTypeVideo];
  1836 + }
  1837 + }
  1838 + }
  1839 +
  1840 + //音频
  1841 + if (connection == [self.audioOutput connectionWithMediaType:AVMediaTypeAudio])
  1842 + {
  1843 + @synchronized(self)
  1844 + {
  1845 + if (_isShooting)
  1846 + {
  1847 + [self appendSampleBuffer:sampleBuffer ofMediaType:AVMediaTypeAudio];
  1848 + }
  1849 + }
  1850 + }
  1851 + }
  1852 +}
  1853 +
  1854 +
  1855 +/**
  1856 + * 开始写入数据
  1857 + */
  1858 +- (void)appendSampleBuffer:(CMSampleBufferRef)sampleBuffer ofMediaType:(NSString *)mediaType
  1859 +{
  1860 + if (sampleBuffer == NULL)
  1861 + {
  1862 + NSLog(@"empty sampleBuffer");
  1863 + return;
  1864 + }
  1865 +
  1866 + // CFRetain(sampleBuffer);
  1867 + // dispatch_async(self.videoQueue, ^{
  1868 + @autoreleasepool
  1869 + {
  1870 + if (!self.canWrite && mediaType == AVMediaTypeVideo)
  1871 + {
  1872 + [self.assetWriter startWriting];
  1873 + [self.assetWriter startSessionAtSourceTime:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)];
  1874 + self.canWrite = YES;
  1875 + }
  1876 +
  1877 + //写入视频数据
  1878 + if (mediaType == AVMediaTypeVideo)
  1879 + {
  1880 + if (self.assetWriterVideoInput.readyForMoreMediaData)
  1881 + {
  1882 + BOOL success = [self.assetWriterVideoInput appendSampleBuffer:sampleBuffer];
  1883 + if (!success)
  1884 + {
  1885 + @synchronized (self)
  1886 + {
  1887 + [self stopVideoRecorder];
  1888 + }
  1889 + }
  1890 + }
  1891 + }
  1892 +
  1893 + //写入音频数据
  1894 + if (mediaType == AVMediaTypeAudio)
  1895 + {
  1896 + if (self.assetWriterAudioInput.readyForMoreMediaData)
  1897 + {
  1898 + BOOL success = [self.assetWriterAudioInput appendSampleBuffer:sampleBuffer];
  1899 + if (!success)
  1900 + {
  1901 + @synchronized (self)
  1902 + {
  1903 + [self stopVideoRecorder];
  1904 + }
  1905 + }
  1906 + }
  1907 + }
  1908 +
  1909 + // CFRelease(sampleBuffer);
  1910 + }
  1911 + // });
  1912 +}
  1913 +
  1914 +#pragma mark - 摄像头聚焦,与缩放
  1915 +
  1916 +/**
  1917 + * 添加点按手势
  1918 + */
  1919 +- (void)addTapGenstureRecognizerForCamera
  1920 +{
  1921 + UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinchGesture:)];
  1922 +
  1923 + pinchGesture.delegate = self;
  1924 +
  1925 + [self.view addGestureRecognizer:pinchGesture];
  1926 +}
  1927 +
  1928 +/**
  1929 + * 点击屏幕,聚焦事件
  1930 + */
  1931 +- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
  1932 +{
  1933 + // 不聚焦的情况:聚焦中,旋转摄像头中,查看录制的视频中,查看照片中
  1934 + if (_isFocusing || touches.count == 0 || _isRotatingCamera || _videoPreviewContainerView || _photoPreviewImageView)
  1935 + {
  1936 + return;
  1937 + }
  1938 +
  1939 + UITouch *touch = nil;
  1940 +
  1941 + for (UITouch *t in touches)
  1942 + {
  1943 + touch = t;
  1944 + break;
  1945 + }
  1946 +
  1947 + CGPoint point = [touch locationInView:self.view];;
  1948 +
  1949 + if (point.y > CGRectGetMaxY(self.tipLabel.frame))
  1950 + {
  1951 + return;
  1952 + }
  1953 +
  1954 + [self setFocusCursorWithPoint:point];
  1955 +}
  1956 +
  1957 +/**
  1958 + * 设置聚焦光标位置
  1959 + *
  1960 + * @param point 光标位置
  1961 + */
  1962 +- (void)setFocusCursorWithPoint:(CGPoint)point
  1963 +{
  1964 + self.isFocusing = YES;
  1965 +
  1966 + self.focusImageView.center = point;
  1967 + self.focusImageView.transform = CGAffineTransformMakeScale(1.5, 1.5);
  1968 + self.focusImageView.alpha = 1;
  1969 +
  1970 + //将UI坐标转化为摄像头坐标
  1971 + CGPoint cameraPoint = [self.captureVideoPreviewLayer captureDevicePointOfInterestForPoint:point];
  1972 + [self focusWithPoint:cameraPoint];
  1973 +
  1974 + __weak typeof(self) weakSelf = self;
  1975 + [UIView animateWithDuration:1.0 animations:^{
  1976 +
  1977 + weakSelf.focusImageView.transform = CGAffineTransformIdentity;
  1978 +
  1979 + } completion:^(BOOL finished) {
  1980 +
  1981 + weakSelf.focusImageView.alpha = 0;
  1982 + weakSelf.isFocusing = NO;
  1983 +
  1984 + }];
  1985 +}
  1986 +
  1987 +/**
  1988 + * 设置聚焦点
  1989 + *
  1990 + * @param point 聚焦点
  1991 + */
  1992 +-(void)focusWithPoint:(CGPoint)point
  1993 +{
  1994 + [self changeDeviceProperty:^(AVCaptureDevice *captureDevice)
  1995 + {
  1996 + // 聚焦
  1997 + if ([captureDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus])
  1998 + {
  1999 + [captureDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
  2000 + }
  2001 + if ([captureDevice isFocusPointOfInterestSupported])
  2002 + {
  2003 + [captureDevice setFocusPointOfInterest:point];
  2004 + }
  2005 + // 曝光
  2006 + if ([captureDevice isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure])
  2007 + {
  2008 + [captureDevice setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
  2009 + }
  2010 + if ([captureDevice isExposurePointOfInterestSupported])
  2011 + {
  2012 + [captureDevice setExposurePointOfInterest:point];
  2013 + }
  2014 + }];
  2015 +}
  2016 +
  2017 +- (void)handlePinchGesture:(UIPinchGestureRecognizer *)recognizer
  2018 +{
  2019 + if (_isShooting)
  2020 + {
  2021 + return;
  2022 + }
  2023 +
  2024 + BOOL allTouchesAreOnTheCaptureVideoPreviewLayer = YES;
  2025 +
  2026 + NSUInteger numTouches = [recognizer numberOfTouches], i;
  2027 + for ( i = 0; i < numTouches; ++i)
  2028 + {
  2029 + CGPoint location = [recognizer locationOfTouch:i inView:self.view];
  2030 + CGPoint convertedLocation = [self.captureVideoPreviewLayer convertPoint:location fromLayer:self.captureVideoPreviewLayer.superlayer];
  2031 + if (![self.captureVideoPreviewLayer containsPoint:convertedLocation])
  2032 + {
  2033 + allTouchesAreOnTheCaptureVideoPreviewLayer = NO;
  2034 + break;
  2035 + }
  2036 + }
  2037 +
  2038 + if (allTouchesAreOnTheCaptureVideoPreviewLayer)
  2039 + {
  2040 + self.effectiveScale = self.beginGestureScale * recognizer.scale;
  2041 + if (self.effectiveScale < 1.0f)
  2042 + {
  2043 + self.effectiveScale = 1.0f;
  2044 + }
  2045 +
  2046 + // NSLog(@"%f-------------->%f------------recognizerScale%f", self.effectiveScale, self.beginGestureScale, recognizer.scale);
  2047 +
  2048 + CGFloat imageMaxScaleAndCropFactor = [[self.captureStillImageOutput connectionWithMediaType:AVMediaTypeVideo] videoMaxScaleAndCropFactor];
  2049 +
  2050 + // NSLog(@"%f", imageMaxScaleAndCropFactor);
  2051 + if (self.effectiveScale > imageMaxScaleAndCropFactor)
  2052 + {
  2053 + self.effectiveScale = imageMaxScaleAndCropFactor;
  2054 + }
  2055 +
  2056 + [self setCaptureVideoPreviewLayerTransformWithScale:self.effectiveScale];
  2057 + }
  2058 +}
  2059 +
  2060 +- (void)setCaptureVideoPreviewLayerTransformWithScale:(CGFloat)scale
  2061 +{
  2062 + self.effectiveScale = scale;
  2063 + [CATransaction begin];
  2064 + [CATransaction setAnimationDuration:0.25f]; //时长最好低于 START_VIDEO_ANIMATION_DURATION
  2065 + [self.captureVideoPreviewLayer setAffineTransform:CGAffineTransformMakeScale(scale, scale)];
  2066 + [CATransaction commit];
  2067 +}
  2068 +
  2069 +#pragma mark - UIGestureRecognizerDelegate
  2070 +
  2071 +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
  2072 +{
  2073 + if ([gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]])
  2074 + {
  2075 + self.beginGestureScale = self.effectiveScale;
  2076 + }
  2077 +
  2078 + return YES;
  2079 +}
  2080 +
  2081 +#pragma mark - 重力感应相关
  2082 +
  2083 +/**
  2084 + * 开始监听屏幕方向
  2085 + */
  2086 +- (void)startUpdateAccelerometer
  2087 +{
  2088 + if ([self.motionManager isAccelerometerAvailable] == YES)
  2089 + {
  2090 + //回调会一直调用,建议获取到就调用下面的停止方法,需要再重新开始,当然如果需求是实时不间断的话可以等离开页面之后再stop
  2091 + [self.motionManager setAccelerometerUpdateInterval:2.0];
  2092 + NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  2093 + __weak typeof(self) weakSelf = self;
  2094 + [self.motionManager startAccelerometerUpdatesToQueue:queue withHandler:^(CMAccelerometerData *accelerometerData, NSError *error)
  2095 + {
  2096 + NSLog(@"输出陀螺仪 = %f",accelerometerData.acceleration.x);
  2097 + double x = accelerometerData.acceleration.x;
  2098 + double y = accelerometerData.acceleration.y;
  2099 + if (fabs(y) >= fabs(x))
  2100 + {
  2101 + if (y >= 0)
  2102 + {
  2103 + // Down
  2104 + NSLog(@"Down");
  2105 + weakSelf.shootingOrientation = UIDeviceOrientationPortraitUpsideDown;
  2106 + }
  2107 + else
  2108 + {
  2109 + // Portrait
  2110 + NSLog(@"Portrait");
  2111 + weakSelf.shootingOrientation = UIDeviceOrientationPortrait;
  2112 + }
  2113 + }
  2114 + else
  2115 + {
  2116 + if (x >= 0)
  2117 + {
  2118 + // Right
  2119 + NSLog(@"Right");
  2120 + weakSelf.shootingOrientation = UIDeviceOrientationLandscapeRight;
  2121 + }
  2122 + else
  2123 + {
  2124 + // Left
  2125 + NSLog(@"Left");
  2126 + weakSelf.shootingOrientation = UIDeviceOrientationLandscapeLeft;
  2127 + }
  2128 + }
  2129 + }];
  2130 + }
  2131 +}
  2132 +
  2133 +/**
  2134 + * 停止监听屏幕方向
  2135 + */
  2136 +- (void)stopUpdateAccelerometer
  2137 +{
  2138 + if ([self.motionManager isAccelerometerActive] == YES)
  2139 + {
  2140 + [self.motionManager stopAccelerometerUpdates];
  2141 + _motionManager = nil;
  2142 + }
  2143 +}
  2144 +
  2145 +#pragma mark - 判断是否有权限
  2146 +
  2147 +/**
  2148 + * 请求权限
  2149 + */
  2150 +- (void)requestAuthorizationForVideo
  2151 +{
  2152 + __weak typeof(self) weakSelf = self;
  2153 +
  2154 + // 请求相机权限
  2155 + AVAuthorizationStatus videoAuthStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
  2156 + if (videoAuthStatus != AVAuthorizationStatusAuthorized)
  2157 + {
  2158 + NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
  2159 +
  2160 + NSString *appName = [infoDictionary objectForKey:@"CFBundleDisplayName"];
  2161 + if (appName == nil)
  2162 + {
  2163 + appName = @"APP";
  2164 + }
  2165 + NSString *message = [NSString stringWithFormat:@"请在iPhone的“设置-隐私”选项中,允许%@访问你的相册,相机和麦克风。", appName];
  2166 +
  2167 + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];//无法拍摄
  2168 +
  2169 + UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  2170 + [weakSelf dismissViewControllerAnimated:YES completion:nil];
  2171 + }];
  2172 +
  2173 + [alertController addAction:okAction];
  2174 + [self presentViewController:alertController animated:YES completion:nil];
  2175 + }
  2176 +
  2177 + // 请求麦克风权限
  2178 + AVAuthorizationStatus audioAuthStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
  2179 + if (audioAuthStatus != AVAuthorizationStatusAuthorized)
  2180 + {
  2181 + NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
  2182 +
  2183 + NSString *appName = [infoDictionary objectForKey:@"CFBundleDisplayName"];
  2184 + if (appName == nil)
  2185 + {
  2186 + appName = @"APP";
  2187 + }
  2188 + NSString *message = [NSString stringWithFormat:@"请在iPhone的“设置-隐私“选项中,允许%@访问你的相机,相册和麦克风。", appName];//[NSString stringWithFormat:@"请在iPhone的““设置-隐私””选项中, 允许%@访问你的麦克风?", appName];
  2189 +
  2190 + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];
  2191 +
  2192 + UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  2193 + [weakSelf dismissViewControllerAnimated:YES completion:nil];
  2194 + }];
  2195 +
  2196 + [alertController addAction:okAction];
  2197 +
  2198 + [self presentViewController:alertController animated:YES completion:nil];
  2199 + }
  2200 +
  2201 +
  2202 +}
  2203 +
  2204 +- (void)requestAuthorizationForPhotoLibrary
  2205 +{
  2206 + __weak typeof(self) weakSelf = self;
  2207 +
  2208 + // 请求照片权限
  2209 + [XFPhotoLibraryManager requestALAssetsLibraryAuthorizationWithCompletion:^(Boolean isAuth) {
  2210 +
  2211 + if (!isAuth)
  2212 + {
  2213 + NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
  2214 +
  2215 + NSString *appName = [infoDictionary objectForKey:@"CFBundleDisplayName"];
  2216 + if (appName == nil)
  2217 + {
  2218 + appName = @"APP";
  2219 + }
  2220 + NSString *message = [NSString stringWithFormat:@"请在iPhone的""设置-隐私-相册""中允许%@访问相册", appName];//@"允许%@访问你的相册?"
  2221 +
  2222 + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];
  2223 +
  2224 + UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  2225 + [weakSelf dismissViewControllerAnimated:YES completion:nil];
  2226 + weakSelf.isAlertShow = NO;
  2227 + }];
  2228 +
  2229 +// UIAlertAction *setAction = [UIAlertAction actionWithTitle:@"设置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  2230 +// NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
  2231 +// if ([[UIApplication sharedApplication] canOpenURL:url])
  2232 +// {
  2233 +// [[UIApplication sharedApplication] openURL:url];
  2234 +// [weakSelf dismissViewControllerAnimated:YES completion:nil];
  2235 +// }
  2236 +// }];
  2237 +
  2238 + [alertController addAction:okAction];
  2239 +// [alertController addAction:setAction];
  2240 +
  2241 + [self presentViewController:alertController animated:YES completion:nil];
  2242 + weakSelf.isAlertShow = YES;
  2243 +
  2244 + }
  2245 + }];
  2246 +}
  2247 +
  2248 +- (void)requestAuthCamera
  2249 +{
  2250 + __weak typeof(self) weakSelf = self;
  2251 +
  2252 + // 请求相机权限
  2253 + AVAuthorizationStatus videoAuthStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
  2254 + if (videoAuthStatus != AVAuthorizationStatusAuthorized)
  2255 + {
  2256 + NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
  2257 +
  2258 + NSString *appName = [infoDictionary objectForKey:@"CFBundleDisplayName"];
  2259 + if (appName == nil)
  2260 + {
  2261 + appName = @"APP";
  2262 + }
  2263 + NSString *message = [NSString stringWithFormat:@"请在iPhone的“设置-隐私”选项中,允许%@访问你的摄像头和麦克风。", appName];
  2264 +
  2265 + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];//无法拍摄
  2266 +
  2267 + UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  2268 + [weakSelf dismissViewControllerAnimated:YES completion:nil];
  2269 + }];
  2270 +
  2271 + [alertController addAction:okAction];
  2272 + [self presentViewController:alertController animated:YES completion:nil];
  2273 + }
  2274 +}
  2275 +
  2276 +#pragma mark - 进入前后台监听
  2277 +
  2278 +- (void)applicationBecomeActive
  2279 +{
  2280 + if (self.player) {
  2281 + [self.player play];
  2282 + }
  2283 +}
  2284 +
  2285 +- (void)enterBackgroundNotification
  2286 +{
  2287 + if (_isPicOrVideo) {
  2288 + //已经拍照或者录视频 不dismiss
  2289 + }else
  2290 + {//没有拍照或者录制视频 dismiss
  2291 + if (!_isAlertShow) {
  2292 + [self dismissViewControllerAnimated:YES completion:nil];
  2293 + }
  2294 + }
  2295 +}
  2296 +
  2297 +#pragma mark - TODO: 音频播放导致崩溃
  2298 +- (void)audioEnter:(NSNotification *)notification {
  2299 + NSDictionary *dic = notification.userInfo;
  2300 + if (dic.count > 0) {
  2301 +// NSString *str = [NSString stringWithFormat:@"%@", dic[Notification_Type]];
  2302 +// if ([str isEqualToString:Notification_ListenChina]) {
  2303 +// self.isAudioPlaying = [CNAudioPlayerManager sharedInstance].isPlaying;
  2304 +// if (self.isAudioPlaying) {
  2305 +// [[CNAudioPlayerManager sharedInstance] pausePlaying];
  2306 +//
  2307 +// }
  2308 +// }
  2309 + }
  2310 +}
  2311 +
  2312 +- (void)audioOut:(NSNotification *)notification {
  2313 + NSDictionary *dic = notification.userInfo;
  2314 + if (dic.count > 0) {
  2315 +// NSString *str = [NSString stringWithFormat:@"%@", dic[Notification_Type]];
  2316 +// if ([str isEqualToString:Notification_ListenChina]) {
  2317 +// if (self.isAudioPlaying) {
  2318 +// [[CNAudioPlayerManager sharedInstance] pausePlaying];
  2319 +//
  2320 +// }
  2321 +// }
  2322 + }
  2323 +
  2324 +}
  2325 +
  2326 +
  2327 +
  2328 +
  2329 +@end
CNLiveAVCamera/Classes/XFPhotoLibraryManager.h 0 → 100644
  1 +//
  2 +// XFPhotoLibraryManager.h
  3 +// WeChatVideoDemo
  4 +//
  5 +// Created by xf-ling on 2017/6/15.
  6 +// Copyright © 2017年 LXF. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +#import <Photos/Photos.h>
  11 +
  12 +
  13 +typedef void(^RequestAssetsLibraryAuthCompletion)(Boolean isAuth);
  14 +typedef void(^SavePhotoCompletionBlock)(UIImage *image, NSError *error);
  15 +typedef void(^SaveVideoCompletionBlock)(NSURL *vedioUrl, NSError *error);
  16 +
  17 +//zx
  18 +typedef void(^GetCameraVideoCompletionBlock)(PHAsset *asset,NSURL *vedioUrl, NSError *error);
  19 +
  20 +
  21 +@interface XFPhotoLibraryManager : NSObject
  22 +
  23 +/**
  24 + * 请求照片权限,注意,强烈要求用户获得照片权限,否则视频写入照片会有崩溃
  25 + */
  26 ++ (void)requestALAssetsLibraryAuthorizationWithCompletion:(RequestAssetsLibraryAuthCompletion) requestAssetsLibraryAuthCompletion;
  27 +
  28 +/**
  29 + * 保存照片
  30 + *
  31 + * @param image UImage
  32 + * @param assetCollectionName 相册名字,不填默认为app名字+相册
  33 + */
  34 ++ (void)savePhotoWithImage:(UIImage *)image andAssetCollectionName:(NSString *)assetCollectionName withCompletion:(SavePhotoCompletionBlock)savePhotoCompletionBlock;
  35 +
  36 +/**
  37 + * 保存视频
  38 + *
  39 + * @param videoUrl 视频地址
  40 + * @param assetCollectionName 相册名字,不填默认为app名字
  41 + */
  42 ++ (void)saveVideoWithVideoUrl:(NSURL *)videoUrl andAssetCollectionName:(NSString *)assetCollectionName withCompletion:(SaveVideoCompletionBlock)saveVideoCompletionBlock;
  43 +
  44 +
  45 +/**
  46 + * zx保存视频
  47 + *
  48 + * @param videoUrl 视频地址
  49 + * @param assetCollectionName 相册名字,不填默认为app名字
  50 + */
  51 ++ (void)zx_saveVideoWithVideoUrl:(NSURL *)videoUrl andAssetCollectionName:(NSString *)assetCollectionName withCompletion:(GetCameraVideoCompletionBlock)getCameraVideoCompletionBlock;
  52 +@end
CNLiveAVCamera/Classes/XFPhotoLibraryManager.m 0 → 100644
  1 +//
  2 +// XFPhotoLibraryManager.m
  3 +// WeChatVideoDemo
  4 +//
  5 +// Created by xf-ling on 2017/6/15.
  6 +// Copyright © 2017年 LXF. All rights reserved.
  7 +//
  8 +
  9 +#import "XFPhotoLibraryManager.h"
  10 +#import <Photos/Photos.h>
  11 +#import <AssetsLibrary/AssetsLibrary.h>
  12 +
  13 +@implementation XFPhotoLibraryManager
  14 +
  15 +/**
  16 + * 请求照片权限,注意,强烈要求用户获得照片权限,否则视频写入照片会有崩溃
  17 + */
  18 ++ (void)requestALAssetsLibraryAuthorizationWithCompletion:(RequestAssetsLibraryAuthCompletion)requestAssetsLibraryAuthCompletion
  19 +{
  20 + PHAuthorizationStatus authStatus = [PHPhotoLibrary authorizationStatus];
  21 + if (authStatus != PHAuthorizationStatusAuthorized) // 未授权
  22 + {
  23 + [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
  24 + if (status != PHAuthorizationStatusAuthorized) //已授权
  25 + {
  26 + NSLog(@"用户拒绝访问相册!");
  27 + if (requestAssetsLibraryAuthCompletion)
  28 + {
  29 + requestAssetsLibraryAuthCompletion(NO);
  30 + }
  31 + }
  32 + else
  33 + {
  34 + NSLog(@"用户允许访问相册!");
  35 + if (requestAssetsLibraryAuthCompletion)
  36 + {
  37 + requestAssetsLibraryAuthCompletion(YES);
  38 + }
  39 + }
  40 + }];
  41 + }
  42 + else
  43 + {
  44 + // nothing
  45 + }
  46 +}
  47 +
  48 +/**
  49 + * 保存照片
  50 + *
  51 + * @param image UImage
  52 + * @param assetCollectionName 相册名字,不填默认为app名字+相册
  53 + */
  54 ++ (void)savePhotoWithImage:(UIImage *)image andAssetCollectionName:(NSString *)assetCollectionName withCompletion:(SavePhotoCompletionBlock)savePhotoCompletionBlock
  55 +{
  56 + if (assetCollectionName == nil)
  57 + {
  58 + NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
  59 +
  60 + assetCollectionName = [infoDictionary objectForKey:@"CFBundleDisplayName"];
  61 +
  62 + if (assetCollectionName == nil)
  63 + {
  64 + assetCollectionName = @"视频相册";
  65 + }
  66 + }
  67 +
  68 + __block NSString *blockAssetCollectionName = assetCollectionName;
  69 + __block UIImage *blockImage = image;
  70 + __block NSString *assetId = nil;
  71 +
  72 + PHPhotoLibrary *library = [PHPhotoLibrary sharedPhotoLibrary];
  73 +
  74 + // 1. 存储图片到"相机胶卷"
  75 + [library performChanges:^{ // 这个block里保存一些"修改"性质的代码
  76 + // 新建一个PHAssetCreationRequest对象, 保存图片到"相机胶卷"
  77 + // 返回PHAsset(图片)的字符串标识
  78 + if (@available(iOS 9.0, *)) {
  79 + assetId = [PHAssetCreationRequest creationRequestForAssetFromImage:blockImage].placeholderForCreatedAsset.localIdentifier;
  80 + } else {
  81 + // Fallback on earlier versions
  82 + }
  83 + } completionHandler:^(BOOL success, NSError * _Nullable error) {
  84 + if (error) {
  85 + NSLog(@"error1%@", error);
  86 + return;
  87 + }
  88 +
  89 + NSLog(@"成功保存图片到相机胶卷中");
  90 +
  91 + // 2. 获得相册对象
  92 + // 获取曾经创建过的自定义视频相册名字
  93 + PHAssetCollection *createdAssetCollection = nil;
  94 + PHFetchResult <PHAssetCollection*> *assetCollections = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
  95 + for (PHAssetCollection *assetCollection in assetCollections)
  96 + {
  97 + if ([assetCollection.localizedTitle isEqualToString:blockAssetCollectionName])
  98 + {
  99 + createdAssetCollection = assetCollection;
  100 + break;
  101 + }
  102 + }
  103 +
  104 + //如果这个自定义框架没有创建过
  105 + if (createdAssetCollection == nil)
  106 + {
  107 + //创建新的[自定义的 Album](相簿\相册)
  108 + [library performChangesAndWait:^{
  109 +
  110 + assetId = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:blockAssetCollectionName].placeholderForCreatedAssetCollection.localIdentifier;
  111 +
  112 + } error:&error];
  113 +
  114 + NSLog(@"error2: %@", error);
  115 +
  116 + //抓取刚创建完的视频相册对象
  117 + createdAssetCollection = [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[assetId] options:nil].firstObject;
  118 +
  119 + }
  120 +
  121 + // 3. 将“相机胶卷”中的图片添加到新的相册
  122 +// [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
  123 +// PHAssetCollectionChangeRequest *request = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:createdAssetCollection];
  124 +//
  125 +// // 根据唯一标示获得相片对象
  126 +// PHAsset *asset = [PHAsset fetchAssetsWithLocalIdentifiers:@[assetId] options:nil].firstObject;
  127 +// // 添加图片到相册中
  128 +// [request addAssets:@[asset]];
  129 +// } completionHandler:^(BOOL success, NSError * _Nullable error) {
  130 +// if (error)
  131 +// {
  132 +// NSLog(@"添加图片到相册中失败");
  133 +// return;
  134 +// }
  135 +//
  136 +// NSLog(@"成功添加图片到相册中");
  137 +// }];
  138 +
  139 + // 将【Camera Roll】(相机胶卷)的视频 添加到【自定义Album】(相簿\相册)中
  140 + [library performChangesAndWait:^{
  141 + PHAssetCollectionChangeRequest *request = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:createdAssetCollection];
  142 +
  143 + [request addAssets:[PHAsset fetchAssetsWithLocalIdentifiers:@[assetId] options:nil]];
  144 +
  145 + } error:&error];
  146 + NSLog(@"error3: %@", error);
  147 + // 提示信息
  148 + if (savePhotoCompletionBlock)
  149 + {
  150 + if (error)
  151 + {
  152 + NSLog(@"保存照片失败!");
  153 +
  154 + savePhotoCompletionBlock(nil, error);
  155 + }
  156 + else
  157 + {
  158 + NSLog(@"保存照片成功!");
  159 +
  160 + savePhotoCompletionBlock(blockImage, nil);
  161 + }
  162 + }
  163 +
  164 + }];
  165 +}
  166 +
  167 +/**
  168 + * 保存视频
  169 + *
  170 + * @param videoUrl 视频地址
  171 + * @param assetCollectionName 相册名字,不填默认为app名字+视频
  172 + */
  173 ++ (void)saveVideoWithVideoUrl:(NSURL *)videoUrl andAssetCollectionName:(NSString *)assetCollectionName withCompletion:(SaveVideoCompletionBlock)saveVideoCompletionBlock
  174 +{
  175 + if (assetCollectionName == nil)
  176 + {
  177 + NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
  178 +
  179 + assetCollectionName = [infoDictionary objectForKey:@"CFBundleDisplayName"];
  180 +
  181 + if (assetCollectionName == nil)
  182 + {
  183 + assetCollectionName = @"视频相册";
  184 + }
  185 + }
  186 +
  187 + __block NSString *blockAssetCollectionName = assetCollectionName;
  188 + __block NSURL *blockVideoUrl = videoUrl;
  189 + PHPhotoLibrary *library = [PHPhotoLibrary sharedPhotoLibrary];
  190 +
  191 + dispatch_async(dispatch_get_main_queue(), ^{
  192 +
  193 + NSError *error = nil;
  194 + __block NSString *assetId = nil;
  195 + __block NSString *assetCollectionId = nil;
  196 +
  197 + // 保存视频到【Camera Roll】(相机胶卷)
  198 + [library performChangesAndWait:^{
  199 +
  200 + assetId = [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:blockVideoUrl].placeholderForCreatedAsset.localIdentifier;
  201 +
  202 + } error:&error];
  203 +
  204 + NSLog(@"error1: %@", error);
  205 +
  206 + // 获取曾经创建过的自定义视频相册名字
  207 + PHAssetCollection *createdAssetCollection = nil;
  208 + PHFetchResult <PHAssetCollection*> *assetCollections = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
  209 + for (PHAssetCollection *assetCollection in assetCollections)
  210 + {
  211 + if ([assetCollection.localizedTitle isEqualToString:blockAssetCollectionName])
  212 + {
  213 + createdAssetCollection = assetCollection;
  214 + break;
  215 + }
  216 + }
  217 +
  218 + //如果这个自定义框架没有创建过
  219 + if (createdAssetCollection == nil)
  220 + {
  221 + //创建新的[自定义的 Album](相簿\相册)
  222 + [library performChangesAndWait:^{
  223 +
  224 + assetCollectionId = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:blockAssetCollectionName].placeholderForCreatedAssetCollection.localIdentifier;
  225 +
  226 + } error:&error];
  227 +
  228 + NSLog(@"error2: %@", error);
  229 +
  230 + //抓取刚创建完的视频相册对象
  231 + createdAssetCollection = [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[assetCollectionId] options:nil].firstObject;
  232 +
  233 + }
  234 +
  235 + // 将【Camera Roll】(相机胶卷)的视频 添加到【自定义Album】(相簿\相册)中
  236 + [library performChangesAndWait:^{
  237 + PHAssetCollectionChangeRequest *request = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:createdAssetCollection];
  238 + [request addAssets:[PHAsset fetchAssetsWithLocalIdentifiers:@[assetId] options:nil]];
  239 +
  240 + } error:&error];
  241 + NSLog(@"error3: %@", error);
  242 +
  243 + // 提示信息
  244 + if (saveVideoCompletionBlock)
  245 + {
  246 + if (error)
  247 + {
  248 + NSLog(@"保存视频失败!");
  249 +
  250 + saveVideoCompletionBlock(nil, error);
  251 + }
  252 + else
  253 + {
  254 + NSLog(@"保存视频成功!");
  255 +
  256 + saveVideoCompletionBlock(blockVideoUrl, nil);
  257 + }
  258 + }
  259 +
  260 + });
  261 +}
  262 +
  263 ++ (void)zx_saveVideoWithVideoUrl:(NSURL *)videoUrl andAssetCollectionName:(NSString *)assetCollectionName withCompletion:(GetCameraVideoCompletionBlock)getCameraVideoCompletionBlock
  264 +{
  265 + if (assetCollectionName == nil)
  266 + {
  267 + NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
  268 +
  269 + assetCollectionName = [infoDictionary objectForKey:@"CFBundleDisplayName"];
  270 +
  271 + if (assetCollectionName == nil)
  272 + {
  273 + assetCollectionName = @"视频相册";
  274 + }
  275 + }
  276 +
  277 + __block NSString *blockAssetCollectionName = assetCollectionName;
  278 + __block NSURL *blockVideoUrl = videoUrl;
  279 + PHPhotoLibrary *library = [PHPhotoLibrary sharedPhotoLibrary];
  280 +
  281 + dispatch_async(dispatch_get_main_queue(), ^{
  282 +
  283 + NSError *error = nil;
  284 + __block NSString *assetId = nil;
  285 + __block NSString *assetCollectionId = nil;
  286 +
  287 + // 保存视频到【Camera Roll】(相机胶卷)
  288 + [library performChangesAndWait:^{
  289 +
  290 + assetId = [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:blockVideoUrl].placeholderForCreatedAsset.localIdentifier;
  291 +
  292 + } error:&error];
  293 +
  294 + NSLog(@"error1: %@", error);
  295 +
  296 + // 获取曾经创建过的自定义视频相册名字
  297 + PHAssetCollection *createdAssetCollection = nil;
  298 + PHFetchResult <PHAssetCollection*> *assetCollections = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
  299 + for (PHAssetCollection *assetCollection in assetCollections)
  300 + {
  301 + if ([assetCollection.localizedTitle isEqualToString:blockAssetCollectionName])
  302 + {
  303 + createdAssetCollection = assetCollection;
  304 + break;
  305 + }
  306 + }
  307 +
  308 + //如果这个自定义框架没有创建过
  309 + if (createdAssetCollection == nil)
  310 + {
  311 + //创建新的[自定义的 Album](相簿\相册)
  312 + [library performChangesAndWait:^{
  313 +
  314 + assetCollectionId = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:blockAssetCollectionName].placeholderForCreatedAssetCollection.localIdentifier;
  315 +
  316 + } error:&error];
  317 +
  318 + NSLog(@"error2: %@", error);
  319 +
  320 + //抓取刚创建完的视频相册对象
  321 + createdAssetCollection = [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[assetCollectionId] options:nil].firstObject;
  322 +
  323 + }
  324 +
  325 + // 将【Camera Roll】(相机胶卷)的视频 添加到【自定义Album】(相簿\相册)中
  326 + [library performChangesAndWait:^{
  327 + PHAssetCollectionChangeRequest *request = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:createdAssetCollection];
  328 + [request addAssets:[PHAsset fetchAssetsWithLocalIdentifiers:@[assetId] options:nil]];
  329 +
  330 + } error:&error];
  331 + NSLog(@"error3: %@", error);
  332 +
  333 +
  334 + PHFetchResult<PHAsset *> *res = [PHAsset fetchAssetsWithLocalIdentifiers:@[assetId] options:nil];
  335 + PHAsset *asset = res[0];
  336 + NSLog(@"asset:\n%@",asset);
  337 +
  338 + if (getCameraVideoCompletionBlock) {
  339 + if (error)
  340 + {
  341 + getCameraVideoCompletionBlock(nil,nil,error);
  342 + }
  343 + else
  344 + {
  345 + getCameraVideoCompletionBlock(asset,blockVideoUrl,nil);
  346 + }
  347 + }
  348 +
  349 +//
  350 +// // 将【Camera Roll】(相机胶卷)的视频 添加到【自定义Album】(相簿\相册)中
  351 +// [library performChangesAndWait:^{
  352 +// PHAssetCollectionChangeRequest *request = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:createdAssetCollection];
  353 +//
  354 +//// PHFetchResult<PHAsset *> *res = [PHAsset fetchAssetsWithLocalIdentifiers:@[assetId] options:nil];
  355 +//// PHAsset *asset1 = res[0];
  356 +//// NSLog(@"asset1:\n%@",asset1);
  357 +//
  358 +// [request addAssets:[PHAsset fetchAssetsWithLocalIdentifiers:@[assetId] options:nil]];
  359 +//
  360 +// } error:&error];
  361 +// NSLog(@"error3: %@", error);
  362 +//
  363 +// // 提示信息
  364 +// if (saveVideoCompletionBlock)
  365 +// {
  366 +// if (error)
  367 +// {
  368 +// NSLog(@"保存视频失败!");
  369 +//
  370 +// saveVideoCompletionBlock(nil, error);
  371 +// }
  372 +// else
  373 +// {
  374 +// NSLog(@"保存视频成功!");
  375 +//
  376 +// saveVideoCompletionBlock(blockVideoUrl, nil);
  377 +// }
  378 +// }
  379 +
  380 + });
  381 +}
  382 +@end
Example/CNLiveAVCamera.xcodeproj/project.pbxproj
@@ -62,7 +62,7 @@ @@ -62,7 +62,7 @@
62 71719F9E1E33DC2100824A3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; 62 71719F9E1E33DC2100824A3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
63 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; 63 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
64 876F38133C52213CA7BB6367 /* Pods_CNLiveAVCamera_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CNLiveAVCamera_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 64 876F38133C52213CA7BB6367 /* Pods_CNLiveAVCamera_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CNLiveAVCamera_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
65 - 91D86358FC3A20D099BF20BF /* CNLiveAVCamera.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = CNLiveAVCamera.podspec; path = ../CNLiveAVCamera.podspec; sourceTree = "<group>"; }; 65 + 91D86358FC3A20D099BF20BF /* CNLiveAVCamera.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = CNLiveAVCamera.podspec; path = ../CNLiveAVCamera.podspec; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
66 A08A1D9662A4589773CD2F34 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; }; 66 A08A1D9662A4589773CD2F34 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
67 B83BAF65F6BCA4FD64ADC040 /* Pods-CNLiveAVCamera_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CNLiveAVCamera_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CNLiveAVCamera_Tests/Pods-CNLiveAVCamera_Tests.debug.xcconfig"; sourceTree = "<group>"; }; 67 B83BAF65F6BCA4FD64ADC040 /* Pods-CNLiveAVCamera_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CNLiveAVCamera_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CNLiveAVCamera_Tests/Pods-CNLiveAVCamera_Tests.debug.xcconfig"; sourceTree = "<group>"; };
68 E7FA68C9FF1F570173346565 /* Pods_CNLiveAVCamera_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CNLiveAVCamera_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 68 E7FA68C9FF1F570173346565 /* Pods_CNLiveAVCamera_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CNLiveAVCamera_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -226,6 +226,7 @@ @@ -226,6 +226,7 @@
226 6003F5AA195388D20070C39A /* Sources */, 226 6003F5AA195388D20070C39A /* Sources */,
227 6003F5AB195388D20070C39A /* Frameworks */, 227 6003F5AB195388D20070C39A /* Frameworks */,
228 6003F5AC195388D20070C39A /* Resources */, 228 6003F5AC195388D20070C39A /* Resources */,
  229 + 9080C32D0AD68113F8131D42 /* [CP] Embed Pods Frameworks */,
229 ); 230 );
230 buildRules = ( 231 buildRules = (
231 ); 232 );
@@ -260,6 +261,7 @@ @@ -260,6 +261,7 @@
260 developmentRegion = English; 261 developmentRegion = English;
261 hasScannedForEncodings = 0; 262 hasScannedForEncodings = 0;
262 knownRegions = ( 263 knownRegions = (
  264 + English,
263 en, 265 en,
264 Base, 266 Base,
265 ); 267 );
@@ -329,18 +331,70 @@ @@ -329,18 +331,70 @@
329 inputPaths = ( 331 inputPaths = (
330 "${SRCROOT}/Pods/Target Support Files/Pods-CNLiveAVCamera_Example/Pods-CNLiveAVCamera_Example-frameworks.sh", 332 "${SRCROOT}/Pods/Target Support Files/Pods-CNLiveAVCamera_Example/Pods-CNLiveAVCamera_Example-frameworks.sh",
331 "${BUILT_PRODUCTS_DIR}/CNLiveAVCamera/CNLiveAVCamera.framework", 333 "${BUILT_PRODUCTS_DIR}/CNLiveAVCamera/CNLiveAVCamera.framework",
  334 + "${BUILT_PRODUCTS_DIR}/CNLiveBaseKit/CNLiveBaseKit.framework",
  335 + "${BUILT_PRODUCTS_DIR}/CNLiveBusinessTools/CNLiveBusinessTools.framework",
  336 + "${BUILT_PRODUCTS_DIR}/CNLiveCommonCategory/CNLiveCommonCategory.framework",
  337 + "${BUILT_PRODUCTS_DIR}/CNLiveUserManagement/CNLiveUserManagement.framework",
  338 + "${BUILT_PRODUCTS_DIR}/MJExtension/MJExtension.framework",
  339 + "${BUILT_PRODUCTS_DIR}/Masonry/Masonry.framework",
  340 + "${BUILT_PRODUCTS_DIR}/QMUIKit/QMUIKit.framework",
  341 + "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
332 ); 342 );
333 name = "[CP] Embed Pods Frameworks"; 343 name = "[CP] Embed Pods Frameworks";
334 outputFileListPaths = ( 344 outputFileListPaths = (
335 ); 345 );
336 outputPaths = ( 346 outputPaths = (
337 "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CNLiveAVCamera.framework", 347 "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CNLiveAVCamera.framework",
  348 + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CNLiveBaseKit.framework",
  349 + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CNLiveBusinessTools.framework",
  350 + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CNLiveCommonCategory.framework",
  351 + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CNLiveUserManagement.framework",
  352 + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MJExtension.framework",
  353 + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Masonry.framework",
  354 + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/QMUIKit.framework",
  355 + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
338 ); 356 );
339 runOnlyForDeploymentPostprocessing = 0; 357 runOnlyForDeploymentPostprocessing = 0;
340 shellPath = /bin/sh; 358 shellPath = /bin/sh;
341 shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CNLiveAVCamera_Example/Pods-CNLiveAVCamera_Example-frameworks.sh\"\n"; 359 shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CNLiveAVCamera_Example/Pods-CNLiveAVCamera_Example-frameworks.sh\"\n";
342 showEnvVarsInLog = 0; 360 showEnvVarsInLog = 0;
343 }; 361 };
  362 + 9080C32D0AD68113F8131D42 /* [CP] Embed Pods Frameworks */ = {
  363 + isa = PBXShellScriptBuildPhase;
  364 + buildActionMask = 2147483647;
  365 + files = (
  366 + );
  367 + inputFileListPaths = (
  368 + );
  369 + inputPaths = (
  370 + "${SRCROOT}/Pods/Target Support Files/Pods-CNLiveAVCamera_Tests/Pods-CNLiveAVCamera_Tests-frameworks.sh",
  371 + "${BUILT_PRODUCTS_DIR}/CNLiveBaseKit/CNLiveBaseKit.framework",
  372 + "${BUILT_PRODUCTS_DIR}/CNLiveBusinessTools/CNLiveBusinessTools.framework",
  373 + "${BUILT_PRODUCTS_DIR}/CNLiveCommonCategory/CNLiveCommonCategory.framework",
  374 + "${BUILT_PRODUCTS_DIR}/CNLiveUserManagement/CNLiveUserManagement.framework",
  375 + "${BUILT_PRODUCTS_DIR}/MJExtension/MJExtension.framework",
  376 + "${BUILT_PRODUCTS_DIR}/Masonry/Masonry.framework",
  377 + "${BUILT_PRODUCTS_DIR}/QMUIKit/QMUIKit.framework",
  378 + "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
  379 + );
  380 + name = "[CP] Embed Pods Frameworks";
  381 + outputFileListPaths = (
  382 + );
  383 + outputPaths = (
  384 + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CNLiveBaseKit.framework",
  385 + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CNLiveBusinessTools.framework",
  386 + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CNLiveCommonCategory.framework",
  387 + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CNLiveUserManagement.framework",
  388 + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MJExtension.framework",
  389 + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Masonry.framework",
  390 + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/QMUIKit.framework",
  391 + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
  392 + );
  393 + runOnlyForDeploymentPostprocessing = 0;
  394 + shellPath = /bin/sh;
  395 + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CNLiveAVCamera_Tests/Pods-CNLiveAVCamera_Tests-frameworks.sh\"\n";
  396 + showEnvVarsInLog = 0;
  397 + };
344 CFC16709BBBEA2470565BEAB /* [CP] Check Pods Manifest.lock */ = { 398 CFC16709BBBEA2470565BEAB /* [CP] Check Pods Manifest.lock */ = {
345 isa = PBXShellScriptBuildPhase; 399 isa = PBXShellScriptBuildPhase;
346 buildActionMask = 2147483647; 400 buildActionMask = 2147483647;
Example/CNLiveAVCamera/CNLiveAVCamera-Info.plist
@@ -45,5 +45,16 @@ @@ -45,5 +45,16 @@
45 <string>UIInterfaceOrientationLandscapeLeft</string> 45 <string>UIInterfaceOrientationLandscapeLeft</string>
46 <string>UIInterfaceOrientationLandscapeRight</string> 46 <string>UIInterfaceOrientationLandscapeRight</string>
47 </array> 47 </array>
  48 +
  49 + <!-- 相册 -->
  50 + <key>NSPhotoLibraryUsageDescription</key>
  51 + <string>需要您的同意,APP才能访问相册</string>
  52 + <!-- 相机 -->
  53 + <key>NSCameraUsageDescription</key>
  54 + <string>需要您的同意,APP才能访问相机</string>
  55 + <!-- 麦克风 -->
  56 + <key>NSMicrophoneUsageDescription</key>
  57 + <string>需要您的同意,APP才能访问麦克风</string>
  58 +
48 </dict> 59 </dict>
49 </plist> 60 </plist>
Example/CNLiveAVCamera/CNLiveViewController.m
@@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
7 // 7 //
8 8
9 #import "CNLiveViewController.h" 9 #import "CNLiveViewController.h"
  10 +#import "XFCameraController.h"
10 11
11 @interface CNLiveViewController () 12 @interface CNLiveViewController ()
12 13
@@ -18,8 +19,62 @@ @@ -18,8 +19,62 @@
18 { 19 {
19 [super viewDidLoad]; 20 [super viewDidLoad];
20 // Do any additional setup after loading the view, typically from a nib. 21 // Do any additional setup after loading the view, typically from a nib.
  22 +
  23 + UIButton *takeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  24 + takeBtn.frame = CGRectMake(0, 0, 150, 150);
  25 + [takeBtn setBackgroundColor:[UIColor yellowColor]];
  26 + [self.view addSubview:takeBtn];
  27 + [takeBtn addTarget:self action:@selector(takeBtnAction:) forControlEvents:UIControlEventTouchUpInside];
  28 +
  29 +
21 } 30 }
22 31
  32 +- (void)takeToXFPhoto
  33 +{
  34 + XFCameraController *cameraController = [[XFCameraController alloc] init];
  35 + cameraController.isCanShootVideo = YES;
  36 + cameraController.conversationId = @"";
  37 +
  38 + __weak XFCameraController *weakCameraController = cameraController;
  39 + __weak typeof(self) weakSelf = self;
  40 + //拍照完成
  41 + cameraController.takePhotosCompletionBlock = ^(UIImage *image, NSError *error) {
  42 + NSLog(@"takePhotosCompletionBlock");
  43 +
  44 + dispatch_async(dispatch_get_main_queue(), ^{
  45 + [weakCameraController dismissViewControllerAnimated:YES completion:nil];
  46 + });
  47 + };
  48 +
  49 + //录制完成
  50 + cameraController.zxshootCompletionBlock = ^(PHAsset *asset, NSURL *videoUrl, CGFloat videoTimeLength, UIImage *thumbnailImage, NSError *error) {
  51 + NSLog(@"shootCompletionBlock");
  52 + if (!error) {
  53 +
  54 + dispatch_async(dispatch_get_main_queue(), ^{
  55 + [weakCameraController dismissViewControllerAnimated:YES completion:nil];
  56 + });
  57 + }
  58 + else
  59 + {
  60 + dispatch_async(dispatch_get_main_queue(), ^{
  61 + NSLog(@"录制失败");
  62 + });
  63 + }
  64 +
  65 + };
  66 +
  67 + [self presentViewController:cameraController animated:YES completion:nil];
  68 +}
  69 +
  70 +- (void)takeBtnAction:(UIButton *)sender {
  71 +
  72 +
  73 + [self takeToXFPhoto];
  74 +
  75 +}
  76 +
  77 +
23 - (void)didReceiveMemoryWarning 78 - (void)didReceiveMemoryWarning
24 { 79 {
25 [super didReceiveMemoryWarning]; 80 [super didReceiveMemoryWarning];
Example/Podfile
  1 +source 'https://github.com/CocoaPods/Specs.git'
  2 +source 'http://bj.gitlab.cnlive.com/ios-team/CNLiveRepos.git'
  3 +
1 use_frameworks! 4 use_frameworks!
2 5
3 -platform :ios, '8.0' 6 +platform :ios, '9.0'
4 7
5 target 'CNLiveAVCamera_Example' do 8 target 'CNLiveAVCamera_Example' do
6 pod 'CNLiveAVCamera', :path => '../' 9 pod 'CNLiveAVCamera', :path => '../'
  10 + pod 'Masonry', '~> 1.0.2'
  11 + pod 'CNLiveBaseKit'
  12 + pod 'QMUIKit','~> 3.1.7'
  13 + pod 'CNLiveBusinessTools'
  14 + pod 'CNLiveCommonCategory'
  15 +
7 16
8 target 'CNLiveAVCamera_Tests' do 17 target 'CNLiveAVCamera_Tests' do
9 inherit! :search_paths 18 inherit! :search_paths
10 - 19 +
  20 + pod 'Masonry', '~> 1.0.2'
  21 + pod 'CNLiveBaseKit'
  22 + pod 'QMUIKit','~> 3.1.7'
  23 + pod 'CNLiveBusinessTools'
  24 + pod 'CNLiveCommonCategory'
11 25
12 end 26 end
13 end 27 end