KZVideoViewController.m 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720
  1. //
  2. // KZVideoViewController.m
  3. // KZWeChatSmallVideo_OC
  4. //
  5. // Created by HouKangzhu on 16/7/18.
  6. // Copyright © 2016年 侯康柱. All rights reserved.
  7. //
  8. #import "KZVideoViewController.h"
  9. #import "KZVideoSupport.h"
  10. #import "KZVideoConfig.h"
  11. #import <AVFoundation/AVFoundation.h>
  12. #import <AssetsLibrary/AssetsLibrary.h>
  13. #import <Photos/Photos.h>
  14. #import <CoreMotion/CoreMotion.h>
  15. #import "KZVideoListViewController.h"
  16. @interface KZVideoViewController()<KZControllerBarDelegate,AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAudioDataOutputSampleBufferDelegate> {
  17. KZStatusBar *_topSlideView;
  18. UIView *_videoView;
  19. KZFocusView *_focusView;
  20. UILabel *_statusInfo;
  21. UILabel *_cancelInfo;
  22. KZControllerBar *_ctrlBar;
  23. AVCaptureSession *_videoSession;
  24. AVCaptureVideoPreviewLayer *_videoPreLayer;
  25. AVCaptureDevice *_videoDevice;
  26. AVCaptureVideoDataOutput *_videoDataOut;
  27. AVCaptureAudioDataOutput *_audioDataOut;
  28. AVAssetWriter *_assetWriter;
  29. AVAssetWriterInputPixelBufferAdaptor *_assetWriterPixelBufferInput;
  30. AVAssetWriterInput *_assetWriterVideoInput;
  31. AVAssetWriterInput *_assetWriterAudioInput;
  32. CMTime _currentSampleTime;
  33. BOOL _recoding;
  34. dispatch_queue_t _recoding_queue;
  35. // dispatch_queue_create("com.video.queue", DISPATCH_QUEUE_SERIAL)
  36. KZVideoModel *_currentRecord;
  37. BOOL _currentRecordIsCancel;
  38. UIView *_eyeView;
  39. }
  40. @property (nonatomic, assign) KZVideoViewShowType showType;
  41. @property(nonatomic, strong)AVCaptureStillImageOutput *imageOutput;
  42. @property(nonatomic,strong) CMMotionManager *motionManager;
  43. @end
  44. static KZVideoViewController *__currentVideoVC = nil;
  45. @implementation KZVideoViewController
  46. - (void)startAnimationWithType:(KZVideoViewShowType)showType selectExist:(BOOL)selectExist {
  47. self.motionManager = [[CMMotionManager alloc] init];
  48. [self.motionManager startAccelerometerUpdates];
  49. _showType = showType;
  50. __currentVideoVC = self;
  51. [self setupSubViews];
  52. self.view.hidden = YES;
  53. BOOL videoExist = [KZVideoUtil existVideo];
  54. UIWindow *keyWindow = [UIApplication sharedApplication].delegate.window;
  55. [keyWindow addSubview:self.view];
  56. if (_showType == KZVideoViewShowTypeSingle && videoExist && selectExist) {
  57. [self ctrollVideoOpenVideoList:nil];
  58. kz_dispatch_after(0.4, ^{
  59. self.view.hidden = NO;
  60. });
  61. }
  62. else {
  63. self.view.hidden = NO;
  64. self.actionView.transform = CGAffineTransformTranslate(CGAffineTransformIdentity, 0, CGRectGetHeight([KZVideoConfig viewFrameWithType:showType]));
  65. [UIView animateWithDuration:0.3 delay:0.1 options:UIViewAnimationOptionCurveLinear animations:^{
  66. self.actionView.transform = CGAffineTransformIdentity;
  67. self.view.backgroundColor = [UIColor colorWithRed: 0.0 green: 0.0 blue: 0.0 alpha: 0.4];
  68. } completion:^(BOOL finished) {
  69. [self viewDidAppear];
  70. }];
  71. }
  72. [self setupVideo];
  73. }
  74. - (void)endAniamtion {
  75. [self.motionManager stopAccelerometerUpdates];
  76. [UIView animateWithDuration:0.3 animations:^{
  77. self.view.backgroundColor = [UIColor clearColor];
  78. self.actionView.transform = CGAffineTransformTranslate(CGAffineTransformIdentity, 0, CGRectGetHeight([KZVideoConfig viewFrameWithType:_showType]));
  79. } completion:^(BOOL finished) {
  80. [self closeView];
  81. }];
  82. }
  83. - (void)closeView {
  84. [_videoSession stopRunning];
  85. [_videoPreLayer removeFromSuperlayer];
  86. _videoPreLayer = nil;
  87. [_videoView removeFromSuperview];
  88. _videoView = nil;
  89. _videoDevice = nil;
  90. _videoDataOut = nil;
  91. _assetWriter = nil;
  92. _assetWriterAudioInput = nil;
  93. _assetWriterVideoInput = nil;
  94. _assetWriterPixelBufferInput = nil;
  95. [self.view removeFromSuperview];
  96. __currentVideoVC = nil;
  97. }
  98. - (void)dealloc {
  99. // NSLog(@"dalloc videoVC");
  100. }
  101. - (void)setupSubViews {
  102. _view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
  103. self.view.backgroundColor = [UIColor clearColor];
  104. UIPanGestureRecognizer *ges = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(moveTopBarAction:)];
  105. [self.view addGestureRecognizer:ges];
  106. _actionView = [[UIView alloc] initWithFrame:[KZVideoConfig viewFrameWithType:_showType]];
  107. [self.view addSubview:_actionView];
  108. _actionView.backgroundColor = kzThemeBlackColor;
  109. _actionView.clipsToBounds = YES;
  110. BOOL isSmallStyle = _showType == KZVideoViewShowTypeSmall;
  111. CGSize videoViewSize = [KZVideoConfig videoViewDefaultSize];
  112. CGFloat topHeight = 64;//isSmallStyle ? 20.0 : 64.0;
  113. CGFloat allHeight = _actionView.frame.size.height;
  114. CGFloat allWidth = _actionView.frame.size.width;
  115. CGFloat buttomHeight = 280.0;//isSmallStyle ? kzControViewHeight : allHeight - topHeight - videoViewSize.height;
  116. _videoView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, videoViewSize.width, videoViewSize.height)];
  117. [self.actionView addSubview:_videoView];
  118. _topSlideView = [[KZStatusBar alloc] initWithFrame:CGRectMake(0, 0, allWidth, topHeight) style:_showType];
  119. if (!isSmallStyle) {
  120. [_topSlideView addCancelTarget:self selector:@selector(endAniamtion)];
  121. }
  122. [self.actionView addSubview:_topSlideView];
  123. _ctrlBar = [[KZControllerBar alloc] initWithFrame:CGRectMake(0, allHeight - buttomHeight, allWidth, buttomHeight)];
  124. [_ctrlBar setupSubViewsWithStyle:_showType];
  125. _ctrlBar.delegate = self;
  126. [self.actionView addSubview:_ctrlBar];
  127. UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(focusAction:)];
  128. tapGesture.delaysTouchesBegan = YES;
  129. [_videoView addGestureRecognizer:tapGesture];
  130. UITapGestureRecognizer *doubleTapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(zoomVideo:)];
  131. doubleTapGesture.numberOfTapsRequired = 2;
  132. doubleTapGesture.numberOfTouchesRequired = 1;
  133. doubleTapGesture.delaysTouchesBegan = YES;
  134. [_videoView addGestureRecognizer:doubleTapGesture];
  135. [tapGesture requireGestureRecognizerToFail:doubleTapGesture];
  136. _focusView = [[KZFocusView alloc] initWithFrame:CGRectMake(0, 0, 60, 60)];
  137. _focusView.backgroundColor = [UIColor clearColor];
  138. _statusInfo = [[UILabel alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(_videoView.frame) - 30, _videoView.frame.size.width, 20)];
  139. _statusInfo.textAlignment = NSTextAlignmentCenter;
  140. _statusInfo.font = [UIFont systemFontOfSize:14.0];
  141. _statusInfo.textColor = [UIColor whiteColor];
  142. _statusInfo.hidden = YES;
  143. [self.actionView addSubview:_statusInfo];
  144. _cancelInfo = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 120, 24)];
  145. _cancelInfo.center = _videoView.center;
  146. _cancelInfo.textAlignment = NSTextAlignmentCenter;
  147. _cancelInfo.textColor = kzThemeWhiteColor;
  148. _cancelInfo.backgroundColor = kzThemeWaringColor;
  149. _cancelInfo.hidden = YES;
  150. [self.actionView addSubview:_cancelInfo];
  151. [_actionView sendSubviewToBack:_videoView];
  152. }
  153. - (void)setupVideo {
  154. NSString *unUseInfo = nil;
  155. if (TARGET_IPHONE_SIMULATOR) {
  156. unUseInfo = @"模拟器不可以的..";
  157. }
  158. AVAuthorizationStatus videoAuthStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
  159. if(videoAuthStatus == ALAuthorizationStatusRestricted || videoAuthStatus == ALAuthorizationStatusDenied){
  160. unUseInfo = @"相机访问受限...";
  161. }
  162. AVAuthorizationStatus audioAuthStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
  163. if(audioAuthStatus == ALAuthorizationStatusRestricted || audioAuthStatus == ALAuthorizationStatusDenied){
  164. unUseInfo = @"录音访问受限...";
  165. }
  166. if (unUseInfo != nil) {
  167. _statusInfo.text = unUseInfo;
  168. _statusInfo.hidden = NO;
  169. _eyeView = [[KZEyeView alloc] initWithFrame:_videoView.bounds];
  170. [_videoView addSubview:_eyeView];
  171. return;
  172. }
  173. _recoding_queue = dispatch_queue_create("com.kzsmallvideo.queue", DISPATCH_QUEUE_SERIAL);
  174. NSArray *devicesVideo = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
  175. NSArray *devicesAudio = [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio];
  176. AVCaptureDeviceInput *videoInput = [AVCaptureDeviceInput deviceInputWithDevice:devicesVideo[0] error:nil];
  177. AVCaptureDeviceInput *audioInput = [AVCaptureDeviceInput deviceInputWithDevice:devicesAudio[0] error:nil];
  178. _videoDevice = devicesVideo[0];
  179. _videoDataOut = [[AVCaptureVideoDataOutput alloc] init];
  180. _videoDataOut.videoSettings = @{(__bridge NSString *)kCVPixelBufferPixelFormatTypeKey:@(kCVPixelFormatType_32BGRA)};
  181. _videoDataOut.alwaysDiscardsLateVideoFrames = YES;
  182. [_videoDataOut setSampleBufferDelegate:self queue:_recoding_queue];
  183. _audioDataOut = [[AVCaptureAudioDataOutput alloc] init];
  184. [_audioDataOut setSampleBufferDelegate:self queue:_recoding_queue];
  185. _videoSession = [[AVCaptureSession alloc] init];
  186. _videoSession.sessionPreset = AVCaptureSessionPresetHigh;
  187. if ([_videoSession canAddInput:videoInput]) {
  188. [_videoSession addInput:videoInput];
  189. }
  190. if ([_videoSession canAddInput:audioInput]) {
  191. [_videoSession addInput:audioInput];
  192. }
  193. if ([_videoSession canAddOutput:_videoDataOut]) {
  194. [_videoSession addOutput:_videoDataOut];
  195. }
  196. if ([_videoSession canAddOutput:self.imageOutput]) {
  197. [_videoSession addOutput:self.imageOutput];
  198. }
  199. if ([_videoSession canAddOutput:_audioDataOut]) {
  200. [_videoSession addOutput:_audioDataOut];
  201. }
  202. CGFloat viewWidth = CGRectGetWidth(_actionView.frame);
  203. CGFloat viewHeight = CGRectGetHeight(_actionView.frame);
  204. _videoPreLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_videoSession];
  205. _videoPreLayer.frame = CGRectMake(0, 0, viewWidth, viewHeight);
  206. _videoPreLayer.position = CGPointMake(viewWidth/2, viewHeight/2);
  207. _videoPreLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
  208. [_videoView.layer addSublayer:_videoPreLayer];
  209. [_videoSession startRunning];
  210. [self viewWillAppear];
  211. }
  212. - (void)viewWillAppear {
  213. _eyeView = [[KZEyeView alloc] initWithFrame:_videoView.bounds];
  214. [_videoView addSubview:_eyeView];
  215. }
  216. - (void)viewDidAppear {
  217. if (TARGET_IPHONE_SIMULATOR) return;
  218. UIView *sysSnapshot = [_eyeView snapshotViewAfterScreenUpdates:NO];
  219. CGFloat videoViewHeight = CGRectGetHeight(_videoView.frame);
  220. CGFloat viewViewWidth = CGRectGetWidth(_videoView.frame);
  221. _eyeView.alpha = 0;
  222. UIView *topView = [sysSnapshot resizableSnapshotViewFromRect:CGRectMake(0, 0, viewViewWidth, videoViewHeight/2) afterScreenUpdates:NO withCapInsets:UIEdgeInsetsZero];
  223. CGRect btmFrame = CGRectMake(0, videoViewHeight/2, viewViewWidth, videoViewHeight/2);
  224. UIView *btmView = [sysSnapshot resizableSnapshotViewFromRect:btmFrame afterScreenUpdates:NO withCapInsets:UIEdgeInsetsZero];
  225. btmView.frame = btmFrame;
  226. [_videoView addSubview:topView];
  227. [_videoView addSubview:btmView];
  228. [UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
  229. topView.transform = CGAffineTransformMakeTranslation(0,-videoViewHeight/2);
  230. btmView.transform = CGAffineTransformMakeTranslation(0, videoViewHeight);
  231. topView.alpha = 0.3;
  232. btmView.alpha = 0.3;
  233. } completion:^(BOOL finished) {
  234. [topView removeFromSuperview];
  235. [btmView removeFromSuperview];
  236. [_eyeView removeFromSuperview];
  237. _eyeView = nil;
  238. [self focusInPointAtVideoView:CGPointMake(_videoView.bounds.size.width/2, _videoView.bounds.size.height/2)];
  239. }];
  240. __block UILabel *zoomLab = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 20)];
  241. zoomLab.center = CGPointMake(_videoView.center.x, CGRectGetMaxY(_videoView.frame) - 16);
  242. zoomLab.font = [UIFont boldSystemFontOfSize:14];
  243. zoomLab.text = WFCString(@"DoubleClickZoomOut");
  244. zoomLab.textColor = [UIColor whiteColor];
  245. zoomLab.textAlignment = NSTextAlignmentCenter;
  246. [_videoView addSubview:zoomLab];
  247. [_videoView bringSubviewToFront:zoomLab];
  248. kz_dispatch_after(1.6, ^{
  249. [zoomLab removeFromSuperview];
  250. });
  251. }
  252. - (void)focusInPointAtVideoView:(CGPoint)point {
  253. CGPoint cameraPoint= [_videoPreLayer captureDevicePointOfInterestForPoint:point];
  254. _focusView.center = point;
  255. [_videoView addSubview:_focusView];
  256. [_videoView bringSubviewToFront:_focusView];
  257. [_focusView focusing];
  258. NSError *error = nil;
  259. if ([_videoDevice lockForConfiguration:&error]) {
  260. if ([_videoDevice isFocusPointOfInterestSupported]) {
  261. _videoDevice.focusPointOfInterest = cameraPoint;
  262. }
  263. if ([_videoDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
  264. _videoDevice.focusMode = AVCaptureFocusModeAutoFocus;
  265. }
  266. if ([_videoDevice isExposureModeSupported:AVCaptureExposureModeAutoExpose]) {
  267. _videoDevice.exposureMode = AVCaptureExposureModeAutoExpose;
  268. }
  269. if ([_videoDevice isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeAutoWhiteBalance]) {
  270. _videoDevice.whiteBalanceMode = AVCaptureWhiteBalanceModeAutoWhiteBalance;
  271. }
  272. [_videoDevice unlockForConfiguration];
  273. }
  274. if (error) {
  275. NSLog(@"聚焦失败:%@",error);
  276. }
  277. kz_dispatch_after(1.0, ^{
  278. [_focusView removeFromSuperview];
  279. });
  280. }
  281. #pragma mark - Actions --
  282. - (void)focusAction:(UITapGestureRecognizer *)gesture {
  283. CGPoint point = [gesture locationInView:_videoView];
  284. [self focusInPointAtVideoView:point];
  285. }
  286. - (void)zoomVideo:(UITapGestureRecognizer *)gesture {
  287. NSError *error = nil;
  288. if ([_videoDevice lockForConfiguration:&error]) {
  289. CGFloat zoom = _videoDevice.videoZoomFactor == 2.0?1.0:2.0;
  290. _videoDevice.videoZoomFactor = zoom;
  291. [_videoDevice unlockForConfiguration];
  292. }
  293. }
  294. - (void)moveTopBarAction:(UIPanGestureRecognizer *)gesture {
  295. CGPoint pointAtView = [gesture locationInView:self.view];
  296. CGRect dafultFrame = [KZVideoConfig viewFrameWithType:_showType];
  297. if (pointAtView.y < dafultFrame.origin.y) {
  298. return;
  299. }
  300. CGPoint pointAtTop = [gesture locationInView:_topSlideView];
  301. if (pointAtTop.y > -10 && pointAtTop.y < 30) {
  302. CGRect actionFrame = _actionView.frame;
  303. actionFrame.origin.y = pointAtView.y;
  304. _actionView.frame = actionFrame;
  305. CGFloat alpha = 0.4*(kzSCREEN_HEIGHT - pointAtView.y)/CGRectGetHeight(_actionView.frame);
  306. self.view.backgroundColor = [UIColor colorWithRed: 0.0 green: 0.0 blue: 0.0 alpha: alpha];
  307. }
  308. if (gesture.state == UIGestureRecognizerStateEnded) {
  309. if (pointAtView.y >= CGRectGetMidY(dafultFrame)) {
  310. [self endAniamtion];
  311. }
  312. else {
  313. [UIView animateWithDuration:0.3 animations:^{
  314. _actionView.frame = dafultFrame;
  315. self.view.backgroundColor = [UIColor colorWithRed: 0.0 green: 0.0 blue: 0.0 alpha: 0.4];
  316. }];
  317. }
  318. }
  319. }
  320. #pragma mark - controllerBarDelegate
  321. - (void)ctrollVideoDidStart:(KZControllerBar *)controllerBar {
  322. _currentRecord = [KZVideoUtil createNewVideo];
  323. _currentRecordIsCancel = NO;
  324. NSURL *outURL = [NSURL fileURLWithPath:_currentRecord.videoAbsolutePath];
  325. [self createWriter:outURL];
  326. _topSlideView.isRecoding = YES;
  327. _statusInfo.textColor = kzThemeTineColor;
  328. _statusInfo.text = WFCString(@"MoveUpCancel");
  329. _statusInfo.hidden = NO;
  330. kz_dispatch_after(0.5, ^{
  331. _statusInfo.hidden = YES;
  332. });
  333. _recoding = YES;
  334. // NSLog(@"视频开始录制");
  335. }
  336. - (void)ctrollVideoDidEnd:(KZControllerBar *)controllerBar {
  337. _topSlideView.isRecoding = NO;
  338. _recoding = NO;
  339. [self saveVideo:^(NSURL *outFileURL) {
  340. if (_delegate) {
  341. [_delegate videoViewController:self didRecordVideo:_currentRecord];
  342. [self endAniamtion];
  343. }
  344. }];
  345. // NSLog(@"视频录制结束");
  346. }
  347. AVCaptureVideoOrientation orientationBaseOnAcceleration(CMAcceleration acceleration) {
  348. AVCaptureVideoOrientation result;
  349. if (acceleration.x >= 0.75) { /// UIDeviceOrientationLandscapeRight
  350. result = AVCaptureVideoOrientationLandscapeLeft;
  351. }
  352. else if (acceleration.x <= -0.75) { /// UIDeviceOrientationLandscapeLeft
  353. result = AVCaptureVideoOrientationLandscapeRight;
  354. }
  355. else if (acceleration.y <= -0.75) { /// UIDeviceOrientationPortrait
  356. result = AVCaptureVideoOrientationPortrait;
  357. }
  358. else if (acceleration.y >= 0.75) { ///UIDeviceOrientationPortraitUpsideDown
  359. result = AVCaptureVideoOrientationPortraitUpsideDown;
  360. }
  361. else {
  362. result = AVCaptureVideoOrientationPortrait;
  363. }
  364. return result;
  365. }
  366. - (void)ctrollImageDidCapture:(KZControllerBar *)controllerBar {
  367. __weak typeof(self) ws = self;
  368. CMAcceleration acceleration = self.motionManager.accelerometerData.acceleration;
  369. AVCaptureVideoOrientation orientation = orientationBaseOnAcceleration(acceleration);
  370. [self captureStillImage:orientation completion:^(UIImage *image) {
  371. [ws.delegate videoViewController:self didCaptureImage:image];
  372. [ws endAniamtion];
  373. }];
  374. }
  375. - (void)ctrollVideoDidCancel:(KZControllerBar *)controllerBar reason:(KZRecordCancelReason)reason{
  376. _currentRecordIsCancel = YES;
  377. _topSlideView.isRecoding = NO;
  378. _recoding = NO;
  379. if (reason == KZRecordCancelReasonTimeShort) {
  380. [KZVideoConfig showHinInfo:@"录制时间过短" inView:_videoView frame:CGRectMake(0,CGRectGetHeight(_videoView.frame)/3*2,CGRectGetWidth(_videoView.frame),20) timeLong:1.0];
  381. }
  382. // NSLog(@"当前视频录制取消");
  383. }
  384. - (void)ctrollVideoWillCancel:(KZControllerBar *)controllerBar {
  385. if (!_cancelInfo.hidden) {
  386. return;
  387. }
  388. _cancelInfo.text = WFCString(@"ReleaseToCancel");
  389. _cancelInfo.hidden = NO;
  390. kz_dispatch_after(0.5, ^{
  391. _cancelInfo.hidden = YES;
  392. });
  393. }
  394. - (void)ctrollVideoDidRecordSEC:(KZControllerBar *)controllerBar {
  395. _topSlideView.isRecoding = YES;
  396. // NSLog(@"视频录又过了 1 秒");
  397. }
  398. - (void)ctrollVideoDidClose:(KZControllerBar *)controllerBar {
  399. // NSLog(@"录制界面关闭");
  400. if (_delegate && [_delegate respondsToSelector:@selector(videoViewControllerDidCancel:)]) {
  401. [_delegate videoViewControllerDidCancel:self];
  402. }
  403. [self endAniamtion];
  404. }
  405. - (void)ctrollVideoOpenVideoList:(KZControllerBar *)controllerBar {
  406. // NSLog(@"查看视频列表");
  407. KZVideoListViewController *listVC = [[KZVideoListViewController alloc] init];
  408. __weak typeof(self) blockSelf = self;
  409. listVC.selectBlock = ^(KZVideoModel *selectModel) {
  410. _currentRecord = selectModel;
  411. if (_delegate) {
  412. [_delegate videoViewController:blockSelf didRecordVideo:_currentRecord];
  413. }
  414. [blockSelf closeView];
  415. };
  416. listVC.didCloseBlock = ^{
  417. if (_showType == KZVideoViewShowTypeSingle) {
  418. [blockSelf viewDidAppear];
  419. }
  420. };
  421. [listVC showAnimationWithType:_showType];
  422. }
  423. - (UIImage *)fixOrientationOfImage:(UIImage*)image {
  424. if (image.imageOrientation == UIImageOrientationUp) return image;
  425. UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);
  426. [image drawInRect:(CGRect){0, 0, image.size}];
  427. UIImage *normalizedImage = UIGraphicsGetImageFromCurrentImageContext();
  428. UIGraphicsEndImageContext();
  429. return normalizedImage;
  430. }
  431. - (void)captureStillImage:(AVCaptureVideoOrientation)orientation
  432. completion:(void (^)(UIImage *image))completion{
  433. AVCaptureConnection *connection = [self.imageOutput connectionWithMediaType:AVMediaTypeVideo];
  434. static NSUInteger tryCount = 0;
  435. if (connection.isVideoOrientationSupported) {
  436. connection.videoOrientation = orientation;
  437. }
  438. __weak typeof(self) weakSelf = self;
  439. id handler = ^(CMSampleBufferRef sampleBuffer, NSError *error) {
  440. if (sampleBuffer != NULL) {
  441. NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:sampleBuffer];
  442. /// 以 Camera 坐标系存储的图片
  443. UIImage *image = [[UIImage alloc] initWithData:imageData];
  444. /// 转化以屏幕坐标系存储
  445. image = [weakSelf fixOrientationOfImage:image];
  446. tryCount = 0;
  447. if (completion) {
  448. completion(image);
  449. }
  450. } else {
  451. tryCount++;
  452. if (tryCount > 3) {
  453. tryCount = 0;
  454. completion(nil);
  455. return;
  456. }
  457. NSLog(@"NULL sampleBuffer: %@", [error localizedDescription]);
  458. [weakSelf captureStillImage:orientation completion:completion];
  459. }
  460. };
  461. // Capture still image
  462. [self.imageOutput captureStillImageAsynchronouslyFromConnection:connection completionHandler:handler];
  463. }
  464. - (AVCaptureStillImageOutput *)imageOutput {
  465. if (!_imageOutput) {
  466. _imageOutput = [[AVCaptureStillImageOutput alloc] init];
  467. _imageOutput.outputSettings = @{AVVideoCodecKey : AVVideoCodecJPEG};
  468. }
  469. return _imageOutput;
  470. }
  471. #pragma mark - AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAudioDataOutputSampleBufferDelegate
  472. - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
  473. if (!_recoding) return;
  474. @autoreleasepool {
  475. _currentSampleTime = CMSampleBufferGetOutputPresentationTimeStamp(sampleBuffer);
  476. if (_assetWriter.status != AVAssetWriterStatusWriting) {
  477. [_assetWriter startWriting];
  478. [_assetWriter startSessionAtSourceTime:_currentSampleTime];
  479. }
  480. if (captureOutput == _videoDataOut) {
  481. if (_assetWriterPixelBufferInput.assetWriterInput.isReadyForMoreMediaData) {
  482. CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
  483. BOOL success = [_assetWriterPixelBufferInput appendPixelBuffer:pixelBuffer withPresentationTime:_currentSampleTime];
  484. if (!success) {
  485. NSLog(@"Pixel Buffer没有append成功");
  486. }
  487. }
  488. }
  489. if (captureOutput == _audioDataOut) {
  490. [_assetWriterAudioInput appendSampleBuffer:sampleBuffer];
  491. }
  492. }
  493. }
  494. - (void)captureOutput:(AVCaptureOutput *)captureOutput didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
  495. }
  496. - (void)createWriter:(NSURL *)assetUrl {
  497. _assetWriter = [AVAssetWriter assetWriterWithURL:assetUrl fileType:AVFileTypeQuickTimeMovie error:nil];
  498. int videoWidth = [KZVideoConfig defualtVideoSize].width;
  499. int videoHeight = [KZVideoConfig defualtVideoSize].height;
  500. /*
  501. NSDictionary *videoCleanApertureSettings = @{
  502. AVVideoCleanApertureWidthKey:@(videoHeight),
  503. AVVideoCleanApertureHeightKey:@(videoWidth),
  504. AVVideoCleanApertureHorizontalOffsetKey:@(200),
  505. AVVideoCleanApertureVerticalOffsetKey:@(0)
  506. };
  507. NSDictionary *videoAspectRatioSettings = @{
  508. AVVideoPixelAspectRatioHorizontalSpacingKey:@(3),
  509. AVVideoPixelAspectRatioVerticalSpacingKey:@(3)
  510. };
  511. NSDictionary *codecSettings = @{
  512. AVVideoAverageBitRateKey:@(960000),
  513. AVVideoMaxKeyFrameIntervalKey:@(1),
  514. AVVideoProfileLevelKey:AVVideoProfileLevelH264Main30,
  515. AVVideoCleanApertureKey: videoCleanApertureSettings,
  516. AVVideoPixelAspectRatioKey:videoAspectRatioSettings
  517. };
  518. */
  519. NSDictionary *outputSettings = @{
  520. AVVideoCodecKey : AVVideoCodecH264,
  521. AVVideoWidthKey : @(videoHeight),
  522. AVVideoHeightKey : @(videoWidth),
  523. AVVideoScalingModeKey:AVVideoScalingModeResizeAspectFill,
  524. // AVVideoCompressionPropertiesKey:codecSettings
  525. };
  526. _assetWriterVideoInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:outputSettings];
  527. _assetWriterVideoInput.expectsMediaDataInRealTime = YES;
  528. _assetWriterVideoInput.transform = CGAffineTransformMakeRotation(M_PI / 2.0);
  529. NSDictionary *audioOutputSettings = @{
  530. AVFormatIDKey:@(kAudioFormatMPEG4AAC),
  531. AVEncoderBitRateKey:@(64000),
  532. AVSampleRateKey:@(44100),
  533. AVNumberOfChannelsKey:@(1),
  534. };
  535. _assetWriterAudioInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings:audioOutputSettings];
  536. _assetWriterAudioInput.expectsMediaDataInRealTime = YES;
  537. NSDictionary *SPBADictionary = @{
  538. (__bridge NSString *)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA),
  539. (__bridge NSString *)kCVPixelBufferWidthKey : @(videoWidth),
  540. (__bridge NSString *)kCVPixelBufferHeightKey : @(videoHeight),
  541. (__bridge NSString *)kCVPixelFormatOpenGLESCompatibility : ((__bridge NSNumber *)kCFBooleanTrue)
  542. };
  543. _assetWriterPixelBufferInput = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:_assetWriterVideoInput sourcePixelBufferAttributes:SPBADictionary];
  544. if ([_assetWriter canAddInput:_assetWriterVideoInput]) {
  545. [_assetWriter addInput:_assetWriterVideoInput];
  546. }else {
  547. NSLog(@"不能添加视频writer的input \(assetWriterVideoInput)");
  548. }
  549. if ([_assetWriter canAddInput:_assetWriterAudioInput]) {
  550. [_assetWriter addInput:_assetWriterAudioInput];
  551. }else {
  552. NSLog(@"不能添加视频writer的input \(assetWriterVideoInput)");
  553. }
  554. }
  555. - (void)saveVideo:(void(^)(NSURL *outFileURL))complier {
  556. if (_recoding) return;
  557. if (!_recoding_queue){
  558. complier(nil);
  559. return;
  560. };
  561. dispatch_async(_recoding_queue, ^{
  562. NSURL *outputFileURL = [NSURL fileURLWithPath:_currentRecord.videoAbsolutePath];
  563. [_assetWriter finishWritingWithCompletionHandler:^{
  564. if (_currentRecordIsCancel) return ;
  565. [KZVideoUtil saveThumImageWithVideoURL:outputFileURL second:1];
  566. if (complier) {
  567. dispatch_async(dispatch_get_main_queue(), ^{
  568. complier(outputFileURL);
  569. });
  570. }
  571. if (_savePhotoAlbum) {
  572. BOOL ios8Later = [[[UIDevice currentDevice] systemVersion] floatValue] >= 8;
  573. if (ios8Later) {
  574. [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
  575. [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:outputFileURL];
  576. } completionHandler:^(BOOL success, NSError * _Nullable error) {
  577. if (!error && success) {
  578. NSLog(@"保存相册成功!");
  579. }
  580. else {
  581. NSLog(@"保存相册失败! :%@",error);
  582. }
  583. }];
  584. }
  585. else {
  586. [[[ALAssetsLibrary alloc] init] writeVideoAtPathToSavedPhotosAlbum:outputFileURL completionBlock:^(NSURL *assetURL, NSError *error) {
  587. if (!error) {
  588. NSLog(@"保存相册成功!");
  589. }
  590. else {
  591. NSLog(@"保存相册失败!");
  592. }
  593. }];
  594. }
  595. }
  596. }];
  597. });
  598. }
  599. @end