| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898 |
- //
- // TUIChatMediaDataProvider.m
- // TUIChat
- //
- // Created by harvy on 2022/12/20.
- // Copyright © 2023 Tencent. All rights reserved.
- //
- #import "TUIChatMediaDataProvider.h"
- #import <AssetsLibrary/AssetsLibrary.h>
- #import <MobileCoreServices/MobileCoreServices.h>
- #import <Photos/Photos.h>
- #import <PhotosUI/PhotosUI.h>
- #import <SDWebImage/SDWebImage.h>
- #import <TIMCommon/TIMDefine.h>
- #import <TIMCommon/TUIUserAuthorizationCenter.h>
- #import <TIMCommon/NSTimer+TUISafe.h>
- #import <TUICore/TUITool.h>
- #import <TUICore/TUICore.h>
- #import "TUICameraViewController.h"
- #import "TUIChatConfig.h"
- #import "AlbumPicker.h"
- #import "MultimediaRecorder.h"
- #define kTUIChatMediaSelectImageMax 9
- @interface TUIChatMediaDataProvider () <PHPickerViewControllerDelegate,
- UINavigationControllerDelegate,
- UIImagePickerControllerDelegate,
- UIDocumentPickerDelegate,
- TUICameraViewControllerDelegate>
- @end
- @implementation TUIChatMediaDataProvider
- #pragma mark - Public API
- - (void)selectPhoto {
- if ([AlbumPicker sharedInstance].advancedAlbumPicker) {
- __weak typeof(self) weakSelf = self;
- __strong typeof(weakSelf.listener) strongListener = weakSelf.listener;
- [[AlbumPicker sharedInstance].advancedAlbumPicker pickMediaWithCaller:self.presentViewController originalMediaPicked:^(NSDictionary *param) {
- if (param) {
- NSString * type = param[@"type"];
- if ([type isEqualToString:@"image"]) {
- // image do nothing
- }
- else if ([type isEqualToString:@"video"]) {
- TUIMessageCellData *placeHolderCellData = param[@"placeHolderCellData"];
- if ([strongListener respondsToSelector:@selector(sendPlaceHolderUIMessage:)]) {
- [strongListener sendPlaceHolderUIMessage:placeHolderCellData];
- }
- TUIChatMediaTask * task = [[TUIChatMediaTask alloc] init];
- task.placeHolderCellData = placeHolderCellData;
- task.msgID = placeHolderCellData.msgID;
- task.conversationID = weakSelf.conversationID;
- if (placeHolderCellData.msgID.length > 0) {
- [TUIChatMediaSendingManager.sharedInstance addMediaTask: task forKey:placeHolderCellData.msgID];
- }
- }
- else {
- // do nothing
- }
- }
- } progressCallback:^(NSDictionary *param) {
- NSLog(@"%@,strongListener:%@",param,strongListener);
- } finishedCallback:^(NSDictionary *param) {
- if (param) {
- V2TIMMessage * message = param[@"message"];
- NSString * type = param[@"type"];
- if ([type isEqualToString:@"image"]) {
- if ([strongListener respondsToSelector:@selector(sendMessage:placeHolderCellData:)]) {
- [strongListener sendMessage:message placeHolderCellData:nil];
- }
- }
- else if ([type isEqualToString:@"video"]) {
- TUIMessageCellData *placeHolderCellData = param[@"placeHolderCellData"];
- if (placeHolderCellData.msgID.length > 0) {
- [TUIChatMediaSendingManager.sharedInstance removeMediaTaskForKey:placeHolderCellData.msgID];
- }
- BOOL canSendByCurrentPage = NO;
- for (id<TUIChatMediaDataListener> currentVC in TUIChatMediaSendingManager.sharedInstance.mediaSendingControllers) {
- if ([currentVC.currentConversationID isEqualToString:self.conversationID]&&
- [currentVC respondsToSelector:@selector(sendMessage:placeHolderCellData:)]) {
- if (currentVC.isPageAppears) {
- [currentVC sendMessage:message placeHolderCellData:placeHolderCellData];
- canSendByCurrentPage = YES;
- break;
- }
- }
- }
- if (!canSendByCurrentPage) {
- if ([strongListener respondsToSelector:@selector(sendMessage:placeHolderCellData:)]) {
- [strongListener sendMessage:message placeHolderCellData:placeHolderCellData];
- }
- }
- }
- else {
- // do nothing
- }
- }
- }];
- }
- else {
- //defalut AlbumPicker
- [self _selectPhoto];
- }
- }
- - (void)_selectPhoto {
- dispatch_async(dispatch_get_main_queue(), ^{
- if (@available(iOS 14.0, *)) {
- PHPickerConfiguration *configuration = [[PHPickerConfiguration alloc] init];
- configuration.filter = [PHPickerFilter anyFilterMatchingSubfilters:@[ [PHPickerFilter imagesFilter], [PHPickerFilter videosFilter] ]];
- configuration.selectionLimit = kTUIChatMediaSelectImageMax;
- PHPickerViewController *picker = [[PHPickerViewController alloc] initWithConfiguration:configuration];
- picker.delegate = self;
- picker.modalPresentationStyle = UIModalPresentationFullScreen;
- picker.view.backgroundColor = [UIColor whiteColor];
- [self.presentViewController presentViewController:picker animated:YES completion:nil];
- } else {
- if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
- UIImagePickerController *picker = [[UIImagePickerController alloc] init];
- picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
- picker.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypePhotoLibrary];
- picker.delegate = self;
- [self.presentViewController presentViewController:picker animated:YES completion:nil];
- }
- }
- });
- }
- - (void)takePicture {
- if ([MultimediaRecorder sharedInstance].advancedVideoRecorder) {
- [[MultimediaRecorder sharedInstance].advancedVideoRecorder takePhoneWithCaller:self.presentViewController successBlock:^(NSURL * _Nonnull uri) {
- NSData *imageData = [NSData dataWithContentsOfURL:uri];
- UIImage *photo = [UIImage imageWithData:imageData];
- NSString *path = [TUIKit_Image_Path stringByAppendingString:[TUITool genImageName:nil]];
- [[NSFileManager defaultManager] createFileAtPath:path
- contents:UIImagePNGRepresentation(photo) attributes:nil];
- if ([self.listener respondsToSelector:@selector(onProvideImage:)]) {
- [self.listener onProvideImage:path];
- }
- } failureBlock:^(NSInteger errorCode, NSString * _Nonnull errorMessage) {
-
- }];
- }
- else {
- //defalut PhotoCamera
- [self _takePicture];
- }
- }
- - (void)_takePicture {
- __weak typeof(self) weakSelf = self;
- void (^actionBlock)(void) = ^(void) {
- TUICameraViewController *vc = [[TUICameraViewController alloc] init];
- vc.type = TUICameraMediaTypePhoto;
- vc.delegate = weakSelf;
- if (weakSelf.presentViewController.navigationController) {
- [weakSelf.presentViewController.navigationController pushViewController:vc animated:YES];
- } else {
- [weakSelf.presentViewController presentViewController:vc animated:YES completion:nil];
- }
- };
- if ([TUIUserAuthorizationCenter isEnableCameraAuthorization]) {
- dispatch_async(dispatch_get_main_queue(), ^{
- actionBlock();
- });
- } else {
- if (![TUIUserAuthorizationCenter isEnableCameraAuthorization]) {
- [TUIUserAuthorizationCenter cameraStateActionWithPopCompletion:^{
- dispatch_async(dispatch_get_main_queue(), ^{
- actionBlock();
- });
- }];
- };
- }
- }
- - (void)executeBlockWithMicroAndCameraAuth:(void(^)(void))block{
- if ([TUIUserAuthorizationCenter isEnableMicroAuthorization] && [TUIUserAuthorizationCenter isEnableCameraAuthorization]) {
- dispatch_async(dispatch_get_main_queue(), block);
- } else {
- if (![TUIUserAuthorizationCenter isEnableMicroAuthorization]) {
- [TUIUserAuthorizationCenter microStateActionWithPopCompletion:^{
- if ([TUIUserAuthorizationCenter isEnableCameraAuthorization]) {
- dispatch_async(dispatch_get_main_queue(), block);
- }
- }];
- }
- if (![TUIUserAuthorizationCenter isEnableCameraAuthorization]) {
- [TUIUserAuthorizationCenter cameraStateActionWithPopCompletion:^{
- if ([TUIUserAuthorizationCenter isEnableMicroAuthorization]) {
- dispatch_async(dispatch_get_main_queue(), block);
- }
- }];
- }
- }
- }
- - (void)takeVideo {
- if ([MultimediaRecorder sharedInstance].advancedVideoRecorder) {
- [[MultimediaRecorder sharedInstance].advancedVideoRecorder recordVideoWithCaller:self.presentViewController successBlock:^(NSURL * _Nonnull uri) {
- if (uri) {
- if ([uri.pathExtension.lowercaseString isEqualToString:@"mp4"]) {
- [self handleVideoPick:YES message:nil videoUrl:uri];
- return;
- }
- else if ([self isImageURL:uri]){
- NSData *imageData = [NSData dataWithContentsOfURL:uri];
- UIImage *photo = [UIImage imageWithData:imageData];
- NSString *path = [TUIKit_Image_Path stringByAppendingString:[TUITool genImageName:nil]];
- [[NSFileManager defaultManager] createFileAtPath:path
- contents:UIImagePNGRepresentation(photo) attributes:nil];
- if ([self.listener respondsToSelector:@selector(onProvideImage:)]) {
- [self.listener onProvideImage:path];
- }
- }
- else {
- [self transcodeIfNeed:YES message:nil videoUrl:uri];
- }
- }
- } failureBlock:^(NSInteger errorCode, NSString * _Nonnull errorMessage) {
-
- }];
-
- return;
- }
- else {
- //defalut VideoRecorder
- [self _takeVideo];
- }
- }
- - (void)_takeVideo {
- __weak typeof(self) weakSelf = self;
- void (^actionBlock)(void) = ^(void) {
- TUICameraViewController *vc = [[TUICameraViewController alloc] init];
- vc.type = TUICameraMediaTypeVideo;
- vc.videoMinimumDuration = 1.5;
- vc.delegate = weakSelf;
- if ([TUIChatConfig defaultConfig].maxVideoRecordDuration > 0) {
- vc.videoMaximumDuration = [TUIChatConfig defaultConfig].maxVideoRecordDuration;
- }
- if (weakSelf.presentViewController.navigationController) {
- [weakSelf.presentViewController.navigationController pushViewController:vc animated:YES];
- } else {
- [weakSelf.presentViewController presentViewController:vc animated:YES completion:nil];
- }
- };
-
- [self executeBlockWithMicroAndCameraAuth:actionBlock];
- }
- - (void)selectFile {
- UIDocumentPickerViewController *picker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[ (NSString *)kUTTypeData ]
- inMode:UIDocumentPickerModeOpen];
- picker.delegate = self;
- [self.presentViewController presentViewController:picker animated:YES completion:nil];
- }
- - (BOOL)isImageURL:(NSURL *)url {
- NSArray *imageExtensions = @[@"jpg", @"jpeg", @"png", @"gif", @"bmp", @"tiff", @"webp", @"heic"];
- NSString *pathExtension = url.pathExtension.lowercaseString;
- return [imageExtensions containsObject:pathExtension];
- }
- #pragma mark - Private Do task
- - (void)handleImagePick:(BOOL)succ message:(NSString *)message imageData:(NSData *)imageData {
- static NSDictionary *imageFormatExtensionMap = nil;
- if (imageFormatExtensionMap == nil) {
- imageFormatExtensionMap = @{
- @(SDImageFormatUndefined) : @"",
- @(SDImageFormatJPEG) : @"jpeg",
- @(SDImageFormatPNG) : @"png",
- @(SDImageFormatGIF) : @"gif",
- @(SDImageFormatTIFF) : @"tiff",
- @(SDImageFormatWebP) : @"webp",
- @(SDImageFormatHEIC) : @"heic",
- @(SDImageFormatHEIF) : @"heif",
- @(SDImageFormatPDF) : @"pdf",
- @(SDImageFormatSVG) : @"svg",
- @(SDImageFormatBMP) : @"bmp",
- @(SDImageFormatRAW) : @"raw"
- };
- }
- dispatch_async(dispatch_get_main_queue(), ^{
- if (succ == NO || imageData == nil) {
- if ([self.listener respondsToSelector:@selector(onProvideImageError:)]) {
- [self.listener onProvideImageError:message];
- }
- return;
- }
- UIImage *image = [UIImage imageWithData:imageData];
- NSData *data = UIImageJPEGRepresentation(image, 1);
- NSString *path = [TUIKit_Image_Path stringByAppendingString:[TUITool genImageName:nil]];
- NSString *extenionName = [imageFormatExtensionMap objectForKey:@(image.sd_imageFormat)];
- if (extenionName.length > 0) {
- path = [path stringByAppendingPathExtension:extenionName];
- }
-
- int32_t imageFormatSizeMax = 28 * 1024 * 1024;
- if (image.sd_imageFormat == SDImageFormatGIF) {
- imageFormatSizeMax = 10 * 1024 * 1024;
- }
-
- if (imageData.length > imageFormatSizeMax) {
- if ([self.listener respondsToSelector:@selector(onProvideFileError:)]) {
- [self.listener onProvideFileError:TIMCommonLocalizableString(TUIKitImageSizeCheckLimited)];
- }
- return;
- }
-
- if (image.sd_imageFormat != SDImageFormatGIF) {
- UIImage *newImage = image;
- UIImageOrientation imageOrientation = image.imageOrientation;
- CGFloat aspectRatio = MIN(1920 / image.size.width, 1920 / image.size.height);
- CGFloat aspectWidth = image.size.width * aspectRatio;
- CGFloat aspectHeight = image.size.height * aspectRatio;
- UIGraphicsBeginImageContext(CGSizeMake(aspectWidth, aspectHeight));
- [image drawInRect:CGRectMake(0, 0, aspectWidth, aspectHeight)];
- newImage = UIGraphicsGetImageFromCurrentImageContext();
- UIGraphicsEndImageContext();
- data = UIImageJPEGRepresentation(newImage, 0.75);
- }
- [[NSFileManager defaultManager] createFileAtPath:path contents:data attributes:nil];
- if ([self.listener respondsToSelector:@selector(onProvideImage:)]) {
- [self.listener onProvideImage:path];
- }
- });
- }
- - (void)transcodeIfNeed:(BOOL)succ message:(NSString *)message videoUrl:(NSURL *)url {
- if (succ == NO || url == nil) {
- [self handleVideoPick:NO message:message videoUrl:nil];
- return;
- }
- if ([url.pathExtension.lowercaseString isEqualToString:@"mp4"]) {
- [self handleVideoPick:succ message:message videoUrl:url];
- return;
- }
- NSString *tempPath = NSTemporaryDirectory();
- NSURL *urlName = [url URLByDeletingPathExtension];
- NSURL *newUrl = [NSURL URLWithString:[NSString stringWithFormat:@"file://%@%@.mp4", tempPath, [urlName.lastPathComponent stringByRemovingPercentEncoding]]];
- NSFileManager *fileManager = [NSFileManager defaultManager];
- if ([fileManager fileExistsAtPath:newUrl.path]) {
- NSError *error;
- BOOL success = [fileManager removeItemAtPath:newUrl.path error:&error];
- if (!success || error) {
- NSAssert1(NO, @"removeItemFail: %@", error.localizedDescription);
- return;
- }
- }
- // mov to mp4
- AVURLAsset *avAsset = [AVURLAsset URLAssetWithURL:url options:nil];
- AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:avAsset presetName:AVAssetExportPresetHighestQuality];
- exportSession.outputURL = newUrl;
- exportSession.outputFileType = AVFileTypeMPEG4;
- exportSession.shouldOptimizeForNetworkUse = YES;
-
- // intercept FirstTime VideoPicture
- NSDictionary *opts = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:AVURLAssetPreferPreciseDurationAndTimingKey];
- AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:url options:opts];
- NSInteger duration = (NSInteger)urlAsset.duration.value / urlAsset.duration.timescale;
- AVAssetImageGenerator *gen = [[AVAssetImageGenerator alloc] initWithAsset:urlAsset];
- gen.appliesPreferredTrackTransform = YES;
- gen.maximumSize = CGSizeMake(192, 192);
- NSError *error = nil;
- CMTime actualTime;
- CMTime time = CMTimeMakeWithSeconds(0.5, 30);
- CGImageRef imageRef = [gen copyCGImageAtTime:time actualTime:&actualTime error:&error];
- UIImage *image = [[UIImage alloc] initWithCGImage:imageRef];
- CGImageRelease(imageRef);
-
- dispatch_async(dispatch_get_main_queue(), ^{
- if ([self.listener respondsToSelector:@selector(onProvidePlaceholderVideoSnapshot:SnapImage:Completion:)]) {
- [self.listener onProvidePlaceholderVideoSnapshot:@"" SnapImage:image Completion:^(BOOL finished, TUIMessageCellData * _Nonnull placeHolderCellData) {
- [exportSession exportAsynchronouslyWithCompletionHandler:^{
- switch ([exportSession status]) {
- case AVAssetExportSessionStatusFailed:
- NSLog(@"Export session failed");
- break;
- case AVAssetExportSessionStatusCancelled:
- NSLog(@"Export canceled");
- break;
- case AVAssetExportSessionStatusCompleted: {
- // Video conversion finished
- NSLog(@"Successful!");
- [self handleVideoPick:succ message:message videoUrl:newUrl placeHolderCellData:placeHolderCellData];
- }
- break;
- default:
- break;
- }
- }];
-
- [NSTimer tui_scheduledTimerWithTimeInterval:.1 repeats:YES block:^(NSTimer * _Nonnull timer) {
- if (exportSession.status == AVAssetExportSessionStatusExporting) {
- NSLog(@"exportSession.progress:%f",exportSession.progress);
- placeHolderCellData.videoTranscodingProgress = exportSession.progress;
- }
- }];
- }];
- }
- else {
- [exportSession exportAsynchronouslyWithCompletionHandler:^{
- switch ([exportSession status]) {
- case AVAssetExportSessionStatusCompleted: {
- // Video conversion finished
- NSLog(@"Successful!");
- [self handleVideoPick:succ message:message videoUrl:newUrl];
- } break;
- default:
- break;
- }
- }];
- }
- });
-
- }
- - (void)transcodeIfNeed:(BOOL)succ message:(NSString *)message videoUrl:(NSURL *)url placeHolderCellData:(TUIMessageCellData*)placeHolderCellData {
- if (succ == NO || url == nil) {
- [self handleVideoPick:NO message:message videoUrl:nil];
- return;
- }
- if ([url.pathExtension.lowercaseString isEqualToString:@"mp4"]) {
- [self handleVideoPick:succ message:message videoUrl:url];
- return;
- }
- NSString *tempPath = NSTemporaryDirectory();
- NSURL *urlName = [url URLByDeletingPathExtension];
- NSURL *newUrl = [NSURL URLWithString:[NSString stringWithFormat:@"file://%@%@.mp4", tempPath, [urlName.lastPathComponent stringByRemovingPercentEncoding]]];
- NSFileManager *fileManager = [NSFileManager defaultManager];
- if ([fileManager fileExistsAtPath:newUrl.path]) {
- NSError *error;
- BOOL success = [fileManager removeItemAtPath:newUrl.path error:&error];
- if (!success || error) {
- NSAssert1(NO, @"removeItemFail: %@", error.localizedDescription);
- return;
- }
- }
- // mov to mp4
- AVURLAsset *avAsset = [AVURLAsset URLAssetWithURL:url options:nil];
- AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:avAsset presetName:AVAssetExportPresetHighestQuality];
- exportSession.outputURL = newUrl;
- exportSession.outputFileType = AVFileTypeMPEG4;
- exportSession.shouldOptimizeForNetworkUse = YES;
-
- // intercept FirstTime VideoPicture
- NSDictionary *opts = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:AVURLAssetPreferPreciseDurationAndTimingKey];
- AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:url options:opts];
- NSInteger duration = (NSInteger)urlAsset.duration.value / urlAsset.duration.timescale;
- AVAssetImageGenerator *gen = [[AVAssetImageGenerator alloc] initWithAsset:urlAsset];
- gen.appliesPreferredTrackTransform = YES;
- gen.maximumSize = CGSizeMake(192, 192);
- NSError *error = nil;
- CMTime actualTime;
- CMTime time = CMTimeMakeWithSeconds(0.5, 30);
- CGImageRef imageRef = [gen copyCGImageAtTime:time actualTime:&actualTime error:&error];
- UIImage *image = [[UIImage alloc] initWithCGImage:imageRef];
- CGImageRelease(imageRef);
-
- dispatch_async(dispatch_get_main_queue(), ^{
- if ([self.listener respondsToSelector:@selector(onProvidePlaceholderVideoSnapshot:SnapImage:Completion:)]) {
- [exportSession exportAsynchronouslyWithCompletionHandler:^{
- switch ([exportSession status]) {
- case AVAssetExportSessionStatusFailed:
- NSLog(@"Export session failed");
- break;
- case AVAssetExportSessionStatusCancelled:
- NSLog(@"Export canceled");
- break;
- case AVAssetExportSessionStatusCompleted: {
- // Video conversion finished
- NSLog(@"Successful!");
- [self handleVideoPick:succ message:message videoUrl:newUrl placeHolderCellData:placeHolderCellData];
- }
- break;
- default:
- break;
- }
- }];
-
- [NSTimer tui_scheduledTimerWithTimeInterval:.1 repeats:YES block:^(NSTimer * _Nonnull timer) {
- if (exportSession.status == AVAssetExportSessionStatusExporting) {
- NSLog(@"exportSession.progress:%f",exportSession.progress);
- placeHolderCellData.videoTranscodingProgress = exportSession.progress;
- }
- }];
- }
- else {
- [exportSession exportAsynchronouslyWithCompletionHandler:^{
- switch ([exportSession status]) {
- case AVAssetExportSessionStatusCompleted: {
- // Video conversion finished
- NSLog(@"Successful!");
- [self handleVideoPick:succ message:message videoUrl:newUrl];
- } break;
- default:
- break;
- }
- }];
- }
- });
-
- }
- - (void)handleVideoPick:(BOOL)succ message:(NSString *)message videoUrl:(NSURL *)videoUrl {
- [self handleVideoPick:succ message:message videoUrl:videoUrl placeHolderCellData:nil];
- }
- - (void)handleVideoPick:(BOOL)succ message:(NSString *)message videoUrl:(NSURL *)videoUrl placeHolderCellData:(TUIMessageCellData*)placeHolderCellData{
- if (succ == NO || videoUrl == nil) {
- if ([self.listener respondsToSelector:@selector(onProvideVideoError:)]) {
- [self.listener onProvideVideoError:message];
- }
- return;
- }
- NSData *videoData = [NSData dataWithContentsOfURL:videoUrl];
- NSString *videoPath = [NSString stringWithFormat:@"%@%@_%u.mp4", TUIKit_Video_Path, [TUITool genVideoName:nil],arc4random()];
- [[NSFileManager defaultManager] createFileAtPath:videoPath contents:videoData attributes:nil];
- NSDictionary *opts = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:AVURLAssetPreferPreciseDurationAndTimingKey];
- AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:videoUrl options:opts];
- NSInteger duration = (NSInteger)urlAsset.duration.value / urlAsset.duration.timescale;
- AVAssetImageGenerator *gen = [[AVAssetImageGenerator alloc] initWithAsset:urlAsset];
- gen.appliesPreferredTrackTransform = YES;
- gen.maximumSize = CGSizeMake(192, 192);
- NSError *error = nil;
- CMTime actualTime;
- CMTime time = CMTimeMakeWithSeconds(0.5, 30);
- CGImageRef imageRef = [gen copyCGImageAtTime:time actualTime:&actualTime error:&error];
- UIImage *image = [[UIImage alloc] initWithCGImage:imageRef];
- CGImageRelease(imageRef);
- NSData *imageData = UIImagePNGRepresentation(image);
- NSString *imagePath = [TUIKit_Video_Path stringByAppendingFormat:@"%@_%u",[TUITool genSnapshotName:nil],arc4random()];
- [[NSFileManager defaultManager] createFileAtPath:imagePath contents:imageData attributes:nil];
- if ([self.listener respondsToSelector:@selector(onProvideVideo:snapshot:duration:placeHolderCellData:)]) {
- [self.listener onProvideVideo:videoPath snapshot:imagePath duration:duration placeHolderCellData:placeHolderCellData];
- }
- }
- #pragma mark - PHPickerViewControllerDelegate
- - (void)picker:(PHPickerViewController *)picker didFinishPicking:(NSArray<PHPickerResult *> *)results API_AVAILABLE(ios(14)) {
- dispatch_async(dispatch_get_main_queue(), ^{
- [picker dismissViewControllerAnimated:YES completion:nil];
- [[[UIApplication sharedApplication] keyWindow] endEditing:YES];
- });
- if (!results || results.count == 0) {
- return;
- }
- PHPickerResult *result = [results firstObject];
- for (PHPickerResult *result in results) {
- [self _dealPHPickerResultFinishPicking:result];
- }
- }
- - (void)_dealPHPickerResultFinishPicking:(PHPickerResult *)result API_AVAILABLE(ios(14)) {
- NSItemProvider *itemProvoider = result.itemProvider;
- __weak typeof(self) weakSelf = self;
- if ([itemProvoider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage]) {
- [itemProvoider loadDataRepresentationForTypeIdentifier:(NSString *)kUTTypeImage
- completionHandler:^(NSData *_Nullable data, NSError *_Nullable error) {
- dispatch_async(dispatch_get_main_queue(), ^{
- BOOL succ = YES;
- NSString *message = nil;
- if (error) {
- succ = NO;
- message = error.localizedDescription;
- }
- [weakSelf handleImagePick:succ message:message imageData:data];
- });
- }];
- } else if ([itemProvoider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeMPEG4]) {
- [itemProvoider loadDataRepresentationForTypeIdentifier:(NSString *)kUTTypeMovie
- completionHandler:^(NSData *_Nullable data, NSError *_Nullable error) {
- dispatch_async(dispatch_get_main_queue(), ^{
- NSString *fileName = @"temp.mp4";
- NSString *tempPath = NSTemporaryDirectory();
- NSString *filePath = [tempPath stringByAppendingPathComponent:fileName];
- if ([NSFileManager.defaultManager isDeletableFileAtPath:filePath]) {
- [NSFileManager.defaultManager removeItemAtPath:filePath error:nil];
- }
- NSURL *newUrl = [NSURL fileURLWithPath:filePath];
- BOOL flag = [NSFileManager.defaultManager createFileAtPath:filePath contents:data attributes:nil];
- [weakSelf transcodeIfNeed:flag message:flag ? nil : @"video not found" videoUrl:newUrl];
- });
- }];
- } else if ([itemProvoider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeMovie]) {
- // Mov type: screen first
- if ([self.listener respondsToSelector:@selector(onProvidePlaceholderVideoSnapshot:SnapImage:Completion:)]) {
- [self.listener onProvidePlaceholderVideoSnapshot:@"" SnapImage:nil Completion:^(BOOL finished, TUIMessageCellData * _Nonnull placeHolderCellData) {
- [itemProvoider loadDataRepresentationForTypeIdentifier:(NSString *)kUTTypeMovie
- completionHandler:^(NSData *_Nullable data, NSError *_Nullable error) {
- dispatch_async(dispatch_get_main_queue(), ^{
- // Non-mp4 format video, temporarily use mov suffix, will be converted to mp4 format later
- NSDate *datenow = [NSDate date];
- NSString *timeSp = [NSString stringWithFormat:@"%ld", (long)([datenow timeIntervalSince1970]*1000)];
- NSString *fileName = [NSString stringWithFormat:@"%@_temp.mov",timeSp];
- NSString *tempPath = NSTemporaryDirectory();
- NSString *filePath = [tempPath stringByAppendingPathComponent:fileName];
- if ([NSFileManager.defaultManager isDeletableFileAtPath:filePath]) {
- [NSFileManager.defaultManager removeItemAtPath:filePath error:nil];
- }
- NSURL *newUrl = [NSURL fileURLWithPath:filePath];
- BOOL flag = [NSFileManager.defaultManager createFileAtPath:filePath contents:data attributes:nil];
- [weakSelf transcodeIfNeed:flag message:flag ? nil : @"movie not found" videoUrl:newUrl placeHolderCellData:placeHolderCellData];
- });
- }];
- }];
- }
- } else {
- NSString *typeIdentifier = result.itemProvider.registeredTypeIdentifiers.firstObject;
- [itemProvoider loadFileRepresentationForTypeIdentifier:typeIdentifier
- completionHandler:^(NSURL *_Nullable url, NSError *_Nullable error) {
- dispatch_async(dispatch_get_main_queue(), ^{
- UIImage *result;
- NSData *data = [NSData dataWithContentsOfURL:url];
- result = [UIImage imageWithData:data];
- /**
- * Can't get url when typeIdentifier is public.jepg on emulator:
- * There is a separate JEPG transcoding issue that only affects the simulator (63426347), please refer to
- * https://developer.apple.com/forums/thread/658135 for more information.
- */
- });
- }];
- }
- }
- #pragma mark - UIImagePickerController
- - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *, id> *)info {
- __weak typeof(self) weakSelf = self;
- picker.delegate = nil;
- [picker dismissViewControllerAnimated:YES
- completion:^{
- NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
- if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) {
- NSURL *url = nil;
- if (@available(iOS 11.0, *)) {
- url = [info objectForKey:UIImagePickerControllerImageURL];
- } else {
- url = [info objectForKey:UIImagePickerControllerReferenceURL];
- }
- BOOL succ = YES;
- NSData *imageData = nil;
- NSString *errorMessage = nil;
- if (url) {
- succ = YES;
- imageData = [NSData dataWithContentsOfURL:url];
- } else {
- succ = NO;
- errorMessage = @"image not found";
- }
- [weakSelf handleImagePick:succ message:errorMessage imageData:imageData];
- } else if ([mediaType isEqualToString:(NSString *)kUTTypeMovie]) {
- NSURL *url = [info objectForKey:UIImagePickerControllerMediaURL];
- if (url) {
- [weakSelf transcodeIfNeed:YES message:nil videoUrl:url];
- return;
- }
- /**
- * In some cases UIImagePickerControllerMediaURL may be empty, use UIImagePickerControllerPHAsset
- */
- PHAsset *asset = nil;
- if (@available(iOS 11.0, *)) {
- asset = [info objectForKey:UIImagePickerControllerPHAsset];
- }
- if (asset) {
- [self originURLWithAsset:asset
- completion:^(BOOL success, NSURL *URL) {
- [weakSelf transcodeIfNeed:success
- message:success ? nil : @"origin url with asset not found"
- videoUrl:URL];
- }];
- return;
- }
- /**
- * UIImagePickerControllerPHAsset may be empty, and other methods need to be used to obtain the original path of the video
- * file
- */
- url = [info objectForKey:UIImagePickerControllerReferenceURL];
- if (url) {
- [weakSelf originURLWithRefrenceURL:url
- completion:^(BOOL success, NSURL *URL) {
- [weakSelf transcodeIfNeed:success
- message:success ? nil : @"origin url with asset not found"
- videoUrl:URL];
- }];
- return;
- }
- // not support the video
- [weakSelf transcodeIfNeed:NO message:@"not support the video" videoUrl:nil];
- }
- }];
- }
- - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
- [picker dismissViewControllerAnimated:YES completion:nil];
- }
- /**
- * Get the original file path based on UIImagePickerControllerReferenceURL
- */
- - (void)originURLWithRefrenceURL:(NSURL *)URL completion:(void (^)(BOOL success, NSURL *URL))completion {
- if (completion == nil) {
- return;
- }
- NSDictionary *queryInfo = [self dictionaryWithURLQuery:URL.query];
- NSString *fileName = @"temp.mp4";
- if ([queryInfo.allKeys containsObject:@"id"] && [queryInfo.allKeys containsObject:@"ext"]) {
- fileName = [NSString stringWithFormat:@"%@.%@", queryInfo[@"id"], [queryInfo[@"ext"] lowercaseString]];
- }
- NSString *tempPath = NSTemporaryDirectory();
- NSString *filePath = [tempPath stringByAppendingPathComponent:fileName];
- if ([NSFileManager.defaultManager isDeletableFileAtPath:filePath]) {
- [NSFileManager.defaultManager removeItemAtPath:filePath error:nil];
- }
- NSURL *newUrl = [NSURL fileURLWithPath:filePath];
- ALAssetsLibrary *assetLibrary = [[ALAssetsLibrary alloc] init];
- [assetLibrary assetForURL:URL
- resultBlock:^(ALAsset *asset) {
- if (asset == nil) {
- completion(NO, nil);
- return;
- }
- ALAssetRepresentation *rep = [asset defaultRepresentation];
- Byte *buffer = (Byte *)malloc(rep.size);
- NSUInteger buffered = [rep getBytes:buffer fromOffset:0.0 length:rep.size error:nil];
- NSData *data = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES]; // this is NSData may be what you want
- BOOL flag = [NSFileManager.defaultManager createFileAtPath:filePath contents:data attributes:nil];
- completion(flag, newUrl);
- }
- failureBlock:^(NSError *err) {
- completion(NO, nil);
- }];
- }
- - (void)originURLWithAsset:(PHAsset *)asset completion:(void (^)(BOOL success, NSURL *URL))completion {
- if (completion == nil) {
- return;
- }
- NSArray<PHAssetResource *> *resources = [PHAssetResource assetResourcesForAsset:asset];
- if (resources.count == 0) {
- completion(NO, nil);
- return;
- }
- PHAssetResourceRequestOptions *options = [[PHAssetResourceRequestOptions alloc] init];
- options.networkAccessAllowed = NO;
- __block BOOL invoked = NO;
- [PHAssetResourceManager.defaultManager requestDataForAssetResource:resources.firstObject
- options:options
- dataReceivedHandler:^(NSData *_Nonnull data) {
- /**
- *
- * There will be a problem of repeated callbacks here
- */
- if (invoked) {
- return;
- }
- invoked = YES;
- if (data == nil) {
- completion(NO, nil);
- return;
- }
- NSString *fileName = @"temp.mp4";
- NSString *tempPath = NSTemporaryDirectory();
- NSString *filePath = [tempPath stringByAppendingPathComponent:fileName];
- if ([NSFileManager.defaultManager isDeletableFileAtPath:filePath]) {
- [NSFileManager.defaultManager removeItemAtPath:filePath error:nil];
- }
- NSURL *newUrl = [NSURL fileURLWithPath:filePath];
- BOOL flag = [NSFileManager.defaultManager createFileAtPath:filePath contents:data attributes:nil];
- completion(flag, newUrl);
- }
- completionHandler:^(NSError *_Nullable error) {
- completion(NO, nil);
- }];
- }
- - (NSDictionary *)dictionaryWithURLQuery:(NSString *)query {
- NSArray *components = [query componentsSeparatedByString:@"&"];
- NSMutableDictionary *dict = [NSMutableDictionary dictionary];
- for (NSString *item in components) {
- NSArray *subs = [item componentsSeparatedByString:@"="];
- if (subs.count == 2) {
- [dict setObject:subs.lastObject forKey:subs.firstObject];
- }
- }
- return [NSDictionary dictionaryWithDictionary:dict];
- ;
- }
- #pragma mark - TUICameraViewControllerDelegate
- - (void)cameraViewController:(TUICameraViewController *)controller didFinishPickingMediaWithVideoURL:(NSURL *)url {
- [self transcodeIfNeed:YES message:nil videoUrl:url];
- }
- - (void)cameraViewController:(TUICameraViewController *)controller didFinishPickingMediaWithImageData:(NSData *)data {
- [self handleImagePick:YES message:nil imageData:data];
- }
- - (void)cameraViewControllerDidCancel:(TUICameraViewController *)controller {
- }
- - (void)cameraViewControllerDidPictureLib:(TUICameraViewController *)controller finishCallback:(void (^)(void))callback {
- [self selectPhoto];
- if (callback) {
- callback();
- }
- }
- #pragma mark - UIDocumentPickerDelegate
- - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url {
- [url startAccessingSecurityScopedResource];
- NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] init];
- NSError *error;
- @weakify(self);
- [coordinator
- coordinateReadingItemAtURL:url
- options:0
- error:&error
- byAccessor:^(NSURL *newURL) {
- @strongify(self);
- NSData *fileData = [NSData dataWithContentsOfURL:newURL options:NSDataReadingMappedIfSafe error:nil];
- NSString *fileName = [url lastPathComponent];
- NSString *filePath = [TUIKit_File_Path stringByAppendingString:fileName];
- if (fileData.length > 1e9 || fileData.length == 0) { // 1e9 bytes = 1GB
- UIAlertController *ac = [UIAlertController alertControllerWithTitle:TIMCommonLocalizableString(TUIKitFileSizeCheckLimited) message:nil preferredStyle:UIAlertControllerStyleAlert];
- [ac tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(Confirm) style:UIAlertActionStyleDefault handler:nil]];
- [self.presentViewController presentViewController:ac animated:YES completion:nil];
- return;
- }
- if ([NSFileManager.defaultManager fileExistsAtPath:filePath]) {
- /**
- * If a file with the same name exists, increment the file name
- */
- int i = 0;
- NSArray *arrayM = [NSFileManager.defaultManager subpathsAtPath:TUIKit_File_Path];
- for (NSString *sub in arrayM) {
- if ([sub.pathExtension isEqualToString:fileName.pathExtension] &&
- [sub.stringByDeletingPathExtension tui_containsString:fileName.stringByDeletingPathExtension]) {
- i++;
- }
- }
- if (i) {
- fileName = [fileName
- stringByReplacingOccurrencesOfString:fileName.stringByDeletingPathExtension
- withString:[NSString stringWithFormat:@"%@(%d)", fileName.stringByDeletingPathExtension, i]];
- filePath = [TUIKit_File_Path stringByAppendingString:fileName];
- }
- }
- [[NSFileManager defaultManager] createFileAtPath:filePath contents:fileData attributes:nil];
- if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
- unsigned long long fileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil] fileSize];
- if ([self.listener respondsToSelector:@selector(onProvideFile:filename:fileSize:)]) {
- [self.listener onProvideFile:filePath filename:fileName fileSize:fileSize];
- }
- } else {
- if ([self.listener respondsToSelector:@selector(onProvideFileError:)]) {
- [self.listener onProvideFileError:@"file not found"];
- }
- }
- }];
- [url stopAccessingSecurityScopedResource];
- [controller dismissViewControllerAnimated:YES completion:nil];
- }
- - (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller {
- [controller dismissViewControllerAnimated:YES completion:nil];
- }
- @end
|