YOUPAIVideoEditVC.m 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. //
  2. // YOUPAIVideoEditVC.m
  3. // VideoEditDemo
  4. //
  5. // Created by 刘志伟 on 2017/8/17.
  6. // Copyright © 2017年 刘志伟. All rights reserved.
  7. //
  8. #define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
  9. #define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
  10. #define EDGE_EXTENSION_FOR_THUMB 20
  11. #import "YOUPAIVideoEditVC.h"
  12. #import "YOUPAIDragEditView.h"
  13. #import <AVFoundation/AVFoundation.h>
  14. #import <MediaPlayer/MediaPlayer.h>
  15. #import "YOUPAIZYSendVideoByLocalVC.h"
  16. #import "UIViewController+TFPresent.h"
  17. @interface YOUPAIVideoEditVC ()<UIScrollViewDelegate>{
  18. UIScrollView *editScrollView;
  19. UIView *bottomView;
  20. YOUPAIDragEditView *leftDragView;
  21. YOUPAIDragEditView *rightDragView;
  22. UIView *line;
  23. UIView *topBorder;
  24. UIView *bottomBorder;
  25. }
  26. @property (nonatomic, strong) AVPlayerItem *playItem;
  27. @property (nonatomic, strong) AVPlayerLayer *playerLayer;
  28. @property (nonatomic, strong) AVPlayer *player;
  29. @property (nonatomic, strong) NSTimer *repeatTimer; // 循环播放计时器
  30. @property (nonatomic, strong) NSTimer *lineMoveTimer; // 播放条移动计时器
  31. @property (nonatomic, strong) NSMutableArray *framesArray; // 视频帧数组
  32. @property (nonatomic, strong) NSString *tempVideoPath;
  33. @property (nonatomic, assign) CGPoint leftStartPoint;
  34. @property (nonatomic, assign) CGPoint rightStartPoint;
  35. @property (nonatomic, assign) BOOL isDraggingRightOverlayView;
  36. @property (nonatomic, assign) BOOL isDraggingLeftOverlayView;
  37. @property (nonatomic, assign) CGFloat startTime; // 编辑框内视频开始时间秒
  38. @property (nonatomic, assign) CGFloat endTime; // 编辑框内视频结束时间秒
  39. @property (nonatomic, assign) CGFloat startPointX; // 编辑框起始点
  40. @property (nonatomic, assign) CGFloat endPointX; // 编辑框结束点
  41. @property (nonatomic, assign) CGFloat IMG_Width; // 视频帧宽度
  42. @property (nonatomic, assign) CGFloat linePositionX; // 播放条的位置
  43. @property (nonatomic, assign) CGFloat boderX; // 编辑框边线X
  44. @property (nonatomic, assign) CGFloat boderWidth; // 编辑框边线长度
  45. @property (nonatomic, assign) CGFloat touchPointX; // 编辑视图区域外触点
  46. @property (nonatomic, assign) BOOL isEdited; // YES:编辑完成
  47. @end
  48. @implementation YOUPAIVideoEditVC
  49. #pragma mark lifeCycle
  50. - (void)viewDidLoad {
  51. [super viewDidLoad];
  52. self.title = @"视频最长一分钟";
  53. self.view.backgroundColor = HexColorFromRGB(0xF6F6F6);
  54. UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
  55. [button addTarget:self action:@selector(notifyDelegateOfDidChange) forControlEvents:UIControlEventTouchUpInside];
  56. button.frame = CGRectMake(0 , 0,56, 24);
  57. [button setTitle:@"下一步" forState:0];
  58. [button setTitleColor:[UIColor whiteColor] forState:0];
  59. [button setBackgroundColor:HexColorFromRGB(0xFF5CA2)];
  60. button.titleLabel.font = LCFont(11);
  61. button.layer.cornerRadius = 12;
  62. button.layer.masksToBounds = YES;
  63. [self.view addSubview:button];
  64. // 设置rightBarButtonItem
  65. UIBarButtonItem *rightItem =[[UIBarButtonItem alloc] initWithCustomView:button];
  66. self.navigationItem.rightBarButtonItem = rightItem;
  67. [self initFunctions];
  68. }
  69. - (void)didReceiveMemoryWarning {
  70. [super didReceiveMemoryWarning];
  71. // Dispose of any resources that can be recreated.
  72. }
  73. - (void)viewWillDisappear:(BOOL)animated{
  74. [super viewWillDisappear:animated];
  75. if (self.player) {
  76. if ( !line.hidden) {
  77. [self invalidatePlayer];
  78. }
  79. }
  80. // [self.player pause];
  81. }
  82. #pragma mark 释放引用
  83. - (void)invalidatePlayer{
  84. [self youpaifstopTimer];
  85. [self.player removeObserver:self forKeyPath:@"timeControlStatus"];
  86. [self.player pause];
  87. [self.playItem removeObserver:self forKeyPath:@"status"];
  88. }
  89. #pragma mark 自定义方法
  90. - (void)initFunctions{
  91. // 手机静音时可播放声音
  92. AVAudioSession *session = [AVAudioSession sharedInstance];
  93. [session setActive:YES error:nil];
  94. [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
  95. [session setCategory:AVAudioSessionCategoryPlayback error:nil];
  96. bottomView = [[UIView alloc] initWithFrame:CGRectMake(0,SCREEN_HEIGHT-120, SCREEN_WIDTH,50)];
  97. // bottomView.backgroundColor = HexColorFromRGB(0xF6F6F6);
  98. // bottomView.backgroundColor = [UIColor yellowColor];
  99. [self.view addSubview:bottomView];
  100. editScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, 50)];
  101. editScrollView.showsHorizontalScrollIndicator = NO;
  102. editScrollView.bounces = NO;
  103. editScrollView.backgroundColor = HexColorFromRGB(0xF6F6F6);
  104. // editScrollView.backgroundColor = [UIColor redColor];
  105. [bottomView addSubview:editScrollView];
  106. editScrollView.delegate = self;
  107. // 添加编辑框上下边线
  108. self.boderX = 45;
  109. self.boderWidth = SCREEN_WIDTH-90;
  110. topBorder = [[UIView alloc] initWithFrame:CGRectMake(self.boderX,0, self.boderWidth, 2)];
  111. topBorder.backgroundColor = HexColorFromRGB(0xFF5CA2);
  112. [bottomView addSubview:topBorder];
  113. bottomBorder = [[UIView alloc] initWithFrame:CGRectMake(self.boderX, 50-2, self.boderWidth, 2)];
  114. bottomBorder.backgroundColor = HexColorFromRGB(0xFF5CA2);
  115. [bottomView addSubview:bottomBorder];
  116. // 添加左右编辑框拖动条
  117. leftDragView = [[YOUPAIDragEditView alloc] initWithFrame:CGRectMake(-(SCREEN_WIDTH-50), 0, SCREEN_WIDTH, 50) Left:YES];
  118. leftDragView.hitTestEdgeInsets = UIEdgeInsetsMake(0, -(EDGE_EXTENSION_FOR_THUMB), 0, -(EDGE_EXTENSION_FOR_THUMB));
  119. [bottomView addSubview:leftDragView];
  120. rightDragView = [[YOUPAIDragEditView alloc] initWithFrame:CGRectMake((SCREEN_WIDTH-50), 0, SCREEN_WIDTH, 50) Left:NO];
  121. rightDragView.hitTestEdgeInsets = UIEdgeInsetsMake(0, -(EDGE_EXTENSION_FOR_THUMB), 0, -(EDGE_EXTENSION_FOR_THUMB));
  122. [bottomView addSubview:rightDragView];
  123. UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(moveOverlayView:)];
  124. [bottomView addGestureRecognizer:panGestureRecognizer];
  125. // 播放条
  126. line = [[UIView alloc] initWithFrame:CGRectMake(10, 0, 3, 50)];
  127. line.backgroundColor = [UIColor colorWithRed:214/255.0 green:230/255.0 blue:247/255.0 alpha:1.0];
  128. [bottomView addSubview:line];
  129. line.hidden = YES;
  130. // UIButton *doneBtn = [[UIButton alloc] initWithFrame:CGRectMake(SCREEN_WIDTH-60, 50, 60, 30)];
  131. // [doneBtn setTitle:@"完成" forState:UIControlStateNormal];
  132. // [doneBtn setTitleColor:[UIColor colorWithRed:14/255.0 green:178/255.0 blue:10/255.0 alpha:1.0] forState:UIControlStateNormal];
  133. // [doneBtn addTarget:self action:@selector(notifyDelegateOfDidChange) forControlEvents:UIControlEventTouchUpInside];
  134. // [bottomView addSubview:doneBtn];
  135. // 默认startTime 0秒 endTime 10秒
  136. CGFloat videoTime = [self avAssetVideoTrackDuration];
  137. NSLog(@"videoTime = %lf",videoTime);
  138. self.startTime = 0;
  139. self.endTime = videoTime > 60 ? 60 :videoTime;
  140. self.startPointX = 0;
  141. self.endPointX = SCREEN_WIDTH;
  142. if (videoTime> 60) {
  143. self.IMG_Width = (SCREEN_WIDTH-100)/60;
  144. }else{
  145. self.IMG_Width = (SCREEN_WIDTH-100)/videoTime;
  146. }
  147. }
  148. - (CGFloat)avAssetVideoTrackDuration{
  149. AVAsset *asset = [AVAsset assetWithURL:self.videoUrl];
  150. NSArray *videoTracks = [asset tracksWithMediaType:AVMediaTypeVideo];
  151. if (videoTracks.count) {
  152. AVAssetTrack *track = videoTracks[0];
  153. return CMTimeGetSeconds(CMTimeRangeGetEnd(track.timeRange));
  154. }
  155. NSArray *audioTracks = [asset tracksWithMediaType:AVMediaTypeAudio];
  156. if (audioTracks.count) {
  157. AVAssetTrack *track = audioTracks[0];
  158. return CMTimeGetSeconds(CMTimeRangeGetEnd(track.timeRange));
  159. }
  160. return -1;
  161. }
  162. #pragma mark 编辑区域手势拖动
  163. - (void)moveOverlayView:(UIPanGestureRecognizer *)gesture{
  164. switch (gesture.state) {
  165. case UIGestureRecognizerStateBegan:
  166. {
  167. [self youpaifstopTimer];
  168. BOOL isRight = [rightDragView youpaifpointInsideImgView:[gesture locationInView:rightDragView]];
  169. BOOL isLeft = [leftDragView youpaifpointInsideImgView:[gesture locationInView:leftDragView]];
  170. _isDraggingRightOverlayView = NO;
  171. _isDraggingLeftOverlayView = NO;
  172. self.touchPointX = [gesture locationInView:bottomView].x;
  173. if (isRight){
  174. self.rightStartPoint = [gesture locationInView:bottomView];
  175. _isDraggingRightOverlayView = YES;
  176. _isDraggingLeftOverlayView = NO;
  177. }
  178. else if (isLeft){
  179. self.leftStartPoint = [gesture locationInView:bottomView];
  180. _isDraggingRightOverlayView = NO;
  181. _isDraggingLeftOverlayView = YES;
  182. }
  183. }
  184. break;
  185. case UIGestureRecognizerStateChanged:
  186. {
  187. CGPoint point = [gesture locationInView:bottomView];
  188. // Left
  189. if (_isDraggingLeftOverlayView){
  190. CGFloat deltaX = point.x - self.leftStartPoint.x;
  191. CGPoint center = leftDragView.center;
  192. center.x += deltaX;
  193. CGFloat durationTime = (SCREEN_WIDTH-100)*2/10; // 最小范围2秒
  194. BOOL flag = (self.endPointX-point.x)>durationTime;
  195. if (center.x >= (50-SCREEN_WIDTH/2) && flag) {
  196. leftDragView.center = center;
  197. self.leftStartPoint = point;
  198. self.startTime = (point.x+editScrollView.contentOffset.x)/self.IMG_Width;
  199. topBorder.frame = CGRectMake(self.boderX+=deltaX/2, 0, self.boderWidth-=deltaX/2, 2);
  200. bottomBorder.frame = CGRectMake(self.boderX+=deltaX/2, 50-2, self.boderWidth-=deltaX/2, 2);
  201. self.startPointX = point.x;
  202. }
  203. CMTime startTime = CMTimeMakeWithSeconds((point.x+editScrollView.contentOffset.x)/self.IMG_Width, self.player.currentTime.timescale);
  204. // 只有视频播放的时候才能够快进和快退1秒以内
  205. [self.player seekToTime:startTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
  206. }
  207. else if (_isDraggingRightOverlayView){ // Right
  208. CGFloat deltaX = point.x - self.rightStartPoint.x;
  209. CGPoint center = rightDragView.center;
  210. center.x += deltaX;
  211. CGFloat durationTime = (SCREEN_WIDTH-100)*2/10; // 最小范围2秒
  212. BOOL flag = (point.x- self.startPointX)>durationTime;
  213. if (center.x <= (SCREEN_WIDTH-50+SCREEN_WIDTH/2) && flag) {
  214. rightDragView.center = center;
  215. self.rightStartPoint = point;
  216. self.endTime = (point.x+editScrollView.contentOffset.x)/self.IMG_Width;
  217. topBorder.frame = CGRectMake(self.boderX, 0, self.boderWidth+=deltaX/2, 2);
  218. bottomBorder.frame = CGRectMake(self.boderX, 50-2, self.boderWidth+=deltaX/2, 2);
  219. self.endPointX = point.x;
  220. }
  221. CMTime startTime = CMTimeMakeWithSeconds((point.x+editScrollView.contentOffset.x)/self.IMG_Width, self.player.currentTime.timescale);
  222. // 只有视频播放的时候才能够快进和快退1秒以内
  223. [self.player seekToTime:startTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
  224. }
  225. else { // 移动scrollView
  226. CGFloat deltaX = point.x - self.touchPointX;
  227. CGFloat newOffset = editScrollView.contentOffset.x-deltaX;
  228. CGPoint currentOffSet = CGPointMake(newOffset, 0);
  229. if (currentOffSet.x >= 0 && currentOffSet.x <= (editScrollView.contentSize.width-SCREEN_WIDTH)) {
  230. editScrollView.contentOffset = CGPointMake(newOffset, 0);
  231. self.touchPointX = point.x;
  232. }
  233. }
  234. }
  235. break;
  236. case UIGestureRecognizerStateEnded:
  237. {
  238. [self startTimer];
  239. }
  240. break;
  241. default:
  242. break;
  243. }
  244. }
  245. #pragma mark 视频裁剪
  246. - (void)notifyDelegateOfDidChange{
  247. self.tempVideoPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"tailor.mp4"];
  248. [self deleteTempFile];
  249. AVAsset *asset = [AVAsset assetWithURL:self.videoUrl];
  250. AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]
  251. initWithAsset:asset presetName:AVAssetExportPresetPassthrough];
  252. NSURL *furl = [NSURL fileURLWithPath:self.tempVideoPath];
  253. exportSession.outputURL = furl;
  254. exportSession.outputFileType = AVFileTypeQuickTimeMovie;
  255. CMTime start = CMTimeMakeWithSeconds(self.startTime, self.player.currentTime.timescale);
  256. CMTime duration = CMTimeMakeWithSeconds(self.endTime - self.startTime, self.player.currentTime.timescale);;
  257. CMTimeRange range = CMTimeRangeMake(start, duration);
  258. exportSession.timeRange = range;
  259. [exportSession exportAsynchronouslyWithCompletionHandler:^{
  260. switch ([exportSession status]) {
  261. case AVAssetExportSessionStatusFailed:
  262. NSLog(@"Export failed: %@", [[exportSession error] localizedDescription]);
  263. break;
  264. case AVAssetExportSessionStatusCancelled:
  265. NSLog(@"Export canceled");
  266. break;
  267. case AVAssetExportSessionStatusCompleted:{
  268. NSLog(@"Export completed");
  269. __weak typeof(self) weakSelf = self;
  270. dispatch_async(dispatch_get_main_queue(), ^{
  271. // UISaveVideoAtPathToSavedPhotosAlbum([furl relativePath], self,@selector(video:didFinishSavingWithError:contextInfo:), nil);
  272. NSLog(@"编辑后的视频路径: %@",weakSelf.tempVideoPath);
  273. weakSelf.isEdited = YES;
  274. // [weakSelf invalidatePlayer];
  275. // [weakSelf initPlayerWithVideoUrl:furl];
  276. // self->bottomView.hidden = YES;
  277. // NSURL *url = [NSURL URLWithString:weakSelf.tempVideoPath];
  278. AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:furl options:nil];
  279. AVAssetImageGenerator *assetGen = [[AVAssetImageGenerator alloc] initWithAsset:asset];
  280. assetGen.appliesPreferredTrackTransform = YES;
  281. CMTime time = CMTimeMakeWithSeconds(0.0, 600);
  282. NSError *error = nil;
  283. CMTime actualTime;
  284. CGImageRef image = [assetGen copyCGImageAtTime:time actualTime:&actualTime error:&error];
  285. UIImage *videoImage = [[UIImage alloc] initWithCGImage:image];
  286. CGImageRelease(image);
  287. YOUPAIZYSendVideoByLocalVC *vc = [[YOUPAIZYSendVideoByLocalVC alloc] init];
  288. vc.youpaipcoverImage = videoImage;
  289. vc.youpaipvideoFileName = furl.absoluteString;
  290. [self pushEffectPresentToVC:vc];
  291. // [self dismissViewControllerAnimated:YES completion:^{
  292. // YOUPAIZYSendVideoByLocalVC *vc = [[YOUPAIZYSendVideoByLocalVC alloc] init];
  293. // vc.coverImage = oneImg;
  294. // vc.videoFileName = weakSelf.tempVideoPath;
  295. // [self pushEffectPresentToVC:vc];
  296. // }];
  297. });
  298. }
  299. break;
  300. default:
  301. NSLog(@"Export other");
  302. break;
  303. }
  304. }];
  305. }
  306. - (void)video:(NSString*)videoPath didFinishSavingWithError:(NSError*)error contextInfo:(void*)contextInfo {
  307. if (error) {
  308. NSLog(@"保存到相册失败");
  309. }
  310. else {
  311. NSLog(@"保存到相册成功");
  312. }
  313. }
  314. - (void)deleteTempFile{
  315. NSURL *url = [NSURL fileURLWithPath:self.tempVideoPath];
  316. NSFileManager *fm = [NSFileManager defaultManager];
  317. BOOL exist = [fm fileExistsAtPath:url.path];
  318. NSError *err;
  319. if (exist) {
  320. [fm removeItemAtURL:url error:&err];
  321. NSLog(@"file deleted");
  322. if (err) {
  323. NSLog(@"file remove error, %@", err.localizedDescription );
  324. }
  325. }
  326. else {
  327. NSLog(@"no file by that name");
  328. }
  329. }
  330. #pragma mark - 初始化player
  331. - (void)initPlayerWithVideoUrl:(NSURL *)videlUrl{
  332. self.playItem = [[AVPlayerItem alloc] initWithURL:videlUrl];
  333. [self.playItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
  334. self.player = [AVPlayer playerWithPlayerItem:self.playItem];
  335. [self.player addObserver:self forKeyPath:@"timeControlStatus" options:NSKeyValueObservingOptionNew context:nil];
  336. self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
  337. self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
  338. self.playerLayer.contentsScale = [UIScreen mainScreen].scale;
  339. self.playerLayer.frame = CGRectMake(0, 80, self.view.bounds.size.width, SCREEN_HEIGHT-80-120);
  340. [self.view.layer addSublayer:self.playerLayer];
  341. }
  342. #pragma mark - KVO属性播放属性监听
  343. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
  344. if ([keyPath isEqualToString:@"status"]) {
  345. switch (self.playItem.status) {
  346. case AVPlayerStatusUnknown:
  347. NSLog(@"KVO:未知状态,此时不能播放");
  348. break;
  349. case AVPlayerStatusReadyToPlay:
  350. if (!_player.timeControlStatus || _player.timeControlStatus != AVPlayerTimeControlStatusPaused) {
  351. [_player play];
  352. if (!self.isEdited) {
  353. line.hidden = NO;
  354. [self startTimer];
  355. }
  356. }
  357. NSLog(@"KVO:准备完毕,可以播放");
  358. break;
  359. case AVPlayerStatusFailed:
  360. NSLog(@"KVO:加载失败,网络或者服务器出现问题");
  361. break;
  362. default:
  363. break;
  364. }
  365. }
  366. if ([keyPath isEqualToString:@"timeControlStatus"]) {
  367. // 剪切完视频后自动循环播放
  368. if (self.player.timeControlStatus == AVPlayerTimeControlStatusPaused) {
  369. [self.player seekToTime:CMTimeMake(0, 1)];
  370. [self.player play];
  371. }
  372. }
  373. }
  374. #pragma mark - 开启计时器
  375. - (void)startTimer{
  376. double duarationTime = (self.endPointX-self.startPointX-20)/SCREEN_WIDTH*10;
  377. line.hidden = NO;
  378. self.linePositionX = self.startPointX+10;
  379. self.lineMoveTimer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(lineMove) userInfo:nil repeats:YES];
  380. // 开启循环播放
  381. self.repeatTimer = [NSTimer scheduledTimerWithTimeInterval:duarationTime target:self selector:@selector(repeatPlay) userInfo:nil repeats:YES];
  382. [self.repeatTimer fire];
  383. }
  384. #pragma mark - 编辑区域循环播放
  385. - (void)repeatPlay{
  386. [self.player play];
  387. CMTime start = CMTimeMakeWithSeconds(self.startTime, self.player.currentTime.timescale);
  388. [self.player seekToTime:start toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
  389. }
  390. #pragma mark - 播放条移动
  391. - (void)lineMove{
  392. double duarationTime = (self.endPointX-self.startPointX-20)/SCREEN_WIDTH*10;
  393. self.linePositionX += 0.01*(self.endPointX - self.startPointX-20)/duarationTime;
  394. if (self.linePositionX >= CGRectGetMinX(rightDragView.frame)-3) {
  395. self.linePositionX = CGRectGetMaxX(leftDragView.frame)+3;
  396. }
  397. line.frame = CGRectMake(self.linePositionX, 0, 3, 50);
  398. }
  399. #pragma mark - 关闭计时器
  400. - (void)youpaifstopTimer{
  401. [self.repeatTimer invalidate];
  402. [self.lineMoveTimer invalidate];
  403. line.hidden = YES;
  404. }
  405. #pragma mark - 读取解析视频帧
  406. - (void)analysisVideoFrames{
  407. // 初始化asset对象
  408. AVURLAsset *videoAsset = [[AVURLAsset alloc]initWithURL:self.videoUrl options:nil];
  409. // 获取总视频的长度 = 总帧数 / 每秒的帧数
  410. long videoSumTime = videoAsset.duration.value / videoAsset.duration.timescale;
  411. // 创建AVAssetImageGenerator对象
  412. AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc]initWithAsset:videoAsset];
  413. generator.maximumSize = bottomView.frame.size;
  414. generator.appliesPreferredTrackTransform = YES;
  415. generator.requestedTimeToleranceBefore = kCMTimeZero;
  416. generator.requestedTimeToleranceAfter = kCMTimeZero;
  417. // 添加需要帧数的时间集合
  418. self.framesArray = [NSMutableArray array];
  419. for (int i = 0; i < videoSumTime; i++) {
  420. CMTime time = CMTimeMake(i *videoAsset.duration.timescale , videoAsset.duration.timescale);
  421. NSValue *value = [NSValue valueWithCMTime:time];
  422. [self.framesArray addObject:value];
  423. }
  424. __block long count = 0;
  425. __weak typeof(self) weakSelf = self;
  426. [generator generateCGImagesAsynchronouslyForTimes:self.framesArray completionHandler:^(CMTime requestedTime, CGImageRef img, CMTime actualTime, AVAssetImageGeneratorResult result, NSError *error){
  427. if (result == AVAssetImageGeneratorSucceeded) {
  428. NSLog(@"%ld",count);
  429. UIImageView *thumImgView = [[UIImageView alloc] initWithFrame:CGRectMake(50+count*weakSelf.IMG_Width, 0, weakSelf.IMG_Width, 50)];
  430. thumImgView.image = [UIImage imageWithCGImage:img];
  431. dispatch_async(dispatch_get_main_queue(), ^{
  432. [self->editScrollView addSubview:thumImgView];
  433. self->editScrollView.contentSize = CGSizeMake(100+count*weakSelf.IMG_Width, 0);
  434. });
  435. count++;
  436. }
  437. if (result == AVAssetImageGeneratorFailed) {
  438. NSLog(@"Failed with error: %@", [error localizedDescription]);
  439. }
  440. if (result == AVAssetImageGeneratorCancelled) {
  441. NSLog(@"AVAssetImageGeneratorCancelled");
  442. }
  443. }];
  444. }
  445. #pragma mark - UIScrollViewDelegate
  446. - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
  447. [self youpaifstopTimer];
  448. }
  449. - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
  450. [self startTimer];
  451. }
  452. - (void)scrollViewDidScroll:(UIScrollView *)scrollView{
  453. [self letScrollViewScrollAndResetPlayerStartTime];
  454. // 视频暂停时可通过 AVPlayerItem 的API - (void)stepByCount:(NSInteger)stepCount; 滑动,目前未找到step的具体大小 官方文档说的不清楚
  455. // NSInteger step = offsetX/(50.0*self.framesArray.count)*72;
  456. // NSLog(@"移动步数:%ld",step);
  457. // if ([self.playItem canStepForward] && step > 0) {
  458. // [self.playItem stepByCount:step];
  459. // }
  460. //
  461. // if ([self.playItem canStepBackward] && step < 0) {
  462. // [self.playItem stepByCount:step];
  463. // }
  464. }
  465. #pragma mark - scrollView滑动时设置
  466. -(void)letScrollViewScrollAndResetPlayerStartTime{
  467. CGFloat offsetX = editScrollView.contentOffset.x;
  468. CMTime startTime;
  469. if (offsetX>=0) {
  470. startTime = CMTimeMakeWithSeconds((offsetX+self.startPointX)/self.IMG_Width, self.player.currentTime.timescale);
  471. CGFloat duration = self.endTime-self.startTime;
  472. self.startTime = (offsetX+self.startPointX)/self.IMG_Width;
  473. self.endTime = self.startTime+duration;
  474. }
  475. else {
  476. startTime = CMTimeMakeWithSeconds(self.startPointX, self.player.currentTime.timescale);
  477. }
  478. // 只有视频播放的时候才能够快进和快退1秒以内
  479. [self.player seekToTime:startTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
  480. }
  481. #pragma mark - setMethod
  482. - (void)setVideoUrl:(NSURL *)videoUrl{
  483. _videoUrl = videoUrl;
  484. if (!self.isEdit) {
  485. [self analysisVideoFrames];
  486. }
  487. else {
  488. leftDragView.hidden = YES;
  489. rightDragView.hidden = YES;
  490. }
  491. [self initPlayerWithVideoUrl:videoUrl];
  492. // UIButton *backBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 20, 60, 50)];
  493. // [backBtn setTitle:@"返回" forState:UIControlStateNormal];
  494. // [backBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
  495. // [backBtn addTarget:self action:@selector(dismissSelfVC) forControlEvents:UIControlEventTouchUpInside];
  496. // [self.view addSubview:backBtn];
  497. }
  498. - (void)dismissSelfVC{
  499. [self dismissViewControllerAnimated:YES completion:^{ }];
  500. }
  501. #pragma mark - getMethod
  502. - (NSMutableArray *)framesArray{
  503. if (!_framesArray) {
  504. _framesArray = [NSMutableArray array];
  505. }
  506. return _framesArray;
  507. }
  508. @end