QMPickedVideoViewController.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. //
  2. // QMPickedVideoViewController.m
  3. // IMSDK-OC
  4. //
  5. // Created by HCF on 16/8/10.
  6. // Copyright © 2016年 HCF. All rights reserved.
  7. //
  8. #import "QMPickedVideoViewController.h"
  9. #import <Photos/Photos.h>
  10. #import "QMVideoTableCell.h"
  11. #import "QMFileTabbarView.h"
  12. #import "QMChatRoomViewController.h"
  13. @interface QMPickedVideoViewController () <UITableViewDelegate, UITableViewDataSource> {
  14. UITableView *_tableView;
  15. PHFetchResult *_photoAssets;
  16. PHCachingImageManager *_cacheManager;
  17. QMFileTabbarView *_tabbarView;
  18. CGFloat _navHeight;
  19. }
  20. @property (nonatomic, strong) NSMutableSet *pickedImageSet;
  21. @end
  22. @implementation QMPickedVideoViewController
  23. - (void)viewDidLoad {
  24. [super viewDidLoad];
  25. CGRect StatusRect = [[UIApplication sharedApplication] statusBarFrame];
  26. CGRect NavRect = self.navigationController.navigationBar.frame;
  27. _navHeight = StatusRect.size.height + NavRect.size.height;
  28. self.view.backgroundColor = [UIColor whiteColor];
  29. _cacheManager = [[PHCachingImageManager alloc] init];
  30. self.pickedImageSet = [[NSMutableSet alloc] init];
  31. _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight-_navHeight-44) style:UITableViewStylePlain];
  32. _tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
  33. _tableView.backgroundColor = [UIColor whiteColor];
  34. _tableView.delegate = self;
  35. _tableView.dataSource = self;
  36. [self.view addSubview:_tableView];
  37. [_tableView registerClass:[QMVideoTableCell class] forCellReuseIdentifier:NSStringFromClass(QMVideoTableCell.self)];
  38. _tabbarView = [[QMFileTabbarView alloc] init];
  39. _tabbarView.frame = CGRectMake(0, kScreenHeight-44-_navHeight, kScreenWidth, 44);
  40. [self.view addSubview:_tabbarView];
  41. __weak QMPickedVideoViewController *strongSelf = self;
  42. _tabbarView.selectAction = ^{
  43. QMChatRoomViewController * tagViewController = nil;
  44. for (UIViewController *viewController in strongSelf.navigationController.viewControllers) {
  45. if ([viewController isKindOfClass:[QMChatRoomViewController class]]) {
  46. tagViewController = (QMChatRoomViewController *)viewController;
  47. [strongSelf.navigationController popToViewController:tagViewController animated:true];
  48. for (PHAsset *asset in strongSelf.pickedImageSet) {
  49. PHVideoRequestOptions* options = [[PHVideoRequestOptions alloc] init];
  50. options.version = PHVideoRequestOptionsVersionOriginal;
  51. options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic;
  52. options.networkAccessAllowed = YES;
  53. [[PHImageManager defaultManager] requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset* avasset, AVAudioMix* audioMix, NSDictionary* info){
  54. NSURL *videoURL = [(AVURLAsset *)avasset URL];
  55. // name
  56. NSString *photoPath = videoURL.absoluteString;
  57. NSArray *array = [photoPath componentsSeparatedByString:@"/"];
  58. AVURLAsset *videoAsset = (AVURLAsset*)avasset;
  59. [strongSelf startExportVideoWithVideoAsset:videoAsset name: array.lastObject completion:^(NSString *outputPath) {
  60. if (!outputPath) {
  61. NSData *videoData = [NSData dataWithContentsOfURL:videoURL];
  62. [videoData writeToFile:outputPath atomically:YES];
  63. }
  64. NSNumber *fileSizeValue = nil;
  65. [[NSURL fileURLWithPath:outputPath] getResourceValue:&fileSizeValue forKey:NSURLFileSizeKey error:nil];
  66. NSString *fileSize = fileSizeValue.intValue > 1024 ? [NSString stringWithFormat:@"%.f MB", (float)fileSizeValue.intValue/1024/1024] : [NSString stringWithFormat:@"%.f KB", (float)fileSizeValue.intValue/1024];
  67. [tagViewController sendFileMessageWithName:array.lastObject AndSize:fileSize AndPath:array.lastObject];
  68. }];
  69. }];
  70. }
  71. }
  72. }
  73. };
  74. dispatch_async(dispatch_get_main_queue(), ^{
  75. PHFetchOptions *options = [[PHFetchOptions alloc] init];
  76. options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
  77. _photoAssets = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeVideo options:options];
  78. [_tableView reloadData];
  79. });
  80. }
  81. - (void)startExportVideoWithVideoAsset:(AVURLAsset *)videoAsset name:(NSString *)name completion:(void (^)(NSString *outputPath))completion {
  82. // Find compatible presets by video asset.
  83. // NSLog(@"%@", videoAsset);
  84. NSArray *presets = [AVAssetExportSession exportPresetsCompatibleWithAsset:videoAsset];
  85. if ([presets containsObject:AVAssetExportPreset640x480]) {
  86. AVAssetExportSession *session = [[AVAssetExportSession alloc]initWithAsset:videoAsset presetName:AVAssetExportPreset640x480];
  87. NSString * outputPath = [NSString stringWithFormat:@"%@/%@", NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0], name];
  88. session.outputURL = [NSURL fileURLWithPath:outputPath];
  89. session.shouldOptimizeForNetworkUse = true;
  90. NSArray *supportedTypeArray = session.supportedFileTypes;
  91. if ([supportedTypeArray containsObject:AVFileTypeMPEG4]) {
  92. session.outputFileType = AVFileTypeMPEG4;
  93. } else if (supportedTypeArray.count == 0) {
  94. return;
  95. } else {
  96. session.outputFileType = [supportedTypeArray objectAtIndex:0];
  97. }
  98. if (![[NSFileManager defaultManager] fileExistsAtPath:[NSHomeDirectory() stringByAppendingFormat:@"/tmp"]]) {
  99. [[NSFileManager defaultManager] createDirectoryAtPath:[NSHomeDirectory() stringByAppendingFormat:@"/tmp"] withIntermediateDirectories:YES attributes:nil error:nil];
  100. }
  101. AVMutableVideoComposition *videoComposition = [self fixedCompositionWithAsset:videoAsset];
  102. if (videoComposition.renderSize.width) {
  103. // 修正视频转向
  104. session.videoComposition = videoComposition;
  105. }
  106. // Begin to export video to the output path asynchronously.
  107. [session exportAsynchronouslyWithCompletionHandler:^(void) {
  108. switch (session.status) {
  109. case AVAssetExportSessionStatusCompleted: {
  110. dispatch_async(dispatch_get_main_queue(), ^{
  111. if (completion) {
  112. completion(outputPath);
  113. }
  114. });
  115. } break;
  116. default: {
  117. if ([[NSFileManager defaultManager] fileExistsAtPath:outputPath]) {
  118. if (completion) {
  119. completion(outputPath);
  120. }
  121. }else {
  122. completion(nil);
  123. }
  124. }
  125. break;
  126. }
  127. }];
  128. }
  129. }
  130. /// 获取优化后的视频转向信息
  131. - (AVMutableVideoComposition *)fixedCompositionWithAsset:(AVAsset *)videoAsset {
  132. AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition];
  133. // 视频转向
  134. int degrees = [self degressFromVideoFileWithAsset:videoAsset];
  135. if (degrees != 0) {
  136. CGAffineTransform translateToCenter;
  137. CGAffineTransform mixedTransform;
  138. videoComposition.frameDuration = CMTimeMake(1, 30);
  139. NSArray *tracks = [videoAsset tracksWithMediaType:AVMediaTypeVideo];
  140. AVAssetTrack *videoTrack = [tracks objectAtIndex:0];
  141. AVMutableVideoCompositionInstruction *roateInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
  142. roateInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, [videoAsset duration]);
  143. AVMutableVideoCompositionLayerInstruction *roateLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
  144. if (degrees == 90) {
  145. // 顺时针旋转90°
  146. translateToCenter = CGAffineTransformMakeTranslation(videoTrack.naturalSize.height, 0.0);
  147. mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI_2);
  148. videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.height,videoTrack.naturalSize.width);
  149. [roateLayerInstruction setTransform:mixedTransform atTime:kCMTimeZero];
  150. } else if(degrees == 180){
  151. // 顺时针旋转180°
  152. translateToCenter = CGAffineTransformMakeTranslation(videoTrack.naturalSize.width, videoTrack.naturalSize.height);
  153. mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI);
  154. videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.width,videoTrack.naturalSize.height);
  155. [roateLayerInstruction setTransform:mixedTransform atTime:kCMTimeZero];
  156. } else if(degrees == 270){
  157. // 顺时针旋转270°
  158. translateToCenter = CGAffineTransformMakeTranslation(0.0, videoTrack.naturalSize.width);
  159. mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI_2*3.0);
  160. videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.height,videoTrack.naturalSize.width);
  161. [roateLayerInstruction setTransform:mixedTransform atTime:kCMTimeZero];
  162. }
  163. roateInstruction.layerInstructions = @[roateLayerInstruction];
  164. // 加入视频方向信息
  165. videoComposition.instructions = @[roateInstruction];
  166. }
  167. return videoComposition;
  168. }
  169. /// 获取视频角度
  170. - (int)degressFromVideoFileWithAsset:(AVAsset *)asset {
  171. int degress = 0;
  172. NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
  173. if([tracks count] > 0) {
  174. AVAssetTrack *videoTrack = [tracks objectAtIndex:0];
  175. CGAffineTransform t = videoTrack.preferredTransform;
  176. if(t.a == 0 && t.b == 1.0 && t.c == -1.0 && t.d == 0){
  177. // Portrait
  178. degress = 90;
  179. } else if(t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0){
  180. // PortraitUpsideDown
  181. degress = 270;
  182. } else if(t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0){
  183. // LandscapeRight
  184. degress = 0;
  185. } else if(t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0){
  186. // LandscapeLeft
  187. degress = 180;
  188. }
  189. }
  190. return degress;
  191. }
  192. - (void)dealloc {
  193. }
  194. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
  195. return 80.0;
  196. }
  197. - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
  198. return 0.1;
  199. }
  200. - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
  201. return 0.1;
  202. }
  203. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  204. return _photoAssets ? _photoAssets.count : 0;
  205. }
  206. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  207. QMVideoTableCell * cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass(QMVideoTableCell.self) forIndexPath:indexPath];
  208. cell.selectionStyle = UITableViewCellSelectionStyleNone;
  209. return cell;
  210. }
  211. - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
  212. if ([cell isKindOfClass:[QMVideoTableCell class]]) {
  213. QMVideoTableCell * displayCell = (QMVideoTableCell *)cell;
  214. displayCell.imageManager = _cacheManager;
  215. if (_photoAssets) {
  216. PHAsset *asset = _photoAssets[indexPath.row];
  217. displayCell.imageAsset = asset;
  218. displayCell.pickedItemImageView.hidden = ![self.pickedImageSet containsObject:asset];
  219. }
  220. }
  221. }
  222. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  223. [tableView deselectRowAtIndexPath:indexPath animated:NO];
  224. if (_photoAssets) {
  225. PHAsset *asset = _photoAssets[indexPath.row];
  226. if ([self.pickedImageSet containsObject:asset]) {
  227. [self.pickedImageSet removeObject:asset];
  228. }else {
  229. if (self.pickedImageSet.count>0) {
  230. return;
  231. }
  232. [self.pickedImageSet addObject:asset];
  233. }
  234. QMVideoTableCell * cell = (QMVideoTableCell *)[tableView cellForRowAtIndexPath:indexPath];
  235. cell.pickedItemImageView.hidden = ![self.pickedImageSet containsObject:asset];
  236. }
  237. if (self.pickedImageSet.count>0) {
  238. _tabbarView.doneButton.selected = YES;
  239. }else {
  240. _tabbarView.doneButton.selected = NO;
  241. }
  242. }
  243. - (void)didReceiveMemoryWarning {
  244. [super didReceiveMemoryWarning];
  245. // Dispose of any resources that can be recreated.
  246. }
  247. @end