前言
上次对 Live Photo 有了基本的认识和使用,这次尝试实现Live Photo 制作过程,末尾附上完整示例链接需要的自取。
代码实现Live Photo效果
注意:
- 经试验使用 tmp 缓存目录读取视频会失败,建议拷贝到 Document 目录
前面我们知道 Live Photo其实就是一个 JPG 加上一个 MOV,只不过这个 MOV里面写入了一些元数据,能让相册正确的识别,基于这个原理我们完全可以自己实现 Live Photo 效果。
实现思路:
-
从相册中选取一个视频并拷贝到 Document 目录
-
从视频中截取首帧图
-
根据首帧图和视频合成 Live Photo图片并存入相册
-
PHLivePhotoView 展示 Live Photo图片
1.相册选取视频
实现UIImagePickerControllerDelegate协议,从选取的结果中读取 UIImagePickerControllerMediaURL 视频路径
- (void)chooseVideoFromLibrary {
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
imagePicker.mediaTypes = @[@"public.movie"];//只获取视频数据
imagePicker.delegate = self;
[self presentViewController:imagePicker animated:YES completion:^{}];
}
#pragma mark - UIImagePickerControllerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {
[picker dismissViewControllerAnimated:YES completion:nil];
NSURL *videoUrlPath = info[UIImagePickerControllerMediaURL];
if (videoUrlPath) {
// 获取视频文件名称并将视频文件移动到document目录下
NSString * lastPading = [[[videoUrlPath absoluteString] componentsSeparatedByString:@"/"] lastObject];
NSString * originPath = [self getFilePathWithKey:lastPading];
self.videoUrl = originPath;
[[NSFileManager defaultManager] copyItemAtURL:videoUrlPath toURL:[NSURL fileURLWithPath:originPath] error:nil];
// 截图首帧图片
self.coverImage.image = [self getVideoImageWithTime:0.0 videoPath:videoUrlPath];
}
}
2.截图首帧图
- (UIImage *)getVideoImageWithTime:(Float64)currentTime videoPath:(NSURL *)path {
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:path options:nil];
AVAssetImageGenerator *gen = [[AVAssetImageGenerator alloc] initWithAsset:asset];
gen.appliesPreferredTrackTransform = YES;
gen.requestedTimeToleranceAfter = kCMTimeZero;// 精确提取某一帧,需要这样处理
gen.requestedTimeToleranceBefore = kCMTimeZero;// 精确提取某一帧,需要这样处理
CMTime time = CMTimeMakeWithSeconds(currentTime, 600);
NSError *error = nil;
CMTime actualTime;
CGImageRef image = [gen copyCGImageAtTime:time actualTime:&actualTime error:&error];
UIImage *img = [[UIImage alloc] initWithCGImage:image];
CMTimeShow(actualTime);
CGImageRelease(image);
return img;
}
3.合成Live Photo
首先引入#import <Photos/Photos.h>
框架,调用performChanges
方法存储
#pragma mark - 保存live photo
- (void)saveButtonClick {
NSString * assetIdentifier = [[NSUUID UUID] UUIDString];
NSString *imagePath = [self getFilePathWithKey:@"image.jpg"];
NSString *movPath = [self getFilePathWithKey:@"mov.mov"];
// 当前首帧图
NSData *imageData = UIImageJPEGRepresentation(self.coverImage.image, 1.0);
// 移除旧临时文件
[[NSFileManager defaultManager] removeItemAtPath:imagePath error:nil];
[[NSFileManager defaultManager] removeItemAtPath:movPath error:nil];
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t globle = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//任务一 写入 图片
dispatch_group_async(group, globle, ^{
JPEG *jpeg = [[JPEG alloc] initWithData:imageData];
[jpeg write:imagePath assetIdentifier:assetIdentifier];
});
//任务二 写入 视频
dispatch_group_async(group, globle, ^{
QuickTimeMov *quickMov = [[QuickTimeMov alloc] initWithPath:self.videoUrl];
[quickMov write:movPath assetIdentifier:assetIdentifier];
});
__weak typeof (self)ws = self;
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 写入相册
PHAuthorizationStatus authstatus = [PHPhotoLibrary authorizationStatus];
if (authstatus == PHAuthorizationStatusAuthorized) { //已经授权
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetCreationRequest * request = [PHAssetCreationRequest creationRequestForAsset];
[request addResourceWithType:PHAssetResourceTypePhoto fileURL:[NSURL URLWithString:imagePath] options:nil];
[request addResourceWithType:PHAssetResourceTypePairedVideo fileURL:[NSURL URLWithString:movPath] options:nil];
} completionHandler:^(BOOL success, NSError * _Nullable error) {
if (success) {
NSLog(@"保存成功");
}
}];
}
});
}
4.展示Live Photo
-
通过 PHLivePhoto 的
+ (PHLivePhotoRequestID)requestLivePhotoWithResourceFileURLs:placeholderImage:targetSize:contentMode:resultHandler:
方法合成 Live Photo -
引入
#import <PhotosUI/PhotosUI.h>
框架使用 PHLivePhotoView 展示Live Photo
PHLivePhotoView * livePhotoView = [[PHLivePhotoView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(livePhotoLabel.frame), CGRectGetWidth(self.view.frame), 300)];
[PHLivePhoto requestLivePhotoWithResourceFileURLs:@[[NSURL fileURLWithPath:movPath], [NSURL fileURLWithPath:imagePath]] placeholderImage:self.coverImage.image targetSize:self.coverImage.image.size contentMode:PHImageContentModeAspectFit resultHandler:^(PHLivePhoto * _Nullable livePhoto, NSDictionary * _Nonnull info) {
if (livePhoto) {
livePhotoView.livePhoto = livePhoto;
}
}];
效果图
项目链接
代码量有点多,我放到了github上,需要的自取
参考
-
https://github.com/liangzhen6/Demo-LivePhoto/blob/master/LivePhoto
-
https://github.com/GUIYIVIEW/LivePhoto-master/blob/master/LivePhoto-master/LivePhoto-master