Commit f2e2ed7862037df69b58e9d339987833d24fbaf9
1 parent
fe1d43b1
提交
Showing
79 changed files
with
4529 additions
and
0 deletions
CNLiveAVCamera.podspec
0 → 100644
| 1 | +# | ||
| 2 | +# Be sure to run `pod lib lint CNLiveAVCamera.podspec' to ensure this is a | ||
| 3 | +# valid spec before submitting. | ||
| 4 | +# | ||
| 5 | +# Any lines starting with a # are optional, but their use is encouraged | ||
| 6 | +# To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html | ||
| 7 | +# | ||
| 8 | + | ||
| 9 | +Pod::Spec.new do |s| | ||
| 10 | + s.name = 'CNLiveAVCamera' | ||
| 11 | + s.version = '0.1.0' | ||
| 12 | + s.summary = '常用相机' | ||
| 13 | + s.description = <<-DESC | ||
| 14 | +TODO: Add long description of the pod here. | ||
| 15 | + DESC | ||
| 16 | + | ||
| 17 | + s.homepage = 'https://github.com/jyyjkt/CNLiveAVCamera' | ||
| 18 | + # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' | ||
| 19 | + s.license = { :type => 'MIT', :file => 'LICENSE' } | ||
| 20 | + s.author = { 'jyyjkt' => 'jyyjkt@qq.com' } | ||
| 21 | + s.source = { :git => 'https://github.com/jyyjkt/CNLiveAVCamera.git', :tag => s.version.to_s } | ||
| 22 | + # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>' | ||
| 23 | + s.ios.deployment_target = '8.0' | ||
| 24 | + s.source_files = 'CNLiveAVCamera/Classes/XFAVCamera/*.{h,m}' | ||
| 25 | + | ||
| 26 | +# s.resource_bundles = { | ||
| 27 | +# 'CNLiveAVCamera' => ['CNLiveAVCamera/Assets/XFAVCamera/*'] | ||
| 28 | +# } | ||
| 29 | + s.resources = 'CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle' | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + s.public_header_files = 'CNLiveAVCamera/Classes/XFAVCamera/*.h' | ||
| 33 | + s.frameworks = 'UIKit', 'Photos' | ||
| 34 | + s.dependency 'Masonry', '~> 1.0.2' | ||
| 35 | + | ||
| 36 | +end |
CNLiveAVCamera/Assets/.gitkeep
0 → 100644
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.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 | +</dict> | ||
| 61 | +</plist> |
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/en.lproj/Root.strings
0 → 100644
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/fb_chongpai.png
0 → 100644
1.98 KB
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/fb_chongpai@2x.png
0 → 100644
3.38 KB
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/fb_chongpai@3x.png
0 → 100644
4.87 KB
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/fb_quxiaoxia.png
0 → 100644
1.26 KB
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/fb_quxiaoxia@2x.png
0 → 100644
1.68 KB
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/fb_quxiaoxia@3x.png
0 → 100644
1.96 KB
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/fb_wancheng.png
0 → 100644
1.97 KB
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/fb_wancheng@2x.png
0 → 100644
3.46 KB
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/fb_wancheng@3x.png
0 → 100644
5.08 KB
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/fb_xiangji.png
0 → 100644
1.32 KB
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/fb_xiangji@2x.png
0 → 100644
1.68 KB
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/fb_xiangji@3x.png
0 → 100644
2.27 KB
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/gz_shoudian.png
0 → 100644
1.11 KB
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/gz_shoudian2.png
0 → 100644
1.13 KB
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/gz_shoudian2@2x.png
0 → 100644
1.34 KB
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/gz_shoudian2@3x.png
0 → 100644
1.52 KB
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/gz_shoudian@2x.png
0 → 100644
1.29 KB
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/gz_shoudian@3x.png
0 → 100644
1.41 KB
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/sight_video_focus@2x.png
0 → 100644
3.02 KB
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/sight_video_focus@3x.png
0 → 100644
3.36 KB
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/zhuangtai_kong.png
0 → 100644
7.43 KB
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/zhuangtai_kong@2x.png
0 → 100644
20.7 KB
CNLiveAVCamera/Assets/XFAVCamera/XFAVCamera.bundle/zhuangtai_kong@3x.png
0 → 100644
41.3 KB
CNLiveAVCamera/Assets/image/fb_chongpai.png
0 → 100644
1.98 KB
CNLiveAVCamera/Assets/image/fb_chongpai@2x.png
0 → 100644
3.38 KB
CNLiveAVCamera/Assets/image/fb_chongpai@3x.png
0 → 100644
4.87 KB
CNLiveAVCamera/Assets/image/fb_quxiaoxia.png
0 → 100644
1.26 KB
CNLiveAVCamera/Assets/image/fb_quxiaoxia@2x.png
0 → 100644
1.68 KB
CNLiveAVCamera/Assets/image/fb_quxiaoxia@3x.png
0 → 100644
1.96 KB
CNLiveAVCamera/Assets/image/fb_wancheng.png
0 → 100644
1.97 KB
CNLiveAVCamera/Assets/image/fb_wancheng@2x.png
0 → 100644
3.46 KB
CNLiveAVCamera/Assets/image/fb_wancheng@3x.png
0 → 100644
5.08 KB
CNLiveAVCamera/Assets/image/fb_xiangji.png
0 → 100644
1.32 KB
CNLiveAVCamera/Assets/image/fb_xiangji@2x.png
0 → 100644
1.68 KB
CNLiveAVCamera/Assets/image/fb_xiangji@3x.png
0 → 100644
2.27 KB
CNLiveAVCamera/Assets/image/gz_shoudian.png
0 → 100644
1.11 KB
CNLiveAVCamera/Assets/image/gz_shoudian2.png
0 → 100644
1.13 KB
CNLiveAVCamera/Assets/image/gz_shoudian2@2x.png
0 → 100644
1.34 KB
CNLiveAVCamera/Assets/image/gz_shoudian2@3x.png
0 → 100644
1.52 KB
CNLiveAVCamera/Assets/image/gz_shoudian@2x.png
0 → 100644
1.29 KB
CNLiveAVCamera/Assets/image/gz_shoudian@3x.png
0 → 100644
1.41 KB
CNLiveAVCamera/Assets/image/sight_video_focus@2x.png
0 → 100644
3.02 KB
CNLiveAVCamera/Assets/image/sight_video_focus@3x.png
0 → 100644
3.36 KB
CNLiveAVCamera/Classes/.gitkeep
0 → 100644
CNLiveAVCamera/Classes/XFAVCamera/NSBundle+CNLiveXFCamera.h
0 → 100644
| 1 | +// | ||
| 2 | +// NSBundle+CNLiveXFCamera.h | ||
| 3 | +// CNLiveAVCamera | ||
| 4 | +// | ||
| 5 | +// Created by 梁星国 on 2018/12/8. | ||
| 6 | +// | ||
| 7 | + | ||
| 8 | +#import <Foundation/Foundation.h> | ||
| 9 | + | ||
| 10 | +NS_ASSUME_NONNULL_BEGIN | ||
| 11 | + | ||
| 12 | +@interface NSBundle (CNLiveXFCamera) | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + 取得相机包bundle | ||
| 17 | + | ||
| 18 | + @return 返回bundle | ||
| 19 | + */ | ||
| 20 | ++ (NSBundle *)xf_imageBundle; | ||
| 21 | + | ||
| 22 | +/** | ||
| 23 | + 取得Bundle | ||
| 24 | + | ||
| 25 | + @param cls 包中类名 | ||
| 26 | + @param package 包 | ||
| 27 | + @param bundleName bundle名 | ||
| 28 | + @return 返回bundle | ||
| 29 | + */ | ||
| 30 | ++ (NSBundle *)xf_imageBundleWithCls:(id)cls package:(NSString *)package bundleName:(NSString *)bundleName; | ||
| 31 | + | ||
| 32 | +@end | ||
| 33 | + | ||
| 34 | +NS_ASSUME_NONNULL_END | ||
| 35 | + | ||
| 36 | +NS_ASSUME_NONNULL_BEGIN | ||
| 37 | + | ||
| 38 | +@interface UIImage (CNLiveXFCamera) | ||
| 39 | + | ||
| 40 | + | ||
| 41 | +/** | ||
| 42 | + 从bundle中取图片 | ||
| 43 | + | ||
| 44 | + @param imageName 图片名 | ||
| 45 | + @return 图片 | ||
| 46 | + */ | ||
| 47 | ++ (UIImage *)imageFromBundle:(NSString *)imageName; | ||
| 48 | + | ||
| 49 | +@end | ||
| 50 | + | ||
| 51 | +NS_ASSUME_NONNULL_END |
CNLiveAVCamera/Classes/XFAVCamera/NSBundle+CNLiveXFCamera.m
0 → 100644
| 1 | +// | ||
| 2 | +// NSBundle+CNLiveXFCamera.m | ||
| 3 | +// CNLiveAVCamera | ||
| 4 | +// | ||
| 5 | +// Created by 梁星国 on 2018/12/8. | ||
| 6 | +// | ||
| 7 | + | ||
| 8 | +#import "NSBundle+CNLiveXFCamera.h" | ||
| 9 | +#import "XFCameraController.h" | ||
| 10 | + | ||
| 11 | +@implementation NSBundle (CNLiveXFCamera) | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + 取得相机包bundle | ||
| 15 | + | ||
| 16 | + @return 返回bundle | ||
| 17 | + */ | ||
| 18 | ++ (NSBundle *)xf_imageBundle { | ||
| 19 | + | ||
| 20 | + return [NSBundle xf_imageBundleWithCls:[XFCameraController class] package:@"CNLiveAVCamera.framework" bundleName:@"XFAVCamera"]; | ||
| 21 | +} | ||
| 22 | + | ||
| 23 | + | ||
| 24 | +/** | ||
| 25 | + 取得Bundle | ||
| 26 | + | ||
| 27 | + @param cls 包中类名 | ||
| 28 | + @param package 包 | ||
| 29 | + @param bundleName bundle名 | ||
| 30 | + @return 返回bundle | ||
| 31 | + */ | ||
| 32 | ++ (NSBundle *)xf_imageBundleWithCls:(id)cls package:(NSString *)package bundleName:(NSString *)bundleName { | ||
| 33 | + | ||
| 34 | + NSBundle *bundle = [NSBundle bundleForClass:[cls class]]; | ||
| 35 | + NSURL *url = [bundle URLForResource:bundleName withExtension:@"bundle"]; | ||
| 36 | + if (!url) { | ||
| 37 | + NSString *resource = [NSString stringWithFormat:@"%@/%@",package,bundleName]; | ||
| 38 | + NSString *path = [[NSBundle mainBundle] pathForResource:resource ofType:@"bundle"]; | ||
| 39 | + if (path) { | ||
| 40 | + resource = [NSString stringWithFormat:@"%@",package]; | ||
| 41 | + path = [[NSBundle mainBundle] pathForResource:resource ofType:@"bundle"]; | ||
| 42 | + if (path) { | ||
| 43 | + url = [NSURL fileURLWithPath:path]; | ||
| 44 | + }else { | ||
| 45 | + url = nil; | ||
| 46 | + } | ||
| 47 | + }else{ | ||
| 48 | + url = [NSURL fileURLWithPath:path]; | ||
| 49 | + } | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + if (url) { | ||
| 53 | + return [NSBundle bundleWithURL:url]; | ||
| 54 | + }else { | ||
| 55 | + return [NSBundle mainBundle]; | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | +} | ||
| 59 | + | ||
| 60 | + | ||
| 61 | + | ||
| 62 | +@end | ||
| 63 | + | ||
| 64 | + | ||
| 65 | +@implementation UIImage (CNLiveXFCamera) | ||
| 66 | + | ||
| 67 | + | ||
| 68 | +/** | ||
| 69 | + 从bundle中取图片 | ||
| 70 | + | ||
| 71 | + @param imageName 图片名 | ||
| 72 | + @return 图片 | ||
| 73 | + */ | ||
| 74 | ++ (UIImage *)imageFromBundle:(NSString *)imageName{ | ||
| 75 | + NSBundle *bundle = [NSBundle xf_imageBundle]; | ||
| 76 | + NSString *name = [NSString stringWithFormat:@"%@",imageName]; | ||
| 77 | + name = [name stringByAppendingString:@"@2x"]; | ||
| 78 | + NSString *imagePath = [bundle pathForResource:name ofType:@"png"]; | ||
| 79 | + if (!imagePath) { | ||
| 80 | + name = [name stringByReplacingOccurrencesOfString:@"@2x" withString:@"@3x"]; | ||
| 81 | + imagePath = [bundle pathForResource:name ofType:@"png"]; | ||
| 82 | + } | ||
| 83 | + UIImage *image = [UIImage imageWithContentsOfFile:imagePath]; | ||
| 84 | + return image; | ||
| 85 | +} | ||
| 86 | + | ||
| 87 | + | ||
| 88 | +@end |
CNLiveAVCamera/Classes/XFAVCamera/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/XFAVCamera/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 CAMERA_RGB | ||
| 23 | +#define CAMERA_RGB(r, g, b, a) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a] | ||
| 24 | +#endif | ||
| 25 | + | ||
| 26 | +@interface XFCameraButton () | ||
| 27 | + | ||
| 28 | +@property (weak, nonatomic) UIView *touchView; | ||
| 29 | +@property (strong, nonatomic) CAShapeLayer *trackLayer; | ||
| 30 | +@property (strong, nonatomic) CAShapeLayer *progressLayer; | ||
| 31 | + | ||
| 32 | +@property (copy, nonatomic) TapEventBlock tapEventBlock; | ||
| 33 | +@property (copy, nonatomic) LongPressEventBlock longPressEventBlock; | ||
| 34 | + | ||
| 35 | +@end | ||
| 36 | + | ||
| 37 | +@implementation XFCameraButton | ||
| 38 | + | ||
| 39 | +#pragma mark - 工厂方法 | ||
| 40 | + | ||
| 41 | ++ (instancetype)defaultCameraButton | ||
| 42 | +{ | ||
| 43 | + // 设置camera view | ||
| 44 | + XFCameraButton *cameraButton = [[XFCameraButton alloc] initWithFrame:CGRectMake(0, 0, CAMERABUTTONWIDTH, CAMERABUTTONWIDTH)]; | ||
| 45 | + [cameraButton.layer setCornerRadius:(CAMERABUTTONWIDTH / 2)]; | ||
| 46 | + cameraButton.backgroundColor = [UIColor whiteColor];//[UIColor colorWithRed:225/255.0f green:225/255.0f blue:230/255.0f alpha:1.0f]; | ||
| 47 | + | ||
| 48 | + // 设置camera view的点击按钮 | ||
| 49 | + CGFloat touchViewX = (CAMERABUTTONWIDTH - TOUCHVIEWWIDTH) / 2; | ||
| 50 | + CGFloat touchViewY = (CAMERABUTTONWIDTH - TOUCHVIEWWIDTH) / 2; | ||
| 51 | + UIView *touchView = [[UIView alloc] initWithFrame:CGRectMake(touchViewX, touchViewY, TOUCHVIEWWIDTH, TOUCHVIEWWIDTH)]; | ||
| 52 | + cameraButton.touchView = touchView; | ||
| 53 | + [cameraButton addSubview:touchView]; | ||
| 54 | + [cameraButton.touchView.layer setCornerRadius:(cameraButton.touchView.bounds.size.width / 2)]; | ||
| 55 | + touchView.backgroundColor = CAMERA_RGB(255, 163, 0, 1); | ||
| 56 | + | ||
| 57 | + [cameraButton initCircleAnimationLayer]; | ||
| 58 | + | ||
| 59 | + return cameraButton; | ||
| 60 | +} | ||
| 61 | + | ||
| 62 | + | ||
| 63 | +#pragma mark - 点击事件与长按事件 | ||
| 64 | + | ||
| 65 | +/** | ||
| 66 | + * 配置点击事件 | ||
| 67 | + */ | ||
| 68 | +- (void)configureTapCameraButtonEventWithBlock:(TapEventBlock)tapEventBlock | ||
| 69 | +{ | ||
| 70 | + self.tapEventBlock = tapEventBlock; | ||
| 71 | + | ||
| 72 | + UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapCameraButtonEvent:)]; | ||
| 73 | + | ||
| 74 | + [self.touchView addGestureRecognizer:tapGestureRecognizer]; | ||
| 75 | +} | ||
| 76 | + | ||
| 77 | +- (void)tapCameraButtonEvent:(UITapGestureRecognizer *)tapGestureRecognizer | ||
| 78 | +{ | ||
| 79 | + if (self.tapEventBlock) | ||
| 80 | + { | ||
| 81 | + self.tapEventBlock(tapGestureRecognizer); | ||
| 82 | + } | ||
| 83 | +} | ||
| 84 | + | ||
| 85 | +/** | ||
| 86 | + * 配置按压事件 | ||
| 87 | + */ | ||
| 88 | +- (void)configureLongPressCameraButtonEventWithBlock:(LongPressEventBlock)longPressEventBlock | ||
| 89 | +{ | ||
| 90 | + self.longPressEventBlock = longPressEventBlock; | ||
| 91 | + | ||
| 92 | + UILongPressGestureRecognizer *longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressCameraButtonEvent:)]; | ||
| 93 | + | ||
| 94 | + [self.touchView addGestureRecognizer:longPressGestureRecognizer]; | ||
| 95 | +} | ||
| 96 | + | ||
| 97 | +- (void)longPressCameraButtonEvent:(UILongPressGestureRecognizer *)longPressGestureRecognizer | ||
| 98 | +{ | ||
| 99 | + if (self.longPressEventBlock) | ||
| 100 | + { | ||
| 101 | + self.longPressEventBlock(longPressGestureRecognizer); | ||
| 102 | + } | ||
| 103 | +} | ||
| 104 | + | ||
| 105 | +#pragma mark - 录制视频按钮动画 | ||
| 106 | + | ||
| 107 | +// 初始化按钮路径 | ||
| 108 | +- (void)initCircleAnimationLayer | ||
| 109 | +{ | ||
| 110 | + float centerX = self.bounds.size.width / 2.0; | ||
| 111 | + float centerY = self.bounds.size.height / 2.0; | ||
| 112 | + //半径 | ||
| 113 | + float radius = (self.bounds.size.width - PROGRESSLINEWIDTH) / 2.0; | ||
| 114 | + | ||
| 115 | + //创建贝塞尔路径 | ||
| 116 | + UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(centerX, centerY) radius:radius startAngle:(-0.5f * M_PI) endAngle:(1.5f * M_PI) clockwise:YES]; | ||
| 117 | + | ||
| 118 | + //添加背景圆环 | ||
| 119 | + CAShapeLayer *backLayer = [CAShapeLayer layer]; | ||
| 120 | + backLayer.frame = self.bounds; | ||
| 121 | + backLayer.fillColor = [[UIColor clearColor] CGColor]; | ||
| 122 | + backLayer.strokeColor = CAMERA_RGB(242, 242, 242,1).CGColor;//[RGB(225, 225, 230, 1.0f) CGColor]; | ||
| 123 | + backLayer.lineWidth = PROGRESSLINEWIDTH; | ||
| 124 | + backLayer.path = [path CGPath]; | ||
| 125 | + backLayer.strokeEnd = 1; | ||
| 126 | + [self.layer addSublayer:backLayer]; | ||
| 127 | + | ||
| 128 | + //创建进度layer | ||
| 129 | + _progressLayer = [CAShapeLayer layer]; | ||
| 130 | + _progressLayer.frame = self.bounds; | ||
| 131 | + _progressLayer.fillColor = [[UIColor clearColor] CGColor]; | ||
| 132 | + //指定path的渲染颜色 | ||
| 133 | + _progressLayer.strokeColor = [[UIColor blackColor] CGColor]; | ||
| 134 | + _progressLayer.lineCap = kCALineCapSquare;//kCALineCapRound; | ||
| 135 | + _progressLayer.lineWidth = PROGRESSLINEWIDTH; | ||
| 136 | + _progressLayer.path = [path CGPath]; | ||
| 137 | + _progressLayer.strokeEnd = 0; | ||
| 138 | + | ||
| 139 | + //设置渐变颜色 | ||
| 140 | + CAGradientLayer *gradientLayer = [CAGradientLayer layer]; | ||
| 141 | + gradientLayer.frame = self.bounds; | ||
| 142 | + | ||
| 143 | + // 渐变颜色 | ||
| 144 | + [gradientLayer setColors:[NSArray arrayWithObjects:(id)[CAMERA_RGB(255, 163, 0,1) CGColor], (id)[CAMERA_RGB(255, 163, 0, 1) CGColor], nil]];//RGB(76, 192, 29, 1.0f) [RGB(76, 192, 29, 1.0f) CGColor] | ||
| 145 | +// [gradientLayer setColors:[NSArray arrayWithObjects:(id)[RGB(28, 178, 29, 1.0f) CGColor], (id)[RGB(255, 203, 0, 1.0f) CGColor], nil]]; | ||
| 146 | + | ||
| 147 | + gradientLayer.startPoint = CGPointMake(0, 0); | ||
| 148 | + gradientLayer.endPoint = CGPointMake(0, 1); | ||
| 149 | + [gradientLayer setMask:_progressLayer]; //用progressLayer来截取渐变层 | ||
| 150 | + [self.layer addSublayer:gradientLayer]; | ||
| 151 | + | ||
| 152 | +} | ||
| 153 | + | ||
| 154 | +// 设置按钮百分比 | ||
| 155 | +- (void)setProgressPercentage:(CGFloat)progressPercentage | ||
| 156 | +{ | ||
| 157 | + _progressPercentage = progressPercentage; | ||
| 158 | + _progressLayer.strokeEnd = progressPercentage; | ||
| 159 | + [_progressLayer removeAllAnimations]; | ||
| 160 | +} | ||
| 161 | + | ||
| 162 | +/** | ||
| 163 | + * 开始录制前的准备动画 | ||
| 164 | + */ | ||
| 165 | +- (void)startShootAnimationWithDuration:(NSTimeInterval)duration | ||
| 166 | +{ | ||
| 167 | + __weak typeof(self) weakSelf = self; | ||
| 168 | + | ||
| 169 | + [UIView animateWithDuration:duration animations:^{ | ||
| 170 | + | ||
| 171 | + weakSelf.transform = CGAffineTransformMakeScale(SHOOTCAMERABUTTONSCALE, SHOOTCAMERABUTTONSCALE); | ||
| 172 | + weakSelf.touchView.transform = CGAffineTransformMakeScale(SHOOTTOUCHVIEWSCALE, SHOOTTOUCHVIEWSCALE); | ||
| 173 | + | ||
| 174 | + } completion:^(BOOL finished) { | ||
| 175 | + // nothing | ||
| 176 | + }]; | ||
| 177 | +} | ||
| 178 | + | ||
| 179 | +/** | ||
| 180 | + * 结束摄影动画 | ||
| 181 | + */ | ||
| 182 | +- (void)stopShootAnimation | ||
| 183 | +{ | ||
| 184 | + self.transform = CGAffineTransformIdentity; | ||
| 185 | + self.touchView.transform = CGAffineTransformIdentity; | ||
| 186 | +} | ||
| 187 | + | ||
| 188 | + | ||
| 189 | +@end | ||
| 190 | + | ||
| 191 | + | ||
| 192 | + | ||
| 193 | + | ||
| 194 | + | ||
| 195 | + | ||
| 196 | + | ||
| 197 | + | ||
| 198 | + | ||
| 199 | + |
CNLiveAVCamera/Classes/XFAVCamera/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 | +@interface UIControl (CameraEventInterval) | ||
| 13 | + | ||
| 14 | +@property (nonatomic, assign) NSTimeInterval camera_cs_acceptEventInterval; // 重复点击的间隔 | ||
| 15 | + | ||
| 16 | +@property (nonatomic, assign) NSTimeInterval camera_cs_acceptEventTime; | ||
| 17 | + | ||
| 18 | +@end | ||
| 19 | + | ||
| 20 | + | ||
| 21 | +typedef void(^TakePhotosCompletionBlock)(UIImage *image, NSError *error); | ||
| 22 | +typedef void(^ShootCompletionBlock)(NSURL *videoUrl, CGFloat videoTimeLength, UIImage *thumbnailImage, NSError *error); | ||
| 23 | + | ||
| 24 | +//zx | ||
| 25 | +typedef void(^ZXShootCompletionBlock)(PHAsset *asset,NSURL *videoUrl, CGFloat videoTimeLength, UIImage *thumbnailImage, NSError *error); | ||
| 26 | + | ||
| 27 | +@interface XFCameraController : UIViewController | ||
| 28 | + | ||
| 29 | +/** | ||
| 30 | + * 拍照完成后的Block回调 | ||
| 31 | + */ | ||
| 32 | +@property (copy, nonatomic) TakePhotosCompletionBlock takePhotosCompletionBlock; | ||
| 33 | + | ||
| 34 | +/** | ||
| 35 | + * 拍摄完成后的Block回调 | ||
| 36 | + */ | ||
| 37 | +@property (copy, nonatomic) ShootCompletionBlock shootCompletionBlock; | ||
| 38 | + | ||
| 39 | +/** | ||
| 40 | + * zx拍摄完成后的Block回调 | ||
| 41 | + */ | ||
| 42 | +@property (copy, nonatomic) ZXShootCompletionBlock zxshootCompletionBlock; | ||
| 43 | + | ||
| 44 | +/** | ||
| 45 | + * 自定义APP相册名字,如果为空则默认为APP的名字 | ||
| 46 | + */ | ||
| 47 | +@property (strong, nonatomic) NSString *assetCollectionName; | ||
| 48 | + | ||
| 49 | +/** | ||
| 50 | + * 视频文件保存文件夹,如果没有定义,默认在document/video文件夹下面 | ||
| 51 | + */ | ||
| 52 | +@property (strong, nonatomic) NSString *videoFilePath; | ||
| 53 | + | ||
| 54 | + | ||
| 55 | +//zx 当前是否可以拍摄视频 BOOL | ||
| 56 | +@property (nonatomic ,assign) BOOL isCanShootVideo; | ||
| 57 | + | ||
| 58 | +//zl__会话ID,用于存储拍摄的视频 | ||
| 59 | +@property (nonatomic ,strong) NSString *conversationId; | ||
| 60 | + | ||
| 61 | +@end |
CNLiveAVCamera/Classes/XFAVCamera/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 <Masonry/Masonry.h> | ||
| 17 | +#import <objc/runtime.h> | ||
| 18 | +#import <UIKit/UIKit.h> | ||
| 19 | +#import "NSBundle+CNLiveXFCamera.h" | ||
| 20 | + | ||
| 21 | + | ||
| 22 | +#ifndef kCameraScreenWidth | ||
| 23 | +#define kCameraScreenWidth [UIScreen mainScreen].bounds.size.width | ||
| 24 | +#endif | ||
| 25 | +#ifndef kCameraScreenHeight | ||
| 26 | +#define kCameraScreenHeight [UIScreen mainScreen].bounds.size.height | ||
| 27 | +#endif | ||
| 28 | + | ||
| 29 | +#define SafeViewBottomHeight (kCameraScreenHeight == 812.0 ? 34.0 : 0.0) | ||
| 30 | +#define VIDEO_FILEPATH @"video" | ||
| 31 | +#define TIMER_INTERVAL 0.01f //定时器记录视频间隔 | ||
| 32 | +#define VIDEO_RECORDER_MAX_TIME 15.0f //视频最大时长 (单位/秒) | ||
| 33 | +#define VIDEO_RECORDER_MIN_TIME 2.0f //最短视频时长 (单位/秒) | ||
| 34 | +#define START_VIDEO_ANIMATION_DURATION 0.3f //录制视频前的动画时间 | ||
| 35 | + | ||
| 36 | +#define CAMERA_IS_IPhoneX_All (CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(375, 812)) ||CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(812, 375)) ||CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(414, 896)) ||CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(896, 414))) | ||
| 37 | + | ||
| 38 | +#define kCameraStatusHeight (int)[UIApplication sharedApplication].statusBarFrame.size.height //x44 非x20 | ||
| 39 | +#define kCameraNavigationBarHeight ((int)[UIApplication sharedApplication].statusBarFrame.size.height+44) //x88 非x64 | ||
| 40 | +#define kCameraVerticalNaviBarHeight ((CAMERA_IS_IPhoneX_All > 0) ? 88 : 44) | ||
| 41 | +#define kCameraVerticalTabBarTotalHeight ((CAMERA_IS_IPhoneX_All > 0) ? 83 : 49) | ||
| 42 | +#define kCameraVerticalBottomSafeHeight ((CAMERA_IS_IPhoneX_All > 0) ? 34 : 0) | ||
| 43 | +#define kCameraVerticalStatusHeight ((CAMERA_IS_IPhoneX_All > 0) ? 44 : 20) | ||
| 44 | +#define kCameraVerticalTopHeight ((CAMERA_IS_IPhoneX_All > 0) ? 24 : 0) | ||
| 45 | +#define kCameraHorizontalTopNaviBarHeight 32 | ||
| 46 | +#define kCameraHorizontalBottomHeight ((CAMERA_IS_IPhoneX_All > 0) ? 21 : 0) | ||
| 47 | +#define kCameraHorizontalLeftHeight ((CAMERA_IS_IPhoneX_All > 0) ? 44 : 0) | ||
| 48 | +#define kCameraHorizontalRightHeight ((CAMERA_IS_IPhoneX_All > 0) ? 44 : 0) | ||
| 49 | +#define kCameraHorizontalTopHeight ((CAMERA_IS_IPhoneX_All > 0) ? 24 : 0) | ||
| 50 | + | ||
| 51 | +#define CameraImageMake(img) [UIImage imageNamed:img inBundle:nil compatibleWithTraitCollection:nil] | ||
| 52 | + | ||
| 53 | +@implementation UIControl (CNEventInterval) | ||
| 54 | + | ||
| 55 | +static const char *UIControl_acceptEventInterval = "UIControl_acceptEventInterval"; | ||
| 56 | +- (NSTimeInterval)camera_cs_acceptEventInterval { | ||
| 57 | + return [objc_getAssociatedObject(self, UIControl_acceptEventInterval) doubleValue]; | ||
| 58 | +} | ||
| 59 | + | ||
| 60 | +- (void)setCamera_cs_acceptEventInterval:(NSTimeInterval)camera_cs_acceptEventInterval { | ||
| 61 | + objc_setAssociatedObject(self, UIControl_acceptEventInterval, @(camera_cs_acceptEventInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC); | ||
| 62 | +} | ||
| 63 | + | ||
| 64 | +static const char *UIControl_acceptEventTime = "UIControl_acceptEventTime"; | ||
| 65 | + | ||
| 66 | +- (NSTimeInterval)camera_cs_acceptEventTime { | ||
| 67 | + return [objc_getAssociatedObject(self, UIControl_acceptEventTime) doubleValue]; | ||
| 68 | +} | ||
| 69 | + | ||
| 70 | +- (void)setCamera_cs_acceptEventTime:(NSTimeInterval)camera_cs_acceptEventTime { | ||
| 71 | + objc_setAssociatedObject(self, UIControl_acceptEventTime, @(camera_cs_acceptEventTime), OBJC_ASSOCIATION_RETAIN_NONATOMIC); | ||
| 72 | +} | ||
| 73 | + | ||
| 74 | + | ||
| 75 | +// 在load时执行hook | ||
| 76 | ++ (void)load { | ||
| 77 | + Method before = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:)); | ||
| 78 | + Method after = class_getInstanceMethod(self, @selector(cs_sendAction:to:forEvent:)); | ||
| 79 | + method_exchangeImplementations(before, after); | ||
| 80 | +} | ||
| 81 | + | ||
| 82 | +- (void)cs_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event { | ||
| 83 | + if ([NSDate date].timeIntervalSince1970 - self.camera_cs_acceptEventTime < self.camera_cs_acceptEventInterval) { | ||
| 84 | + return; | ||
| 85 | + } | ||
| 86 | + if (self.camera_cs_acceptEventInterval > 0) { | ||
| 87 | + self.camera_cs_acceptEventTime = [NSDate date].timeIntervalSince1970; | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + [self cs_sendAction:action to:target forEvent:event]; | ||
| 91 | +} | ||
| 92 | + | ||
| 93 | +@end | ||
| 94 | + | ||
| 95 | + | ||
| 96 | +typedef void(^PropertyChangeBlock)(AVCaptureDevice *captureDevice); | ||
| 97 | + | ||
| 98 | +@interface XFCameraController() <AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAudioDataOutputSampleBufferDelegate, UIGestureRecognizerDelegate> | ||
| 99 | +{ | ||
| 100 | + BOOL _isPicOrVideo;//已经拍照或者录制视频标识,进入后台时不dismiss | ||
| 101 | +} | ||
| 102 | +@property (nonatomic ,assign) BOOL isAlertShow; | ||
| 103 | +@property (nonatomic, strong) dispatch_queue_t videoQueue; | ||
| 104 | + | ||
| 105 | +@property (strong, nonatomic) AVCaptureSession *captureSession; //负责输入和输出设备之间的数据传递 | ||
| 106 | + | ||
| 107 | +@property (nonatomic, strong) AVCaptureDeviceInput *videoInput; //视频输入 | ||
| 108 | +@property (nonatomic, strong) AVCaptureDeviceInput *audioInput; //声音输入 | ||
| 109 | +@property (nonatomic, strong) AVCaptureVideoDataOutput *videoOutput; | ||
| 110 | +@property (nonatomic, strong) AVCaptureAudioDataOutput *audioOutput; | ||
| 111 | + | ||
| 112 | +@property (strong, nonatomic) AVCaptureStillImageOutput *captureStillImageOutput; //照片输出流 | ||
| 113 | + | ||
| 114 | +@property (nonatomic, strong) AVAssetWriter *assetWriter; | ||
| 115 | +@property (nonatomic, strong) AVAssetWriterInput *assetWriterVideoInput; | ||
| 116 | +@property (nonatomic, strong) AVAssetWriterInput *assetWriterAudioInput; | ||
| 117 | +@property (nonatomic, strong) NSDictionary *videoCompressionSettings; | ||
| 118 | +@property (nonatomic, strong) NSDictionary *audioCompressionSettings; | ||
| 119 | +@property (nonatomic, assign) BOOL canWrite; | ||
| 120 | + | ||
| 121 | +@property (strong, nonatomic) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer; //预览图层 | ||
| 122 | + | ||
| 123 | +@property (nonatomic, strong) NSTimer *timer; //记录录制时间 | ||
| 124 | + | ||
| 125 | +@property (nonatomic, strong) UIButton *rotateCameraButton; | ||
| 126 | +@property (nonatomic, strong) UIButton *flashButton; | ||
| 127 | +@property (nonatomic, strong) UIButton *takeButton; //拍摄按钮 | ||
| 128 | +@property (nonatomic, strong) UIButton *closeButton; | ||
| 129 | +@property (nonatomic, strong) UILabel *tipLabel; | ||
| 130 | +@property (strong, nonatomic) XFCameraButton *cameraButton; //拍摄按钮 | ||
| 131 | + | ||
| 132 | +@property (nonatomic, strong) UIImageView *focusImageView; //聚焦视图 | ||
| 133 | +@property (assign, nonatomic) Boolean isFocusing; //镜头正在聚焦 | ||
| 134 | +@property (assign, nonatomic) Boolean isTakingPhoto; //正在拍摄照片 lxg | ||
| 135 | +@property (assign, nonatomic) Boolean isShooting; //正在拍摄 | ||
| 136 | +@property (assign, nonatomic) Boolean isRotatingCamera; //正在旋转摄像头 | ||
| 137 | + | ||
| 138 | +//捏合缩放摄像头 | ||
| 139 | +@property (nonatomic,assign) CGFloat beginGestureScale; //记录开始的缩放比例 | ||
| 140 | +@property (nonatomic,assign) CGFloat effectiveScale; //最后的缩放比例 | ||
| 141 | + | ||
| 142 | +// 拍照摄像后的预览模块 | ||
| 143 | +@property (nonatomic, strong) UIButton *cancelButton; | ||
| 144 | +@property (nonatomic, strong) UIButton *confirmButton; | ||
| 145 | +@property (strong, nonatomic) UIView *photoPreviewContainerView; //相片预览ContainerView | ||
| 146 | +@property (strong, nonatomic) UIImageView *photoPreviewImageView; //相片预览ImageView | ||
| 147 | +@property (strong, nonatomic) UIView *videoPreviewContainerView; //视频预览View | ||
| 148 | +@property (strong, nonatomic) NSURL *videoURL; //视频文件地址 | ||
| 149 | +@property (strong, nonatomic) AVPlayerLayer *playerLayer; | ||
| 150 | +@property (strong, nonatomic) AVPlayer *player; | ||
| 151 | +@property (strong, nonatomic) AVPlayerItem *playerItem; | ||
| 152 | +@property (assign, nonatomic) CGFloat currentVideoTimeLength; //当前小视频总时长 | ||
| 153 | + | ||
| 154 | +@property (assign, nonatomic) UIDeviceOrientation shootingOrientation; //拍摄中的手机方向 | ||
| 155 | +@property (strong, nonatomic) CMMotionManager *motionManager; | ||
| 156 | + | ||
| 157 | +@end | ||
| 158 | + | ||
| 159 | +@implementation XFCameraController{ | ||
| 160 | + | ||
| 161 | + CGFloat timeLength; //时间长度 | ||
| 162 | + | ||
| 163 | +} | ||
| 164 | + | ||
| 165 | +#pragma mark - 控制器方法 | ||
| 166 | + | ||
| 167 | +- (void)viewDidLoad | ||
| 168 | +{ | ||
| 169 | + [super viewDidLoad]; | ||
| 170 | + self.view.backgroundColor = [UIColor blackColor]; | ||
| 171 | + | ||
| 172 | + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(enterBackgroundNotification) name:UIApplicationDidEnterBackgroundNotification object:nil]; | ||
| 173 | + | ||
| 174 | + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil]; | ||
| 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 | + | ||
| 188 | + [self.view addSubview:self.rotateCameraButton]; | ||
| 189 | + [self.view addSubview:self.flashButton]; | ||
| 190 | + [self.view addSubview:self.takeButton]; | ||
| 191 | + [self.view addSubview:self.closeButton]; | ||
| 192 | + [self.view addSubview:self.tipLabel]; | ||
| 193 | + [self.view addSubview:self.focusImageView]; | ||
| 194 | + [self.view addSubview:self.cancelButton]; | ||
| 195 | + [self.view addSubview:self.confirmButton]; | ||
| 196 | + | ||
| 197 | + CGFloat rotateOffest = CAMERA_IS_IPhoneX_All ? kCameraNavigationBarHeight : 20 ; | ||
| 198 | + [self.rotateCameraButton mas_makeConstraints:^(MASConstraintMaker *make) { | ||
| 199 | + make.width.mas_equalTo(50); | ||
| 200 | + make.height.mas_equalTo(30); | ||
| 201 | + make.top.mas_equalTo(self.view.mas_top).mas_offset(rotateOffest); | ||
| 202 | + make.right.mas_equalTo(self.view.mas_right).mas_offset(-15); | ||
| 203 | + }]; | ||
| 204 | + | ||
| 205 | + [self.flashButton mas_makeConstraints:^(MASConstraintMaker *make) { | ||
| 206 | + make.width.mas_equalTo(50); | ||
| 207 | + make.height.mas_equalTo(30); | ||
| 208 | + make.top.mas_equalTo(self.view.mas_top).mas_offset(rotateOffest); | ||
| 209 | + make.right.mas_equalTo(self.rotateCameraButton.mas_left); | ||
| 210 | + }]; | ||
| 211 | + | ||
| 212 | + //拍摄按钮 | ||
| 213 | + CGFloat bottom = CAMERA_IS_IPhoneX_All ? -55-kCameraVerticalBottomSafeHeight : -20; | ||
| 214 | + [self.takeButton mas_makeConstraints:^(MASConstraintMaker *make) { | ||
| 215 | + make.width.mas_equalTo(120); | ||
| 216 | + make.height.mas_equalTo(120); | ||
| 217 | + make.centerX.mas_equalTo(self.view.mas_centerX); | ||
| 218 | + make.bottom.mas_equalTo(self.view.mas_bottom).mas_offset(bottom); | ||
| 219 | + }]; | ||
| 220 | + | ||
| 221 | + [self.closeButton mas_makeConstraints:^(MASConstraintMaker *make) { | ||
| 222 | + make.width.mas_equalTo(60); | ||
| 223 | + make.height.mas_equalTo(30); | ||
| 224 | + make.centerY.mas_equalTo(self.takeButton.mas_centerY); | ||
| 225 | + make.right.mas_equalTo(self.takeButton.mas_left).mas_offset(-20); | ||
| 226 | + }]; | ||
| 227 | + | ||
| 228 | + [self.tipLabel mas_makeConstraints:^(MASConstraintMaker *make) { | ||
| 229 | + make.centerX.mas_equalTo(self.view.mas_centerX); | ||
| 230 | + make.bottom.mas_equalTo(self.takeButton.mas_top).mas_offset(-20); | ||
| 231 | + }]; | ||
| 232 | + | ||
| 233 | + [self.focusImageView mas_makeConstraints:^(MASConstraintMaker *make) { | ||
| 234 | + make.width.mas_equalTo(60); | ||
| 235 | + make.height.mas_equalTo(60); | ||
| 236 | + make.centerX.mas_equalTo(self.view.mas_centerX); | ||
| 237 | + make.centerY.mas_equalTo(self.view.mas_centerY); | ||
| 238 | + }]; | ||
| 239 | + | ||
| 240 | + [self.cancelButton mas_makeConstraints:^(MASConstraintMaker *make) { | ||
| 241 | + make.width.mas_equalTo(60); | ||
| 242 | + make.height.mas_equalTo(60); | ||
| 243 | + make.left.mas_equalTo(self.view.mas_left).mas_offset(40); | ||
| 244 | + make.bottom.mas_equalTo(self.view.mas_bottom).mas_offset(-60-kCameraVerticalBottomSafeHeight); | ||
| 245 | + }]; | ||
| 246 | + | ||
| 247 | + [self.confirmButton mas_makeConstraints:^(MASConstraintMaker *make) { | ||
| 248 | + make.width.mas_equalTo(60); | ||
| 249 | + make.height.mas_equalTo(60); | ||
| 250 | + make.right.mas_equalTo(self.view.mas_right).mas_offset(-40); | ||
| 251 | + make.bottom.mas_equalTo(self.cancelButton.mas_bottom); | ||
| 252 | + }]; | ||
| 253 | + | ||
| 254 | +} | ||
| 255 | + | ||
| 256 | +- (void)viewWillAppear:(BOOL)animated | ||
| 257 | +{ | ||
| 258 | + [super viewWillAppear:animated]; | ||
| 259 | + | ||
| 260 | + AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; | ||
| 261 | + if (authStatus == AVAuthorizationStatusRestricted || authStatus == AVAuthorizationStatusDenied) | ||
| 262 | + { | ||
| 263 | + [self requestAuthorizationForVideo]; | ||
| 264 | + } | ||
| 265 | + | ||
| 266 | + //判断用户是否允许访问麦克风权限 | ||
| 267 | + authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]; | ||
| 268 | + if (authStatus == AVAuthorizationStatusRestricted || authStatus == AVAuthorizationStatusDenied) | ||
| 269 | + { | ||
| 270 | + [self requestAuthorizationForVideo]; | ||
| 271 | + } | ||
| 272 | + [self requestAuthorizationForPhotoLibrary]; | ||
| 273 | + | ||
| 274 | + [self initAVCaptureSession]; | ||
| 275 | + | ||
| 276 | + [self configDefaultUIDisplay]; | ||
| 277 | + | ||
| 278 | + [self addTapGenstureRecognizerForCamera]; | ||
| 279 | + | ||
| 280 | +} | ||
| 281 | + | ||
| 282 | +- (void)viewDidAppear:(BOOL)animated | ||
| 283 | +{ | ||
| 284 | + [super viewDidAppear:animated]; | ||
| 285 | + | ||
| 286 | + [self startSession]; | ||
| 287 | + | ||
| 288 | + [self setFocusCursorWithPoint:self.view.center]; | ||
| 289 | + | ||
| 290 | + if (self.isCanShootVideo) | ||
| 291 | + { | ||
| 292 | + self.tipLabel.text = @"点击拍照,长按摄像"; | ||
| 293 | + }else | ||
| 294 | + { | ||
| 295 | + self.tipLabel.text = @"点击拍照,不可摄像"; | ||
| 296 | + } | ||
| 297 | + | ||
| 298 | + [self tipLabelAnimation]; | ||
| 299 | + | ||
| 300 | +} | ||
| 301 | + | ||
| 302 | +- (void)viewWillDisappear:(BOOL)animated | ||
| 303 | +{ | ||
| 304 | + [super viewWillDisappear:animated]; | ||
| 305 | + | ||
| 306 | + [self setCaptureVideoPreviewLayerTransformWithScale:1.0f]; | ||
| 307 | + | ||
| 308 | + // 显示状态栏 | ||
| 309 | + // [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone]; | ||
| 310 | + [UIApplication sharedApplication].statusBarHidden = YES; | ||
| 311 | +} | ||
| 312 | + | ||
| 313 | +- (BOOL)prefersStatusBarHidden | ||
| 314 | +{ | ||
| 315 | + return YES; | ||
| 316 | +} | ||
| 317 | + | ||
| 318 | +- (void)viewDidDisappear:(BOOL)animated | ||
| 319 | +{ | ||
| 320 | + [super viewDidDisappear:animated]; | ||
| 321 | + | ||
| 322 | + [self stopSession]; | ||
| 323 | + | ||
| 324 | + [self stopUpdateAccelerometer]; | ||
| 325 | +} | ||
| 326 | + | ||
| 327 | +#pragma mark - 释放 | ||
| 328 | +- (void)dealloc | ||
| 329 | +{ | ||
| 330 | + NSLog(@"XFCameraController 释放了"); | ||
| 331 | + [[NSNotificationCenter defaultCenter] removeObserver:self]; | ||
| 332 | +} | ||
| 333 | + | ||
| 334 | +- (void)didReceiveMemoryWarning | ||
| 335 | +{ | ||
| 336 | + [super didReceiveMemoryWarning]; | ||
| 337 | +} | ||
| 338 | + | ||
| 339 | +#pragma mark - 控件方法 | ||
| 340 | + | ||
| 341 | +/** | ||
| 342 | + * 关闭当前界面 | ||
| 343 | + */ | ||
| 344 | +- (void)closeBtnFunc | ||
| 345 | +{ | ||
| 346 | + [self dismissViewControllerAnimated:YES completion:nil]; | ||
| 347 | +} | ||
| 348 | + | ||
| 349 | +/** | ||
| 350 | + * 切换前后摄像头 | ||
| 351 | + */ | ||
| 352 | +- (void)rotateCameraBtnFunc:(UIButton *)btn | ||
| 353 | +{ | ||
| 354 | + btn.selected = !btn.selected; | ||
| 355 | + if (btn.selected) { | ||
| 356 | + self.flashButton.hidden = YES; | ||
| 357 | + } else { | ||
| 358 | + self.flashButton.hidden = NO; | ||
| 359 | + } | ||
| 360 | + _isRotatingCamera = YES; | ||
| 361 | + | ||
| 362 | + AVCaptureDevice *currentDevice = [self.videoInput device]; | ||
| 363 | + AVCaptureDevicePosition currentPosition = [currentDevice position]; | ||
| 364 | + | ||
| 365 | + AVCaptureDevice *toChangeDevice; | ||
| 366 | + AVCaptureDevicePosition toChangePosition = AVCaptureDevicePositionFront; | ||
| 367 | + if (currentPosition == AVCaptureDevicePositionUnspecified || currentPosition == AVCaptureDevicePositionFront) | ||
| 368 | + { | ||
| 369 | + toChangePosition = AVCaptureDevicePositionBack; | ||
| 370 | + } | ||
| 371 | + toChangeDevice = [self getCameraDeviceWithPosition:toChangePosition]; | ||
| 372 | + | ||
| 373 | + //获得要调整的设备输入对象 | ||
| 374 | + AVCaptureDeviceInput *toChangeDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:toChangeDevice error:nil]; | ||
| 375 | + | ||
| 376 | + //改变会话的配置前一定要先开启配置,配置完成后提交配置改变 | ||
| 377 | + [self.captureSession beginConfiguration]; | ||
| 378 | + //移除原有输入对象 | ||
| 379 | + [self.captureSession removeInput:self.videoInput]; | ||
| 380 | + //添加新的输入对象 | ||
| 381 | + if ([self.captureSession canAddInput:toChangeDeviceInput]) | ||
| 382 | + { | ||
| 383 | + [self.captureSession addInput:toChangeDeviceInput]; | ||
| 384 | + self.videoInput = toChangeDeviceInput; | ||
| 385 | + } | ||
| 386 | + | ||
| 387 | + //提交会话配置 | ||
| 388 | + [self.captureSession commitConfiguration]; | ||
| 389 | + | ||
| 390 | + _isRotatingCamera = NO; | ||
| 391 | +} | ||
| 392 | + | ||
| 393 | +- (void)flashBtnFunc:(UIButton *)btn | ||
| 394 | +{ | ||
| 395 | + btn.selected = !btn.selected; | ||
| 396 | +#pragma mark - TODO: 更改是否打开手电筒 | ||
| 397 | + if (btn.selected) | ||
| 398 | + { | ||
| 399 | +// [SGQRCodeHelperTool SG_openFlashlight]; | ||
| 400 | + }else | ||
| 401 | + { | ||
| 402 | +// [SGQRCodeHelperTool SG_CloseFlashlight]; | ||
| 403 | + } | ||
| 404 | +} | ||
| 405 | + | ||
| 406 | +- (void)cancelBtnfunc | ||
| 407 | +{ | ||
| 408 | + _isTakingPhoto = NO; | ||
| 409 | + [self removePlayerItemNotification]; | ||
| 410 | + | ||
| 411 | + [self startAnimationGroup]; | ||
| 412 | +} | ||
| 413 | + | ||
| 414 | +/** | ||
| 415 | + * 确认按钮并返回代理 | ||
| 416 | + */ | ||
| 417 | +- (void)confirmBtnFunc | ||
| 418 | +{ | ||
| 419 | + | ||
| 420 | + if (self.playerLayer) { | ||
| 421 | + self.playerLayer.hidden = YES; | ||
| 422 | + } | ||
| 423 | + | ||
| 424 | + if (self.captureVideoPreviewLayer) { | ||
| 425 | + self.captureVideoPreviewLayer.hidden = NO; | ||
| 426 | + } | ||
| 427 | + | ||
| 428 | + __weak typeof(self) weakSelf = self; | ||
| 429 | + if (self.photoPreviewImageView) | ||
| 430 | + { | ||
| 431 | + UIImage *finalImage = [self cutImageWithView:self.photoPreviewImageView]; | ||
| 432 | + | ||
| 433 | + // [XFPhotoLibraryManager savePhotoWithImage:finalImage andAssetCollectionName:self.assetCollectionName withCompletion:^(UIImage *image, NSError *error) { | ||
| 434 | + // | ||
| 435 | + if (self.takePhotosCompletionBlock) | ||
| 436 | + { | ||
| 437 | + // if (error) | ||
| 438 | + // { | ||
| 439 | + // NSLog(@"保存照片失败!"); | ||
| 440 | + // weakSelf.takePhotosCompletionBlock(nil, error); | ||
| 441 | + // } | ||
| 442 | + // else | ||
| 443 | + // { | ||
| 444 | + // NSLog(@"保存照片成功!"); | ||
| 445 | + weakSelf.takePhotosCompletionBlock(finalImage, nil); | ||
| 446 | + // } | ||
| 447 | + } | ||
| 448 | + | ||
| 449 | + // }]; | ||
| 450 | + | ||
| 451 | + self.confirmButton.userInteractionEnabled = NO; | ||
| 452 | + | ||
| 453 | + } | ||
| 454 | + else | ||
| 455 | + { | ||
| 456 | + [self.player.currentItem cancelPendingSeeks]; | ||
| 457 | + [self.player.currentItem.asset cancelLoading]; | ||
| 458 | + [self.player pause]; | ||
| 459 | + | ||
| 460 | + _playerItem = nil; | ||
| 461 | + [self.player replaceCurrentItemWithPlayerItem:nil]; | ||
| 462 | + self.player = nil; | ||
| 463 | + | ||
| 464 | + [_playerLayer removeFromSuperlayer]; | ||
| 465 | + _playerLayer = nil; | ||
| 466 | + | ||
| 467 | + [weakSelf cropWithVideoUrlStr:weakSelf.videoURL start:0 end:weakSelf.currentVideoTimeLength completion:^(NSURL *outputURL, Float64 videoDuration, BOOL isSuccess) { | ||
| 468 | + | ||
| 469 | + if (isSuccess) | ||
| 470 | + { | ||
| 471 | + | ||
| 472 | + [XFPhotoLibraryManager zx_saveVideoWithVideoUrl:outputURL andAssetCollectionName:nil withCompletion:^(PHAsset *asset, NSURL *videoUrl, NSError *error) { | ||
| 473 | + if (weakSelf.zxshootCompletionBlock) | ||
| 474 | + { | ||
| 475 | + if (error) | ||
| 476 | + { | ||
| 477 | + NSLog(@"保存视频失败!"); | ||
| 478 | + weakSelf.zxshootCompletionBlock(asset,nil, 0, nil, error); | ||
| 479 | + } | ||
| 480 | + else | ||
| 481 | + { | ||
| 482 | + NSLog(@"保存视频成功!"); | ||
| 483 | + | ||
| 484 | + // 获取视频的第一帧图片 | ||
| 485 | + UIImage *image = [weakSelf thumbnailImageRequestWithVideoUrl:videoUrl andTime:0.01f]; | ||
| 486 | + | ||
| 487 | + | ||
| 488 | + weakSelf.zxshootCompletionBlock(asset,videoUrl, videoDuration, image, nil); | ||
| 489 | + | ||
| 490 | + [[NSFileManager defaultManager] removeItemAtURL:weakSelf.videoURL error:nil]; | ||
| 491 | + weakSelf.videoURL = nil; | ||
| 492 | + } | ||
| 493 | + } | ||
| 494 | + | ||
| 495 | + weakSelf.confirmButton.userInteractionEnabled = NO; | ||
| 496 | + }]; | ||
| 497 | + | ||
| 498 | + // [XFPhotoLibraryManager saveVideoWithVideoUrl:outputURL andAssetCollectionName:nil withCompletion:^(NSURL *videoUrl, NSError *error) { | ||
| 499 | + // | ||
| 500 | + // if (self.shootCompletionBlock) | ||
| 501 | + // { | ||
| 502 | + // if (error) | ||
| 503 | + // { | ||
| 504 | + // NSLog(@"保存视频失败!"); | ||
| 505 | + // weakSelf.shootCompletionBlock(nil, 0, nil, error); | ||
| 506 | + // } | ||
| 507 | + // else | ||
| 508 | + // { | ||
| 509 | + // NSLog(@"保存视频成功!"); | ||
| 510 | + // | ||
| 511 | + // // 获取视频的第一帧图片 | ||
| 512 | + // UIImage *image = [weakSelf thumbnailImageRequestWithVideoUrl:videoUrl andTime:0.01f]; | ||
| 513 | + // | ||
| 514 | + // weakSelf.shootCompletionBlock(videoUrl, videoDuration, image, nil); | ||
| 515 | + // | ||
| 516 | + // [[NSFileManager defaultManager] removeItemAtURL:weakSelf.videoURL error:nil]; | ||
| 517 | + // weakSelf.videoURL = nil; | ||
| 518 | + // } | ||
| 519 | + // } | ||
| 520 | + // | ||
| 521 | + // weakSelf.confirmButton.userInteractionEnabled = NO; | ||
| 522 | + // | ||
| 523 | + // }]; | ||
| 524 | + } | ||
| 525 | + else | ||
| 526 | + { | ||
| 527 | + NSLog(@"保存视频失败!"); | ||
| 528 | + [[NSFileManager defaultManager] removeItemAtURL:weakSelf.videoURL error:nil]; | ||
| 529 | + weakSelf.videoURL = nil; | ||
| 530 | + [[NSFileManager defaultManager] removeItemAtURL:outputURL error:nil]; | ||
| 531 | + } | ||
| 532 | + }]; | ||
| 533 | + } | ||
| 534 | +} | ||
| 535 | + | ||
| 536 | +#pragma mark - 懒加载 | ||
| 537 | +- (AVCaptureSession *)captureSession | ||
| 538 | +{ | ||
| 539 | + if (_captureSession == nil) | ||
| 540 | + { | ||
| 541 | + _captureSession = [[AVCaptureSession alloc] init]; | ||
| 542 | + | ||
| 543 | + if ([_captureSession canSetSessionPreset:AVCaptureSessionPresetHigh]) | ||
| 544 | + { | ||
| 545 | + _captureSession.sessionPreset = AVCaptureSessionPresetHigh; | ||
| 546 | + } | ||
| 547 | + } | ||
| 548 | + | ||
| 549 | + return _captureSession; | ||
| 550 | +} | ||
| 551 | + | ||
| 552 | +- (dispatch_queue_t)videoQueue | ||
| 553 | +{ | ||
| 554 | + if (!_videoQueue) | ||
| 555 | + { | ||
| 556 | + _videoQueue = dispatch_get_main_queue(); | ||
| 557 | + } | ||
| 558 | + | ||
| 559 | + return _videoQueue; | ||
| 560 | +} | ||
| 561 | + | ||
| 562 | +- (CMMotionManager *)motionManager | ||
| 563 | +{ | ||
| 564 | + if (!_motionManager) | ||
| 565 | + { | ||
| 566 | + _motionManager = [[CMMotionManager alloc] init]; | ||
| 567 | + } | ||
| 568 | + return _motionManager; | ||
| 569 | +} | ||
| 570 | + | ||
| 571 | +- (UIButton *)rotateCameraButton | ||
| 572 | +{ | ||
| 573 | + if (!_rotateCameraButton) | ||
| 574 | + { | ||
| 575 | + _rotateCameraButton = [[UIButton alloc] init]; | ||
| 576 | + [_rotateCameraButton setImage:[UIImage imageFromBundle:@"fb_xiangji"] forState:UIControlStateNormal]; | ||
| 577 | + [_rotateCameraButton addTarget:self action:@selector(rotateCameraBtnFunc:) forControlEvents:UIControlEventTouchUpInside]; | ||
| 578 | + } | ||
| 579 | + return _rotateCameraButton; | ||
| 580 | +} | ||
| 581 | + | ||
| 582 | +- (UIButton *)flashButton | ||
| 583 | +{ | ||
| 584 | + if (!_flashButton) | ||
| 585 | + { | ||
| 586 | + _flashButton = [[UIButton alloc] init]; | ||
| 587 | + [_flashButton setImage:[UIImage imageFromBundle:@"gz_shoudian"] forState:UIControlStateNormal]; | ||
| 588 | + [_flashButton setImage:[UIImage imageFromBundle:@"gz_shoudian2"] forState:UIControlStateSelected]; | ||
| 589 | + [_flashButton addTarget:self action:@selector(flashBtnFunc:) forControlEvents:UIControlEventTouchUpInside]; | ||
| 590 | + } | ||
| 591 | + return _flashButton; | ||
| 592 | +} | ||
| 593 | + | ||
| 594 | +//拍摄按钮 | ||
| 595 | +- (UIButton *)takeButton | ||
| 596 | +{ | ||
| 597 | + if (!_takeButton) | ||
| 598 | + { | ||
| 599 | + _takeButton = [[UIButton alloc] init]; | ||
| 600 | + } | ||
| 601 | + | ||
| 602 | + return _takeButton; | ||
| 603 | +} | ||
| 604 | + | ||
| 605 | +- (UIButton *)closeButton | ||
| 606 | +{ | ||
| 607 | + if (!_closeButton) | ||
| 608 | + { | ||
| 609 | + _closeButton = [[UIButton alloc] init]; | ||
| 610 | + [_closeButton setImage:[UIImage imageFromBundle:@"fb_quxiaoxia"] forState:UIControlStateNormal]; | ||
| 611 | + [_closeButton addTarget:self action:@selector(closeBtnFunc) forControlEvents:UIControlEventTouchUpInside]; | ||
| 612 | + } | ||
| 613 | + return _closeButton; | ||
| 614 | +} | ||
| 615 | + | ||
| 616 | +- (UILabel *)tipLabel | ||
| 617 | +{ | ||
| 618 | + if (!_tipLabel) | ||
| 619 | + { | ||
| 620 | + _tipLabel = [[UILabel alloc] init]; | ||
| 621 | + _tipLabel.font = [UIFont systemFontOfSize:14]; | ||
| 622 | + _tipLabel.textColor = [UIColor whiteColor]; | ||
| 623 | + } | ||
| 624 | + return _tipLabel; | ||
| 625 | +} | ||
| 626 | + | ||
| 627 | +- (UIImageView *)focusImageView | ||
| 628 | +{ | ||
| 629 | + if (!_focusImageView) | ||
| 630 | + { | ||
| 631 | + _focusImageView = [[UIImageView alloc] init]; | ||
| 632 | + _focusImageView.image = [UIImage imageFromBundle:@"sight_video_focus"]; | ||
| 633 | + } | ||
| 634 | + return _focusImageView; | ||
| 635 | +} | ||
| 636 | + | ||
| 637 | +// 拍照摄像后的预览模块 | ||
| 638 | +- (UIButton *)cancelButton | ||
| 639 | +{ | ||
| 640 | + if (!_cancelButton) | ||
| 641 | + { | ||
| 642 | + _cancelButton = [[UIButton alloc] init]; | ||
| 643 | + [_cancelButton setImage:[UIImage imageFromBundle:@"fb_chongpai"] forState:UIControlStateNormal]; | ||
| 644 | + [_cancelButton addTarget:self action:@selector(cancelBtnfunc) forControlEvents:UIControlEventTouchUpInside]; | ||
| 645 | + } | ||
| 646 | + return _cancelButton; | ||
| 647 | +} | ||
| 648 | + | ||
| 649 | +// 拍照摄像后的预览模块 | ||
| 650 | +- (UIButton *)confirmButton | ||
| 651 | +{ | ||
| 652 | + if (!_confirmButton) | ||
| 653 | + { | ||
| 654 | + _confirmButton = [[UIButton alloc] init]; | ||
| 655 | + [_confirmButton setImage:[UIImage imageFromBundle:@"fb_wancheng"] forState:UIControlStateNormal]; | ||
| 656 | + [_confirmButton addTarget:self action:@selector(confirmBtnFunc) forControlEvents:UIControlEventTouchUpInside]; | ||
| 657 | + _confirmButton.camera_cs_acceptEventInterval = 2.0f; | ||
| 658 | + | ||
| 659 | + } | ||
| 660 | + return _confirmButton; | ||
| 661 | +} | ||
| 662 | + | ||
| 663 | +#pragma mark - 私有方法 | ||
| 664 | + | ||
| 665 | +/** | ||
| 666 | + * 初始化AVCapture会话 | ||
| 667 | + */ | ||
| 668 | +- (void)initAVCaptureSession | ||
| 669 | +{ | ||
| 670 | + //1、添加 "视频" 与 "音频" 输入流到session | ||
| 671 | + [self setupVideo]; | ||
| 672 | + | ||
| 673 | + [self setupAudio]; | ||
| 674 | + | ||
| 675 | + //2、添加图片,movie输出流到session | ||
| 676 | + [self setupCaptureStillImageOutput]; | ||
| 677 | + | ||
| 678 | + //3、创建视频预览层,用于实时展示摄像头状态 | ||
| 679 | + [self setupCaptureVideoPreviewLayer]; | ||
| 680 | + | ||
| 681 | + //设置静音状态也可播放声音 | ||
| 682 | + AVAudioSession *audioSession = [AVAudioSession sharedInstance]; | ||
| 683 | + [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil]; | ||
| 684 | +} | ||
| 685 | + | ||
| 686 | +/** | ||
| 687 | + * 设置视频输入 | ||
| 688 | + */ | ||
| 689 | +- (void)setupVideo | ||
| 690 | +{ | ||
| 691 | + AVCaptureDevice *captureDevice = [self getCameraDeviceWithPosition:AVCaptureDevicePositionBack]; | ||
| 692 | + if (!captureDevice) | ||
| 693 | + { | ||
| 694 | + NSLog(@"取得后置摄像头时出现问题."); | ||
| 695 | + | ||
| 696 | + return; | ||
| 697 | + } | ||
| 698 | + | ||
| 699 | + NSError *error = nil; | ||
| 700 | + self.videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:captureDevice error:&error]; | ||
| 701 | + if (error) | ||
| 702 | + { | ||
| 703 | + NSLog(@"取得设备输入videoInput对象时出错,错误原因:%@", error); | ||
| 704 | + | ||
| 705 | + return; | ||
| 706 | + } | ||
| 707 | + | ||
| 708 | + //3、将设备输出添加到会话中 | ||
| 709 | + if ([self.captureSession canAddInput:self.videoInput]) | ||
| 710 | + { | ||
| 711 | + [self.captureSession addInput:self.videoInput]; | ||
| 712 | + } | ||
| 713 | + | ||
| 714 | + self.videoOutput = [[AVCaptureVideoDataOutput alloc] init]; | ||
| 715 | + self.videoOutput.alwaysDiscardsLateVideoFrames = YES; //立即丢弃旧帧,节省内存,默认YES | ||
| 716 | + [self.videoOutput setSampleBufferDelegate:self queue:self.videoQueue]; | ||
| 717 | + if ([self.captureSession canAddOutput:self.videoOutput]) | ||
| 718 | + { | ||
| 719 | + [self.captureSession addOutput:self.videoOutput]; | ||
| 720 | + } | ||
| 721 | +} | ||
| 722 | + | ||
| 723 | +/** | ||
| 724 | + * 设置音频录入 | ||
| 725 | + */ | ||
| 726 | +- (void)setupAudio | ||
| 727 | +{ | ||
| 728 | + NSError *error = nil; | ||
| 729 | + self.audioInput = [[AVCaptureDeviceInput alloc] initWithDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio] error:&error]; | ||
| 730 | + if (error) | ||
| 731 | + { | ||
| 732 | + NSLog(@"取得设备输入audioInput对象时出错,错误原因:%@", error); | ||
| 733 | + | ||
| 734 | + return; | ||
| 735 | + } | ||
| 736 | + if ([self.captureSession canAddInput:self.audioInput]) | ||
| 737 | + { | ||
| 738 | + [self.captureSession addInput:self.audioInput]; | ||
| 739 | + } | ||
| 740 | + | ||
| 741 | + self.audioOutput = [[AVCaptureAudioDataOutput alloc] init]; | ||
| 742 | + [self.audioOutput setSampleBufferDelegate:self queue:self.videoQueue]; | ||
| 743 | + if([self.captureSession canAddOutput:self.audioOutput]) | ||
| 744 | + { | ||
| 745 | + [self.captureSession addOutput:self.audioOutput]; | ||
| 746 | + } | ||
| 747 | +} | ||
| 748 | + | ||
| 749 | +/** | ||
| 750 | + * 设置图片输出 | ||
| 751 | + */ | ||
| 752 | +- (void)setupCaptureStillImageOutput | ||
| 753 | +{ | ||
| 754 | + self.captureStillImageOutput = [[AVCaptureStillImageOutput alloc] init]; | ||
| 755 | + NSDictionary *outputSettings = @{ | ||
| 756 | + AVVideoCodecKey:AVVideoCodecJPEG | ||
| 757 | + }; | ||
| 758 | + [_captureStillImageOutput setOutputSettings:outputSettings]; | ||
| 759 | + | ||
| 760 | + if ([self.captureSession canAddOutput:_captureStillImageOutput]) | ||
| 761 | + { | ||
| 762 | + [self.captureSession addOutput:_captureStillImageOutput]; | ||
| 763 | + } | ||
| 764 | +} | ||
| 765 | + | ||
| 766 | +/** | ||
| 767 | + * 设置预览layer | ||
| 768 | + */ | ||
| 769 | +- (void)setupCaptureVideoPreviewLayer | ||
| 770 | +{ | ||
| 771 | + _captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession]; | ||
| 772 | + | ||
| 773 | + CALayer *layer = self.view.layer; | ||
| 774 | + | ||
| 775 | + _captureVideoPreviewLayer.frame = CGRectMake(0, 0, kCameraScreenWidth, kCameraScreenHeight); | ||
| 776 | + | ||
| 777 | + CGFloat centerY = self.view.frame.origin.y + self.view.frame.size.height * 0.5; | ||
| 778 | + CGRect frame = _captureVideoPreviewLayer.frame; | ||
| 779 | + frame.origin.y = centerY - frame.size.height * 0.5; | ||
| 780 | + _captureVideoPreviewLayer.frame = frame; | ||
| 781 | + | ||
| 782 | + _captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspect; //填充模式 | ||
| 783 | + | ||
| 784 | + [layer addSublayer:_captureVideoPreviewLayer]; | ||
| 785 | +} | ||
| 786 | + | ||
| 787 | +/** | ||
| 788 | + * 开启会话 | ||
| 789 | + */ | ||
| 790 | +- (void)startSession | ||
| 791 | +{ | ||
| 792 | + if (![self.captureSession isRunning]) | ||
| 793 | + { | ||
| 794 | + [self.captureSession startRunning]; | ||
| 795 | + } | ||
| 796 | +} | ||
| 797 | + | ||
| 798 | +/** | ||
| 799 | + * 停止会话 | ||
| 800 | + */ | ||
| 801 | +- (void)stopSession | ||
| 802 | +{ | ||
| 803 | + if ([self.captureSession isRunning]) | ||
| 804 | + { | ||
| 805 | + [self.captureSession stopRunning]; | ||
| 806 | + } | ||
| 807 | +} | ||
| 808 | + | ||
| 809 | +/** | ||
| 810 | + * 开始拍照录像动画组合 | ||
| 811 | + */ | ||
| 812 | +- (void)startAnimationGroup | ||
| 813 | +{ | ||
| 814 | + [self configDefaultUIDisplay]; | ||
| 815 | + | ||
| 816 | + [self setFocusCursorWithPoint:self.view.center]; | ||
| 817 | + | ||
| 818 | + | ||
| 819 | + | ||
| 820 | + [self tipLabelAnimation]; | ||
| 821 | +} | ||
| 822 | + | ||
| 823 | +/** | ||
| 824 | + * 配置默认UI信息 | ||
| 825 | + */ | ||
| 826 | +- (void)configDefaultUIDisplay | ||
| 827 | +{ | ||
| 828 | + _isPicOrVideo = NO; | ||
| 829 | + if (self.photoPreviewImageView)//图片设置置空 | ||
| 830 | + { | ||
| 831 | + [self.photoPreviewImageView removeFromSuperview]; | ||
| 832 | + [self.photoPreviewContainerView removeFromSuperview]; | ||
| 833 | + self.photoPreviewImageView = nil; | ||
| 834 | + self.photoPreviewContainerView = nil; | ||
| 835 | + } | ||
| 836 | + if (self.playerLayer)//视频设置置空 | ||
| 837 | + { | ||
| 838 | + [self.player pause]; | ||
| 839 | + self.player = nil; | ||
| 840 | + self.playerItem = nil; | ||
| 841 | + self.playerLayer.hidden = YES; | ||
| 842 | + [self.playerLayer removeFromSuperlayer]; | ||
| 843 | + self.playerLayer = nil; | ||
| 844 | + self.cameraButton.progressPercentage = 0.0f; | ||
| 845 | + self.videoPreviewContainerView = nil; | ||
| 846 | + [[NSFileManager defaultManager] removeItemAtURL:self.videoURL error:nil]; | ||
| 847 | + self.videoURL = nil; | ||
| 848 | + } | ||
| 849 | + if (self.captureVideoPreviewLayer) { | ||
| 850 | + self.captureVideoPreviewLayer.hidden = NO; | ||
| 851 | + } | ||
| 852 | + | ||
| 853 | + //更改 | ||
| 854 | + if (_captureVideoPreviewLayer.superlayer == self.view.layer) { | ||
| 855 | + [_captureVideoPreviewLayer removeFromSuperlayer]; | ||
| 856 | + [self.view.layer insertSublayer:_captureVideoPreviewLayer atIndex:(unsigned)self.view.layer.sublayers.count]; | ||
| 857 | + } | ||
| 858 | + | ||
| 859 | + [self.view bringSubviewToFront:self.rotateCameraButton]; | ||
| 860 | + [self.view bringSubviewToFront:self.flashButton]; | ||
| 861 | + [self.view bringSubviewToFront:self.closeButton]; | ||
| 862 | + [self.rotateCameraButton setHidden:NO]; | ||
| 863 | + [self.flashButton setHidden:NO]; | ||
| 864 | + [self.closeButton setHidden:NO]; | ||
| 865 | + | ||
| 866 | + | ||
| 867 | + [self.view bringSubviewToFront:self.tipLabel]; | ||
| 868 | + [self.tipLabel setAlpha:0]; | ||
| 869 | + | ||
| 870 | + [self.cancelButton setHidden:YES]; | ||
| 871 | + [self.confirmButton setHidden:YES]; | ||
| 872 | + | ||
| 873 | + // 设置拍照按钮 | ||
| 874 | + if (_cameraButton == nil) | ||
| 875 | + { | ||
| 876 | + XFCameraButton *cameraButton = [XFCameraButton defaultCameraButton]; | ||
| 877 | + _cameraButton = cameraButton; | ||
| 878 | + | ||
| 879 | + [self.view addSubview:cameraButton]; | ||
| 880 | + CGFloat cameraBtnX = (kCameraScreenWidth - cameraButton.bounds.size.width) / 2; | ||
| 881 | + | ||
| 882 | + CGFloat bottom = CAMERA_IS_IPhoneX_All ? 90 + SafeViewBottomHeight : 20+30; | ||
| 883 | + | ||
| 884 | + CGFloat cameraBtnY = kCameraScreenHeight - cameraButton.bounds.size.height - bottom; //距离底部60 | ||
| 885 | + cameraButton.frame = CGRectMake(cameraBtnX, cameraBtnY, cameraButton.bounds.size.width, cameraButton.bounds.size.height); | ||
| 886 | + [self.view bringSubviewToFront:cameraButton]; | ||
| 887 | + | ||
| 888 | + // 设置拍照按钮点击事件 | ||
| 889 | + __weak typeof(self) weakSelf = self; | ||
| 890 | + // 配置拍照方法 | ||
| 891 | + [cameraButton configureTapCameraButtonEventWithBlock:^(UITapGestureRecognizer *tapGestureRecognizer) { | ||
| 892 | + if (self->_isTakingPhoto) { | ||
| 893 | + return; | ||
| 894 | + } | ||
| 895 | + [weakSelf requestAuthCamera]; | ||
| 896 | + [weakSelf takePhotos:tapGestureRecognizer]; | ||
| 897 | + | ||
| 898 | + }]; | ||
| 899 | + if (self.isCanShootVideo) { | ||
| 900 | + // 配置拍摄方法 | ||
| 901 | + [cameraButton configureLongPressCameraButtonEventWithBlock:^(UILongPressGestureRecognizer *longPressGestureRecognizer) { | ||
| 902 | + [weakSelf requestAuthorizationForVideo]; | ||
| 903 | + [weakSelf longPressCameraButtonFunc:longPressGestureRecognizer]; | ||
| 904 | + }]; | ||
| 905 | + } | ||
| 906 | + } | ||
| 907 | + [self.cameraButton setHidden:NO]; | ||
| 908 | + [self.view bringSubviewToFront:self.cameraButton]; | ||
| 909 | + | ||
| 910 | + [self setCaptureVideoPreviewLayerTransformWithScale:1.0f]; | ||
| 911 | + | ||
| 912 | + // 对焦imageView | ||
| 913 | + [self.view bringSubviewToFront:self.focusImageView]; | ||
| 914 | + [self.focusImageView setAlpha:0]; | ||
| 915 | + | ||
| 916 | + // 监听屏幕方向 | ||
| 917 | + [self startUpdateAccelerometer]; | ||
| 918 | +} | ||
| 919 | + | ||
| 920 | +/** | ||
| 921 | + * 提示语动画 | ||
| 922 | + */ | ||
| 923 | +- (void)tipLabelAnimation | ||
| 924 | +{ | ||
| 925 | + [self.view bringSubviewToFront:self.tipLabel]; | ||
| 926 | + | ||
| 927 | + __weak typeof(self) weakSelf = self; | ||
| 928 | + [UIView animateWithDuration:1.0f delay:0.5f options:UIViewAnimationOptionCurveEaseInOut animations:^{ | ||
| 929 | + | ||
| 930 | + [weakSelf.tipLabel setAlpha:1]; | ||
| 931 | + | ||
| 932 | + } completion:^(BOOL finished) { | ||
| 933 | + | ||
| 934 | + [UIView animateWithDuration:1.0f delay:3.0f options:UIViewAnimationOptionCurveEaseInOut animations:^{ | ||
| 935 | + | ||
| 936 | + [weakSelf.tipLabel setAlpha:0]; | ||
| 937 | + | ||
| 938 | + } completion:nil]; | ||
| 939 | + | ||
| 940 | + }]; | ||
| 941 | +} | ||
| 942 | + | ||
| 943 | +/** | ||
| 944 | + * 取得指定位置的摄像头 | ||
| 945 | + * | ||
| 946 | + * @param position 摄像头位置 | ||
| 947 | + * | ||
| 948 | + * @return 摄像头设备 | ||
| 949 | + */ | ||
| 950 | +- (AVCaptureDevice *)getCameraDeviceWithPosition:(AVCaptureDevicePosition )position | ||
| 951 | +{ | ||
| 952 | + NSArray *cameras = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; | ||
| 953 | + for (AVCaptureDevice *camera in cameras) | ||
| 954 | + { | ||
| 955 | + if ([camera position] == position) | ||
| 956 | + { | ||
| 957 | + return camera; | ||
| 958 | + } | ||
| 959 | + } | ||
| 960 | + return nil; | ||
| 961 | +} | ||
| 962 | + | ||
| 963 | +/** | ||
| 964 | + * 改变设备属性的统一操作方法 | ||
| 965 | + * | ||
| 966 | + * @param propertyChange 属性改变操作 | ||
| 967 | + */ | ||
| 968 | +- (void)changeDeviceProperty:(PropertyChangeBlock)propertyChange | ||
| 969 | +{ | ||
| 970 | + AVCaptureDevice *captureDevice = [self.videoInput device]; | ||
| 971 | + NSError *error; | ||
| 972 | + | ||
| 973 | + //注意改变设备属性前一定要首先调用lockForConfiguration:调用完之后使用unlockForConfiguration方法解锁 | ||
| 974 | + if ([captureDevice lockForConfiguration:&error]) | ||
| 975 | + { | ||
| 976 | + propertyChange(captureDevice); | ||
| 977 | + [captureDevice unlockForConfiguration]; | ||
| 978 | + } | ||
| 979 | + else | ||
| 980 | + { | ||
| 981 | + NSLog(@"设置设备属性过程发生错误,错误信息:%@",error.localizedDescription); | ||
| 982 | + } | ||
| 983 | +} | ||
| 984 | + | ||
| 985 | +#pragma mark - 拍照功能 | ||
| 986 | + | ||
| 987 | +/** | ||
| 988 | + * 拍照方法 | ||
| 989 | + */ | ||
| 990 | +- (void)takePhotos:(UITapGestureRecognizer *)tapGestureRecognizer | ||
| 991 | +{ | ||
| 992 | + | ||
| 993 | + __weak typeof(self) weakSelf = self; | ||
| 994 | + // 请求相机权限 | ||
| 995 | + AVAuthorizationStatus videoAuthStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; | ||
| 996 | + if (videoAuthStatus != AVAuthorizationStatusAuthorized) | ||
| 997 | + { | ||
| 998 | + NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary]; | ||
| 999 | + | ||
| 1000 | + NSString *appName = [infoDictionary objectForKey:@"CFBundleDisplayName"]; | ||
| 1001 | + if (appName == nil) | ||
| 1002 | + { | ||
| 1003 | + appName = @"APP"; | ||
| 1004 | + } | ||
| 1005 | + NSString *message = [NSString stringWithFormat:@"请在iPhone的“设置-隐私”选项中,允许%@访问你的相册,相机和麦克风。", appName]; | ||
| 1006 | + | ||
| 1007 | + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];//无法拍摄 | ||
| 1008 | + | ||
| 1009 | + UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { | ||
| 1010 | + [weakSelf dismissViewControllerAnimated:YES completion:nil]; | ||
| 1011 | + }]; | ||
| 1012 | + | ||
| 1013 | + [alertController addAction:okAction]; | ||
| 1014 | + [self presentViewController:alertController animated:YES completion:nil]; | ||
| 1015 | + | ||
| 1016 | + return; | ||
| 1017 | + } | ||
| 1018 | + | ||
| 1019 | + // 请求麦克风权限 | ||
| 1020 | + AVAuthorizationStatus audioAuthStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]; | ||
| 1021 | + if (audioAuthStatus != AVAuthorizationStatusAuthorized) | ||
| 1022 | + { | ||
| 1023 | + NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary]; | ||
| 1024 | + | ||
| 1025 | + NSString *appName = [infoDictionary objectForKey:@"CFBundleDisplayName"]; | ||
| 1026 | + if (appName == nil) | ||
| 1027 | + { | ||
| 1028 | + appName = @"APP"; | ||
| 1029 | + } | ||
| 1030 | + NSString *message = [NSString stringWithFormat:@"请在iPhone的“设置-隐私“选项中,允许%@访问你的相册,相机和麦克风。", appName];//[NSString stringWithFormat:@"请在iPhone的““设置-隐私””选项中, 允许%@访问你的麦克风?", appName]; | ||
| 1031 | + | ||
| 1032 | + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert]; | ||
| 1033 | + | ||
| 1034 | + UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { | ||
| 1035 | + [weakSelf dismissViewControllerAnimated:YES completion:nil]; | ||
| 1036 | + }]; | ||
| 1037 | + | ||
| 1038 | + [alertController addAction:okAction]; | ||
| 1039 | + | ||
| 1040 | + [self presentViewController:alertController animated:YES completion:nil]; | ||
| 1041 | + | ||
| 1042 | + return; | ||
| 1043 | + } | ||
| 1044 | + | ||
| 1045 | + _isTakingPhoto = YES; | ||
| 1046 | + //根据设备输出获得连接 | ||
| 1047 | + AVCaptureConnection *captureConnection = [self.captureStillImageOutput connectionWithMediaType:AVMediaTypeVideo]; | ||
| 1048 | + | ||
| 1049 | + [captureConnection setVideoScaleAndCropFactor:self.effectiveScale]; | ||
| 1050 | + | ||
| 1051 | + //根据连接取得设备输出的数据 | ||
| 1052 | + [self.captureStillImageOutput captureStillImageAsynchronouslyFromConnection:captureConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) { | ||
| 1053 | + if (imageDataSampleBuffer) | ||
| 1054 | + { | ||
| 1055 | + NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer]; | ||
| 1056 | + UIImage *image = [UIImage imageWithData:imageData]; | ||
| 1057 | + | ||
| 1058 | + [self previewPhotoWithImage:image]; | ||
| 1059 | + | ||
| 1060 | + self->_isTakingPhoto = YES; | ||
| 1061 | + }else{ | ||
| 1062 | + self->_isTakingPhoto = NO; | ||
| 1063 | + } | ||
| 1064 | + | ||
| 1065 | + }]; | ||
| 1066 | +} | ||
| 1067 | + | ||
| 1068 | +/** | ||
| 1069 | + * 预览图片 | ||
| 1070 | + */ | ||
| 1071 | +- (void)previewPhotoWithImage:(UIImage *)image | ||
| 1072 | +{ | ||
| 1073 | + [self stopUpdateAccelerometer]; | ||
| 1074 | + | ||
| 1075 | + [self.cameraButton setHidden:YES]; | ||
| 1076 | + [self.closeButton setHidden:YES]; | ||
| 1077 | + [self.rotateCameraButton setHidden:YES]; | ||
| 1078 | + [self.flashButton setHidden:YES]; | ||
| 1079 | + | ||
| 1080 | + UIImage *finalImage = nil; | ||
| 1081 | + if (self.shootingOrientation == UIDeviceOrientationLandscapeRight) | ||
| 1082 | + { | ||
| 1083 | + finalImage = [self rotateImage:image withOrientation:UIImageOrientationDown]; | ||
| 1084 | + } | ||
| 1085 | + else if (self.shootingOrientation == UIDeviceOrientationLandscapeLeft) | ||
| 1086 | + { | ||
| 1087 | + finalImage = [self rotateImage:image withOrientation:UIImageOrientationUp]; | ||
| 1088 | + } | ||
| 1089 | + else if (self.shootingOrientation == UIDeviceOrientationPortraitUpsideDown) | ||
| 1090 | + { | ||
| 1091 | + finalImage = [self rotateImage:image withOrientation:UIImageOrientationLeft]; | ||
| 1092 | + } | ||
| 1093 | + else | ||
| 1094 | + { | ||
| 1095 | + finalImage = [self rotateImage:image withOrientation:UIImageOrientationRight]; | ||
| 1096 | + } | ||
| 1097 | + | ||
| 1098 | + self.photoPreviewImageView = [[UIImageView alloc] init]; | ||
| 1099 | + float videoRatio = finalImage.size.width / finalImage.size.height; | ||
| 1100 | + if (self.shootingOrientation == UIDeviceOrientationLandscapeRight || self.shootingOrientation == UIDeviceOrientationLandscapeLeft) | ||
| 1101 | + { | ||
| 1102 | + CGFloat height = kCameraScreenWidth * videoRatio; | ||
| 1103 | + CGFloat y = (kCameraScreenHeight - height) / 2; | ||
| 1104 | + [self.photoPreviewImageView setFrame:CGRectMake(0, y, kCameraScreenWidth, height)]; | ||
| 1105 | + } | ||
| 1106 | + else | ||
| 1107 | + { | ||
| 1108 | + if (CAMERA_IS_IPhoneX_All) { | ||
| 1109 | + [self.photoPreviewImageView setFrame:CGRectMake(0, 0, kCameraScreenWidth, 667)]; | ||
| 1110 | + CGFloat centerY = self.view.frame.origin.y + self.view.frame.size.height * 0.5; | ||
| 1111 | + CGRect frame = _captureVideoPreviewLayer.frame; | ||
| 1112 | + frame.origin.y = centerY - frame.size.height * 0.5; | ||
| 1113 | + self.photoPreviewImageView.frame = frame; | ||
| 1114 | + }else | ||
| 1115 | + { | ||
| 1116 | + [self.photoPreviewImageView setFrame:CGRectMake(0, 0, kCameraScreenWidth, kCameraScreenHeight)]; | ||
| 1117 | + } | ||
| 1118 | + } | ||
| 1119 | + self.photoPreviewImageView.image = finalImage; | ||
| 1120 | + | ||
| 1121 | + self.photoPreviewContainerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kCameraScreenWidth, kCameraScreenHeight)]; | ||
| 1122 | + self.photoPreviewContainerView.backgroundColor = [UIColor blackColor]; | ||
| 1123 | + [self.photoPreviewContainerView addSubview:self.photoPreviewImageView]; | ||
| 1124 | + [self.view addSubview:self.photoPreviewContainerView]; | ||
| 1125 | + [self.view bringSubviewToFront:self.photoPreviewImageView]; | ||
| 1126 | + [self.view bringSubviewToFront:self.cancelButton]; | ||
| 1127 | + [self.view bringSubviewToFront:self.confirmButton]; | ||
| 1128 | + [self.cancelButton setHidden:NO]; | ||
| 1129 | + [self.confirmButton setHidden:NO]; | ||
| 1130 | + | ||
| 1131 | + _isPicOrVideo = YES; | ||
| 1132 | +} | ||
| 1133 | + | ||
| 1134 | +- (UIImage *)cutImageWithView:(UIView *)view | ||
| 1135 | +{ | ||
| 1136 | + UIGraphicsBeginImageContextWithOptions(view.frame.size, NO, 0); | ||
| 1137 | + [view.layer renderInContext:UIGraphicsGetCurrentContext()]; | ||
| 1138 | + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); | ||
| 1139 | + UIGraphicsEndImageContext(); | ||
| 1140 | + | ||
| 1141 | + return image; | ||
| 1142 | +} | ||
| 1143 | + | ||
| 1144 | +#pragma mark - 视频录制 | ||
| 1145 | + | ||
| 1146 | +/** | ||
| 1147 | + * 录制视频方法 | ||
| 1148 | + */ | ||
| 1149 | +- (void)longPressCameraButtonFunc:(UILongPressGestureRecognizer *)sender | ||
| 1150 | +{ | ||
| 1151 | + AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; | ||
| 1152 | + if (authStatus == AVAuthorizationStatusRestricted || authStatus ==AVAuthorizationStatusDenied) | ||
| 1153 | + { | ||
| 1154 | + return; | ||
| 1155 | + } | ||
| 1156 | + | ||
| 1157 | + //判断用户是否允许访问麦克风权限 | ||
| 1158 | + authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]; | ||
| 1159 | + if (authStatus == AVAuthorizationStatusRestricted || authStatus ==AVAuthorizationStatusDenied) | ||
| 1160 | + { | ||
| 1161 | + return; | ||
| 1162 | + } | ||
| 1163 | + | ||
| 1164 | + switch (sender.state) { | ||
| 1165 | + case UIGestureRecognizerStateBegan: | ||
| 1166 | + [self startVideoRecorder]; | ||
| 1167 | + break; | ||
| 1168 | + case UIGestureRecognizerStateCancelled: | ||
| 1169 | + [self stopVideoRecorder]; | ||
| 1170 | + break; | ||
| 1171 | + case UIGestureRecognizerStateEnded: | ||
| 1172 | + [self stopVideoRecorder]; | ||
| 1173 | + break; | ||
| 1174 | + case UIGestureRecognizerStateFailed: | ||
| 1175 | + [self stopVideoRecorder]; | ||
| 1176 | + break; | ||
| 1177 | + default: | ||
| 1178 | + break; | ||
| 1179 | + } | ||
| 1180 | + | ||
| 1181 | +} | ||
| 1182 | + | ||
| 1183 | +/** | ||
| 1184 | + * 开始录制视频 | ||
| 1185 | + */ | ||
| 1186 | +- (void)startVideoRecorder | ||
| 1187 | +{ | ||
| 1188 | + _isShooting = YES; | ||
| 1189 | + | ||
| 1190 | + [self stopUpdateAccelerometer]; | ||
| 1191 | + | ||
| 1192 | + [self setCaptureVideoPreviewLayerTransformWithScale:1.0f]; | ||
| 1193 | + | ||
| 1194 | + [self.cameraButton startShootAnimationWithDuration:START_VIDEO_ANIMATION_DURATION]; | ||
| 1195 | + | ||
| 1196 | + __weak typeof(self) weakSelf = self; | ||
| 1197 | + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(START_VIDEO_ANIMATION_DURATION * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ | ||
| 1198 | + | ||
| 1199 | + NSURL *url = [NSURL fileURLWithPath:[weakSelf createVideoFilePath]]; | ||
| 1200 | + self.videoURL = url; | ||
| 1201 | + | ||
| 1202 | + [self setUpWriter]; | ||
| 1203 | + | ||
| 1204 | + [weakSelf timerFired]; | ||
| 1205 | + | ||
| 1206 | + }); | ||
| 1207 | +} | ||
| 1208 | + | ||
| 1209 | +/** | ||
| 1210 | + * 结束录制视频 | ||
| 1211 | + */ | ||
| 1212 | +- (void)stopVideoRecorder | ||
| 1213 | +{ | ||
| 1214 | + if (_isShooting) | ||
| 1215 | + { | ||
| 1216 | + _isShooting = NO; | ||
| 1217 | + self.cameraButton.progressPercentage = 0.0f; | ||
| 1218 | + [self.cameraButton stopShootAnimation]; | ||
| 1219 | + [self timerStop]; | ||
| 1220 | + | ||
| 1221 | + __weak __typeof(self)weakSelf = self; | ||
| 1222 | + if(_assetWriter && _assetWriter.status == AVAssetWriterStatusWriting) | ||
| 1223 | + { | ||
| 1224 | + // dispatch_async(self.videoQueue, ^{ | ||
| 1225 | + [_assetWriter finishWritingWithCompletionHandler:^{ | ||
| 1226 | + weakSelf.canWrite = NO; | ||
| 1227 | + weakSelf.assetWriter = nil; | ||
| 1228 | + weakSelf.assetWriterAudioInput = nil; | ||
| 1229 | + weakSelf.assetWriterVideoInput = nil; | ||
| 1230 | + }]; | ||
| 1231 | + // }); | ||
| 1232 | + } | ||
| 1233 | + | ||
| 1234 | + if (timeLength < VIDEO_RECORDER_MIN_TIME) | ||
| 1235 | + { | ||
| 1236 | + return; | ||
| 1237 | + } | ||
| 1238 | + | ||
| 1239 | + [self.cameraButton setHidden:YES]; | ||
| 1240 | + | ||
| 1241 | + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ | ||
| 1242 | + | ||
| 1243 | + [weakSelf previewVideoAfterShoot]; | ||
| 1244 | + | ||
| 1245 | + }); | ||
| 1246 | + } | ||
| 1247 | + else | ||
| 1248 | + { | ||
| 1249 | + // nothing | ||
| 1250 | + } | ||
| 1251 | +} | ||
| 1252 | + | ||
| 1253 | +/** | ||
| 1254 | + * 设置写入视频属性 | ||
| 1255 | + */ | ||
| 1256 | +- (void)setUpWriter | ||
| 1257 | +{ | ||
| 1258 | + if (self.videoURL == nil) | ||
| 1259 | + { | ||
| 1260 | + return; | ||
| 1261 | + } | ||
| 1262 | + | ||
| 1263 | + self.assetWriter = [AVAssetWriter assetWriterWithURL:self.videoURL fileType:AVFileTypeMPEG4 error:nil]; | ||
| 1264 | + //写入视频大小 | ||
| 1265 | + NSInteger numPixels = kCameraScreenWidth * kCameraScreenHeight; | ||
| 1266 | + //每像素比特 | ||
| 1267 | + CGFloat bitsPerPixel = 16.0; | ||
| 1268 | + NSInteger bitsPerSecond = numPixels * bitsPerPixel; | ||
| 1269 | + | ||
| 1270 | + // 码率和帧率设置 | ||
| 1271 | + NSDictionary *compressionProperties = @{ AVVideoAverageBitRateKey : @(bitsPerSecond), | ||
| 1272 | + AVVideoExpectedSourceFrameRateKey : @(30), | ||
| 1273 | + AVVideoMaxKeyFrameIntervalKey : @(30), | ||
| 1274 | + AVVideoProfileLevelKey : AVVideoProfileLevelH264BaselineAutoLevel }; | ||
| 1275 | + | ||
| 1276 | + //视频属性 | ||
| 1277 | + CGFloat videoWidthKey = kCameraScreenHeight; | ||
| 1278 | + if (CAMERA_IS_IPhoneX_All) { | ||
| 1279 | + videoWidthKey = 667; | ||
| 1280 | + } | ||
| 1281 | + self.videoCompressionSettings = @{ AVVideoCodecKey : AVVideoCodecH264, | ||
| 1282 | + AVVideoScalingModeKey : AVVideoScalingModeResizeAspectFill, | ||
| 1283 | + AVVideoWidthKey : @(videoWidthKey * 2), | ||
| 1284 | + AVVideoHeightKey : @(kCameraScreenWidth * 2), | ||
| 1285 | + AVVideoCompressionPropertiesKey : compressionProperties }; | ||
| 1286 | + _assetWriterVideoInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:self.videoCompressionSettings]; | ||
| 1287 | + //expectsMediaDataInRealTime 必须设为yes,需要从capture session 实时获取数据 | ||
| 1288 | + _assetWriterVideoInput.expectsMediaDataInRealTime = YES; | ||
| 1289 | + | ||
| 1290 | + if (self.shootingOrientation == UIDeviceOrientationLandscapeRight) | ||
| 1291 | + { | ||
| 1292 | + _assetWriterVideoInput.transform = CGAffineTransformMakeRotation(M_PI); | ||
| 1293 | + } | ||
| 1294 | + else if (self.shootingOrientation == UIDeviceOrientationLandscapeLeft) | ||
| 1295 | + { | ||
| 1296 | + _assetWriterVideoInput.transform = CGAffineTransformMakeRotation(0); | ||
| 1297 | + } | ||
| 1298 | + else if (self.shootingOrientation == UIDeviceOrientationPortraitUpsideDown) | ||
| 1299 | + { | ||
| 1300 | + _assetWriterVideoInput.transform = CGAffineTransformMakeRotation(M_PI + (M_PI / 2.0)); | ||
| 1301 | + } | ||
| 1302 | + else | ||
| 1303 | + { | ||
| 1304 | + _assetWriterVideoInput.transform = CGAffineTransformMakeRotation(M_PI / 2.0); | ||
| 1305 | + } | ||
| 1306 | + | ||
| 1307 | + // 音频设置 | ||
| 1308 | + self.audioCompressionSettings = @{ AVEncoderBitRatePerChannelKey : @(28000), | ||
| 1309 | + AVFormatIDKey : @(kAudioFormatMPEG4AAC), | ||
| 1310 | + AVNumberOfChannelsKey : @(1), | ||
| 1311 | + AVSampleRateKey : @(22050) }; | ||
| 1312 | + | ||
| 1313 | + _assetWriterAudioInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings:self.audioCompressionSettings]; | ||
| 1314 | + _assetWriterAudioInput.expectsMediaDataInRealTime = YES; | ||
| 1315 | + | ||
| 1316 | + if ([_assetWriter canAddInput:_assetWriterVideoInput]) | ||
| 1317 | + { | ||
| 1318 | + [_assetWriter addInput:_assetWriterVideoInput]; | ||
| 1319 | + } | ||
| 1320 | + else | ||
| 1321 | + { | ||
| 1322 | + NSLog(@"AssetWriter videoInput append Failed"); | ||
| 1323 | + } | ||
| 1324 | + | ||
| 1325 | + if ([_assetWriter canAddInput:_assetWriterAudioInput]) | ||
| 1326 | + { | ||
| 1327 | + [_assetWriter addInput:_assetWriterAudioInput]; | ||
| 1328 | + } | ||
| 1329 | + else | ||
| 1330 | + { | ||
| 1331 | + NSLog(@"AssetWriter audioInput Append Failed"); | ||
| 1332 | + } | ||
| 1333 | + | ||
| 1334 | + _canWrite = NO; | ||
| 1335 | +} | ||
| 1336 | + | ||
| 1337 | +- (NSString *)createVideoFilePath | ||
| 1338 | +{ | ||
| 1339 | + // 创建视频文件的存储路径 | ||
| 1340 | + NSString *filePath = [self createVideoFolderPath]; | ||
| 1341 | + if (filePath == nil) | ||
| 1342 | + { | ||
| 1343 | + return nil; | ||
| 1344 | + } | ||
| 1345 | + | ||
| 1346 | + NSString *videoType = @".mp4"; | ||
| 1347 | + NSString *videoDestDateString = [self createFileNamePrefix]; | ||
| 1348 | + NSString *videoFileName = [videoDestDateString stringByAppendingString:videoType]; | ||
| 1349 | + | ||
| 1350 | + NSUInteger idx = 1; | ||
| 1351 | + /*We only allow 10000 same file name*/ | ||
| 1352 | + NSString *finalPath = [NSString stringWithFormat:@"%@/fileType=videoType_%@", filePath, videoFileName]; | ||
| 1353 | + | ||
| 1354 | + while (idx % 10000 && [[NSFileManager defaultManager] fileExistsAtPath:finalPath]) | ||
| 1355 | + { | ||
| 1356 | + finalPath = [NSString stringWithFormat:@"%@/%@_(%lu)%@", filePath, videoDestDateString, (unsigned long)idx++, videoType]; | ||
| 1357 | + } | ||
| 1358 | + | ||
| 1359 | + return finalPath; | ||
| 1360 | +} | ||
| 1361 | + | ||
| 1362 | +- (NSString *)createVideoFolderPath | ||
| 1363 | +{ | ||
| 1364 | +#pragma mark - TODO: 路径参数配置 写个新属性 | ||
| 1365 | +// if (self.conversationId) { | ||
| 1366 | +// return [CNTools newChatFilePathByUserId:self.conversationId]; | ||
| 1367 | +// } | ||
| 1368 | +// return [CNTools chatFilePathByUserId:[IMAPlatform sharedInstance].host.userId]; | ||
| 1369 | +// | ||
| 1370 | + NSFileManager *fileManager = [NSFileManager defaultManager]; | ||
| 1371 | + NSString *homePath = NSHomeDirectory(); | ||
| 1372 | + | ||
| 1373 | + NSString *tmpFilePath; | ||
| 1374 | + | ||
| 1375 | + if (homePath.length > 0) | ||
| 1376 | + { | ||
| 1377 | + NSString *documentPath = [homePath stringByAppendingString:@"/Documents"]; | ||
| 1378 | + if ([fileManager fileExistsAtPath:documentPath isDirectory:NULL] == YES) | ||
| 1379 | + { | ||
| 1380 | + BOOL success = NO; | ||
| 1381 | + | ||
| 1382 | + NSArray *paths = [fileManager contentsOfDirectoryAtPath:documentPath error:nil]; | ||
| 1383 | + | ||
| 1384 | + //offline file folder | ||
| 1385 | + tmpFilePath = [documentPath stringByAppendingString:[NSString stringWithFormat:@"/%@", VIDEO_FILEPATH]]; | ||
| 1386 | + if ([paths containsObject:VIDEO_FILEPATH] == NO) | ||
| 1387 | + { | ||
| 1388 | + success = [fileManager createDirectoryAtPath:tmpFilePath withIntermediateDirectories:YES attributes:nil error:nil]; | ||
| 1389 | + if (!success) | ||
| 1390 | + { | ||
| 1391 | + tmpFilePath = nil; | ||
| 1392 | + } | ||
| 1393 | + } | ||
| 1394 | + return tmpFilePath; | ||
| 1395 | + } | ||
| 1396 | + } | ||
| 1397 | + | ||
| 1398 | + return false; | ||
| 1399 | +} | ||
| 1400 | + | ||
| 1401 | +/** | ||
| 1402 | + * 创建文件名 | ||
| 1403 | + * | ||
| 1404 | + */ | ||
| 1405 | +- (NSString *)createFileNamePrefix | ||
| 1406 | +{ | ||
| 1407 | + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; | ||
| 1408 | + [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss zzz"]; | ||
| 1409 | + | ||
| 1410 | + NSString *destDateString = [dateFormatter stringFromDate:[NSDate date]]; | ||
| 1411 | + destDateString = [destDateString stringByReplacingOccurrencesOfString:@" " withString:@"-"]; | ||
| 1412 | + destDateString = [destDateString stringByReplacingOccurrencesOfString:@"+" withString:@"-"]; | ||
| 1413 | + destDateString = [destDateString stringByReplacingOccurrencesOfString:@":" withString:@"-"]; | ||
| 1414 | + | ||
| 1415 | + return destDateString; | ||
| 1416 | +} | ||
| 1417 | + | ||
| 1418 | +/** | ||
| 1419 | + * 开启定时器 | ||
| 1420 | + */ | ||
| 1421 | +- (void)timerFired | ||
| 1422 | +{ | ||
| 1423 | + timeLength = 0; | ||
| 1424 | + self.timer = [NSTimer scheduledTimerWithTimeInterval:TIMER_INTERVAL target:self selector:@selector(timerRecord) userInfo:nil repeats:YES]; | ||
| 1425 | +} | ||
| 1426 | + | ||
| 1427 | +/** | ||
| 1428 | + * 绿色转圈百分比计算 | ||
| 1429 | + */ | ||
| 1430 | +- (void)timerRecord | ||
| 1431 | +{ | ||
| 1432 | + if (!_isShooting) | ||
| 1433 | + { | ||
| 1434 | + [self timerStop]; | ||
| 1435 | + return ; | ||
| 1436 | + } | ||
| 1437 | + | ||
| 1438 | + // 时间大于VIDEO_RECORDER_MAX_TIME则停止录制 | ||
| 1439 | + if (timeLength > VIDEO_RECORDER_MAX_TIME) | ||
| 1440 | + { | ||
| 1441 | + [self stopVideoRecorder]; | ||
| 1442 | + } | ||
| 1443 | + | ||
| 1444 | + timeLength += TIMER_INTERVAL; | ||
| 1445 | + | ||
| 1446 | + // NSLog(@"%lf", timeLength / VIDEO_RECORDER_MAX_TIME); | ||
| 1447 | + | ||
| 1448 | + self.cameraButton.progressPercentage = timeLength / VIDEO_RECORDER_MAX_TIME; | ||
| 1449 | + | ||
| 1450 | +} | ||
| 1451 | + | ||
| 1452 | +/** | ||
| 1453 | + * 停止定时器 | ||
| 1454 | + */ | ||
| 1455 | +- (void)timerStop | ||
| 1456 | +{ | ||
| 1457 | + if ([self.timer isValid]) | ||
| 1458 | + { | ||
| 1459 | + [self.timer invalidate]; | ||
| 1460 | + self.timer = nil; | ||
| 1461 | + } | ||
| 1462 | +} | ||
| 1463 | + | ||
| 1464 | +/** | ||
| 1465 | + * 预览录制的视频 | ||
| 1466 | + */ | ||
| 1467 | +- (void)previewVideoAfterShoot | ||
| 1468 | +{ | ||
| 1469 | + if (self.videoURL == nil || self.playerLayer != nil) | ||
| 1470 | + { | ||
| 1471 | + return; | ||
| 1472 | + } | ||
| 1473 | + | ||
| 1474 | + _isPicOrVideo = YES; | ||
| 1475 | + | ||
| 1476 | + AVURLAsset *asset = [AVURLAsset assetWithURL:self.videoURL]; | ||
| 1477 | + | ||
| 1478 | + //获取视频总时长 | ||
| 1479 | + Float64 duration = CMTimeGetSeconds(asset.duration); | ||
| 1480 | + | ||
| 1481 | + self.currentVideoTimeLength = duration; | ||
| 1482 | + | ||
| 1483 | +// // 初始化AVPlayer | ||
| 1484 | +// self.videoPreviewContainerView = [[UIView alloc] init]; | ||
| 1485 | +// | ||
| 1486 | +// self.videoPreviewContainerView.frame = CGRectMake(0, 0, kScreenWidth, kScreenHeight); | ||
| 1487 | +// self.videoPreviewContainerView.backgroundColor = [UIColor blackColor]; | ||
| 1488 | + | ||
| 1489 | + self.playerItem = [AVPlayerItem playerItemWithAsset:asset]; | ||
| 1490 | + self.player = [[AVPlayer alloc] initWithPlayerItem:_playerItem]; | ||
| 1491 | + self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; | ||
| 1492 | + self.playerLayer.frame = CGRectMake(0, 0, kCameraScreenWidth, kCameraScreenHeight); | ||
| 1493 | + self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect; | ||
| 1494 | + self.playerLayer.hidden = NO; | ||
| 1495 | + | ||
| 1496 | + [self.view.layer addSublayer:self.playerLayer]; | ||
| 1497 | + | ||
| 1498 | + // 其余UI布局设置 | ||
| 1499 | +// [self.view addSubview:self.videoPreviewContainerView]; | ||
| 1500 | +// [self.view bringSubviewToFront:self.videoPreviewContainerView]; | ||
| 1501 | + | ||
| 1502 | +// [self.view.layer qmui_bringSublayerToFront:self.playerLayer]; | ||
| 1503 | + //更改 | ||
| 1504 | + if (self.playerLayer.superlayer == self.view.layer) { | ||
| 1505 | + [self.playerLayer removeFromSuperlayer]; | ||
| 1506 | + [self.view.layer insertSublayer:self.playerLayer atIndex:(unsigned)self.view.layer.sublayers.count]; | ||
| 1507 | + } | ||
| 1508 | + | ||
| 1509 | + self.captureVideoPreviewLayer.hidden = YES; | ||
| 1510 | + [self.view bringSubviewToFront:self.cancelButton]; | ||
| 1511 | + [self.view bringSubviewToFront:self.confirmButton]; | ||
| 1512 | + [self.cameraButton setHidden:YES]; | ||
| 1513 | + [self.closeButton setHidden:YES]; | ||
| 1514 | + [self.rotateCameraButton setHidden:YES]; | ||
| 1515 | + [self.flashButton setHidden:YES]; | ||
| 1516 | + [self.cancelButton setHidden:NO]; | ||
| 1517 | + [self.confirmButton setHidden:NO]; | ||
| 1518 | + self.captureVideoPreviewLayer.hidden = YES; | ||
| 1519 | + | ||
| 1520 | + // 重复播放预览视频 | ||
| 1521 | + [self addNotificationWithPlayerItem]; | ||
| 1522 | + | ||
| 1523 | + // 开始播放 | ||
| 1524 | + [self.player play]; | ||
| 1525 | +} | ||
| 1526 | + | ||
| 1527 | +/** | ||
| 1528 | + * 截取指定时间的视频缩略图 | ||
| 1529 | + * | ||
| 1530 | + * @param timeBySecond 时间点,单位:s | ||
| 1531 | + */ | ||
| 1532 | +- (UIImage *)thumbnailImageRequestWithVideoUrl:(NSURL *)videoUrl andTime:(CGFloat)timeBySecond | ||
| 1533 | +{ | ||
| 1534 | + if (self.videoURL == nil) | ||
| 1535 | + { | ||
| 1536 | + return nil; | ||
| 1537 | + } | ||
| 1538 | + | ||
| 1539 | + AVURLAsset *urlAsset = [AVURLAsset assetWithURL:videoUrl]; | ||
| 1540 | + | ||
| 1541 | + //根据AVURLAsset创建AVAssetImageGenerator | ||
| 1542 | + AVAssetImageGenerator *imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:urlAsset]; | ||
| 1543 | + /*截图 | ||
| 1544 | + * requestTime:缩略图创建时间 | ||
| 1545 | + * actualTime:缩略图实际生成的时间 | ||
| 1546 | + */ | ||
| 1547 | + NSError *error = nil; | ||
| 1548 | + CMTime requestTime = CMTimeMakeWithSeconds(timeBySecond, 10); //CMTime是表示电影时间信息的结构体,第一个参数表示是视频第几秒,第二个参数表示每秒帧数.(如果要活的某一秒的第几帧可以使用CMTimeMake方法) | ||
| 1549 | + CMTime actualTime; | ||
| 1550 | + CGImageRef cgImage = [imageGenerator copyCGImageAtTime:requestTime actualTime:&actualTime error:&error]; | ||
| 1551 | + if(error) | ||
| 1552 | + { | ||
| 1553 | + NSLog(@"截取视频缩略图时发生错误,错误信息:%@", error.localizedDescription); | ||
| 1554 | + return nil; | ||
| 1555 | + } | ||
| 1556 | + | ||
| 1557 | + CMTimeShow(actualTime); | ||
| 1558 | + UIImage *image = [UIImage imageWithCGImage:cgImage]; | ||
| 1559 | + CGImageRelease(cgImage); | ||
| 1560 | + | ||
| 1561 | + UIImage *finalImage = nil; | ||
| 1562 | + if (self.shootingOrientation == UIDeviceOrientationLandscapeRight) | ||
| 1563 | + { | ||
| 1564 | + finalImage = [self rotateImage:image withOrientation:UIImageOrientationDown]; | ||
| 1565 | + } | ||
| 1566 | + else if (self.shootingOrientation == UIDeviceOrientationLandscapeLeft) | ||
| 1567 | + { | ||
| 1568 | + finalImage = [self rotateImage:image withOrientation:UIImageOrientationUp]; | ||
| 1569 | + } | ||
| 1570 | + else if (self.shootingOrientation == UIDeviceOrientationPortraitUpsideDown) | ||
| 1571 | + { | ||
| 1572 | + finalImage = [self rotateImage:image withOrientation:UIImageOrientationLeft]; | ||
| 1573 | + } | ||
| 1574 | + else | ||
| 1575 | + { | ||
| 1576 | + finalImage = [self rotateImage:image withOrientation:UIImageOrientationRight]; | ||
| 1577 | + } | ||
| 1578 | + | ||
| 1579 | + return finalImage; | ||
| 1580 | +} | ||
| 1581 | + | ||
| 1582 | +/** | ||
| 1583 | + * 图片旋转 | ||
| 1584 | + */ | ||
| 1585 | +- (UIImage *)rotateImage:(UIImage *)image withOrientation:(UIImageOrientation)orientation | ||
| 1586 | +{ | ||
| 1587 | + long double rotate = 0.0; | ||
| 1588 | + CGRect rect; | ||
| 1589 | + float translateX = 0; | ||
| 1590 | + float translateY = 0; | ||
| 1591 | + float scaleX = 1.0; | ||
| 1592 | + float scaleY = 1.0; | ||
| 1593 | + | ||
| 1594 | + switch (orientation) | ||
| 1595 | + { | ||
| 1596 | + case UIImageOrientationLeft: | ||
| 1597 | + rotate = M_PI_2; | ||
| 1598 | + rect = CGRectMake(0, 0, image.size.height, image.size.width); | ||
| 1599 | + translateX = 0; | ||
| 1600 | + translateY = -rect.size.width; | ||
| 1601 | + scaleY = rect.size.width/rect.size.height; | ||
| 1602 | + scaleX = rect.size.height/rect.size.width; | ||
| 1603 | + break; | ||
| 1604 | + case UIImageOrientationRight: | ||
| 1605 | + rotate = 3 * M_PI_2; | ||
| 1606 | + rect = CGRectMake(0, 0, image.size.height, image.size.width); | ||
| 1607 | + translateX = -rect.size.height; | ||
| 1608 | + translateY = 0; | ||
| 1609 | + scaleY = rect.size.width/rect.size.height; | ||
| 1610 | + scaleX = rect.size.height/rect.size.width; | ||
| 1611 | + break; | ||
| 1612 | + case UIImageOrientationDown: | ||
| 1613 | + rotate = M_PI; | ||
| 1614 | + rect = CGRectMake(0, 0, image.size.width, image.size.height); | ||
| 1615 | + translateX = -rect.size.width; | ||
| 1616 | + translateY = -rect.size.height; | ||
| 1617 | + break; | ||
| 1618 | + default: | ||
| 1619 | + rotate = 0.0; | ||
| 1620 | + rect = CGRectMake(0, 0, image.size.width, image.size.height); | ||
| 1621 | + translateX = 0; | ||
| 1622 | + translateY = 0; | ||
| 1623 | + break; | ||
| 1624 | + } | ||
| 1625 | + | ||
| 1626 | + UIGraphicsBeginImageContext(rect.size); | ||
| 1627 | + CGContextRef context = UIGraphicsGetCurrentContext(); | ||
| 1628 | + //做CTM变换 | ||
| 1629 | + CGContextTranslateCTM(context, 0.0, rect.size.height); | ||
| 1630 | + CGContextScaleCTM(context, 1.0, -1.0); | ||
| 1631 | + CGContextRotateCTM(context, rotate); | ||
| 1632 | + CGContextTranslateCTM(context, translateX, translateY); | ||
| 1633 | + | ||
| 1634 | + CGContextScaleCTM(context, scaleX, scaleY); | ||
| 1635 | + //绘制图片 | ||
| 1636 | + CGContextDrawImage(context, CGRectMake(0, 0, rect.size.width, rect.size.height), image.CGImage); | ||
| 1637 | + | ||
| 1638 | + UIImage *newPic = UIGraphicsGetImageFromCurrentImageContext(); | ||
| 1639 | + | ||
| 1640 | + return newPic; | ||
| 1641 | +} | ||
| 1642 | + | ||
| 1643 | +#pragma mark - 截取视频方法 | ||
| 1644 | + | ||
| 1645 | +- (void)cropWithVideoUrlStr:(NSURL *)videoUrl start:(CGFloat)startTime end:(CGFloat)endTime completion:(void (^)(NSURL *outputURL, Float64 videoDuration, BOOL isSuccess))completionHandle | ||
| 1646 | +{ | ||
| 1647 | + AVURLAsset *asset =[[AVURLAsset alloc] initWithURL:videoUrl options:nil]; | ||
| 1648 | + | ||
| 1649 | + //获取视频总时长 | ||
| 1650 | + Float64 duration = CMTimeGetSeconds(asset.duration); | ||
| 1651 | + | ||
| 1652 | + if (duration > VIDEO_RECORDER_MAX_TIME) | ||
| 1653 | + { | ||
| 1654 | + duration = VIDEO_RECORDER_MAX_TIME; | ||
| 1655 | + } | ||
| 1656 | + | ||
| 1657 | + startTime = 0; | ||
| 1658 | + endTime = duration; | ||
| 1659 | + | ||
| 1660 | + NSString *outputFilePath = [self createVideoFilePath]; | ||
| 1661 | + NSURL *outputFileUrl = [NSURL fileURLWithPath:outputFilePath]; | ||
| 1662 | + | ||
| 1663 | + NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:asset]; | ||
| 1664 | + if ([compatiblePresets containsObject:AVAssetExportPresetMediumQuality]) | ||
| 1665 | + { | ||
| 1666 | + | ||
| 1667 | + AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] | ||
| 1668 | + initWithAsset:asset presetName:AVAssetExportPresetPassthrough]; | ||
| 1669 | + | ||
| 1670 | + NSURL *outputURL = outputFileUrl; | ||
| 1671 | + | ||
| 1672 | + exportSession.outputURL = outputURL; | ||
| 1673 | + exportSession.outputFileType = AVFileTypeMPEG4; | ||
| 1674 | + exportSession.shouldOptimizeForNetworkUse = YES; | ||
| 1675 | + | ||
| 1676 | + CMTime start = CMTimeMakeWithSeconds(startTime, asset.duration.timescale); | ||
| 1677 | + CMTime duration = CMTimeMakeWithSeconds(endTime - startTime,asset.duration.timescale); | ||
| 1678 | + CMTimeRange range = CMTimeRangeMake(start, duration); | ||
| 1679 | + exportSession.timeRange = range; | ||
| 1680 | + | ||
| 1681 | + [exportSession exportAsynchronouslyWithCompletionHandler:^{ | ||
| 1682 | + switch ([exportSession status]) { | ||
| 1683 | + case AVAssetExportSessionStatusFailed: | ||
| 1684 | + { | ||
| 1685 | + NSLog(@"合成失败:%@", [[exportSession error] description]); | ||
| 1686 | + completionHandle(outputURL, endTime, NO); | ||
| 1687 | + } | ||
| 1688 | + break; | ||
| 1689 | + case AVAssetExportSessionStatusCancelled: | ||
| 1690 | + { | ||
| 1691 | + completionHandle(outputURL, endTime, NO); | ||
| 1692 | + } | ||
| 1693 | + break; | ||
| 1694 | + case AVAssetExportSessionStatusCompleted: | ||
| 1695 | + { | ||
| 1696 | + completionHandle(outputURL, endTime, YES); | ||
| 1697 | + } | ||
| 1698 | + break; | ||
| 1699 | + default: | ||
| 1700 | + { | ||
| 1701 | + completionHandle(outputURL, endTime, NO); | ||
| 1702 | + } break; | ||
| 1703 | + } | ||
| 1704 | + }]; | ||
| 1705 | + } | ||
| 1706 | +} | ||
| 1707 | + | ||
| 1708 | +#pragma mark - 预览视频通知 | ||
| 1709 | +/** | ||
| 1710 | + * 添加播放器通知 | ||
| 1711 | + */ | ||
| 1712 | +-(void)addNotificationWithPlayerItem | ||
| 1713 | +{ | ||
| 1714 | + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playVideoFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem]; | ||
| 1715 | +} | ||
| 1716 | + | ||
| 1717 | +-(void)removePlayerItemNotification | ||
| 1718 | +{ | ||
| 1719 | + [[NSNotificationCenter defaultCenter] removeObserver:self]; | ||
| 1720 | +} | ||
| 1721 | + | ||
| 1722 | +/** | ||
| 1723 | + * 播放完成通知 | ||
| 1724 | + * | ||
| 1725 | + * @param notification 通知对象 | ||
| 1726 | + */ | ||
| 1727 | +-(void)playVideoFinished:(NSNotification *)notification | ||
| 1728 | +{ | ||
| 1729 | + // NSLog(@"视频播放完成."); | ||
| 1730 | + | ||
| 1731 | + // 播放完成后重复播放 | ||
| 1732 | + // 跳到最新的时间点开始播放 | ||
| 1733 | + [self.player seekToTime:CMTimeMake(0, 1)]; | ||
| 1734 | + [self.player play]; | ||
| 1735 | +} | ||
| 1736 | + | ||
| 1737 | +#pragma mark - AVCaptureVideoDataOutputSampleBufferDelegate AVCaptureAudioDataOutputSampleBufferDelegate | ||
| 1738 | + | ||
| 1739 | +- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection | ||
| 1740 | +{ | ||
| 1741 | + if (_isRotatingCamera) | ||
| 1742 | + { | ||
| 1743 | + return; | ||
| 1744 | + } | ||
| 1745 | + | ||
| 1746 | + @autoreleasepool | ||
| 1747 | + { | ||
| 1748 | + //视频 | ||
| 1749 | + if (connection == [self.videoOutput connectionWithMediaType:AVMediaTypeVideo]) | ||
| 1750 | + { | ||
| 1751 | + @synchronized(self) | ||
| 1752 | + { | ||
| 1753 | + if (_isShooting) | ||
| 1754 | + { | ||
| 1755 | + [self appendSampleBuffer:sampleBuffer ofMediaType:AVMediaTypeVideo]; | ||
| 1756 | + } | ||
| 1757 | + } | ||
| 1758 | + } | ||
| 1759 | + | ||
| 1760 | + //音频 | ||
| 1761 | + if (connection == [self.audioOutput connectionWithMediaType:AVMediaTypeAudio]) | ||
| 1762 | + { | ||
| 1763 | + @synchronized(self) | ||
| 1764 | + { | ||
| 1765 | + if (_isShooting) | ||
| 1766 | + { | ||
| 1767 | + [self appendSampleBuffer:sampleBuffer ofMediaType:AVMediaTypeAudio]; | ||
| 1768 | + } | ||
| 1769 | + } | ||
| 1770 | + } | ||
| 1771 | + } | ||
| 1772 | +} | ||
| 1773 | + | ||
| 1774 | + | ||
| 1775 | +/** | ||
| 1776 | + * 开始写入数据 | ||
| 1777 | + */ | ||
| 1778 | +- (void)appendSampleBuffer:(CMSampleBufferRef)sampleBuffer ofMediaType:(NSString *)mediaType | ||
| 1779 | +{ | ||
| 1780 | + if (sampleBuffer == NULL) | ||
| 1781 | + { | ||
| 1782 | + NSLog(@"empty sampleBuffer"); | ||
| 1783 | + return; | ||
| 1784 | + } | ||
| 1785 | + | ||
| 1786 | + // CFRetain(sampleBuffer); | ||
| 1787 | + // dispatch_async(self.videoQueue, ^{ | ||
| 1788 | + @autoreleasepool | ||
| 1789 | + { | ||
| 1790 | + if (!self.canWrite && mediaType == AVMediaTypeVideo) | ||
| 1791 | + { | ||
| 1792 | + [self.assetWriter startWriting]; | ||
| 1793 | + [self.assetWriter startSessionAtSourceTime:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)]; | ||
| 1794 | + self.canWrite = YES; | ||
| 1795 | + } | ||
| 1796 | + | ||
| 1797 | + //写入视频数据 | ||
| 1798 | + if (mediaType == AVMediaTypeVideo) | ||
| 1799 | + { | ||
| 1800 | + if (self.assetWriterVideoInput.readyForMoreMediaData) | ||
| 1801 | + { | ||
| 1802 | + BOOL success = [self.assetWriterVideoInput appendSampleBuffer:sampleBuffer]; | ||
| 1803 | + if (!success) | ||
| 1804 | + { | ||
| 1805 | + @synchronized (self) | ||
| 1806 | + { | ||
| 1807 | + [self stopVideoRecorder]; | ||
| 1808 | + } | ||
| 1809 | + } | ||
| 1810 | + } | ||
| 1811 | + } | ||
| 1812 | + | ||
| 1813 | + //写入音频数据 | ||
| 1814 | + if (mediaType == AVMediaTypeAudio) | ||
| 1815 | + { | ||
| 1816 | + if (self.assetWriterAudioInput.readyForMoreMediaData) | ||
| 1817 | + { | ||
| 1818 | + BOOL success = [self.assetWriterAudioInput appendSampleBuffer:sampleBuffer]; | ||
| 1819 | + if (!success) | ||
| 1820 | + { | ||
| 1821 | + @synchronized (self) | ||
| 1822 | + { | ||
| 1823 | + [self stopVideoRecorder]; | ||
| 1824 | + } | ||
| 1825 | + } | ||
| 1826 | + } | ||
| 1827 | + } | ||
| 1828 | + | ||
| 1829 | + // CFRelease(sampleBuffer); | ||
| 1830 | + } | ||
| 1831 | + // }); | ||
| 1832 | +} | ||
| 1833 | + | ||
| 1834 | +#pragma mark - 摄像头聚焦,与缩放 | ||
| 1835 | + | ||
| 1836 | +/** | ||
| 1837 | + * 添加点按手势 | ||
| 1838 | + */ | ||
| 1839 | +- (void)addTapGenstureRecognizerForCamera | ||
| 1840 | +{ | ||
| 1841 | + UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinchGesture:)]; | ||
| 1842 | + | ||
| 1843 | + pinchGesture.delegate = self; | ||
| 1844 | + | ||
| 1845 | + [self.view addGestureRecognizer:pinchGesture]; | ||
| 1846 | +} | ||
| 1847 | + | ||
| 1848 | +/** | ||
| 1849 | + * 点击屏幕,聚焦事件 | ||
| 1850 | + */ | ||
| 1851 | +- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event | ||
| 1852 | +{ | ||
| 1853 | + // 不聚焦的情况:聚焦中,旋转摄像头中,查看录制的视频中,查看照片中 | ||
| 1854 | + if (_isFocusing || touches.count == 0 || _isRotatingCamera || _videoPreviewContainerView || _photoPreviewImageView) | ||
| 1855 | + { | ||
| 1856 | + return; | ||
| 1857 | + } | ||
| 1858 | + | ||
| 1859 | + UITouch *touch = nil; | ||
| 1860 | + | ||
| 1861 | + for (UITouch *t in touches) | ||
| 1862 | + { | ||
| 1863 | + touch = t; | ||
| 1864 | + break; | ||
| 1865 | + } | ||
| 1866 | + | ||
| 1867 | + CGPoint point = [touch locationInView:self.view];; | ||
| 1868 | + | ||
| 1869 | + if (point.y > CGRectGetMaxY(self.tipLabel.frame)) | ||
| 1870 | + { | ||
| 1871 | + return; | ||
| 1872 | + } | ||
| 1873 | + | ||
| 1874 | + [self setFocusCursorWithPoint:point]; | ||
| 1875 | +} | ||
| 1876 | + | ||
| 1877 | +/** | ||
| 1878 | + * 设置聚焦光标位置 | ||
| 1879 | + * | ||
| 1880 | + * @param point 光标位置 | ||
| 1881 | + */ | ||
| 1882 | +- (void)setFocusCursorWithPoint:(CGPoint)point | ||
| 1883 | +{ | ||
| 1884 | + self.isFocusing = YES; | ||
| 1885 | + | ||
| 1886 | + self.focusImageView.center = point; | ||
| 1887 | + self.focusImageView.transform = CGAffineTransformMakeScale(1.5, 1.5); | ||
| 1888 | + self.focusImageView.alpha = 1; | ||
| 1889 | + | ||
| 1890 | + //将UI坐标转化为摄像头坐标 | ||
| 1891 | + CGPoint cameraPoint = [self.captureVideoPreviewLayer captureDevicePointOfInterestForPoint:point]; | ||
| 1892 | + [self focusWithPoint:cameraPoint]; | ||
| 1893 | + | ||
| 1894 | + __weak typeof(self) weakSelf = self; | ||
| 1895 | + [UIView animateWithDuration:1.0 animations:^{ | ||
| 1896 | + | ||
| 1897 | + weakSelf.focusImageView.transform = CGAffineTransformIdentity; | ||
| 1898 | + | ||
| 1899 | + } completion:^(BOOL finished) { | ||
| 1900 | + | ||
| 1901 | + weakSelf.focusImageView.alpha = 0; | ||
| 1902 | + weakSelf.isFocusing = NO; | ||
| 1903 | + | ||
| 1904 | + }]; | ||
| 1905 | +} | ||
| 1906 | + | ||
| 1907 | +/** | ||
| 1908 | + * 设置聚焦点 | ||
| 1909 | + * | ||
| 1910 | + * @param point 聚焦点 | ||
| 1911 | + */ | ||
| 1912 | +-(void)focusWithPoint:(CGPoint)point | ||
| 1913 | +{ | ||
| 1914 | + [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) | ||
| 1915 | + { | ||
| 1916 | + // 聚焦 | ||
| 1917 | + if ([captureDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) | ||
| 1918 | + { | ||
| 1919 | + [captureDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus]; | ||
| 1920 | + } | ||
| 1921 | + if ([captureDevice isFocusPointOfInterestSupported]) | ||
| 1922 | + { | ||
| 1923 | + [captureDevice setFocusPointOfInterest:point]; | ||
| 1924 | + } | ||
| 1925 | + // 曝光 | ||
| 1926 | + if ([captureDevice isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) | ||
| 1927 | + { | ||
| 1928 | + [captureDevice setExposureMode:AVCaptureExposureModeContinuousAutoExposure]; | ||
| 1929 | + } | ||
| 1930 | + if ([captureDevice isExposurePointOfInterestSupported]) | ||
| 1931 | + { | ||
| 1932 | + [captureDevice setExposurePointOfInterest:point]; | ||
| 1933 | + } | ||
| 1934 | + }]; | ||
| 1935 | +} | ||
| 1936 | + | ||
| 1937 | +- (void)handlePinchGesture:(UIPinchGestureRecognizer *)recognizer | ||
| 1938 | +{ | ||
| 1939 | + if (_isShooting) | ||
| 1940 | + { | ||
| 1941 | + return; | ||
| 1942 | + } | ||
| 1943 | + | ||
| 1944 | + BOOL allTouchesAreOnTheCaptureVideoPreviewLayer = YES; | ||
| 1945 | + | ||
| 1946 | + NSUInteger numTouches = [recognizer numberOfTouches], i; | ||
| 1947 | + for ( i = 0; i < numTouches; ++i) | ||
| 1948 | + { | ||
| 1949 | + CGPoint location = [recognizer locationOfTouch:i inView:self.view]; | ||
| 1950 | + CGPoint convertedLocation = [self.captureVideoPreviewLayer convertPoint:location fromLayer:self.captureVideoPreviewLayer.superlayer]; | ||
| 1951 | + if (![self.captureVideoPreviewLayer containsPoint:convertedLocation]) | ||
| 1952 | + { | ||
| 1953 | + allTouchesAreOnTheCaptureVideoPreviewLayer = NO; | ||
| 1954 | + break; | ||
| 1955 | + } | ||
| 1956 | + } | ||
| 1957 | + | ||
| 1958 | + if (allTouchesAreOnTheCaptureVideoPreviewLayer) | ||
| 1959 | + { | ||
| 1960 | + self.effectiveScale = self.beginGestureScale * recognizer.scale; | ||
| 1961 | + if (self.effectiveScale < 1.0f) | ||
| 1962 | + { | ||
| 1963 | + self.effectiveScale = 1.0f; | ||
| 1964 | + } | ||
| 1965 | + | ||
| 1966 | + // NSLog(@"%f-------------->%f------------recognizerScale%f", self.effectiveScale, self.beginGestureScale, recognizer.scale); | ||
| 1967 | + | ||
| 1968 | + CGFloat imageMaxScaleAndCropFactor = [[self.captureStillImageOutput connectionWithMediaType:AVMediaTypeVideo] videoMaxScaleAndCropFactor]; | ||
| 1969 | + | ||
| 1970 | + // NSLog(@"%f", imageMaxScaleAndCropFactor); | ||
| 1971 | + if (self.effectiveScale > imageMaxScaleAndCropFactor) | ||
| 1972 | + { | ||
| 1973 | + self.effectiveScale = imageMaxScaleAndCropFactor; | ||
| 1974 | + } | ||
| 1975 | + | ||
| 1976 | + [self setCaptureVideoPreviewLayerTransformWithScale:self.effectiveScale]; | ||
| 1977 | + } | ||
| 1978 | +} | ||
| 1979 | + | ||
| 1980 | +- (void)setCaptureVideoPreviewLayerTransformWithScale:(CGFloat)scale | ||
| 1981 | +{ | ||
| 1982 | + self.effectiveScale = scale; | ||
| 1983 | + [CATransaction begin]; | ||
| 1984 | + [CATransaction setAnimationDuration:0.25f]; //时长最好低于 START_VIDEO_ANIMATION_DURATION | ||
| 1985 | + [self.captureVideoPreviewLayer setAffineTransform:CGAffineTransformMakeScale(scale, scale)]; | ||
| 1986 | + [CATransaction commit]; | ||
| 1987 | +} | ||
| 1988 | + | ||
| 1989 | +#pragma mark - UIGestureRecognizerDelegate | ||
| 1990 | + | ||
| 1991 | +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer | ||
| 1992 | +{ | ||
| 1993 | + if ([gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]]) | ||
| 1994 | + { | ||
| 1995 | + self.beginGestureScale = self.effectiveScale; | ||
| 1996 | + } | ||
| 1997 | + | ||
| 1998 | + return YES; | ||
| 1999 | +} | ||
| 2000 | + | ||
| 2001 | +#pragma mark - 重力感应相关 | ||
| 2002 | + | ||
| 2003 | +/** | ||
| 2004 | + * 开始监听屏幕方向 | ||
| 2005 | + */ | ||
| 2006 | +- (void)startUpdateAccelerometer | ||
| 2007 | +{ | ||
| 2008 | + if ([self.motionManager isAccelerometerAvailable] == YES) | ||
| 2009 | + { | ||
| 2010 | + //回调会一直调用,建议获取到就调用下面的停止方法,需要再重新开始,当然如果需求是实时不间断的话可以等离开页面之后再stop | ||
| 2011 | + [self.motionManager setAccelerometerUpdateInterval:1.0]; | ||
| 2012 | + [self.motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) | ||
| 2013 | + { | ||
| 2014 | + double x = accelerometerData.acceleration.x; | ||
| 2015 | + double y = accelerometerData.acceleration.y; | ||
| 2016 | + if (fabs(y) >= fabs(x)) | ||
| 2017 | + { | ||
| 2018 | + if (y >= 0) | ||
| 2019 | + { | ||
| 2020 | + // Down | ||
| 2021 | + NSLog(@"Down"); | ||
| 2022 | + self->_shootingOrientation = UIDeviceOrientationPortraitUpsideDown; | ||
| 2023 | + } | ||
| 2024 | + else | ||
| 2025 | + { | ||
| 2026 | + // Portrait | ||
| 2027 | + NSLog(@"Portrait"); | ||
| 2028 | + self->_shootingOrientation = UIDeviceOrientationPortrait; | ||
| 2029 | + } | ||
| 2030 | + } | ||
| 2031 | + else | ||
| 2032 | + { | ||
| 2033 | + if (x >= 0) | ||
| 2034 | + { | ||
| 2035 | + // Right | ||
| 2036 | + NSLog(@"Right"); | ||
| 2037 | + self->_shootingOrientation = UIDeviceOrientationLandscapeRight; | ||
| 2038 | + } | ||
| 2039 | + else | ||
| 2040 | + { | ||
| 2041 | + // Left | ||
| 2042 | + NSLog(@"Left"); | ||
| 2043 | + self->_shootingOrientation = UIDeviceOrientationLandscapeLeft; | ||
| 2044 | + } | ||
| 2045 | + } | ||
| 2046 | + }]; | ||
| 2047 | + } | ||
| 2048 | +} | ||
| 2049 | + | ||
| 2050 | +/** | ||
| 2051 | + * 停止监听屏幕方向 | ||
| 2052 | + */ | ||
| 2053 | +- (void)stopUpdateAccelerometer | ||
| 2054 | +{ | ||
| 2055 | + if ([self.motionManager isAccelerometerActive] == YES) | ||
| 2056 | + { | ||
| 2057 | + [self.motionManager stopAccelerometerUpdates]; | ||
| 2058 | + _motionManager = nil; | ||
| 2059 | + } | ||
| 2060 | +} | ||
| 2061 | + | ||
| 2062 | +#pragma mark - 判断是否有权限 | ||
| 2063 | + | ||
| 2064 | +/** | ||
| 2065 | + * 请求权限 | ||
| 2066 | + */ | ||
| 2067 | +- (void)requestAuthorizationForVideo | ||
| 2068 | +{ | ||
| 2069 | + __weak typeof(self) weakSelf = self; | ||
| 2070 | + | ||
| 2071 | + // 请求相机权限 | ||
| 2072 | + AVAuthorizationStatus videoAuthStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; | ||
| 2073 | + if (videoAuthStatus != AVAuthorizationStatusAuthorized) | ||
| 2074 | + { | ||
| 2075 | + NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary]; | ||
| 2076 | + | ||
| 2077 | + NSString *appName = [infoDictionary objectForKey:@"CFBundleDisplayName"]; | ||
| 2078 | + if (appName == nil) | ||
| 2079 | + { | ||
| 2080 | + appName = @"APP"; | ||
| 2081 | + } | ||
| 2082 | + NSString *message = [NSString stringWithFormat:@"请在iPhone的“设置-隐私”选项中,允许%@访问你的相册,相机和麦克风。", appName]; | ||
| 2083 | + | ||
| 2084 | + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];//无法拍摄 | ||
| 2085 | + | ||
| 2086 | + UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { | ||
| 2087 | + [weakSelf dismissViewControllerAnimated:YES completion:nil]; | ||
| 2088 | + }]; | ||
| 2089 | + | ||
| 2090 | + [alertController addAction:okAction]; | ||
| 2091 | + [self presentViewController:alertController animated:YES completion:nil]; | ||
| 2092 | + } | ||
| 2093 | + | ||
| 2094 | + // 请求麦克风权限 | ||
| 2095 | + AVAuthorizationStatus audioAuthStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]; | ||
| 2096 | + if (audioAuthStatus != AVAuthorizationStatusAuthorized) | ||
| 2097 | + { | ||
| 2098 | + NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary]; | ||
| 2099 | + | ||
| 2100 | + NSString *appName = [infoDictionary objectForKey:@"CFBundleDisplayName"]; | ||
| 2101 | + if (appName == nil) | ||
| 2102 | + { | ||
| 2103 | + appName = @"APP"; | ||
| 2104 | + } | ||
| 2105 | + NSString *message = [NSString stringWithFormat:@"请在iPhone的“设置-隐私“选项中,允许%@访问你的相机,相册和麦克风。", appName];//[NSString stringWithFormat:@"请在iPhone的““设置-隐私””选项中, 允许%@访问你的麦克风?", appName]; | ||
| 2106 | + | ||
| 2107 | + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert]; | ||
| 2108 | + | ||
| 2109 | + UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { | ||
| 2110 | + [weakSelf dismissViewControllerAnimated:YES completion:nil]; | ||
| 2111 | + }]; | ||
| 2112 | + | ||
| 2113 | + [alertController addAction:okAction]; | ||
| 2114 | + | ||
| 2115 | + [self presentViewController:alertController animated:YES completion:nil]; | ||
| 2116 | + } | ||
| 2117 | + | ||
| 2118 | + | ||
| 2119 | +} | ||
| 2120 | + | ||
| 2121 | +- (void)requestAuthorizationForPhotoLibrary | ||
| 2122 | +{ | ||
| 2123 | + __weak typeof(self) weakSelf = self; | ||
| 2124 | + | ||
| 2125 | + // 请求照片权限 | ||
| 2126 | + [XFPhotoLibraryManager requestALAssetsLibraryAuthorizationWithCompletion:^(Boolean isAuth) { | ||
| 2127 | + | ||
| 2128 | + if (!isAuth) | ||
| 2129 | + { | ||
| 2130 | + NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary]; | ||
| 2131 | + | ||
| 2132 | + NSString *appName = [infoDictionary objectForKey:@"CFBundleDisplayName"]; | ||
| 2133 | + if (appName == nil) | ||
| 2134 | + { | ||
| 2135 | + appName = @"APP"; | ||
| 2136 | + } | ||
| 2137 | + NSString *message = [NSString stringWithFormat:@"请在iPhone的""设置-隐私-相册""中允许%@访问相册", appName];//@"允许%@访问你的相册?" | ||
| 2138 | + | ||
| 2139 | + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert]; | ||
| 2140 | + | ||
| 2141 | + UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { | ||
| 2142 | + [weakSelf dismissViewControllerAnimated:YES completion:nil]; | ||
| 2143 | + weakSelf.isAlertShow = NO; | ||
| 2144 | + }]; | ||
| 2145 | + | ||
| 2146 | +// UIAlertAction *setAction = [UIAlertAction actionWithTitle:@"设置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { | ||
| 2147 | +// NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; | ||
| 2148 | +// if ([[UIApplication sharedApplication] canOpenURL:url]) | ||
| 2149 | +// { | ||
| 2150 | +// [[UIApplication sharedApplication] openURL:url]; | ||
| 2151 | +// [weakSelf dismissViewControllerAnimated:YES completion:nil]; | ||
| 2152 | +// } | ||
| 2153 | +// }]; | ||
| 2154 | + | ||
| 2155 | + [alertController addAction:okAction]; | ||
| 2156 | +// [alertController addAction:setAction]; | ||
| 2157 | + | ||
| 2158 | + [self presentViewController:alertController animated:YES completion:nil]; | ||
| 2159 | + self->_isAlertShow = YES; | ||
| 2160 | + | ||
| 2161 | + } | ||
| 2162 | + }]; | ||
| 2163 | +} | ||
| 2164 | + | ||
| 2165 | +- (void)requestAuthCamera | ||
| 2166 | +{ | ||
| 2167 | + __weak typeof(self) weakSelf = self; | ||
| 2168 | + | ||
| 2169 | + // 请求相机权限 | ||
| 2170 | + AVAuthorizationStatus videoAuthStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; | ||
| 2171 | + if (videoAuthStatus != AVAuthorizationStatusAuthorized) | ||
| 2172 | + { | ||
| 2173 | + NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary]; | ||
| 2174 | + | ||
| 2175 | + NSString *appName = [infoDictionary objectForKey:@"CFBundleDisplayName"]; | ||
| 2176 | + if (appName == nil) | ||
| 2177 | + { | ||
| 2178 | + appName = @"APP"; | ||
| 2179 | + } | ||
| 2180 | + NSString *message = [NSString stringWithFormat:@"请在iPhone的“设置-隐私”选项中,允许%@访问你的摄像头和麦克风。", appName]; | ||
| 2181 | + | ||
| 2182 | + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];//无法拍摄 | ||
| 2183 | + | ||
| 2184 | + UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { | ||
| 2185 | + [weakSelf dismissViewControllerAnimated:YES completion:nil]; | ||
| 2186 | + }]; | ||
| 2187 | + | ||
| 2188 | + [alertController addAction:okAction]; | ||
| 2189 | + [self presentViewController:alertController animated:YES completion:nil]; | ||
| 2190 | + } | ||
| 2191 | +} | ||
| 2192 | + | ||
| 2193 | +#pragma mark - 进入前后台监听 | ||
| 2194 | + | ||
| 2195 | +- (void)applicationBecomeActive | ||
| 2196 | +{ | ||
| 2197 | + if (self.player) { | ||
| 2198 | + [self.player play]; | ||
| 2199 | + } | ||
| 2200 | +} | ||
| 2201 | + | ||
| 2202 | +- (void)enterBackgroundNotification | ||
| 2203 | +{ | ||
| 2204 | + if (_isPicOrVideo) { | ||
| 2205 | + //已经拍照或者录视频 不dismiss | ||
| 2206 | + }else | ||
| 2207 | + {//没有拍照或者录制视频 dismiss | ||
| 2208 | + if (!_isAlertShow) { | ||
| 2209 | + [self dismissViewControllerAnimated:YES completion:nil]; | ||
| 2210 | + } | ||
| 2211 | + } | ||
| 2212 | +} | ||
| 2213 | + | ||
| 2214 | +@end |
CNLiveAVCamera/Classes/XFAVCamera/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 | +typedef void(^RequestAssetsLibraryAuthCompletion)(Boolean isAuth); | ||
| 13 | +typedef void(^SavePhotoCompletionBlock)(UIImage *image, NSError *error); | ||
| 14 | +typedef void(^SaveVideoCompletionBlock)(NSURL *vedioUrl, NSError *error); | ||
| 15 | + | ||
| 16 | +//zx | ||
| 17 | +typedef void(^GetCameraVideoCompletionBlock)(PHAsset *asset,NSURL *vedioUrl, NSError *error); | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +@interface XFPhotoLibraryManager : NSObject | ||
| 21 | + | ||
| 22 | +/** | ||
| 23 | + * 请求照片权限,注意,强烈要求用户获得照片权限,否则视频写入照片会有崩溃 | ||
| 24 | + */ | ||
| 25 | ++ (void)requestALAssetsLibraryAuthorizationWithCompletion:(RequestAssetsLibraryAuthCompletion) requestAssetsLibraryAuthCompletion; | ||
| 26 | + | ||
| 27 | +/** | ||
| 28 | + * 保存照片 | ||
| 29 | + * | ||
| 30 | + * @param image UImage | ||
| 31 | + * @param assetCollectionName 相册名字,不填默认为app名字+相册 | ||
| 32 | + */ | ||
| 33 | ++ (void)savePhotoWithImage:(UIImage *)image andAssetCollectionName:(NSString *)assetCollectionName withCompletion:(SavePhotoCompletionBlock)savePhotoCompletionBlock; | ||
| 34 | + | ||
| 35 | +/** | ||
| 36 | + * 保存视频 | ||
| 37 | + * | ||
| 38 | + * @param videoUrl 视频地址 | ||
| 39 | + * @param assetCollectionName 相册名字,不填默认为app名字 | ||
| 40 | + */ | ||
| 41 | ++ (void)saveVideoWithVideoUrl:(NSURL *)videoUrl andAssetCollectionName:(NSString *)assetCollectionName withCompletion:(SaveVideoCompletionBlock)saveVideoCompletionBlock; | ||
| 42 | + | ||
| 43 | + | ||
| 44 | +/** | ||
| 45 | + * zx保存视频 | ||
| 46 | + * | ||
| 47 | + * @param videoUrl 视频地址 | ||
| 48 | + * @param assetCollectionName 相册名字,不填默认为app名字 | ||
| 49 | + */ | ||
| 50 | ++ (void)zx_saveVideoWithVideoUrl:(NSURL *)videoUrl andAssetCollectionName:(NSString *)assetCollectionName withCompletion:(GetCameraVideoCompletionBlock)getCameraVideoCompletionBlock; | ||
| 51 | +@end |
CNLiveAVCamera/Classes/XFAVCamera/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
0 → 100644
| 1 | +// !$*UTF8*$! | ||
| 2 | +{ | ||
| 3 | + archiveVersion = 1; | ||
| 4 | + classes = { | ||
| 5 | + }; | ||
| 6 | + objectVersion = 46; | ||
| 7 | + objects = { | ||
| 8 | + | ||
| 9 | +/* Begin PBXBuildFile section */ | ||
| 10 | + 528A8ADF0285C491B820F82E /* Pods_CNLiveAVCamera_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 774E0E4E88E7EA58FB0DBF0F /* Pods_CNLiveAVCamera_Example.framework */; }; | ||
| 11 | + 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; | ||
| 12 | + 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; }; | ||
| 13 | + 6003F592195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; | ||
| 14 | + 6003F598195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F596195388D20070C39A /* InfoPlist.strings */; }; | ||
| 15 | + 6003F59A195388D20070C39A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F599195388D20070C39A /* main.m */; }; | ||
| 16 | + 6003F59E195388D20070C39A /* CNLiveAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F59D195388D20070C39A /* CNLiveAppDelegate.m */; }; | ||
| 17 | + 6003F5A7195388D20070C39A /* CNLiveViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5A6195388D20070C39A /* CNLiveViewController.m */; }; | ||
| 18 | + 6003F5A9195388D20070C39A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5A8195388D20070C39A /* Images.xcassets */; }; | ||
| 19 | + 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; | ||
| 20 | + 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; | ||
| 21 | + 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; | ||
| 22 | + 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; | ||
| 23 | + 6003F5BC195388D20070C39A /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5BB195388D20070C39A /* Tests.m */; }; | ||
| 24 | + 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */; }; | ||
| 25 | + 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; | ||
| 26 | + D6B4616FF3648DB1BE0A9900 /* Pods_CNLiveAVCamera_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7062A8FD28D9C146CB478315 /* Pods_CNLiveAVCamera_Tests.framework */; }; | ||
| 27 | +/* End PBXBuildFile section */ | ||
| 28 | + | ||
| 29 | +/* Begin PBXContainerItemProxy section */ | ||
| 30 | + 6003F5B3195388D20070C39A /* PBXContainerItemProxy */ = { | ||
| 31 | + isa = PBXContainerItemProxy; | ||
| 32 | + containerPortal = 6003F582195388D10070C39A /* Project object */; | ||
| 33 | + proxyType = 1; | ||
| 34 | + remoteGlobalIDString = 6003F589195388D20070C39A; | ||
| 35 | + remoteInfo = CNLiveAVCamera; | ||
| 36 | + }; | ||
| 37 | +/* End PBXContainerItemProxy section */ | ||
| 38 | + | ||
| 39 | +/* Begin PBXCopyFilesBuildPhase section */ | ||
| 40 | + 399A590F21BA760F0092926F /* Embed Frameworks */ = { | ||
| 41 | + isa = PBXCopyFilesBuildPhase; | ||
| 42 | + buildActionMask = 2147483647; | ||
| 43 | + dstPath = ""; | ||
| 44 | + dstSubfolderSpec = 10; | ||
| 45 | + files = ( | ||
| 46 | + ); | ||
| 47 | + name = "Embed Frameworks"; | ||
| 48 | + runOnlyForDeploymentPostprocessing = 0; | ||
| 49 | + }; | ||
| 50 | +/* End PBXCopyFilesBuildPhase section */ | ||
| 51 | + | ||
| 52 | +/* Begin PBXFileReference section */ | ||
| 53 | + 232FB65DACAACEB088650B26 /* Pods-CNLiveAVCamera_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CNLiveAVCamera_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-CNLiveAVCamera_Example/Pods-CNLiveAVCamera_Example.release.xcconfig"; sourceTree = "<group>"; }; | ||
| 54 | + 399A590521BA760F0092926F /* CNLiveAVCamera.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CNLiveAVCamera.h; sourceTree = "<group>"; }; | ||
| 55 | + 399A590621BA760F0092926F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; | ||
| 56 | + 4D1880C33D23BC4C4F02DDC2 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; }; | ||
| 57 | + 4F6BCE6DD63FA804E1500FF2 /* 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>"; }; | ||
| 58 | + 6003F58A195388D20070C39A /* CNLiveAVCamera_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CNLiveAVCamera_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; | ||
| 59 | + 6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; | ||
| 60 | + 6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; | ||
| 61 | + 6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; | ||
| 62 | + 6003F595195388D20070C39A /* CNLiveAVCamera-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CNLiveAVCamera-Info.plist"; sourceTree = "<group>"; }; | ||
| 63 | + 6003F597195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; }; | ||
| 64 | + 6003F599195388D20070C39A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; | ||
| 65 | + 6003F59B195388D20070C39A /* CNLiveAVCamera-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CNLiveAVCamera-Prefix.pch"; sourceTree = "<group>"; }; | ||
| 66 | + 6003F59C195388D20070C39A /* CNLiveAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CNLiveAppDelegate.h; sourceTree = "<group>"; }; | ||
| 67 | + 6003F59D195388D20070C39A /* CNLiveAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CNLiveAppDelegate.m; sourceTree = "<group>"; }; | ||
| 68 | + 6003F5A5195388D20070C39A /* CNLiveViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CNLiveViewController.h; sourceTree = "<group>"; }; | ||
| 69 | + 6003F5A6195388D20070C39A /* CNLiveViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CNLiveViewController.m; sourceTree = "<group>"; }; | ||
| 70 | + 6003F5A8195388D20070C39A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; }; | ||
| 71 | + 6003F5AE195388D20070C39A /* CNLiveAVCamera_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CNLiveAVCamera_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; | ||
| 72 | + 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; | ||
| 73 | + 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = "<group>"; }; | ||
| 74 | + 6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; }; | ||
| 75 | + 6003F5BB195388D20070C39A /* Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Tests.m; sourceTree = "<group>"; }; | ||
| 76 | + 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Prefix.pch"; sourceTree = "<group>"; }; | ||
| 77 | + 7062A8FD28D9C146CB478315 /* Pods_CNLiveAVCamera_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CNLiveAVCamera_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; | ||
| 78 | + 71719F9E1E33DC2100824A3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; | ||
| 79 | + 774E0E4E88E7EA58FB0DBF0F /* Pods_CNLiveAVCamera_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CNLiveAVCamera_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; | ||
| 80 | + 774EA667BA71566BA72B8A5C /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; }; | ||
| 81 | + 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; | ||
| 82 | + D34C72B66F62C1A82C1D9FA8 /* CNLiveAVCamera.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = CNLiveAVCamera.podspec; path = ../CNLiveAVCamera.podspec; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; | ||
| 83 | + D41556E2EE76E873ED9847B4 /* Pods-CNLiveAVCamera_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CNLiveAVCamera_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-CNLiveAVCamera_Tests/Pods-CNLiveAVCamera_Tests.release.xcconfig"; sourceTree = "<group>"; }; | ||
| 84 | + E61E5CDE06F66F688BB4ADF8 /* Pods-CNLiveAVCamera_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CNLiveAVCamera_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CNLiveAVCamera_Example/Pods-CNLiveAVCamera_Example.debug.xcconfig"; sourceTree = "<group>"; }; | ||
| 85 | +/* End PBXFileReference section */ | ||
| 86 | + | ||
| 87 | +/* Begin PBXFrameworksBuildPhase section */ | ||
| 88 | + 6003F587195388D20070C39A /* Frameworks */ = { | ||
| 89 | + isa = PBXFrameworksBuildPhase; | ||
| 90 | + buildActionMask = 2147483647; | ||
| 91 | + files = ( | ||
| 92 | + 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */, | ||
| 93 | + 6003F592195388D20070C39A /* UIKit.framework in Frameworks */, | ||
| 94 | + 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */, | ||
| 95 | + 528A8ADF0285C491B820F82E /* Pods_CNLiveAVCamera_Example.framework in Frameworks */, | ||
| 96 | + ); | ||
| 97 | + runOnlyForDeploymentPostprocessing = 0; | ||
| 98 | + }; | ||
| 99 | + 6003F5AB195388D20070C39A /* Frameworks */ = { | ||
| 100 | + isa = PBXFrameworksBuildPhase; | ||
| 101 | + buildActionMask = 2147483647; | ||
| 102 | + files = ( | ||
| 103 | + 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */, | ||
| 104 | + 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */, | ||
| 105 | + 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */, | ||
| 106 | + D6B4616FF3648DB1BE0A9900 /* Pods_CNLiveAVCamera_Tests.framework in Frameworks */, | ||
| 107 | + ); | ||
| 108 | + runOnlyForDeploymentPostprocessing = 0; | ||
| 109 | + }; | ||
| 110 | +/* End PBXFrameworksBuildPhase section */ | ||
| 111 | + | ||
| 112 | +/* Begin PBXGroup section */ | ||
| 113 | + 0C13D0041B2063E549BF5B2A /* Pods */ = { | ||
| 114 | + isa = PBXGroup; | ||
| 115 | + children = ( | ||
| 116 | + E61E5CDE06F66F688BB4ADF8 /* Pods-CNLiveAVCamera_Example.debug.xcconfig */, | ||
| 117 | + 232FB65DACAACEB088650B26 /* Pods-CNLiveAVCamera_Example.release.xcconfig */, | ||
| 118 | + 4F6BCE6DD63FA804E1500FF2 /* Pods-CNLiveAVCamera_Tests.debug.xcconfig */, | ||
| 119 | + D41556E2EE76E873ED9847B4 /* Pods-CNLiveAVCamera_Tests.release.xcconfig */, | ||
| 120 | + ); | ||
| 121 | + name = Pods; | ||
| 122 | + sourceTree = "<group>"; | ||
| 123 | + }; | ||
| 124 | + 399A590421BA760F0092926F /* CNLiveAVCamera */ = { | ||
| 125 | + isa = PBXGroup; | ||
| 126 | + children = ( | ||
| 127 | + 399A590521BA760F0092926F /* CNLiveAVCamera.h */, | ||
| 128 | + 399A590621BA760F0092926F /* Info.plist */, | ||
| 129 | + ); | ||
| 130 | + path = CNLiveAVCamera; | ||
| 131 | + sourceTree = "<group>"; | ||
| 132 | + }; | ||
| 133 | + 6003F581195388D10070C39A = { | ||
| 134 | + isa = PBXGroup; | ||
| 135 | + children = ( | ||
| 136 | + 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */, | ||
| 137 | + 6003F593195388D20070C39A /* Example for CNLiveAVCamera */, | ||
| 138 | + 6003F5B5195388D20070C39A /* Tests */, | ||
| 139 | + 399A590421BA760F0092926F /* CNLiveAVCamera */, | ||
| 140 | + 6003F58C195388D20070C39A /* Frameworks */, | ||
| 141 | + 6003F58B195388D20070C39A /* Products */, | ||
| 142 | + 0C13D0041B2063E549BF5B2A /* Pods */, | ||
| 143 | + ); | ||
| 144 | + sourceTree = "<group>"; | ||
| 145 | + }; | ||
| 146 | + 6003F58B195388D20070C39A /* Products */ = { | ||
| 147 | + isa = PBXGroup; | ||
| 148 | + children = ( | ||
| 149 | + 6003F58A195388D20070C39A /* CNLiveAVCamera_Example.app */, | ||
| 150 | + 6003F5AE195388D20070C39A /* CNLiveAVCamera_Tests.xctest */, | ||
| 151 | + ); | ||
| 152 | + name = Products; | ||
| 153 | + sourceTree = "<group>"; | ||
| 154 | + }; | ||
| 155 | + 6003F58C195388D20070C39A /* Frameworks */ = { | ||
| 156 | + isa = PBXGroup; | ||
| 157 | + children = ( | ||
| 158 | + 6003F58D195388D20070C39A /* Foundation.framework */, | ||
| 159 | + 6003F58F195388D20070C39A /* CoreGraphics.framework */, | ||
| 160 | + 6003F591195388D20070C39A /* UIKit.framework */, | ||
| 161 | + 6003F5AF195388D20070C39A /* XCTest.framework */, | ||
| 162 | + 774E0E4E88E7EA58FB0DBF0F /* Pods_CNLiveAVCamera_Example.framework */, | ||
| 163 | + 7062A8FD28D9C146CB478315 /* Pods_CNLiveAVCamera_Tests.framework */, | ||
| 164 | + ); | ||
| 165 | + name = Frameworks; | ||
| 166 | + sourceTree = "<group>"; | ||
| 167 | + }; | ||
| 168 | + 6003F593195388D20070C39A /* Example for CNLiveAVCamera */ = { | ||
| 169 | + isa = PBXGroup; | ||
| 170 | + children = ( | ||
| 171 | + 6003F59C195388D20070C39A /* CNLiveAppDelegate.h */, | ||
| 172 | + 6003F59D195388D20070C39A /* CNLiveAppDelegate.m */, | ||
| 173 | + 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */, | ||
| 174 | + 6003F5A5195388D20070C39A /* CNLiveViewController.h */, | ||
| 175 | + 6003F5A6195388D20070C39A /* CNLiveViewController.m */, | ||
| 176 | + 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */, | ||
| 177 | + 6003F5A8195388D20070C39A /* Images.xcassets */, | ||
| 178 | + 6003F594195388D20070C39A /* Supporting Files */, | ||
| 179 | + ); | ||
| 180 | + name = "Example for CNLiveAVCamera"; | ||
| 181 | + path = CNLiveAVCamera; | ||
| 182 | + sourceTree = "<group>"; | ||
| 183 | + }; | ||
| 184 | + 6003F594195388D20070C39A /* Supporting Files */ = { | ||
| 185 | + isa = PBXGroup; | ||
| 186 | + children = ( | ||
| 187 | + 6003F595195388D20070C39A /* CNLiveAVCamera-Info.plist */, | ||
| 188 | + 6003F596195388D20070C39A /* InfoPlist.strings */, | ||
| 189 | + 6003F599195388D20070C39A /* main.m */, | ||
| 190 | + 6003F59B195388D20070C39A /* CNLiveAVCamera-Prefix.pch */, | ||
| 191 | + ); | ||
| 192 | + name = "Supporting Files"; | ||
| 193 | + sourceTree = "<group>"; | ||
| 194 | + }; | ||
| 195 | + 6003F5B5195388D20070C39A /* Tests */ = { | ||
| 196 | + isa = PBXGroup; | ||
| 197 | + children = ( | ||
| 198 | + 6003F5BB195388D20070C39A /* Tests.m */, | ||
| 199 | + 6003F5B6195388D20070C39A /* Supporting Files */, | ||
| 200 | + ); | ||
| 201 | + path = Tests; | ||
| 202 | + sourceTree = "<group>"; | ||
| 203 | + }; | ||
| 204 | + 6003F5B6195388D20070C39A /* Supporting Files */ = { | ||
| 205 | + isa = PBXGroup; | ||
| 206 | + children = ( | ||
| 207 | + 6003F5B7195388D20070C39A /* Tests-Info.plist */, | ||
| 208 | + 6003F5B8195388D20070C39A /* InfoPlist.strings */, | ||
| 209 | + 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */, | ||
| 210 | + ); | ||
| 211 | + name = "Supporting Files"; | ||
| 212 | + sourceTree = "<group>"; | ||
| 213 | + }; | ||
| 214 | + 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */ = { | ||
| 215 | + isa = PBXGroup; | ||
| 216 | + children = ( | ||
| 217 | + D34C72B66F62C1A82C1D9FA8 /* CNLiveAVCamera.podspec */, | ||
| 218 | + 4D1880C33D23BC4C4F02DDC2 /* README.md */, | ||
| 219 | + 774EA667BA71566BA72B8A5C /* LICENSE */, | ||
| 220 | + ); | ||
| 221 | + name = "Podspec Metadata"; | ||
| 222 | + sourceTree = "<group>"; | ||
| 223 | + }; | ||
| 224 | +/* End PBXGroup section */ | ||
| 225 | + | ||
| 226 | +/* Begin PBXNativeTarget section */ | ||
| 227 | + 6003F589195388D20070C39A /* CNLiveAVCamera_Example */ = { | ||
| 228 | + isa = PBXNativeTarget; | ||
| 229 | + buildConfigurationList = 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "CNLiveAVCamera_Example" */; | ||
| 230 | + buildPhases = ( | ||
| 231 | + 8B8C0C69B53DCA1342E71BF1 /* [CP] Check Pods Manifest.lock */, | ||
| 232 | + 6003F586195388D20070C39A /* Sources */, | ||
| 233 | + 6003F587195388D20070C39A /* Frameworks */, | ||
| 234 | + 6003F588195388D20070C39A /* Resources */, | ||
| 235 | + A36B99FFC3152DBE818FC863 /* [CP] Embed Pods Frameworks */, | ||
| 236 | + 399A590F21BA760F0092926F /* Embed Frameworks */, | ||
| 237 | + ); | ||
| 238 | + buildRules = ( | ||
| 239 | + ); | ||
| 240 | + dependencies = ( | ||
| 241 | + ); | ||
| 242 | + name = CNLiveAVCamera_Example; | ||
| 243 | + productName = CNLiveAVCamera; | ||
| 244 | + productReference = 6003F58A195388D20070C39A /* CNLiveAVCamera_Example.app */; | ||
| 245 | + productType = "com.apple.product-type.application"; | ||
| 246 | + }; | ||
| 247 | + 6003F5AD195388D20070C39A /* CNLiveAVCamera_Tests */ = { | ||
| 248 | + isa = PBXNativeTarget; | ||
| 249 | + buildConfigurationList = 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "CNLiveAVCamera_Tests" */; | ||
| 250 | + buildPhases = ( | ||
| 251 | + FCD1D0667405CC357BEAB4F9 /* [CP] Check Pods Manifest.lock */, | ||
| 252 | + 6003F5AA195388D20070C39A /* Sources */, | ||
| 253 | + 6003F5AB195388D20070C39A /* Frameworks */, | ||
| 254 | + 6003F5AC195388D20070C39A /* Resources */, | ||
| 255 | + AD0B9B37F4F185810B0589E2 /* [CP] Embed Pods Frameworks */, | ||
| 256 | + ); | ||
| 257 | + buildRules = ( | ||
| 258 | + ); | ||
| 259 | + dependencies = ( | ||
| 260 | + 6003F5B4195388D20070C39A /* PBXTargetDependency */, | ||
| 261 | + ); | ||
| 262 | + name = CNLiveAVCamera_Tests; | ||
| 263 | + productName = CNLiveAVCameraTests; | ||
| 264 | + productReference = 6003F5AE195388D20070C39A /* CNLiveAVCamera_Tests.xctest */; | ||
| 265 | + productType = "com.apple.product-type.bundle.unit-test"; | ||
| 266 | + }; | ||
| 267 | +/* End PBXNativeTarget section */ | ||
| 268 | + | ||
| 269 | +/* Begin PBXProject section */ | ||
| 270 | + 6003F582195388D10070C39A /* Project object */ = { | ||
| 271 | + isa = PBXProject; | ||
| 272 | + attributes = { | ||
| 273 | + CLASSPREFIX = CNLive; | ||
| 274 | + LastUpgradeCheck = 0720; | ||
| 275 | + ORGANIZATIONNAME = jyyjkt; | ||
| 276 | + TargetAttributes = { | ||
| 277 | + 6003F589195388D20070C39A = { | ||
| 278 | + DevelopmentTeam = 94X49GG3ZW; | ||
| 279 | + }; | ||
| 280 | + 6003F5AD195388D20070C39A = { | ||
| 281 | + TestTargetID = 6003F589195388D20070C39A; | ||
| 282 | + }; | ||
| 283 | + }; | ||
| 284 | + }; | ||
| 285 | + buildConfigurationList = 6003F585195388D10070C39A /* Build configuration list for PBXProject "CNLiveAVCamera" */; | ||
| 286 | + compatibilityVersion = "Xcode 3.2"; | ||
| 287 | + developmentRegion = English; | ||
| 288 | + hasScannedForEncodings = 0; | ||
| 289 | + knownRegions = ( | ||
| 290 | + en, | ||
| 291 | + Base, | ||
| 292 | + ); | ||
| 293 | + mainGroup = 6003F581195388D10070C39A; | ||
| 294 | + productRefGroup = 6003F58B195388D20070C39A /* Products */; | ||
| 295 | + projectDirPath = ""; | ||
| 296 | + projectRoot = ""; | ||
| 297 | + targets = ( | ||
| 298 | + 6003F589195388D20070C39A /* CNLiveAVCamera_Example */, | ||
| 299 | + 6003F5AD195388D20070C39A /* CNLiveAVCamera_Tests */, | ||
| 300 | + ); | ||
| 301 | + }; | ||
| 302 | +/* End PBXProject section */ | ||
| 303 | + | ||
| 304 | +/* Begin PBXResourcesBuildPhase section */ | ||
| 305 | + 6003F588195388D20070C39A /* Resources */ = { | ||
| 306 | + isa = PBXResourcesBuildPhase; | ||
| 307 | + buildActionMask = 2147483647; | ||
| 308 | + files = ( | ||
| 309 | + 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */, | ||
| 310 | + 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */, | ||
| 311 | + 6003F5A9195388D20070C39A /* Images.xcassets in Resources */, | ||
| 312 | + 6003F598195388D20070C39A /* InfoPlist.strings in Resources */, | ||
| 313 | + ); | ||
| 314 | + runOnlyForDeploymentPostprocessing = 0; | ||
| 315 | + }; | ||
| 316 | + 6003F5AC195388D20070C39A /* Resources */ = { | ||
| 317 | + isa = PBXResourcesBuildPhase; | ||
| 318 | + buildActionMask = 2147483647; | ||
| 319 | + files = ( | ||
| 320 | + 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */, | ||
| 321 | + ); | ||
| 322 | + runOnlyForDeploymentPostprocessing = 0; | ||
| 323 | + }; | ||
| 324 | +/* End PBXResourcesBuildPhase section */ | ||
| 325 | + | ||
| 326 | +/* Begin PBXShellScriptBuildPhase section */ | ||
| 327 | + 8B8C0C69B53DCA1342E71BF1 /* [CP] Check Pods Manifest.lock */ = { | ||
| 328 | + isa = PBXShellScriptBuildPhase; | ||
| 329 | + buildActionMask = 2147483647; | ||
| 330 | + files = ( | ||
| 331 | + ); | ||
| 332 | + inputFileListPaths = ( | ||
| 333 | + ); | ||
| 334 | + inputPaths = ( | ||
| 335 | + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", | ||
| 336 | + "${PODS_ROOT}/Manifest.lock", | ||
| 337 | + ); | ||
| 338 | + name = "[CP] Check Pods Manifest.lock"; | ||
| 339 | + outputFileListPaths = ( | ||
| 340 | + ); | ||
| 341 | + outputPaths = ( | ||
| 342 | + "$(DERIVED_FILE_DIR)/Pods-CNLiveAVCamera_Example-checkManifestLockResult.txt", | ||
| 343 | + ); | ||
| 344 | + runOnlyForDeploymentPostprocessing = 0; | ||
| 345 | + shellPath = /bin/sh; | ||
| 346 | + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; | ||
| 347 | + showEnvVarsInLog = 0; | ||
| 348 | + }; | ||
| 349 | + A36B99FFC3152DBE818FC863 /* [CP] Embed Pods Frameworks */ = { | ||
| 350 | + isa = PBXShellScriptBuildPhase; | ||
| 351 | + buildActionMask = 2147483647; | ||
| 352 | + files = ( | ||
| 353 | + ); | ||
| 354 | + inputFileListPaths = ( | ||
| 355 | + ); | ||
| 356 | + inputPaths = ( | ||
| 357 | + "${SRCROOT}/Pods/Target Support Files/Pods-CNLiveAVCamera_Example/Pods-CNLiveAVCamera_Example-frameworks.sh", | ||
| 358 | + "${BUILT_PRODUCTS_DIR}/CNLiveAVCamera/CNLiveAVCamera.framework", | ||
| 359 | + "${BUILT_PRODUCTS_DIR}/Masonry/Masonry.framework", | ||
| 360 | + ); | ||
| 361 | + name = "[CP] Embed Pods Frameworks"; | ||
| 362 | + outputFileListPaths = ( | ||
| 363 | + ); | ||
| 364 | + outputPaths = ( | ||
| 365 | + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CNLiveAVCamera.framework", | ||
| 366 | + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Masonry.framework", | ||
| 367 | + ); | ||
| 368 | + runOnlyForDeploymentPostprocessing = 0; | ||
| 369 | + shellPath = /bin/sh; | ||
| 370 | + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CNLiveAVCamera_Example/Pods-CNLiveAVCamera_Example-frameworks.sh\"\n"; | ||
| 371 | + showEnvVarsInLog = 0; | ||
| 372 | + }; | ||
| 373 | + AD0B9B37F4F185810B0589E2 /* [CP] Embed Pods Frameworks */ = { | ||
| 374 | + isa = PBXShellScriptBuildPhase; | ||
| 375 | + buildActionMask = 2147483647; | ||
| 376 | + files = ( | ||
| 377 | + ); | ||
| 378 | + inputFileListPaths = ( | ||
| 379 | + ); | ||
| 380 | + inputPaths = ( | ||
| 381 | + "${SRCROOT}/Pods/Target Support Files/Pods-CNLiveAVCamera_Tests/Pods-CNLiveAVCamera_Tests-frameworks.sh", | ||
| 382 | + "${BUILT_PRODUCTS_DIR}/Masonry/Masonry.framework", | ||
| 383 | + ); | ||
| 384 | + name = "[CP] Embed Pods Frameworks"; | ||
| 385 | + outputFileListPaths = ( | ||
| 386 | + ); | ||
| 387 | + outputPaths = ( | ||
| 388 | + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Masonry.framework", | ||
| 389 | + ); | ||
| 390 | + runOnlyForDeploymentPostprocessing = 0; | ||
| 391 | + shellPath = /bin/sh; | ||
| 392 | + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CNLiveAVCamera_Tests/Pods-CNLiveAVCamera_Tests-frameworks.sh\"\n"; | ||
| 393 | + showEnvVarsInLog = 0; | ||
| 394 | + }; | ||
| 395 | + FCD1D0667405CC357BEAB4F9 /* [CP] Check Pods Manifest.lock */ = { | ||
| 396 | + isa = PBXShellScriptBuildPhase; | ||
| 397 | + buildActionMask = 2147483647; | ||
| 398 | + files = ( | ||
| 399 | + ); | ||
| 400 | + inputFileListPaths = ( | ||
| 401 | + ); | ||
| 402 | + inputPaths = ( | ||
| 403 | + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", | ||
| 404 | + "${PODS_ROOT}/Manifest.lock", | ||
| 405 | + ); | ||
| 406 | + name = "[CP] Check Pods Manifest.lock"; | ||
| 407 | + outputFileListPaths = ( | ||
| 408 | + ); | ||
| 409 | + outputPaths = ( | ||
| 410 | + "$(DERIVED_FILE_DIR)/Pods-CNLiveAVCamera_Tests-checkManifestLockResult.txt", | ||
| 411 | + ); | ||
| 412 | + runOnlyForDeploymentPostprocessing = 0; | ||
| 413 | + shellPath = /bin/sh; | ||
| 414 | + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; | ||
| 415 | + showEnvVarsInLog = 0; | ||
| 416 | + }; | ||
| 417 | +/* End PBXShellScriptBuildPhase section */ | ||
| 418 | + | ||
| 419 | +/* Begin PBXSourcesBuildPhase section */ | ||
| 420 | + 6003F586195388D20070C39A /* Sources */ = { | ||
| 421 | + isa = PBXSourcesBuildPhase; | ||
| 422 | + buildActionMask = 2147483647; | ||
| 423 | + files = ( | ||
| 424 | + 6003F59E195388D20070C39A /* CNLiveAppDelegate.m in Sources */, | ||
| 425 | + 6003F5A7195388D20070C39A /* CNLiveViewController.m in Sources */, | ||
| 426 | + 6003F59A195388D20070C39A /* main.m in Sources */, | ||
| 427 | + ); | ||
| 428 | + runOnlyForDeploymentPostprocessing = 0; | ||
| 429 | + }; | ||
| 430 | + 6003F5AA195388D20070C39A /* Sources */ = { | ||
| 431 | + isa = PBXSourcesBuildPhase; | ||
| 432 | + buildActionMask = 2147483647; | ||
| 433 | + files = ( | ||
| 434 | + 6003F5BC195388D20070C39A /* Tests.m in Sources */, | ||
| 435 | + ); | ||
| 436 | + runOnlyForDeploymentPostprocessing = 0; | ||
| 437 | + }; | ||
| 438 | +/* End PBXSourcesBuildPhase section */ | ||
| 439 | + | ||
| 440 | +/* Begin PBXTargetDependency section */ | ||
| 441 | + 6003F5B4195388D20070C39A /* PBXTargetDependency */ = { | ||
| 442 | + isa = PBXTargetDependency; | ||
| 443 | + target = 6003F589195388D20070C39A /* CNLiveAVCamera_Example */; | ||
| 444 | + targetProxy = 6003F5B3195388D20070C39A /* PBXContainerItemProxy */; | ||
| 445 | + }; | ||
| 446 | +/* End PBXTargetDependency section */ | ||
| 447 | + | ||
| 448 | +/* Begin PBXVariantGroup section */ | ||
| 449 | + 6003F596195388D20070C39A /* InfoPlist.strings */ = { | ||
| 450 | + isa = PBXVariantGroup; | ||
| 451 | + children = ( | ||
| 452 | + 6003F597195388D20070C39A /* en */, | ||
| 453 | + ); | ||
| 454 | + name = InfoPlist.strings; | ||
| 455 | + sourceTree = "<group>"; | ||
| 456 | + }; | ||
| 457 | + 6003F5B8195388D20070C39A /* InfoPlist.strings */ = { | ||
| 458 | + isa = PBXVariantGroup; | ||
| 459 | + children = ( | ||
| 460 | + 6003F5B9195388D20070C39A /* en */, | ||
| 461 | + ); | ||
| 462 | + name = InfoPlist.strings; | ||
| 463 | + sourceTree = "<group>"; | ||
| 464 | + }; | ||
| 465 | + 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */ = { | ||
| 466 | + isa = PBXVariantGroup; | ||
| 467 | + children = ( | ||
| 468 | + 71719F9E1E33DC2100824A3D /* Base */, | ||
| 469 | + ); | ||
| 470 | + name = LaunchScreen.storyboard; | ||
| 471 | + sourceTree = "<group>"; | ||
| 472 | + }; | ||
| 473 | +/* End PBXVariantGroup section */ | ||
| 474 | + | ||
| 475 | +/* Begin XCBuildConfiguration section */ | ||
| 476 | + 6003F5BD195388D20070C39A /* Debug */ = { | ||
| 477 | + isa = XCBuildConfiguration; | ||
| 478 | + buildSettings = { | ||
| 479 | + ALWAYS_SEARCH_USER_PATHS = NO; | ||
| 480 | + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; | ||
| 481 | + CLANG_CXX_LIBRARY = "libc++"; | ||
| 482 | + CLANG_ENABLE_MODULES = YES; | ||
| 483 | + CLANG_ENABLE_OBJC_ARC = YES; | ||
| 484 | + CLANG_WARN_BOOL_CONVERSION = YES; | ||
| 485 | + CLANG_WARN_CONSTANT_CONVERSION = YES; | ||
| 486 | + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; | ||
| 487 | + CLANG_WARN_EMPTY_BODY = YES; | ||
| 488 | + CLANG_WARN_ENUM_CONVERSION = YES; | ||
| 489 | + CLANG_WARN_INT_CONVERSION = YES; | ||
| 490 | + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; | ||
| 491 | + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | ||
| 492 | + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; | ||
| 493 | + COPY_PHASE_STRIP = NO; | ||
| 494 | + ENABLE_TESTABILITY = YES; | ||
| 495 | + GCC_C_LANGUAGE_STANDARD = gnu99; | ||
| 496 | + GCC_DYNAMIC_NO_PIC = NO; | ||
| 497 | + GCC_OPTIMIZATION_LEVEL = 0; | ||
| 498 | + GCC_PREPROCESSOR_DEFINITIONS = ( | ||
| 499 | + "DEBUG=1", | ||
| 500 | + "$(inherited)", | ||
| 501 | + ); | ||
| 502 | + GCC_SYMBOLS_PRIVATE_EXTERN = NO; | ||
| 503 | + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; | ||
| 504 | + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; | ||
| 505 | + GCC_WARN_UNDECLARED_SELECTOR = YES; | ||
| 506 | + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | ||
| 507 | + GCC_WARN_UNUSED_FUNCTION = YES; | ||
| 508 | + GCC_WARN_UNUSED_VARIABLE = YES; | ||
| 509 | + IPHONEOS_DEPLOYMENT_TARGET = 9.3; | ||
| 510 | + ONLY_ACTIVE_ARCH = YES; | ||
| 511 | + SDKROOT = iphoneos; | ||
| 512 | + TARGETED_DEVICE_FAMILY = "1,2"; | ||
| 513 | + }; | ||
| 514 | + name = Debug; | ||
| 515 | + }; | ||
| 516 | + 6003F5BE195388D20070C39A /* Release */ = { | ||
| 517 | + isa = XCBuildConfiguration; | ||
| 518 | + buildSettings = { | ||
| 519 | + ALWAYS_SEARCH_USER_PATHS = NO; | ||
| 520 | + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; | ||
| 521 | + CLANG_CXX_LIBRARY = "libc++"; | ||
| 522 | + CLANG_ENABLE_MODULES = YES; | ||
| 523 | + CLANG_ENABLE_OBJC_ARC = YES; | ||
| 524 | + CLANG_WARN_BOOL_CONVERSION = YES; | ||
| 525 | + CLANG_WARN_CONSTANT_CONVERSION = YES; | ||
| 526 | + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; | ||
| 527 | + CLANG_WARN_EMPTY_BODY = YES; | ||
| 528 | + CLANG_WARN_ENUM_CONVERSION = YES; | ||
| 529 | + CLANG_WARN_INT_CONVERSION = YES; | ||
| 530 | + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; | ||
| 531 | + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | ||
| 532 | + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; | ||
| 533 | + COPY_PHASE_STRIP = YES; | ||
| 534 | + ENABLE_NS_ASSERTIONS = NO; | ||
| 535 | + GCC_C_LANGUAGE_STANDARD = gnu99; | ||
| 536 | + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; | ||
| 537 | + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; | ||
| 538 | + GCC_WARN_UNDECLARED_SELECTOR = YES; | ||
| 539 | + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | ||
| 540 | + GCC_WARN_UNUSED_FUNCTION = YES; | ||
| 541 | + GCC_WARN_UNUSED_VARIABLE = YES; | ||
| 542 | + IPHONEOS_DEPLOYMENT_TARGET = 9.3; | ||
| 543 | + SDKROOT = iphoneos; | ||
| 544 | + TARGETED_DEVICE_FAMILY = "1,2"; | ||
| 545 | + VALIDATE_PRODUCT = YES; | ||
| 546 | + }; | ||
| 547 | + name = Release; | ||
| 548 | + }; | ||
| 549 | + 6003F5C0195388D20070C39A /* Debug */ = { | ||
| 550 | + isa = XCBuildConfiguration; | ||
| 551 | + baseConfigurationReference = E61E5CDE06F66F688BB4ADF8 /* Pods-CNLiveAVCamera_Example.debug.xcconfig */; | ||
| 552 | + buildSettings = { | ||
| 553 | + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ||
| 554 | + CODE_SIGN_IDENTITY = "iPhone Developer"; | ||
| 555 | + DEVELOPMENT_TEAM = 94X49GG3ZW; | ||
| 556 | + GCC_PRECOMPILE_PREFIX_HEADER = YES; | ||
| 557 | + GCC_PREFIX_HEADER = "CNLiveAVCamera/CNLiveAVCamera-Prefix.pch"; | ||
| 558 | + INFOPLIST_FILE = "CNLiveAVCamera/CNLiveAVCamera-Info.plist"; | ||
| 559 | + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; | ||
| 560 | + MACH_O_TYPE = mh_execute; | ||
| 561 | + MODULE_NAME = ExampleApp; | ||
| 562 | + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; | ||
| 563 | + PRODUCT_NAME = "$(TARGET_NAME)"; | ||
| 564 | + WRAPPER_EXTENSION = app; | ||
| 565 | + }; | ||
| 566 | + name = Debug; | ||
| 567 | + }; | ||
| 568 | + 6003F5C1195388D20070C39A /* Release */ = { | ||
| 569 | + isa = XCBuildConfiguration; | ||
| 570 | + baseConfigurationReference = 232FB65DACAACEB088650B26 /* Pods-CNLiveAVCamera_Example.release.xcconfig */; | ||
| 571 | + buildSettings = { | ||
| 572 | + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ||
| 573 | + CODE_SIGN_IDENTITY = "iPhone Developer"; | ||
| 574 | + DEVELOPMENT_TEAM = 94X49GG3ZW; | ||
| 575 | + GCC_PRECOMPILE_PREFIX_HEADER = YES; | ||
| 576 | + GCC_PREFIX_HEADER = "CNLiveAVCamera/CNLiveAVCamera-Prefix.pch"; | ||
| 577 | + INFOPLIST_FILE = "CNLiveAVCamera/CNLiveAVCamera-Info.plist"; | ||
| 578 | + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; | ||
| 579 | + MACH_O_TYPE = mh_execute; | ||
| 580 | + MODULE_NAME = ExampleApp; | ||
| 581 | + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; | ||
| 582 | + PRODUCT_NAME = "$(TARGET_NAME)"; | ||
| 583 | + WRAPPER_EXTENSION = app; | ||
| 584 | + }; | ||
| 585 | + name = Release; | ||
| 586 | + }; | ||
| 587 | + 6003F5C3195388D20070C39A /* Debug */ = { | ||
| 588 | + isa = XCBuildConfiguration; | ||
| 589 | + baseConfigurationReference = 4F6BCE6DD63FA804E1500FF2 /* Pods-CNLiveAVCamera_Tests.debug.xcconfig */; | ||
| 590 | + buildSettings = { | ||
| 591 | + BUNDLE_LOADER = "$(TEST_HOST)"; | ||
| 592 | + FRAMEWORK_SEARCH_PATHS = ( | ||
| 593 | + "$(SDKROOT)/Developer/Library/Frameworks", | ||
| 594 | + "$(inherited)", | ||
| 595 | + "$(DEVELOPER_FRAMEWORKS_DIR)", | ||
| 596 | + ); | ||
| 597 | + GCC_PRECOMPILE_PREFIX_HEADER = YES; | ||
| 598 | + GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; | ||
| 599 | + GCC_PREPROCESSOR_DEFINITIONS = ( | ||
| 600 | + "DEBUG=1", | ||
| 601 | + "$(inherited)", | ||
| 602 | + ); | ||
| 603 | + INFOPLIST_FILE = "Tests/Tests-Info.plist"; | ||
| 604 | + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; | ||
| 605 | + PRODUCT_NAME = "$(TARGET_NAME)"; | ||
| 606 | + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CNLiveAVCamera_Example.app/CNLiveAVCamera_Example"; | ||
| 607 | + WRAPPER_EXTENSION = xctest; | ||
| 608 | + }; | ||
| 609 | + name = Debug; | ||
| 610 | + }; | ||
| 611 | + 6003F5C4195388D20070C39A /* Release */ = { | ||
| 612 | + isa = XCBuildConfiguration; | ||
| 613 | + baseConfigurationReference = D41556E2EE76E873ED9847B4 /* Pods-CNLiveAVCamera_Tests.release.xcconfig */; | ||
| 614 | + buildSettings = { | ||
| 615 | + BUNDLE_LOADER = "$(TEST_HOST)"; | ||
| 616 | + FRAMEWORK_SEARCH_PATHS = ( | ||
| 617 | + "$(SDKROOT)/Developer/Library/Frameworks", | ||
| 618 | + "$(inherited)", | ||
| 619 | + "$(DEVELOPER_FRAMEWORKS_DIR)", | ||
| 620 | + ); | ||
| 621 | + GCC_PRECOMPILE_PREFIX_HEADER = YES; | ||
| 622 | + GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; | ||
| 623 | + INFOPLIST_FILE = "Tests/Tests-Info.plist"; | ||
| 624 | + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; | ||
| 625 | + PRODUCT_NAME = "$(TARGET_NAME)"; | ||
| 626 | + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CNLiveAVCamera_Example.app/CNLiveAVCamera_Example"; | ||
| 627 | + WRAPPER_EXTENSION = xctest; | ||
| 628 | + }; | ||
| 629 | + name = Release; | ||
| 630 | + }; | ||
| 631 | +/* End XCBuildConfiguration section */ | ||
| 632 | + | ||
| 633 | +/* Begin XCConfigurationList section */ | ||
| 634 | + 6003F585195388D10070C39A /* Build configuration list for PBXProject "CNLiveAVCamera" */ = { | ||
| 635 | + isa = XCConfigurationList; | ||
| 636 | + buildConfigurations = ( | ||
| 637 | + 6003F5BD195388D20070C39A /* Debug */, | ||
| 638 | + 6003F5BE195388D20070C39A /* Release */, | ||
| 639 | + ); | ||
| 640 | + defaultConfigurationIsVisible = 0; | ||
| 641 | + defaultConfigurationName = Release; | ||
| 642 | + }; | ||
| 643 | + 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "CNLiveAVCamera_Example" */ = { | ||
| 644 | + isa = XCConfigurationList; | ||
| 645 | + buildConfigurations = ( | ||
| 646 | + 6003F5C0195388D20070C39A /* Debug */, | ||
| 647 | + 6003F5C1195388D20070C39A /* Release */, | ||
| 648 | + ); | ||
| 649 | + defaultConfigurationIsVisible = 0; | ||
| 650 | + defaultConfigurationName = Release; | ||
| 651 | + }; | ||
| 652 | + 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "CNLiveAVCamera_Tests" */ = { | ||
| 653 | + isa = XCConfigurationList; | ||
| 654 | + buildConfigurations = ( | ||
| 655 | + 6003F5C3195388D20070C39A /* Debug */, | ||
| 656 | + 6003F5C4195388D20070C39A /* Release */, | ||
| 657 | + ); | ||
| 658 | + defaultConfigurationIsVisible = 0; | ||
| 659 | + defaultConfigurationName = Release; | ||
| 660 | + }; | ||
| 661 | +/* End XCConfigurationList section */ | ||
| 662 | + }; | ||
| 663 | + rootObject = 6003F582195388D10070C39A /* Project object */; | ||
| 664 | +} |
Example/CNLiveAVCamera.xcodeproj/xcshareddata/xcschemes/CNLiveAVCamera-Example.xcscheme
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<Scheme | ||
| 3 | + LastUpgradeVersion = "0720" | ||
| 4 | + version = "1.3"> | ||
| 5 | + <BuildAction | ||
| 6 | + parallelizeBuildables = "YES" | ||
| 7 | + buildImplicitDependencies = "YES"> | ||
| 8 | + <BuildActionEntries> | ||
| 9 | + <BuildActionEntry | ||
| 10 | + buildForTesting = "YES" | ||
| 11 | + buildForRunning = "YES" | ||
| 12 | + buildForProfiling = "YES" | ||
| 13 | + buildForArchiving = "YES" | ||
| 14 | + buildForAnalyzing = "YES"> | ||
| 15 | + <BuildableReference | ||
| 16 | + BuildableIdentifier = "primary" | ||
| 17 | + BlueprintIdentifier = "6003F589195388D20070C39A" | ||
| 18 | + BuildableName = "CNLiveAVCamera_Example.app" | ||
| 19 | + BlueprintName = "CNLiveAVCamera_Example" | ||
| 20 | + ReferencedContainer = "container:CNLiveAVCamera.xcodeproj"> | ||
| 21 | + </BuildableReference> | ||
| 22 | + </BuildActionEntry> | ||
| 23 | + </BuildActionEntries> | ||
| 24 | + </BuildAction> | ||
| 25 | + <TestAction | ||
| 26 | + buildConfiguration = "Debug" | ||
| 27 | + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" | ||
| 28 | + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" | ||
| 29 | + shouldUseLaunchSchemeArgsEnv = "YES"> | ||
| 30 | + <Testables> | ||
| 31 | + <TestableReference | ||
| 32 | + skipped = "NO"> | ||
| 33 | + <BuildableReference | ||
| 34 | + BuildableIdentifier = "primary" | ||
| 35 | + BlueprintIdentifier = "6003F5AD195388D20070C39A" | ||
| 36 | + BuildableName = "CNLiveAVCamera_Tests.xctest" | ||
| 37 | + BlueprintName = "CNLiveAVCamera_Tests" | ||
| 38 | + ReferencedContainer = "container:CNLiveAVCamera.xcodeproj"> | ||
| 39 | + </BuildableReference> | ||
| 40 | + </TestableReference> | ||
| 41 | + </Testables> | ||
| 42 | + <MacroExpansion> | ||
| 43 | + <BuildableReference | ||
| 44 | + BuildableIdentifier = "primary" | ||
| 45 | + BlueprintIdentifier = "6003F589195388D20070C39A" | ||
| 46 | + BuildableName = "CNLiveAVCamera_Example.app" | ||
| 47 | + BlueprintName = "CNLiveAVCamera_Example" | ||
| 48 | + ReferencedContainer = "container:CNLiveAVCamera.xcodeproj"> | ||
| 49 | + </BuildableReference> | ||
| 50 | + </MacroExpansion> | ||
| 51 | + <AdditionalOptions> | ||
| 52 | + </AdditionalOptions> | ||
| 53 | + </TestAction> | ||
| 54 | + <LaunchAction | ||
| 55 | + buildConfiguration = "Debug" | ||
| 56 | + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" | ||
| 57 | + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" | ||
| 58 | + launchStyle = "0" | ||
| 59 | + useCustomWorkingDirectory = "NO" | ||
| 60 | + ignoresPersistentStateOnLaunch = "NO" | ||
| 61 | + debugDocumentVersioning = "YES" | ||
| 62 | + debugServiceExtension = "internal" | ||
| 63 | + allowLocationSimulation = "YES"> | ||
| 64 | + <BuildableProductRunnable | ||
| 65 | + runnableDebuggingMode = "0"> | ||
| 66 | + <BuildableReference | ||
| 67 | + BuildableIdentifier = "primary" | ||
| 68 | + BlueprintIdentifier = "6003F589195388D20070C39A" | ||
| 69 | + BuildableName = "CNLiveAVCamera_Example.app" | ||
| 70 | + BlueprintName = "CNLiveAVCamera_Example" | ||
| 71 | + ReferencedContainer = "container:CNLiveAVCamera.xcodeproj"> | ||
| 72 | + </BuildableReference> | ||
| 73 | + </BuildableProductRunnable> | ||
| 74 | + <AdditionalOptions> | ||
| 75 | + </AdditionalOptions> | ||
| 76 | + </LaunchAction> | ||
| 77 | + <ProfileAction | ||
| 78 | + buildConfiguration = "Release" | ||
| 79 | + shouldUseLaunchSchemeArgsEnv = "YES" | ||
| 80 | + savedToolIdentifier = "" | ||
| 81 | + useCustomWorkingDirectory = "NO" | ||
| 82 | + debugDocumentVersioning = "YES"> | ||
| 83 | + <BuildableProductRunnable | ||
| 84 | + runnableDebuggingMode = "0"> | ||
| 85 | + <BuildableReference | ||
| 86 | + BuildableIdentifier = "primary" | ||
| 87 | + BlueprintIdentifier = "6003F589195388D20070C39A" | ||
| 88 | + BuildableName = "CNLiveAVCamera_Example.app" | ||
| 89 | + BlueprintName = "CNLiveAVCamera_Example" | ||
| 90 | + ReferencedContainer = "container:CNLiveAVCamera.xcodeproj"> | ||
| 91 | + </BuildableReference> | ||
| 92 | + </BuildableProductRunnable> | ||
| 93 | + </ProfileAction> | ||
| 94 | + <AnalyzeAction | ||
| 95 | + buildConfiguration = "Debug"> | ||
| 96 | + </AnalyzeAction> | ||
| 97 | + <ArchiveAction | ||
| 98 | + buildConfiguration = "Release" | ||
| 99 | + revealArchiveInOrganizer = "YES"> | ||
| 100 | + </ArchiveAction> | ||
| 101 | +</Scheme> |
Example/CNLiveAVCamera/Base.lproj/LaunchScreen.storyboard
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM"> | ||
| 3 | + <device id="retina4_7" orientation="portrait"> | ||
| 4 | + <adaptation id="fullscreen"/> | ||
| 5 | + </device> | ||
| 6 | + <dependencies> | ||
| 7 | + <deployment identifier="iOS"/> | ||
| 8 | + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/> | ||
| 9 | + <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> | ||
| 10 | + </dependencies> | ||
| 11 | + <scenes> | ||
| 12 | + <!--View Controller--> | ||
| 13 | + <scene sceneID="EHf-IW-A2E"> | ||
| 14 | + <objects> | ||
| 15 | + <viewController id="01J-lp-oVM" sceneMemberID="viewController"> | ||
| 16 | + <layoutGuides> | ||
| 17 | + <viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/> | ||
| 18 | + <viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/> | ||
| 19 | + </layoutGuides> | ||
| 20 | + <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> | ||
| 21 | + <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> | ||
| 22 | + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | ||
| 23 | + <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | ||
| 24 | + </view> | ||
| 25 | + </viewController> | ||
| 26 | + <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> | ||
| 27 | + </objects> | ||
| 28 | + <point key="canvasLocation" x="53" y="375"/> | ||
| 29 | + </scene> | ||
| 30 | + </scenes> | ||
| 31 | +</document> |
Example/CNLiveAVCamera/Base.lproj/Main.storyboard
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="whP-gf-Uak"> | ||
| 3 | + <device id="retina4_7" orientation="portrait"> | ||
| 4 | + <adaptation id="fullscreen"/> | ||
| 5 | + </device> | ||
| 6 | + <dependencies> | ||
| 7 | + <deployment identifier="iOS"/> | ||
| 8 | + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/> | ||
| 9 | + <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> | ||
| 10 | + </dependencies> | ||
| 11 | + <scenes> | ||
| 12 | + <!--View Controller--> | ||
| 13 | + <scene sceneID="wQg-tq-qST"> | ||
| 14 | + <objects> | ||
| 15 | + <viewController id="whP-gf-Uak" customClass="CNLiveViewController" sceneMemberID="viewController"> | ||
| 16 | + <layoutGuides> | ||
| 17 | + <viewControllerLayoutGuide type="top" id="uEw-UM-LJ8"/> | ||
| 18 | + <viewControllerLayoutGuide type="bottom" id="Mvr-aV-6Um"/> | ||
| 19 | + </layoutGuides> | ||
| 20 | + <view key="view" contentMode="scaleToFill" id="TpU-gO-2f1"> | ||
| 21 | + <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> | ||
| 22 | + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | ||
| 23 | + <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | ||
| 24 | + </view> | ||
| 25 | + </viewController> | ||
| 26 | + <placeholder placeholderIdentifier="IBFirstResponder" id="tc2-Qw-aMS" userLabel="First Responder" sceneMemberID="firstResponder"/> | ||
| 27 | + </objects> | ||
| 28 | + <point key="canvasLocation" x="305" y="433"/> | ||
| 29 | + </scene> | ||
| 30 | + </scenes> | ||
| 31 | +</document> |
Example/CNLiveAVCamera/CNLiveAVCamera-Info.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>CFBundleDevelopmentRegion</key> | ||
| 6 | + <string>en</string> | ||
| 7 | + <key>CFBundleDisplayName</key> | ||
| 8 | + <string>${PRODUCT_NAME}</string> | ||
| 9 | + <key>CFBundleExecutable</key> | ||
| 10 | + <string>${EXECUTABLE_NAME}</string> | ||
| 11 | + <key>CFBundleIdentifier</key> | ||
| 12 | + <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | ||
| 13 | + <key>CFBundleInfoDictionaryVersion</key> | ||
| 14 | + <string>6.0</string> | ||
| 15 | + <key>CFBundleName</key> | ||
| 16 | + <string>${PRODUCT_NAME}</string> | ||
| 17 | + <key>CFBundlePackageType</key> | ||
| 18 | + <string>APPL</string> | ||
| 19 | + <key>CFBundleShortVersionString</key> | ||
| 20 | + <string>1.0</string> | ||
| 21 | + <key>CFBundleSignature</key> | ||
| 22 | + <string>????</string> | ||
| 23 | + <key>CFBundleVersion</key> | ||
| 24 | + <string>1.0</string> | ||
| 25 | + <key>LSRequiresIPhoneOS</key> | ||
| 26 | + <true/> | ||
| 27 | + <key>UILaunchStoryboardName</key> | ||
| 28 | + <string>LaunchScreen</string> | ||
| 29 | + <key>UIMainStoryboardFile</key> | ||
| 30 | + <string>Main</string> | ||
| 31 | + <key>UIRequiredDeviceCapabilities</key> | ||
| 32 | + <array> | ||
| 33 | + <string>armv7</string> | ||
| 34 | + </array> | ||
| 35 | + <key>UISupportedInterfaceOrientations</key> | ||
| 36 | + <array> | ||
| 37 | + <string>UIInterfaceOrientationPortrait</string> | ||
| 38 | + <string>UIInterfaceOrientationLandscapeLeft</string> | ||
| 39 | + <string>UIInterfaceOrientationLandscapeRight</string> | ||
| 40 | + </array> | ||
| 41 | + <key>UISupportedInterfaceOrientations~ipad</key> | ||
| 42 | + <array> | ||
| 43 | + <string>UIInterfaceOrientationPortrait</string> | ||
| 44 | + <string>UIInterfaceOrientationPortraitUpsideDown</string> | ||
| 45 | + <string>UIInterfaceOrientationLandscapeLeft</string> | ||
| 46 | + <string>UIInterfaceOrientationLandscapeRight</string> | ||
| 47 | + </array> | ||
| 48 | + | ||
| 49 | + <key>NSCameraUsageDescription</key> | ||
| 50 | + <string>需要允许开启相机才能在发布生活圈或聊天时使用录制视频和拍照功能</string> | ||
| 51 | + <key>NSContactsUsageDescription</key> | ||
| 52 | + <string>需要允许访问通讯录才能获取手机联系人中的网++用户</string> | ||
| 53 | + <key>NSLocationAlwaysAndWhenInUseUsageDescription</key> | ||
| 54 | + <string>需要允许使用您的位置信息才能获取您所在的城市</string> | ||
| 55 | + <key>NSLocationAlwaysUsageDescription</key> | ||
| 56 | + <string>需要允许使用您的位置信息才能获取您所在的城市</string> | ||
| 57 | + <key>NSLocationWhenInUseUsageDescription</key> | ||
| 58 | + <string>需要允许使用您的位置信息才能获取您所在的城市</string> | ||
| 59 | + <key>NSMicrophoneUsageDescription</key> | ||
| 60 | + <string>需要允许开启麦克风才能使用录制视频和语音等服务</string> | ||
| 61 | + <key>NSPhotoLibraryAddUsageDescription</key> | ||
| 62 | + <string></string> | ||
| 63 | + <key>NSPhotoLibraryUsageDescription</key> | ||
| 64 | + <string>需要允许打开相册才能访问相册照片和保存照片</string> | ||
| 65 | + | ||
| 66 | +</dict> | ||
| 67 | +</plist> |
Example/CNLiveAVCamera/CNLiveAVCamera-Prefix.pch
0 → 100644
| 1 | +// | ||
| 2 | +// Prefix header | ||
| 3 | +// | ||
| 4 | +// The contents of this file are implicitly included at the beginning of every source file. | ||
| 5 | +// | ||
| 6 | + | ||
| 7 | +#import <Availability.h> | ||
| 8 | + | ||
| 9 | +#ifndef __IPHONE_5_0 | ||
| 10 | +#warning "This project uses features only available in iOS SDK 5.0 and later." | ||
| 11 | +#endif | ||
| 12 | + | ||
| 13 | +#ifdef __OBJC__ | ||
| 14 | + @import UIKit; | ||
| 15 | + @import Foundation; | ||
| 16 | +#endif |
Example/CNLiveAVCamera/CNLiveAVCamera.h
0 → 100644
| 1 | +// | ||
| 2 | +// CNLiveAVCamera.h | ||
| 3 | +// CNLiveAVCamera | ||
| 4 | +// | ||
| 5 | +// Created by 梁星国 on 2018/12/7. | ||
| 6 | +// Copyright © 2018年 jyyjkt. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import <UIKit/UIKit.h> | ||
| 10 | + | ||
| 11 | +//! Project version number for CNLiveAVCamera. | ||
| 12 | +FOUNDATION_EXPORT double CNLiveAVCameraVersionNumber; | ||
| 13 | + | ||
| 14 | +//! Project version string for CNLiveAVCamera. | ||
| 15 | +FOUNDATION_EXPORT const unsigned char CNLiveAVCameraVersionString[]; | ||
| 16 | + | ||
| 17 | +// In this header, you should import all the public headers of your framework using statements like #import <CNLiveAVCamera/PublicHeader.h> | ||
| 18 | + | ||
| 19 | + |
Example/CNLiveAVCamera/CNLiveAppDelegate.h
0 → 100644
| 1 | +// | ||
| 2 | +// CNLiveAppDelegate.h | ||
| 3 | +// CNLiveAVCamera | ||
| 4 | +// | ||
| 5 | +// Created by jyyjkt on 12/07/2018. | ||
| 6 | +// Copyright (c) 2018 jyyjkt. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +@import UIKit; | ||
| 10 | + | ||
| 11 | +@interface CNLiveAppDelegate : UIResponder <UIApplicationDelegate> | ||
| 12 | + | ||
| 13 | +@property (strong, nonatomic) UIWindow *window; | ||
| 14 | + | ||
| 15 | +@end |
Example/CNLiveAVCamera/CNLiveAppDelegate.m
0 → 100644
| 1 | +// | ||
| 2 | +// CNLiveAppDelegate.m | ||
| 3 | +// CNLiveAVCamera | ||
| 4 | +// | ||
| 5 | +// Created by jyyjkt on 12/07/2018. | ||
| 6 | +// Copyright (c) 2018 jyyjkt. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import "CNLiveAppDelegate.h" | ||
| 10 | + | ||
| 11 | +@implementation CNLiveAppDelegate | ||
| 12 | + | ||
| 13 | +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions | ||
| 14 | +{ | ||
| 15 | + // Override point for customization after application launch. | ||
| 16 | + return YES; | ||
| 17 | +} | ||
| 18 | + | ||
| 19 | +- (void)applicationWillResignActive:(UIApplication *)application | ||
| 20 | +{ | ||
| 21 | + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. | ||
| 22 | + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. | ||
| 23 | +} | ||
| 24 | + | ||
| 25 | +- (void)applicationDidEnterBackground:(UIApplication *)application | ||
| 26 | +{ | ||
| 27 | + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. | ||
| 28 | + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. | ||
| 29 | +} | ||
| 30 | + | ||
| 31 | +- (void)applicationWillEnterForeground:(UIApplication *)application | ||
| 32 | +{ | ||
| 33 | + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +- (void)applicationDidBecomeActive:(UIApplication *)application | ||
| 37 | +{ | ||
| 38 | + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. | ||
| 39 | +} | ||
| 40 | + | ||
| 41 | +- (void)applicationWillTerminate:(UIApplication *)application | ||
| 42 | +{ | ||
| 43 | + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +@end |
Example/CNLiveAVCamera/CNLiveViewController.h
0 → 100644
Example/CNLiveAVCamera/CNLiveViewController.m
0 → 100644
| 1 | +// | ||
| 2 | +// CNLiveViewController.m | ||
| 3 | +// CNLiveAVCamera | ||
| 4 | +// | ||
| 5 | +// Created by jyyjkt on 12/07/2018. | ||
| 6 | +// Copyright (c) 2018 jyyjkt. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import "CNLiveViewController.h" | ||
| 10 | +#import <CNLiveAVCamera/XFCameraController.h> | ||
| 11 | + | ||
| 12 | +@interface CNLiveViewController () | ||
| 13 | + | ||
| 14 | +@end | ||
| 15 | + | ||
| 16 | +@implementation CNLiveViewController | ||
| 17 | + | ||
| 18 | +- (void)viewDidLoad | ||
| 19 | +{ | ||
| 20 | + [super viewDidLoad]; | ||
| 21 | + | ||
| 22 | + UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; | ||
| 23 | + button.frame = CGRectMake(100, 100, 100, 100); | ||
| 24 | + [button setTitle:@"进入" forState:UIControlStateNormal]; | ||
| 25 | + [button setBackgroundColor:[UIColor yellowColor]]; | ||
| 26 | + [button addTarget: self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside]; | ||
| 27 | + [self.view addSubview:button]; | ||
| 28 | +} | ||
| 29 | + | ||
| 30 | +- (void)buttonAction:(id)sender { | ||
| 31 | + [self takeToXFPhoto]; | ||
| 32 | + | ||
| 33 | +} | ||
| 34 | + | ||
| 35 | + | ||
| 36 | +#pragma mark - 私有方法 | ||
| 37 | +-(void)takeToXFPhoto { | ||
| 38 | + XFCameraController *cameraController = [[XFCameraController alloc]init]; | ||
| 39 | + cameraController.isCanShootVideo = YES; | ||
| 40 | + __weak XFCameraController *weakCameraController = cameraController; | ||
| 41 | + cameraController.takePhotosCompletionBlock = ^(UIImage *image, NSError *error) { | ||
| 42 | + if (!error) { | ||
| 43 | + NSLog(@"------------输出图片=%@",image); | ||
| 44 | + dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 45 | + [weakCameraController dismissViewControllerAnimated:YES completion:nil]; | ||
| 46 | + }); | ||
| 47 | + | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + }; | ||
| 51 | + //拍摄的是视频 | ||
| 52 | + cameraController.zxshootCompletionBlock = ^(PHAsset *asset, NSURL *videoUrl, CGFloat videoTimeLength, UIImage *thumbnailImage, NSError *error) { | ||
| 53 | + if (!error) { | ||
| 54 | + NSLog(@"------------输出视频路径=%@",videoUrl); | ||
| 55 | + dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 56 | + [weakCameraController dismissViewControllerAnimated:YES completion:nil]; | ||
| 57 | + }); | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + }; | ||
| 61 | + [self presentViewController:cameraController animated:YES completion:nil]; | ||
| 62 | +} | ||
| 63 | + | ||
| 64 | + | ||
| 65 | +- (void)didReceiveMemoryWarning | ||
| 66 | +{ | ||
| 67 | + [super didReceiveMemoryWarning]; | ||
| 68 | + // Dispose of any resources that can be recreated. | ||
| 69 | +} | ||
| 70 | + | ||
| 71 | +@end |
Example/CNLiveAVCamera/Images.xcassets/AppIcon.appiconset/Contents.json
0 → 100644
| 1 | +{ | ||
| 2 | + "images" : [ | ||
| 3 | + { | ||
| 4 | + "idiom" : "iphone", | ||
| 5 | + "size" : "20x20", | ||
| 6 | + "scale" : "2x" | ||
| 7 | + }, | ||
| 8 | + { | ||
| 9 | + "idiom" : "iphone", | ||
| 10 | + "size" : "20x20", | ||
| 11 | + "scale" : "3x" | ||
| 12 | + }, | ||
| 13 | + { | ||
| 14 | + "idiom" : "iphone", | ||
| 15 | + "size" : "29x29", | ||
| 16 | + "scale" : "2x" | ||
| 17 | + }, | ||
| 18 | + { | ||
| 19 | + "idiom" : "iphone", | ||
| 20 | + "size" : "29x29", | ||
| 21 | + "scale" : "3x" | ||
| 22 | + }, | ||
| 23 | + { | ||
| 24 | + "idiom" : "iphone", | ||
| 25 | + "size" : "40x40", | ||
| 26 | + "scale" : "2x" | ||
| 27 | + }, | ||
| 28 | + { | ||
| 29 | + "idiom" : "iphone", | ||
| 30 | + "size" : "40x40", | ||
| 31 | + "scale" : "3x" | ||
| 32 | + }, | ||
| 33 | + { | ||
| 34 | + "idiom" : "iphone", | ||
| 35 | + "size" : "60x60", | ||
| 36 | + "scale" : "2x" | ||
| 37 | + }, | ||
| 38 | + { | ||
| 39 | + "idiom" : "iphone", | ||
| 40 | + "size" : "60x60", | ||
| 41 | + "scale" : "3x" | ||
| 42 | + }, | ||
| 43 | + { | ||
| 44 | + "idiom" : "ipad", | ||
| 45 | + "size" : "20x20", | ||
| 46 | + "scale" : "1x" | ||
| 47 | + }, | ||
| 48 | + { | ||
| 49 | + "idiom" : "ipad", | ||
| 50 | + "size" : "20x20", | ||
| 51 | + "scale" : "2x" | ||
| 52 | + }, | ||
| 53 | + { | ||
| 54 | + "idiom" : "ipad", | ||
| 55 | + "size" : "29x29", | ||
| 56 | + "scale" : "1x" | ||
| 57 | + }, | ||
| 58 | + { | ||
| 59 | + "idiom" : "ipad", | ||
| 60 | + "size" : "29x29", | ||
| 61 | + "scale" : "2x" | ||
| 62 | + }, | ||
| 63 | + { | ||
| 64 | + "idiom" : "ipad", | ||
| 65 | + "size" : "40x40", | ||
| 66 | + "scale" : "1x" | ||
| 67 | + }, | ||
| 68 | + { | ||
| 69 | + "idiom" : "ipad", | ||
| 70 | + "size" : "40x40", | ||
| 71 | + "scale" : "2x" | ||
| 72 | + }, | ||
| 73 | + { | ||
| 74 | + "idiom" : "ipad", | ||
| 75 | + "size" : "76x76", | ||
| 76 | + "scale" : "1x" | ||
| 77 | + }, | ||
| 78 | + { | ||
| 79 | + "idiom" : "ipad", | ||
| 80 | + "size" : "76x76", | ||
| 81 | + "scale" : "2x" | ||
| 82 | + }, | ||
| 83 | + { | ||
| 84 | + "idiom" : "ipad", | ||
| 85 | + "size" : "83.5x83.5", | ||
| 86 | + "scale" : "2x" | ||
| 87 | + }, | ||
| 88 | + { | ||
| 89 | + "idiom" : "ios-marketing", | ||
| 90 | + "size" : "1024x1024", | ||
| 91 | + "scale" : "1x" | ||
| 92 | + } | ||
| 93 | + ], | ||
| 94 | + "info" : { | ||
| 95 | + "version" : 1, | ||
| 96 | + "author" : "xcode" | ||
| 97 | + } | ||
| 98 | +} |
Example/CNLiveAVCamera/Info.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>CFBundleDevelopmentRegion</key> | ||
| 6 | + <string>$(DEVELOPMENT_LANGUAGE)</string> | ||
| 7 | + <key>CFBundleExecutable</key> | ||
| 8 | + <string>$(EXECUTABLE_NAME)</string> | ||
| 9 | + <key>CFBundleIdentifier</key> | ||
| 10 | + <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | ||
| 11 | + <key>CFBundleInfoDictionaryVersion</key> | ||
| 12 | + <string>6.0</string> | ||
| 13 | + <key>CFBundleName</key> | ||
| 14 | + <string>$(PRODUCT_NAME)</string> | ||
| 15 | + <key>CFBundlePackageType</key> | ||
| 16 | + <string>FMWK</string> | ||
| 17 | + <key>CFBundleShortVersionString</key> | ||
| 18 | + <string>1.0</string> | ||
| 19 | + <key>CFBundleVersion</key> | ||
| 20 | + <string>$(CURRENT_PROJECT_VERSION)</string> | ||
| 21 | +</dict> | ||
| 22 | +</plist> |
Example/CNLiveAVCamera/en.lproj/InfoPlist.strings
0 → 100644
Example/CNLiveAVCamera/main.m
0 → 100644
| 1 | +// | ||
| 2 | +// main.m | ||
| 3 | +// CNLiveAVCamera | ||
| 4 | +// | ||
| 5 | +// Created by jyyjkt on 12/07/2018. | ||
| 6 | +// Copyright (c) 2018 jyyjkt. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +@import UIKit; | ||
| 10 | +#import "CNLiveAppDelegate.h" | ||
| 11 | + | ||
| 12 | +int main(int argc, char * argv[]) | ||
| 13 | +{ | ||
| 14 | + @autoreleasepool { | ||
| 15 | + return UIApplicationMain(argc, argv, nil, NSStringFromClass([CNLiveAppDelegate class])); | ||
| 16 | + } | ||
| 17 | +} |
Example/Podfile
0 → 100644
Example/Tests/Tests-Info.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>CFBundleDevelopmentRegion</key> | ||
| 6 | + <string>en</string> | ||
| 7 | + <key>CFBundleExecutable</key> | ||
| 8 | + <string>${EXECUTABLE_NAME}</string> | ||
| 9 | + <key>CFBundleIdentifier</key> | ||
| 10 | + <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | ||
| 11 | + <key>CFBundleInfoDictionaryVersion</key> | ||
| 12 | + <string>6.0</string> | ||
| 13 | + <key>CFBundlePackageType</key> | ||
| 14 | + <string>BNDL</string> | ||
| 15 | + <key>CFBundleShortVersionString</key> | ||
| 16 | + <string>1.0</string> | ||
| 17 | + <key>CFBundleSignature</key> | ||
| 18 | + <string>????</string> | ||
| 19 | + <key>CFBundleVersion</key> | ||
| 20 | + <string>1</string> | ||
| 21 | +</dict> | ||
| 22 | +</plist> |
Example/Tests/Tests-Prefix.pch
0 → 100644
Example/Tests/Tests.m
0 → 100644
| 1 | +// | ||
| 2 | +// CNLiveAVCameraTests.m | ||
| 3 | +// CNLiveAVCameraTests | ||
| 4 | +// | ||
| 5 | +// Created by jyyjkt on 12/07/2018. | ||
| 6 | +// Copyright (c) 2018 jyyjkt. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +@import XCTest; | ||
| 10 | + | ||
| 11 | +@interface Tests : XCTestCase | ||
| 12 | + | ||
| 13 | +@end | ||
| 14 | + | ||
| 15 | +@implementation Tests | ||
| 16 | + | ||
| 17 | +- (void)setUp | ||
| 18 | +{ | ||
| 19 | + [super setUp]; | ||
| 20 | + // Put setup code here. This method is called before the invocation of each test method in the class. | ||
| 21 | +} | ||
| 22 | + | ||
| 23 | +- (void)tearDown | ||
| 24 | +{ | ||
| 25 | + // Put teardown code here. This method is called after the invocation of each test method in the class. | ||
| 26 | + [super tearDown]; | ||
| 27 | +} | ||
| 28 | + | ||
| 29 | +- (void)testExample | ||
| 30 | +{ | ||
| 31 | + XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); | ||
| 32 | +} | ||
| 33 | + | ||
| 34 | +@end | ||
| 35 | + |
Example/Tests/en.lproj/InfoPlist.strings
0 → 100644
LICENSE
0 → 100644
| 1 | +Copyright (c) 2018 jyyjkt <jyyjkt@qq.com> | ||
| 2 | + | ||
| 3 | +Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 4 | +of this software and associated documentation files (the "Software"), to deal | ||
| 5 | +in the Software without restriction, including without limitation the rights | ||
| 6 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 7 | +copies of the Software, and to permit persons to whom the Software is | ||
| 8 | +furnished to do so, subject to the following conditions: | ||
| 9 | + | ||
| 10 | +The above copyright notice and this permission notice shall be included in | ||
| 11 | +all copies or substantial portions of the Software. | ||
| 12 | + | ||
| 13 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 14 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 15 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 16 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 17 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 18 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| 19 | +THE SOFTWARE. |
README.md
| 1 | +# CNLiveAVCamera | ||
| 2 | + | ||
| 3 | +[](https://travis-ci.org/jyyjkt/CNLiveAVCamera) | ||
| 4 | +[](https://cocoapods.org/pods/CNLiveAVCamera) | ||
| 5 | +[](https://cocoapods.org/pods/CNLiveAVCamera) | ||
| 6 | +[](https://cocoapods.org/pods/CNLiveAVCamera) | ||
| 7 | + | ||
| 8 | +## Example | ||
| 9 | + | ||
| 10 | +To run the example project, clone the repo, and run `pod install` from the Example directory first. | ||
| 11 | + | ||
| 12 | +## Requirements | ||
| 13 | + | ||
| 14 | +## Installation | ||
| 15 | + | ||
| 16 | +CNLiveAVCamera is available through [CocoaPods](https://cocoapods.org). To install | ||
| 17 | +it, simply add the following line to your Podfile: | ||
| 18 | + | ||
| 19 | +```ruby | ||
| 20 | +pod 'CNLiveAVCamera' | ||
| 21 | +``` | ||
| 22 | + | ||
| 23 | +## Author | ||
| 24 | + | ||
| 25 | +jyyjkt, jyyjkt@qq.com | ||
| 26 | + | ||
| 27 | +## License | ||
| 28 | + | ||
| 29 | +CNLiveAVCamera is available under the MIT license. See the LICENSE file for more info. |
_Pods.xcodeproj
0 → 120000