Files
featherVoice/Pods/YBImageBrowser/Video/YBIBVideoData.m
2025-08-08 10:49:36 +08:00

297 lines
10 KiB
Objective-C

//
// YBIBVideoData.m
// YBImageBrowserDemo
//
// Created by 波儿菜 on 2019/7/10.
// Copyright © 2019 杨波. All rights reserved.
//
#import "YBIBVideoData.h"
#import "YBIBVideoCell.h"
#import "YBIBVideoData+Internal.h"
#import "YBIBUtilities.h"
#import "YBIBPhotoAlbumManager.h"
#import "YBIBCopywriter.h"
extern CGImageRef YYCGImageCreateDecodedCopy(CGImageRef imageRef, BOOL decodeForDisplay);
@interface YBIBVideoData () <NSURLSessionDelegate>
@end
@implementation YBIBVideoData {
NSURLSessionDownloadTask *_downloadTask;
}
#pragma mark - life cycle
- (instancetype)init {
self = [super init];
if (self) {
[self initValue];
}
return self;
}
- (void)initValue {
_loadingFirstFrame = NO;
_loadingAVAssetFromPHAsset = NO;
_downloading = NO;
_interactionProfile = [YBIBInteractionProfile new];
_repeatPlayCount = 0;
_autoPlayCount = 0;
_shouldHideForkButton = NO;
_allowSaveToPhotoAlbum = YES;
}
#pragma mark - load data
- (void)loadData {
// Always load 'thumbImage'.
[self loadThumbImage];
if (self.videoAVAsset) {
[self.delegate yb_videoData:self readyForAVAsset:self.videoAVAsset];
} else if (self.videoPHAsset) {
[self loadAVAssetFromPHAsset];
} else {
[self.delegate yb_videoIsInvalidForData:self];
}
}
- (void)loadAVAssetFromPHAsset {
if (!self.videoPHAsset) return;
if (self.isLoadingAVAssetFromPHAsset) {
self.loadingAVAssetFromPHAsset = YES;
return;
}
self.loadingAVAssetFromPHAsset = YES;
[YBIBPhotoAlbumManager getAVAssetWithPHAsset:self.videoPHAsset completion:^(AVAsset * _Nullable asset) {
YBIB_DISPATCH_ASYNC_MAIN(^{
self.loadingAVAssetFromPHAsset = NO;
self.videoAVAsset = asset;
[self.delegate yb_videoData:self readyForAVAsset:self.videoAVAsset];
[self loadThumbImage];
})
}];
}
- (void)loadThumbImage {
if (self.thumbImage) {
[self.delegate yb_videoData:self readyForThumbImage:self.thumbImage];
} else if (self.projectiveView && [self.projectiveView isKindOfClass:UIImageView.self] && ((UIImageView *)self.projectiveView).image) {
self.thumbImage = ((UIImageView *)self.projectiveView).image;
[self.delegate yb_videoData:self readyForThumbImage:self.thumbImage];
} else {
[self loadThumbImage_firstFrame];
}
}
- (void)loadThumbImage_firstFrame {
if (!self.videoAVAsset) return;
if (self.isLoadingFirstFrame) {
self.loadingFirstFrame = YES;
return;
}
self.loadingFirstFrame = YES;
CGSize containerSize = self.yb_containerSize(self.yb_currentOrientation());
CGSize maximumSize = containerSize;
__weak typeof(self) wSelf = self;
YBIB_DISPATCH_ASYNC(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
AVAssetImageGenerator *generator = [AVAssetImageGenerator assetImageGeneratorWithAsset:self.videoAVAsset];
generator.appliesPreferredTrackTransform = YES;
generator.maximumSize = maximumSize;
NSError *error = nil;
CGImageRef cgImage = [generator copyCGImageAtTime:CMTimeMake(0, 1) actualTime:NULL error:&error];
CGImageRef decodedImage = YYCGImageCreateDecodedCopy(cgImage, YES);
UIImage *resultImage = [UIImage imageWithCGImage:decodedImage];
if (cgImage) CGImageRelease(cgImage);
if (decodedImage) CGImageRelease(decodedImage);
YBIB_DISPATCH_ASYNC_MAIN(^{
__strong typeof(wSelf) self = wSelf;
if (!self) return;
self.loadingFirstFrame = NO;
if (!error && resultImage) {
self.thumbImage = resultImage;
[self.delegate yb_videoData:self readyForThumbImage:self.thumbImage];
}
})
})
}
#pragma mark - <YBIBDataProtocol>
@synthesize yb_currentOrientation = _yb_currentOrientation;
@synthesize yb_containerView = _yb_containerView;
@synthesize yb_containerSize = _yb_containerSize;
@synthesize yb_isHideTransitioning = _yb_isHideTransitioning;
@synthesize yb_auxiliaryViewHandler = _yb_auxiliaryViewHandler;
- (nonnull Class)yb_classOfCell {
return YBIBVideoCell.self;
}
- (UIView *)yb_projectiveView {
return self.projectiveView;
}
- (CGRect)yb_imageViewFrameWithContainerSize:(CGSize)containerSize imageSize:(CGSize)imageSize orientation:(UIDeviceOrientation)orientation {
if (containerSize.width <= 0 || containerSize.height <= 0 || imageSize.width <= 0 || imageSize.height <= 0) return CGRectZero;
CGFloat x = 0, y = 0, width = 0, height = 0;
if (imageSize.width / imageSize.height >= containerSize.width / containerSize.height) {
width = containerSize.width;
height = containerSize.width * (imageSize.height / imageSize.width);
x = 0;
y = (containerSize.height - height) / 2.0;
} else {
height = containerSize.height;
width = containerSize.height * (imageSize.width / imageSize.height);
x = (containerSize.width - width) / 2.0;
y = 0;
}
return CGRectMake(x, y, width, height);
}
- (void)yb_preload {
if (!self.delegate) {
[self loadData];
}
}
- (BOOL)yb_allowSaveToPhotoAlbum {
return self.allowSaveToPhotoAlbum;
}
- (void)yb_saveToPhotoAlbum {
void(^unableToSave)(void) = ^(){
[self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].unableToSave];
};
if (self.videoAVAsset && [self.videoAVAsset isKindOfClass:AVURLAsset.class]) {
AVURLAsset *asset = (AVURLAsset *)self.videoAVAsset;
NSURL *URL = asset.URL;
if ([URL.scheme isEqualToString:@"file"]) {
NSString *path = URL.path;
if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(path)) {
UISaveVideoAtPathToSavedPhotosAlbum(path, self, @selector(UISaveVideoAtPathToSavedPhotosAlbum_videoPath:didFinishSavingWithError:contextInfo:), nil);
} else {
unableToSave();
}
} else if ([URL.scheme containsString:@"http"]) {
[self downloadWithURL:URL];
} else {
unableToSave();
}
} else {
unableToSave();
}
}
#pragma mark - private
- (void)UISaveVideoAtPathToSavedPhotosAlbum_videoPath:(NSString *)videoPath didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo{
if (error) {
[self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].saveToPhotoAlbumFailed];
} else {
[self.yb_auxiliaryViewHandler() yb_showCorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].saveToPhotoAlbumSuccess];
}
}
- (void)downloadWithURL:(NSURL *)URL {
if (self.isDownloading) {
self.downloading = YES;
return;
}
self.downloading = YES;
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
_downloadTask = [session downloadTaskWithURL:URL];
[_downloadTask resume];
}
#pragma mark - <NSURLSessionDelegate>
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
CGFloat progress = totalBytesWritten / (double)totalBytesExpectedToWrite;
if (progress < 0) progress = 0;
if (progress > 1) progress = 1;
[self.delegate yb_videoData:self downloadingWithProgress:progress];
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(nullable NSError *)error {
if (error) {
[self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].downloadFailed];
}
self.downloading = NO;
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location {
NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *file = [cache stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
[[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:file] error:nil];
if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(file)) {
UISaveVideoAtPathToSavedPhotosAlbum(file, self, @selector(UISaveVideoAtPathToSavedPhotosAlbum_videoPath:didFinishSavingWithError:contextInfo:), nil);
} else {
[self.yb_auxiliaryViewHandler() yb_showIncorrectToastWithContainer:self.yb_containerView text:[YBIBCopywriter sharedCopywriter].saveToPhotoAlbumFailed];
}
self.downloading = NO;
}
#pragma mark - getters & setters
- (void)setVideoURL:(NSURL *)videoURL{
_videoURL = [videoURL isKindOfClass:NSString.class] ? [NSURL URLWithString:(NSString *)videoURL] : videoURL;
self.videoAVAsset = [AVURLAsset URLAssetWithURL:_videoURL options:nil];
}
- (void)setDownloading:(BOOL)downloading {
_downloading = downloading;
if (downloading) {
[self.delegate yb_videoData:self downloadingWithProgress:0];
} else {
[self.delegate yb_finishDownloadingForData:self];
}
}
- (void)setLoadingAVAssetFromPHAsset:(BOOL)loadingAVAssetFromPHAsset {
_loadingAVAssetFromPHAsset = loadingAVAssetFromPHAsset;
if (loadingAVAssetFromPHAsset) {
[self.delegate yb_startLoadingAVAssetFromPHAssetForData:self];
} else {
[self.delegate yb_finishLoadingAVAssetFromPHAssetForData:self];
}
}
- (void)setLoadingFirstFrame:(BOOL)loadingFirstFrame {
_loadingFirstFrame = loadingFirstFrame;
if (loadingFirstFrame) {
[self.delegate yb_startLoadingFirstFrameForData:self];
} else {
[self.delegate yb_finishLoadingFirstFrameForData:self];
}
}
@synthesize delegate = _delegate;
- (void)setDelegate:(id<YBIBVideoDataDelegate>)delegate {
_delegate = delegate;
if (delegate) {
[self loadData];
}
}
- (id<YBIBVideoDataDelegate>)delegate {
// Stop sending data to the '_delegate' if it is transiting.
return self.yb_isHideTransitioning() ? nil : _delegate;
}
@end