Commit 97faca4e by 邓成林

新建工程

parents
File added
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import <UIKit/UIKit.h>
UIKIT_EXTERN void JPDispatchSyncOnMainQueue(void (^block)(void));
UIKIT_EXTERN void JPDispatchAsyncOnMainQueue(void (^block)(void));
UIKIT_EXTERN void JPDispatchAsyncOnQueue(dispatch_queue_t queue, void (^block)(void));
UIKIT_EXTERN void JPDispatchSyncOnQueue(dispatch_queue_t queue, void (^block)(void));
UIKIT_EXTERN void JPDispatchAsyncOnNextRunloop(void (^block)(void));
UIKIT_EXTERN void JPDispatchAfterTimeIntervalInSecond(NSTimeInterval timeInterval, void (^block)(void));
/**
* benchmark 工具(返回值为代码每次执行耗时, 单位为 ns), 注意线上环境禁用.
*
* @param count benchmark 次数.
* @param block benchmark 代码.
*/
UIKIT_EXTERN int64_t jp_dispatch_benchmark(size_t count, void (^block)(void));
#define JPAssertMainThread NSAssert([NSThread isMainThread], @"代码应该在主线程调用.")
#define JPAssertNotMainThread NSAssert(![NSThread isMainThread], @"代码不应该在主线程调用.")
\ No newline at end of file
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import "JPGCDExtensions.h"
#import <pthread.h>
extern uint64_t dispatch_benchmark(size_t count, void (^block)(void));
void JPDispatchSyncOnMainQueue(void (^block)(void)) {
if (!block) {
return;
}
if (pthread_main_np()) {
block();
return;
}
dispatch_sync(dispatch_get_main_queue(), block);
}
void JPDispatchAsyncOnMainQueue(void (^block)(void)) {
if (!block) {
return;
}
if (pthread_main_np()) {
JPDispatchAsyncOnNextRunloop(block);
return;
}
dispatch_async(dispatch_get_main_queue(), block);
}
void JPDispatchAsyncOnNextRunloop(void (^block)(void)) {
dispatch_async(dispatch_get_main_queue(), block);
}
void JPDispatchAsyncOnQueue(dispatch_queue_t queue, void (^block)(void)) {
if (!queue) {
dispatch_async(dispatch_get_main_queue(), block);
return;
}
dispatch_async(queue, block);
}
void JPDispatchSyncOnQueue(dispatch_queue_t queue, void (^block)(void)) {
if (!queue) {
dispatch_sync(dispatch_get_main_queue(), block);
return;
}
dispatch_sync(queue, block);
}
void JPDispatchAfterTimeIntervalInSecond(NSTimeInterval timeInterval, void (^block)(void)) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), block);
}
int64_t jp_dispatch_benchmark(size_t count, void (^block)(void)) {
return dispatch_benchmark(count, block);
}
\ No newline at end of file
//
// Created by NewPan on 2018/9/5.
// Copyright (c) 2018 NewPan. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
/**
* 依赖注入工具类.
*/
#define jp_metamacro_stringify_(VALUE) # VALUE
#define jp_metamacro_stringify(VALUE) \
jp_metamacro_stringify_(VALUE)
#define jp_concrete \
optional \
#define jp_concreteprotocol(NAME) \
interface NAME ## _JPProtocolMethodContainer : NSObject < NAME > {} \
@end \
@implementation NAME ## _JPProtocolMethodContainer \
+ (void)load { \
if (!jp_addConcreteProtocol(objc_getProtocol(jp_metamacro_stringify(NAME)), self)) \
fprintf(stderr, "ERROR: Could not load concrete protocol %s\n", jp_metamacro_stringify(NAME)); \
} \
__attribute__((constructor)) \
static void rs_ ## NAME ## _inject (void) { \
jp_loadConcreteProtocol(objc_getProtocol(jp_metamacro_stringify(NAME))); \
}
BOOL jp_addConcreteProtocol(Protocol *protocol, Class methodContainer);
void jp_loadConcreteProtocol(Protocol *protocol);
//
// Created by NewPan on 2018/9/5.
// Copyright (c) 2018 NewPan. All rights reserved.
//
#import "JPMethodInjecting.h"
#import <pthread.h>
#import "JPVideoPlayerSupportUtils.h"
typedef struct JPSpecialProtocol {
__unsafe_unretained Protocol *protocol;
Class containerClass;
BOOL ready;
} JPSpecialProtocol;
static JPSpecialProtocol * restrict jp_specialProtocols = NULL;
static size_t jp_specialProtocolCount = 0;
static size_t jp_specialProtocolCapacity = 0;
static size_t jp_specialProtocolsReady = 0;
static pthread_mutex_t jp_specialProtocolsLock = PTHREAD_MUTEX_INITIALIZER;
static NSRecursiveLock *jpinjecting_recursiveLock;
BOOL rs_loadSpecialProtocol (Protocol *protocol, Class containerClass) {
@autoreleasepool {
NSCParameterAssert(protocol != nil);
if (pthread_mutex_lock(&jp_specialProtocolsLock) != 0) {
fprintf(stderr, "ERROR: Could not synchronize on special protocol data\n");
return NO;
}
if (jp_specialProtocolCount == SIZE_MAX) {
pthread_mutex_unlock(&jp_specialProtocolsLock);
return NO;
}
if (jp_specialProtocolCount >= jp_specialProtocolCapacity) {
size_t newCapacity;
if (jp_specialProtocolCapacity == 0)
newCapacity = 1;
else {
newCapacity = jp_specialProtocolCapacity << 1;
if (newCapacity < jp_specialProtocolCapacity) {
newCapacity = SIZE_MAX;
if (newCapacity <= jp_specialProtocolCapacity) {
pthread_mutex_unlock(&jp_specialProtocolsLock);
return NO;
}
}
}
void * restrict ptr = realloc(jp_specialProtocols, sizeof(*jp_specialProtocols) * newCapacity);
if (!ptr) {
pthread_mutex_unlock(&jp_specialProtocolsLock);
return NO;
}
jp_specialProtocols = ptr;
jp_specialProtocolCapacity = newCapacity;
}
assert(jp_specialProtocolCount < jp_specialProtocolCapacity);
#ifndef __clang_analyzer__
jp_specialProtocols[jp_specialProtocolCount] = (JPSpecialProtocol){
.protocol = protocol,
.containerClass = containerClass,
.ready = NO,
};
#endif
++jp_specialProtocolCount;
pthread_mutex_unlock(&jp_specialProtocolsLock);
}
return YES;
}
static void rs_orderSpecialProtocols(void) {
qsort_b(jp_specialProtocols, jp_specialProtocolCount, sizeof(JPSpecialProtocol), ^(const void *a, const void *b){
if (a == b)
return 0;
const JPSpecialProtocol *protoA = a;
const JPSpecialProtocol *protoB = b;
int (^protocolInjectionPriority)(const JPSpecialProtocol *) = ^(const JPSpecialProtocol *specialProtocol){
int runningTotal = 0;
for (size_t i = 0;i < jp_specialProtocolCount;++i) {
if (specialProtocol == jp_specialProtocols + i)
continue;
if (protocol_conformsToProtocol(specialProtocol->protocol, jp_specialProtocols[i].protocol))
runningTotal++;
}
return runningTotal;
};
return protocolInjectionPriority(protoB) - protocolInjectionPriority(protoA);
});
}
void rs_specialProtocolReadyForInjection (Protocol *protocol) {
@autoreleasepool {
NSCParameterAssert(protocol != nil);
if (pthread_mutex_lock(&jp_specialProtocolsLock) != 0) {
fprintf(stderr, "ERROR: Could not synchronize on special protocol data\n");
return;
}
for (size_t i = 0;i < jp_specialProtocolCount;++i) {
if (jp_specialProtocols[i].protocol == protocol) {
if (!jp_specialProtocols[i].ready) {
jp_specialProtocols[i].ready = YES;
assert(jp_specialProtocolsReady < jp_specialProtocolCount);
if (++jp_specialProtocolsReady == jp_specialProtocolCount)
rs_orderSpecialProtocols();
}
break;
}
}
pthread_mutex_unlock(&jp_specialProtocolsLock);
}
}
static void rs_logInstanceAndClassMethod(Class cls) {
unsigned imethodCount = 0;
Method *imethodList = class_copyMethodList(cls, &imethodCount);
NSLog(@"instance Method--------------------");
for (unsigned methodIndex = 0;methodIndex < imethodCount;++methodIndex) {
Method method = imethodList[methodIndex];
SEL selector = method_getName(method);
NSLog(@"%@", [NSString stringWithFormat:@"-[%@ %@]", NSStringFromClass(cls), NSStringFromSelector(selector)]);
}
free(imethodList); imethodList = NULL;
unsigned cmethodCount = 0;
Method *cmethodList = class_copyMethodList(object_getClass(cls), &cmethodCount);
NSLog(@"class Method--------------------");
for (unsigned methodIndex = 0;methodIndex < cmethodCount;++methodIndex) {
Method method = cmethodList[methodIndex];
SEL selector = method_getName(method);
NSLog(@"%@", [NSString stringWithFormat:@"+[%@ %@]", NSStringFromClass(cls), NSStringFromSelector(selector)]);
}
free(cmethodList); cmethodList = NULL;
NSLog(@"end----------------------------------------");
}
static void rs_injectConcreteProtocolInjectMethod(Class containerClass, Class pairClass) {
unsigned imethodCount = 0;
Method *imethodList = class_copyMethodList(containerClass, &imethodCount);
for (unsigned methodIndex = 0;methodIndex < imethodCount;++methodIndex) {
Method method = imethodList[methodIndex];
SEL selector = method_getName(method);
IMP imp = method_getImplementation(method);
const char *types = method_getTypeEncoding(method);
class_addMethod(pairClass, selector, imp, types);
}
free(imethodList); imethodList = NULL;
(void)[containerClass class];
unsigned cmethodCount = 0;
Method *cmethodList = class_copyMethodList(object_getClass(containerClass), &cmethodCount);
Class metaclass = object_getClass(pairClass);
for (unsigned methodIndex = 0;methodIndex < cmethodCount;++methodIndex) {
Method method = cmethodList[methodIndex];
SEL selector = method_getName(method);
if (selector == @selector(initialize)) {
continue;
}
IMP imp = method_getImplementation(method);
const char *types = method_getTypeEncoding(method);
class_addMethod(metaclass, selector, imp, types);
}
free(cmethodList); cmethodList = NULL;
(void)[containerClass class];
}
static NSArray * rs_injectMethod(id object) {
NSMutableArray *rs_matchSpecialProtocolsToClass = @[].mutableCopy;
for (size_t i = 0;i < jp_specialProtocolCount;++i) {
@autoreleasepool {
Protocol *protocol = jp_specialProtocols[i].protocol;
if (!class_conformsToProtocol([object class], protocol)) {
continue;
}
[rs_matchSpecialProtocolsToClass addObject:[NSValue value:&jp_specialProtocols[i] withObjCType:@encode(struct JPSpecialProtocol)]];
}
}
if(!rs_matchSpecialProtocolsToClass.count) {
return nil;
}
struct JPSpecialProtocol protocol;
for(NSValue *value in rs_matchSpecialProtocolsToClass) {
[value getValue:&protocol];
rs_injectConcreteProtocolInjectMethod(protocol.containerClass, [object class]);
}
return rs_matchSpecialProtocolsToClass.copy;
}
static bool rs_resolveMethodForObject(id object) {
@autoreleasepool {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
jpinjecting_recursiveLock = [NSRecursiveLock new];
});
[jpinjecting_recursiveLock lock];
// 处理继承自有注入的父类.
Class currentClass = [object class];
NSArray *matchSpecialProtocolsToClass = nil;
do {
NSArray *protocols = rs_injectMethod(currentClass);
if(!matchSpecialProtocolsToClass) {
matchSpecialProtocolsToClass = protocols;
}
}while((currentClass = class_getSuperclass(currentClass)));
if(!matchSpecialProtocolsToClass.count) {
[jpinjecting_recursiveLock unlock];
return nil;
}
[jpinjecting_recursiveLock unlock];
return YES;
}
}
BOOL jp_addConcreteProtocol(Protocol *protocol, Class methodContainer) {
return rs_loadSpecialProtocol(protocol, methodContainer);
}
void jp_loadConcreteProtocol(Protocol *protocol) {
rs_specialProtocolReadyForInjection(protocol);
}
@interface NSObject(JPInjecting)
@end
@implementation NSObject(JPInjecting)
+ (void)load {
NSError *iError;
NSError *cError;
[self jp_swizzleClassMethod:@selector(resolveInstanceMethod:)
withClassMethod:@selector(jpinjecting_resolveInstanceMethod:)
error:&iError];
[self jp_swizzleClassMethod:@selector(resolveClassMethod:)
withClassMethod:@selector(jpinjecting_resolveClassMethod:)
error:&cError];
NSParameterAssert(!iError);
NSParameterAssert(!cError);
}
+ (BOOL)jpinjecting_resolveClassMethod:(SEL)sel {
if(rs_resolveMethodForObject(self)) {
return YES;
}
return [self jpinjecting_resolveClassMethod:sel];
}
+ (BOOL)jpinjecting_resolveInstanceMethod:(SEL)sel {
if(rs_resolveMethodForObject(self)) {
return YES;
}
return [self jpinjecting_resolveInstanceMethod:sel];
}
@end
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import <Foundation/Foundation.h>
#import "JPVideoPlayerCompat.h"
@class AVAssetResourceLoadingRequest,
JPVideoPlayerCacheFile,
JPResourceLoadingRequestTask;
NS_ASSUME_NONNULL_BEGIN
@protocol JPResourceLoadingRequestTaskDelegate<NSObject>
@optional
/**
* This method call when the request received response.
*
* @param requestTask The current instance.
* @param response The response of request.
*/
- (void)requestTask:(JPResourceLoadingRequestTask *)requestTask
didReceiveResponse:(NSURLResponse *)response;
/**
* This method call when the request received data.
*
* @param requestTask The current instance.
* @param data A fragment video data.
*/
- (void)requestTask:(JPResourceLoadingRequestTask *)requestTask
didReceiveData:(NSData *)data;
/**
* This method call when the request task did complete.
*
* @param requestTask The current instance.
* @param error The request error, nil mean success.
*/
- (void)requestTask:(JPResourceLoadingRequestTask *)requestTask
didCompleteWithError:(NSError *)error;
@end
@interface JPResourceLoadingRequestTask : NSObject
@property (nonatomic, weak) id<JPResourceLoadingRequestTaskDelegate> delegate;
/**
* The loadingRequest passed in when this class initialize.
*/
@property (nonatomic, strong, readonly) AVAssetResourceLoadingRequest *loadingRequest;
/**
* The range passed in when this class initialize.
*/
@property(nonatomic, assign, readonly) NSRange requestRange;
/**
* The cache file take responsibility for save video data to disk and read cached video from disk.
*
* @see `JPVideoPlayerCacheFile`.
*/
@property (nonatomic, strong, readonly) JPVideoPlayerCacheFile *cacheFile;
/**
* The url custom passed in.
*/
@property (nonatomic, strong, readonly) NSURL *customURL;
/**
* A flag represent the video file of requestRange is cached on disk or not.
*/
@property(nonatomic, assign, readonly, getter=isCached) BOOL cached;
@property (nonatomic, assign, readonly, getter = isExecuting) BOOL executing;
@property (nonatomic, assign, readonly, getter = isFinished) BOOL finished;
@property (nonatomic, assign, readonly, getter = isCancelled) BOOL cancelled;
/**
* Convenience method to fetch instance of this class.
*
* @param loadingRequest The loadingRequest from `AVPlayer`.
* @param requestRange The range need request from web.
* @param cacheFile The cache file take responsibility for save video data to disk and read cached video from disk.
* @param customURL The url custom passed in.
* @param cached A flag represent the video file of requestRange is cached on disk or not.
*
* @return A instance of this class.
*/
+ (instancetype)requestTaskWithLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest
requestRange:(NSRange)requestRange
cacheFile:(JPVideoPlayerCacheFile *)cacheFile
customURL:(NSURL *)customURL
cached:(BOOL)cached;
/**
* Designated initializer method.
*
* @param loadingRequest The loadingRequest from `AVPlayer`.
* @param requestRange The range need request from web.
* @param cacheFile The cache file take responsibility for save video data to disk and read cached video from disk.
* @param customURL The url custom passed in.
* @param cached A flag represent the video file of requestRange is cached on disk or not.
*
* @return A instance of this class.
*/
- (instancetype)initWithLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest
requestRange:(NSRange)requestRange
cacheFile:(JPVideoPlayerCacheFile *)cacheFile
customURL:(NSURL *)customURL
cached:(BOOL)cached NS_DESIGNATED_INITIALIZER;
/**
* The request did receive response.
*
* @param response The response of request.
*/
- (void)requestDidReceiveResponse:(NSURLResponse *)response;
/**
* The request did receive data.
*
* @param data A fragment video data.
* @param completion Call when store the data finished.
*/
- (void)requestDidReceiveData:(NSData *)data
storedCompletion:(dispatch_block_t)completion;
/**
* The request did finish.
*
* @param error The request error, if nil mean success.
*/
- (void)requestDidCompleteWithError:(NSError *_Nullable)error NS_REQUIRES_SUPER;
/**
* Begins the execution of the task, execute on main queue.
*/
- (void)start NS_REQUIRES_SUPER;
/**
* Begins the execution of the task on given queue.
*
* @param queue A dispatch queue.
*/
- (void)startOnQueue:(dispatch_queue_t)queue NS_REQUIRES_SUPER;
/**
* Advises the task object that it should stop executing its task.
*/
- (void)cancel NS_REQUIRES_SUPER;
@end
@interface JPResourceLoadingRequestLocalTask: JPResourceLoadingRequestTask
@end
@interface JPResourceLoadingRequestWebTask: JPResourceLoadingRequestTask
/**
* The operation's task.
*/
@property (strong, nonatomic, readonly) NSURLSessionDataTask *dataTask;
/**
* The request used by the operation's task.
*/
@property (strong, nonatomic, nullable) NSURLRequest *request;
/**
* The JPVideoPlayerDownloaderOptions for the receiver.
*/
@property (assign, nonatomic) JPVideoPlayerDownloaderOptions options;
/**
* This is weak because it is injected by whoever manages this session.
* If this gets nil-ed out, we won't be able to run.
* the task associated with this operation.
*/
@property (weak, nonatomic, nullable) NSURLSession *unownedSession;
@end
NS_ASSUME_NONNULL_END
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import "JPVideoPlayerCompat.h"
#import "JPVideoPlayerProtocol.h"
NS_ASSUME_NONNULL_BEGIN
@class JPVideoPlayer,
JPResourceLoadingRequestWebTask,
JPVideoPlayerResourceLoader;
@protocol JPVideoPlayerInternalDelegate <NSObject>
@required
/**
* This method will be called when the current instance receive new loading request.
*
* @param videoPlayer The current `JPVideoPlayer`.
* @prama requestTask A abstract instance packageing the loading request.
*/
- (void)videoPlayer:(JPVideoPlayer *)videoPlayer
didReceiveLoadingRequestTask:(JPResourceLoadingRequestWebTask *)requestTask;
@optional
/**
* Controls which video should automatic replay when the video is playing completed.
*
* @param videoPlayer The current `JPVideoPlayer`.
* @param videoURL The url of the video to be play.
*
* @return Return NO to prevent replay for the video. If not implemented, YES is implied.
*/
- (BOOL)videoPlayer:(nonnull JPVideoPlayer *)videoPlayer
shouldAutoReplayVideoForURL:(nonnull NSURL *)videoURL;
/**
* Notify the player status.
*
* @param videoPlayer The current `JPVideoPlayer`.
* @param playerStatus The current player status.
*/
- (void)videoPlayer:(nonnull JPVideoPlayer *)videoPlayer
playerStatusDidChange:(JPVideoPlayerStatus)playerStatus;
/**
* Notify the playing progress value. this method will be called on main thread.
*
* @param videoPlayer The current `videoPlayer`.
* @param elapsedSeconds The current played seconds.
* @param totalSeconds The total seconds of this video for given url.
*/
- (void)videoPlayerPlayProgressDidChange:(nonnull JPVideoPlayer *)videoPlayer
elapsedSeconds:(double)elapsedSeconds
totalSeconds:(double)totalSeconds;
/**
* Called on some error raise in player.
*
* @param videoPlayer The current instance.
* @param error The error.
*/
- (void)videoPlayer:(nonnull JPVideoPlayer *)videoPlayer
playFailedWithError:(NSError *)error;
@end
@interface JPVideoPlayerModel : NSObject<JPVideoPlayerPlaybackProtocol>
/**
* The current player's layer.
*/
@property (nonatomic, strong, readonly, nullable) AVPlayerLayer *playerLayer;
/**
* The player to play video.
*/
@property (nonatomic, strong, readonly, nullable) AVPlayer *player;
/**
* The resourceLoader for the videoPlayer.
*/
@property (nonatomic, strong, readonly, nullable) JPVideoPlayerResourceLoader *resourceLoader;
/**
* options
*/
@property (nonatomic, assign, readonly) JPVideoPlayerOptions playerOptions;
@end
@interface JPVideoPlayer : NSObject<JPVideoPlayerPlaybackProtocol>
@property (nonatomic, weak, nullable) id<JPVideoPlayerInternalDelegate> delegate;
@property (nonatomic, strong, readonly, nullable) JPVideoPlayerModel *playerModel;
@property (nonatomic, assign, readonly) JPVideoPlayerStatus playerStatus;
/**
* Play the existed video file in disk.
*
* @param url The video url to play.
* @param fullVideoCachePath The full video file path in disk.
* @param showLayer The layer to show the video display layer.
* @param configuration The block will be call when video player config finished. because initialize player is not synchronize,
* so other category method is disabled before config finished.
*
* @return token (@see JPPlayVideoManagerModel) that can be passed to -stopPlayVideo: to stop play.
*/
- (JPVideoPlayerModel *_Nullable)playExistedVideoWithURL:(NSURL *)url
fullVideoCachePath:(NSString *)fullVideoCachePath
options:(JPVideoPlayerOptions)options
showOnLayer:(CALayer *)showLayer
configuration:(JPVideoPlayerConfiguration)configuration;
/**
* Play the not existed video from web.
*
* @param url The video url to play.
* @param options The options to use when downloading the video. @see JPVideoPlayerOptions for the possible values.
* @param showLayer The view to show the video display layer.
* @param configuration The block will be call when video player config finished. because initialize player is not synchronize,
* so other category method is disabled before config finished.
*
* @return token (@see JPPlayVideoManagerModel) that can be passed to -stopPlayVideo: to stop play.
*/
- (JPVideoPlayerModel *_Nullable)playVideoWithURL:(NSURL *)url
options:(JPVideoPlayerOptions)options
showLayer:(CALayer *)showLayer
configuration:(JPVideoPlayerConfiguration)configuration;
/**
* Call this method to resume play.
*
* @param showLayer The view to show the video display layer.
* @param options The options to use when downloading the video. @see JPVideoPlayerOptions for the possible values.
* @param configuration The block will be call when video player config finished. because initialize player is not synchronize,
* so other category method is disabled before config finished.
*/
- (void)resumePlayWithShowLayer:(CALayer *)showLayer
options:(JPVideoPlayerOptions)options
configuration:(JPVideoPlayerConfiguration)configuration;
/**
* This method used to seek to record playback when hava record playback history.
*/
- (void)seekToTimeWhenRecordPlayback:(CMTime)time;
@end
NS_ASSUME_NONNULL_END
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import <Foundation/Foundation.h>
#import "JPVideoPlayerCompat.h"
NS_ASSUME_NONNULL_BEGIN
@interface JPVideoPlayerCacheConfiguration : NSObject
/**
* The maximum length of time to keep an video in the cache, in seconds
*/
@property (assign, nonatomic) NSInteger maxCacheAge;
/**
* The maximum size of the cache, in bytes.
* If the cache Beyond this value, it will delete the video file by the cache time automatic.
*/
@property (assign, nonatomic) NSUInteger maxCacheSize;
/**
* disable iCloud backup [defaults to YES]
*/
@property (assign, nonatomic) BOOL shouldDisableiCloud;
@end
typedef NS_ENUM(NSInteger, JPVideoPlayerCacheType) {
/**
* The video wasn't available the JPVideoPlayer caches.
*/
JPVideoPlayerCacheTypeNone,
/**
* The video was obtained on the disk cache.
*/
JPVideoPlayerCacheTypeExisted,
/**
* A location source.
*/
JPVideoPlayerCacheTypeLocation
};
typedef void(^JPVideoPlayerCacheQueryCompletion)(NSString * _Nullable videoPath, JPVideoPlayerCacheType cacheType);
typedef void(^JPVideoPlayerCheckCacheCompletion)(BOOL isInDiskCache);
typedef void(^JPVideoPlayerCalculateSizeCompletion)(NSUInteger fileCount, NSUInteger totalSize);
/**
* JPVideoPlayerCache maintains a disk cache. Disk cache write operations are performed
* asynchronous so it doesn’t add unnecessary latency to the UI.
*/
@interface JPVideoPlayerCache : NSObject
#pragma mark - Singleton and initialization
/**
* Cache Config object - storing all kind of settings.
*/
@property (nonatomic, readonly) JPVideoPlayerCacheConfiguration *cacheConfiguration;
/**
* Init with given cacheConfig.
*
* @see `JPVideoPlayerCacheConfig`.
*/
- (instancetype)initWithCacheConfiguration:(JPVideoPlayerCacheConfiguration * _Nullable)cacheConfiguration NS_DESIGNATED_INITIALIZER;
/**
* Returns global shared cache instance.
*
* @return JPVideoPlayerCache global instance.
*/
+ (instancetype)sharedCache;
# pragma mark - Query and Retrieve Options
/**
* Async check if video exists in disk cache already (does not load the video).
*
* @param key The key describing the url.
* @param completion The block to be executed when the check is done.
* @note the completion block will be always executed on the main queue.
*/
- (void)diskVideoExistsWithKey:(NSString *)key
completion:(JPVideoPlayerCheckCacheCompletion _Nullable)completion;
/**
* Operation that queries the cache asynchronously and call the completion when done.
*
* @param key The unique key used to store the wanted video.
* @param completion The completion block. Will not get called if the operation is cancelled.
*/
- (void)queryCacheOperationForKey:(NSString *)key
completion:(JPVideoPlayerCacheQueryCompletion _Nullable)completion;
/**
* Async check if video exists in disk cache already (does not load the video).
*
* @param path The path need to check in disk.
*
* @return If the file is existed for given video path, return YES, return NO, otherwise.
*/
- (BOOL)diskVideoExistsOnPath:(NSString *)path;
# pragma mark - Clear Cache Events
/**
* Remove the video data from disk cache asynchronously
*
* @param key The unique video cache key.
* @param completion A block that should be executed after the video has been removed (optional).
*/
- (void)removeVideoCacheForKey:(NSString *)key
completion:(dispatch_block_t _Nullable)completion;
/**
* Async remove all expired cached video from disk. Non-blocking method - returns immediately.
*
* @param completion A block that should be executed after cache expiration completes (optional)
*/
- (void)deleteOldFilesOnCompletion:(dispatch_block_t _Nullable)completion;
/**
* Async clear all disk cached videos. Non-blocking method - returns immediately.
*
* @param completion A block that should be executed after cache expiration completes (optional).
*/
- (void)clearDiskOnCompletion:(dispatch_block_t _Nullable)completion;
/**
* Async clear videos cache in disk on version 2.x.
*
* @param completion A block that should be executed after cache expiration completes (optional).
*/
- (void)clearVideoCacheOnVersion2OnCompletion:(dispatch_block_t _Nullable)completion;
# pragma mark - Cache Info
/**
* To check is have enough free size in disk to cache file with given size.
*
* @param fileSize the need to cache size of file.
*
* @return if the disk have enough size to cache the given size file, return YES, return NO otherwise.
*/
- (BOOL)haveFreeSizeToCacheFileWithSize:(NSUInteger)fileSize;
/**
* Get the free size of device.
*
* @return the free size of device.
*/
- (unsigned long long)getDiskFreeSize;
/**
* Get the size used by the disk cache, synchronously.
*/
- (unsigned long long)getSize;
/**
* Get the number of images in the disk cache, synchronously.
*/
- (NSUInteger)getDiskCount;
/**
* Calculate the disk cache's size, asynchronously .
*/
- (void)calculateSizeOnCompletion:(JPVideoPlayerCalculateSizeCompletion _Nullable)completion;
# pragma mark - File Name
/**
* Generate the video file's name for given key.
*
* @return the file's name.
*/
- (NSString *)cacheFileNameForKey:(NSString *)key;
@end
@interface JPVideoPlayerCache(Deprecated)
/**
* Remove the video data from disk cache asynchronously
*
* @param key The unique video cache key.
* @param completion A block that should be executed after the video has been removed (optional).
*/
- (void)removeFullCacheForKey:(NSString *)key
completion:(dispatch_block_t _Nullable)completion JPDEPRECATED_ATTRIBUTE("`removeFullCacheForKey:completion:` is deprecated on 3.0.")
/**
* Clear the temporary cache video for given key.
*
* @param key The unique flag for the given url in this framework.
* @param completion A block that should be executed after the video has been removed (optional).
*/
- (void)removeTempCacheForKey:(NSString *)key
completion:(nullable dispatch_block_t)completion JPDEPRECATED_ATTRIBUTE("`removeTempCacheForKey:completion:` is deprecated on 3.0.")
/**
* Async delete all temporary cached videos. Non-blocking method - returns immediately.
* @param completion A block that should be executed after cache expiration completes (optional).
*/
- (void)deleteAllTempCacheOnCompletion:(dispatch_block_t _Nullable)completion JPDEPRECATED_ATTRIBUTE("`deleteAllTempCacheOnCompletion:` is deprecated on 3.0.");
@end
NS_ASSUME_NONNULL_END
\ No newline at end of file
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface JPVideoPlayerCacheFile : NSObject
#pragma mark - Properties
/**
* The cache file path of video data.
*/
@property (nonatomic, copy, readonly) NSString *cacheFilePath;
/**
* The index file cache path. because the video datas cache in disk maybe are discontinuous like:
* "01010101*********0101*****010101010101"(the 0 and 1 represent video data, and the * represent no data).
* So we need index to map this video data, and the index file is a colletion of indexes store in disk.
*/
@property (nonatomic, copy, readonly, nullable) NSString *indexFilePath;
/**
* The video data expected length.
* Note this value is not always equal to the cache video data length.
*/
@property (nonatomic, assign, readonly) NSUInteger fileLength;
/**
* The fragment of video data that cached in disk.
*/
@property (nonatomic, strong, readonly, nullable) NSArray<NSValue *> *fragmentRanges;
/**
* The offset of read postion.
*/
@property (nonatomic, assign, readonly) NSUInteger readOffset;
/**
* The response header from web.
*/
@property (nonatomic, copy, readonly, nullable) NSDictionary *responseHeaders;
/**
* A flag represent the video data is cache finished or not.
*/
@property (nonatomic, readonly) BOOL isCompleted;
/**
* A flag represent read offset is point to the end of video data file.
*/
@property (nonatomic, readonly) BOOL isEOF;
/**
* A flag represent file length is greater than 0.
*/
@property (nonatomic, readonly) BOOL isFileLengthValid;
/**
* The cached video data length.
*/
@property (nonatomic, readonly) NSUInteger cachedDataBound;
#pragma mark - Methods
/**
* Convenience method to fetch instance of this class.
* Note this class take responsibility for save video data to disk and read cached video from disk.
*
* @param filePath The video data cache path.
* @param indexFilePath The index file cache path.
*
* @return A instance of this class.
*/
+ (instancetype)cacheFileWithFilePath:(NSString *)filePath
indexFilePath:(NSString *)indexFilePath;
/**
* Designated initializer method.
* Note this class take responsibility for save video data to disk and read cached video from disk.
*
* @param filePath The video data cache path.
* @param indexFilePath The index file cache path.
*
* @return A instance of this class.
*/
- (instancetype)initWithFilePath:(NSString *)filePath
indexFilePath:(NSString *)indexFilePath NS_DESIGNATED_INITIALIZER;
#pragma mark - Store
/**
* Call this method to store video data to disk.
*
* @param data Video data.
* @param offset The offset of the data in video file.
* @param synchronize A flag indicator store index to index file synchronize or not.
* @param completion Call when store the data finished.
*/
- (void)storeVideoData:(NSData *)data
atOffset:(NSUInteger)offset
synchronize:(BOOL)synchronize
storedCompletion:(dispatch_block_t)completion;
/**
* Set the response from web when request video data.
*
* @param response The response from web when request video data.
*
* @return The result of storing response.
*/
- (BOOL)storeResponse:(NSHTTPURLResponse *)response;
/**
* Store index to index file synchronize.
*
* @return The result of store index to index file successed or failed.
*/
- (BOOL)synchronize;
#pragma mark - Read
/**
* Fetch data from the readOffset to given length position.
* Note call `seekToPosition:` to set the readOffset before call this method.
* Note the data not always have video data if the data not cached in disk.
*
* @param length The length of data.
*
* @return Data from the header to given length position.
*/
- (NSData * _Nullable)readDataWithLength:(NSUInteger)length;
/**
* Fetch data in given range.
* Note the data not always have video data if the data not cached in disk.
*
* @param range The range in file.
*
* @return Data in given range.
*/
- (NSData *)dataWithRange:(NSRange)range;
#pragma mark - Remove
/**
* Remove the cached video data.
*/
- (void)removeCache;
#pragma mark - Range
/**
* Fetch the cached video data range for given range.
*
* @param range A given range.
*
* @return The cached video data range for given range.
*/
- (NSRange)cachedRangeForRange:(NSRange)range;
/**
* Fetch the range of cached video data contain given position.
*
* @param position A position point to a point of video file.
*
* @return The range of cached video data contain given position.
*/
- (NSRange)cachedRangeContainsPosition:(NSUInteger)position;
/**
* Find the first range of video data not cached in given postion.
*
* @param position A position point to a point of video file.
*
* @return The first range of video data not cached in given postion.
*/
- (NSRange)firstNotCachedRangeFromPosition:(NSUInteger)position;
#pragma mark - Seek
/**
* Set the fileHandle seek to the given offset.
*
* @param position A position point to a point of video file.
*/
- (void)seekToPosition:(NSUInteger)position;
/**
* Set the fileHandle seek to end of file.
*/
- (void)seekToEnd;
@end
NS_ASSUME_NONNULL_END
\ No newline at end of file
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import <UIKit/UIKit.h>
#import "JPVideoPlayerCompat.h"
UIKIT_EXTERN NSString * _Nonnull const JPVideoPlayerCacheVideoPathForTemporaryFile;
UIKIT_EXTERN NSString * _Nonnull const JPVideoPlayerCacheVideoPathForFullFile;
NS_ASSUME_NONNULL_BEGIN
@interface JPVideoPlayerCachePath : NSObject
/**
* Get the video cache path on version 3.x.
*
* @return The file path.
*/
+ (NSString *)videoCachePath;
/**
* Fetch the video cache path for given key on version 3.x.
*
* @param key A given key.
*
* @return The file path.
*/
+ (NSString *)videoCachePathForKey:(NSString *)key;
/**
* Fetch the video cache path and create video file for given key on version 3.x.
*
* @param key A given key.
*
* @return The file path.
*/
+ (NSString *)createVideoFileIfNeedThenFetchItForKey:(NSString *)key;
/**
* Fetch the index file path for given key on version 3.x.
*
* @param key A given key.
*
* @return The path of index file.
*/
+ (NSString *)videoCacheIndexFilePathForKey:(NSString *)key;
/**
* Fetch the index file path and create video index file for given key on version 3.x.
*
* @param key A given key.
*
* @return The path of index file.
*/
+ (NSString *)createVideoIndexFileIfNeedThenFetchItForKey:(NSString *)key;
/**
* Fetch the playback record file path.
*
* @return The path of playback record.
*/
+ (NSString *)videoPlaybackRecordFilePath;
@end
@interface JPVideoPlayerCachePath(Deprecated)
/**
* Get the local video cache path for temporary video file.
*
* @param key The unique flag for the given url in this framework.
*
* @return the temporary file path.
*/
+ (NSString *)videoCacheTemporaryPathForKey:(NSString *)key JPDEPRECATED_ATTRIBUTE("`videoCacheTemporaryPathForKey:` is deprecated on 3.0.")
/**
* Get the local video cache path for all full video file.
*
* @param key The unique flag for the given url in this framework.
*
* @return the full file path.
*/
+ (NSString *)videoCacheFullPathForKey:(NSString *)key JPDEPRECATED_ATTRIBUTE("`videoCacheFullPathForKey:` is deprecated on 3.0.")
/**
* Get the local video cache path for all temporary video file on version 2.x.
*
* @return the temporary file path.
*/
+ (NSString *)videoCachePathForAllTemporaryFile JPDEPRECATED_ATTRIBUTE("`videoCachePathForAllTemporaryFile` is deprecated on 3.0.")
/**
* Get the local video cache path for all full video file on version 2.x.
*
* @return the full file path.
*/
+ (NSString *)videoCachePathForAllFullFile JPDEPRECATED_ATTRIBUTE("`videoCachePathForAllFullFile` is deprecated on 3.0.")
@end
NS_ASSUME_NONNULL_END
\ No newline at end of file
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import "JPVideoPlayerCachePath.h"
#import "JPVideoPlayerCache.h"
NSString * const JPVideoPlayerCacheVideoPathForTemporaryFile = @"/TemporaryFile";
NSString * const JPVideoPlayerCacheVideoPathForFullFile = @"/FullFile";
static NSString * const kJPVideoPlayerCacheVideoPathDomain = @"/com.jpvideoplayer.www";
static NSString * const kJPVideoPlayerCacheVideoFileExtension = @".mp4";
static NSString * const kJPVideoPlayerCacheVideoIndexFileExtension = @".index";
static NSString * const kJPVideoPlayerCacheVideoPlaybackRecordFileExtension = @".record";
@implementation JPVideoPlayerCachePath
#pragma mark - Public
+ (NSString *)videoCachePath {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).lastObject
stringByAppendingPathComponent:kJPVideoPlayerCacheVideoPathDomain];
if (![fileManager fileExistsAtPath:path]){
[fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
}
return path;
}
+ (NSString *)videoCachePathForKey:(NSString *)key {
if (!key) {
return nil;
}
NSString *videoCachePath = [self videoCachePath];
NSString *filePath = [videoCachePath stringByAppendingPathComponent:[JPVideoPlayerCache.sharedCache cacheFileNameForKey:key]];
return filePath;
}
+ (NSString *)createVideoFileIfNeedThenFetchItForKey:(NSString *)key {
NSString *filePath = [self videoCachePathForKey:key];
if(!filePath){
return nil;
}
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:filePath]) {
[fileManager createFileAtPath:filePath contents:nil attributes:nil];
}
return filePath;
}
+ (NSString *)videoCacheIndexFilePathForKey:(NSString *)key {
if (!key) {
return nil;
}
NSString *videoCachePath = [self videoCachePath];
NSString *filePath = [videoCachePath stringByAppendingPathComponent:[JPVideoPlayerCache.sharedCache cacheFileNameForKey:key]];
filePath = [filePath stringByAppendingString:kJPVideoPlayerCacheVideoIndexFileExtension];
return filePath;
}
+ (NSString *)createVideoIndexFileIfNeedThenFetchItForKey:(NSString *)key {
NSString *filePath = [self videoCacheIndexFilePathForKey:key];
if(!filePath){
return nil;
}
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:filePath]) {
[fileManager createFileAtPath:filePath contents:nil attributes:nil];
}
return filePath;
}
+ (NSString *)videoPlaybackRecordFilePath {
NSString *filePath = [self videoCachePath];
if(!filePath){
return nil;
}
filePath = [filePath stringByAppendingPathComponent:kJPVideoPlayerCacheVideoPlaybackRecordFileExtension];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:filePath]) {
[fileManager createFileAtPath:filePath contents:nil attributes:nil];
}
return filePath;
}
@end
@implementation JPVideoPlayerCachePath(Deprecated)
+ (NSString *)videoCacheTemporaryPathForKey:(NSString * _Nonnull)key{
if (!key) {
return nil;
}
NSString *path = [self getFilePathWithAppendingString:JPVideoPlayerCacheVideoPathForTemporaryFile];
path = [path stringByAppendingPathComponent:[JPVideoPlayerCache.sharedCache cacheFileNameForKey:key]];
path = [path stringByAppendingString:kJPVideoPlayerCacheVideoFileExtension];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:path]) {
[fileManager createFileAtPath:path contents:nil attributes:nil];
}
return path;
}
+ (NSString *)videoCacheFullPathForKey:(NSString * _Nonnull)key{
if (!key) {
return nil;
}
NSString *path = [self getFilePathWithAppendingString:JPVideoPlayerCacheVideoPathForFullFile];
NSString *fileName = [[JPVideoPlayerCache.sharedCache cacheFileNameForKey:key]
stringByAppendingString:kJPVideoPlayerCacheVideoFileExtension];
path = [path stringByAppendingPathComponent:fileName];
return path;
}
+ (NSString *)videoCachePathForAllTemporaryFile{
return [self getFilePathWithAppendingString:JPVideoPlayerCacheVideoPathForTemporaryFile];
}
+ (NSString *)videoCachePathForAllFullFile{
return [self getFilePathWithAppendingString:JPVideoPlayerCacheVideoPathForFullFile];
}
+ (NSString *)getFilePathWithAppendingString:(nonnull NSString *)apdStr{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).lastObject
stringByAppendingPathComponent:apdStr];
if (![fileManager fileExistsAtPath:path]){
[fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
}
return path;
}
@end
//
// Created by NewPan on 2019-02-24.
// Copyright (c) 2019 NewPan. All rights reserved.
//
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSInteger , JPVideoPlayerUnreachableCellType) {
JPVideoPlayerUnreachableCellTypeNone = 0,
JPVideoPlayerUnreachableCellTypeTop,
JPVideoPlayerUnreachableCellTypeDown
};
NS_ASSUME_NONNULL_BEGIN
@protocol JPVideoPlayerCellProtocol <NSObject>
@required
/**
* The video path url.
*
* @note The url may a web url or local file url.
*/
@property (nonatomic, nullable) NSURL *jp_videoURL;
/**
* The view to display video layer.
*/
@property (nonatomic, nullable) UIView *jp_videoPlayView;
/**
* The style of cell cannot stop in screen center.
*/
@property(nonatomic) JPVideoPlayerUnreachableCellType jp_unreachableCellType;
/**
* Returns a Boolean value that indicates whether a given cell is equal to
* the receiver using `jp_videoURL` comparison.
*
* @param cell The cell with which to compare the receiver.
*
* @return YES if cell is equivalent to the receiver (if they have the same `jp_videoURL` comparison), otherwise NO.
*/
- (BOOL)jp_isEqualToCell:(UIView<JPVideoPlayerCellProtocol> *)cell;
@end
NS_ASSUME_NONNULL_END
\ No newline at end of file
//
// Created by NewPan on 2019-02-24.
// Copyright (c) 2019 NewPan. All rights reserved.
//
#import "JPVideoPlayerCellProtocol.h"
#import "JPMethodInjecting.h"
@jp_concreteprotocol(JPVideoPlayerCellProtocol)
- (void)setJp_videoURL:(NSURL *)jp_videoURL {
objc_setAssociatedObject(self, @selector(jp_videoURL), jp_videoURL, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSURL *)jp_videoURL {
return objc_getAssociatedObject(self, _cmd);
}
- (void)setJp_videoPlayView:(UIView *)jp_videoPlayView {
objc_setAssociatedObject(self, @selector(jp_videoPlayView), jp_videoPlayView, OBJC_ASSOCIATION_ASSIGN);
}
- (UIView *)jp_videoPlayView {
return objc_getAssociatedObject(self, _cmd);
}
- (void)setJp_unreachableCellType:(JPVideoPlayerUnreachableCellType)jp_unreachableCellType {
objc_setAssociatedObject(self, @selector(jp_unreachableCellType), @(jp_unreachableCellType), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (JPVideoPlayerUnreachableCellType)jp_unreachableCellType {
return [objc_getAssociatedObject(self, _cmd) integerValue];
}
- (BOOL)jp_isEqualToCell:(UIView<JPVideoPlayerCellProtocol> *)cell {
if(!self.jp_videoURL && !cell.jp_videoURL){
return self == cell;
}
return [self.jp_videoURL.absoluteString isEqualToString:cell.jp_videoURL.absoluteString];
}
@end
\ No newline at end of file
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import <UIKit/UIKit.h>
#import <AVFoundation/AVAssetResourceLoader.h>
#import <objc/runtime.h>
#import "JPGCDExtensions.h"
@class JPVideoPlayerModel;
#ifndef JPVideoPlayerCompat
#define JPVideoPlayerCompat
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, JPVideoPlayViewInterfaceOrientation) {
JPVideoPlayViewInterfaceOrientationUnknown = 0,
JPVideoPlayViewInterfaceOrientationPortrait,
JPVideoPlayViewInterfaceOrientationLandscape,
};
typedef NS_ENUM(NSUInteger, JPVideoPlayerStatus) {
JPVideoPlayerStatusUnknown = 0,
JPVideoPlayerStatusBuffering,
JPVideoPlayerStatusReadyToPlay,
JPVideoPlayerStatusPlaying,
JPVideoPlayerStatusPause,
JPVideoPlayerStatusFailed,
JPVideoPlayerStatusStop,
};
typedef NS_ENUM(NSUInteger, JPLogLevel) {
// no log output.
JPLogLevelNone = 0,
// output debug, warning and error log.
JPLogLevelError = 1,
// output debug and warning log.
JPLogLevelWarning = 2,
// output debug log.
JPLogLevelDebug = 3,
};
typedef NS_OPTIONS(NSUInteger, JPVideoPlayerOptions) {
/**
* By default, when a URL fail to be downloaded, the URL is blacklisted so the library won't keep trying.
* This flag disable this blacklisting.
*/
JPVideoPlayerRetryFailed = 1 << 0,
/**
* In iOS 4+, continue the download of the video if the app goes to background. This is achieved by asking the system for
* extra time in background to let the request finish. If the background task expires the operation will be cancelled.
*/
JPVideoPlayerContinueInBackground = 1 << 1,
/**
* Handles cookies stored in NSHTTPCookieStore by setting
* NSMutableURLRequest.HTTPShouldHandleCookies = YES;
*/
JPVideoPlayerHandleCookies = 1 << 2,
/**
* Enable to allow untrusted SSL certificates.
* Useful for testing purposes. Use with caution in production.
*/
JPVideoPlayerAllowInvalidSSLCertificates = 1 << 3,
/**
* Playing video muted.
*/
JPVideoPlayerMutedPlay = 1 << 4,
/**
* Stretch to fill layer bounds.
*/
JPVideoPlayerLayerVideoGravityResize = 1 << 5,
/**
* Preserve aspect ratio; fit within layer bounds.
* Default value.
*/
JPVideoPlayerLayerVideoGravityResizeAspect = 1 << 6,
/**
* Preserve aspect ratio; fill layer bounds.
*/
JPVideoPlayerLayerVideoGravityResizeAspectFill = 1 << 7,
// TODO: Disable cache if need.
};
typedef NS_OPTIONS(NSUInteger, JPVideoPlayerDownloaderOptions) {
/**
* Call completion block with nil video/videoData if the image was read from NSURLCache
* (to be combined with `JPVideoPlayerDownloaderUseNSURLCache`).
*/
JPVideoPlayerDownloaderIgnoreCachedResponse = 1 << 0,
/**
* In iOS 4+, continue the download of the video if the app goes to background. This is achieved by asking the system for
* extra time in background to let the request finish. If the background task expires the operation will be cancelled.
*/
JPVideoPlayerDownloaderContinueInBackground = 1 << 1,
/**
* Handles cookies stored in NSHTTPCookieStore by setting
* NSMutableURLRequest.HTTPShouldHandleCookies = YES;
*/
JPVideoPlayerDownloaderHandleCookies = 1 << 2,
/**
* Enable to allow untrusted SSL certificates.
* Useful for testing purposes. Use with caution in production.
*/
JPVideoPlayerDownloaderAllowInvalidSSLCertificates = 1 << 3,
};
typedef void(^JPPlayVideoConfiguration)(UIView *_Nonnull view, JPVideoPlayerModel *_Nonnull playerModel);
typedef void(^JPVideoPlayerConfiguration)(JPVideoPlayerModel *_Nonnull playerModel);
UIKIT_EXTERN NSString * _Nonnull const JPVideoPlayerDownloadStartNotification;
UIKIT_EXTERN NSString * _Nonnull const JPVideoPlayerDownloadReceiveResponseNotification;
UIKIT_EXTERN NSString * _Nonnull const JPVideoPlayerDownloadStopNotification;
UIKIT_EXTERN NSString * _Nonnull const JPVideoPlayerDownloadFinishNotification;
UIKIT_EXTERN NSString *const JPVideoPlayerErrorDomain;
FOUNDATION_EXTERN const NSRange JPInvalidRange;
static JPLogLevel _logLevel;
#define JPDEPRECATED_ATTRIBUTE(msg) __attribute__((deprecated(msg)));
/**
* Call this method to check range valid or not.
*
* @param range The range wanna check valid.
*
* @return Yes means valid, otherwise NO.
*/
BOOL JPValidByteRange(NSRange range);
/**
* Call this method to check range is valid file range or not.
*
* @param range The range wanna check valid.
*
* @return Yes means valid, otherwise NO.
*/
BOOL JPValidFileRange(NSRange range);
/**
* Call this method to check the end point of range1 is equal to the start point of range2,
* or the end point of range2 is equal to the start point of range2,
* or this two range have intersection and the intersection greater than 0.
*
* @param range1 A file range.
* @param range2 A file range.
*
* @return YES means those two range can be merge, otherwise NO.
*/
BOOL JPRangeCanMerge(NSRange range1, NSRange range2);
/**
* Convert a range to HTTP range header string.
*
* @param range A range.
*
* @return HTTP range header string
*/
NSString* JPRangeToHTTPRangeHeader(NSRange range);
/**
* Generate error object with error message.
*
* @param description The error message.
*
* @return A `NSError` object.
*/
NSError *JPErrorWithDescription(NSString *description);
#endif
NS_ASSUME_NONNULL_END
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import "JPVideoPlayerCompat.h"
#import <AVFoundation/AVFoundation.h>
NSString *const JPVideoPlayerDownloadStartNotification = @"www.jpvideplayer.download.start.notification";
NSString *const JPVideoPlayerDownloadReceiveResponseNotification = @"www.jpvideoplayer.download.received.response.notification";
NSString *const JPVideoPlayerDownloadStopNotification = @"www.jpvideplayer.download.stop.notification";
NSString *const JPVideoPlayerDownloadFinishNotification = @"www.jpvideplayer.download.finished.notification";
NSString *const JPVideoPlayerErrorDomain = @"com.jpvideoplayer.error.domain.www";
const NSRange JPInvalidRange = {NSNotFound, 0};
BOOL JPValidByteRange(NSRange range) {
return ((range.location != NSNotFound) || (range.length > 0));
}
BOOL JPValidFileRange(NSRange range) {
return ((range.location != NSNotFound) && range.length > 0 && range.length != NSUIntegerMax);
}
BOOL JPRangeCanMerge(NSRange range1, NSRange range2) {
return (NSMaxRange(range1) == range2.location) || (NSMaxRange(range2) == range1.location) || NSIntersectionRange(range1, range2).length > 0;
}
NSString* JPRangeToHTTPRangeHeader(NSRange range) {
if (JPValidByteRange(range)) {
if (range.location == NSNotFound) {
return [NSString stringWithFormat:@"bytes=-%tu",range.length];
}
else if (range.length == NSUIntegerMax) {
return [NSString stringWithFormat:@"bytes=%tu-",range.location];
}
else {
return [NSString stringWithFormat:@"bytes=%tu-%tu",range.location, NSMaxRange(range) - 1];
}
}
else {
return nil;
}
}
NSError *JPErrorWithDescription(NSString *description) {
assert(description);
if(!description.length){
return nil;
}
return [NSError errorWithDomain:JPVideoPlayerErrorDomain
code:0 userInfo:@{
NSLocalizedDescriptionKey : description
}];
}
\ No newline at end of file
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import "JPVideoPlayerProtocol.h"
@class JPVideoPlayerControlProgressView,
JPVideoPlayerControlView;
NS_ASSUME_NONNULL_BEGIN
UIKIT_EXTERN NSString *JPVideoPlayerControlProgressViewUserDidStartDragNotification;
UIKIT_EXTERN NSString *JPVideoPlayerControlProgressViewUserDidEndDragNotification;
@interface JPVideoPlayerControlProgressView : UIView<JPVideoPlayerControlProgressProtocol>
@property (nonatomic, strong, readonly) NSArray<NSValue *> *rangesValue;
@property (nonatomic, assign, readonly) NSUInteger fileLength;
@property (nonatomic, assign, readonly) NSTimeInterval totalSeconds;
@property (nonatomic, assign, readonly) NSTimeInterval elapsedSeconds;
@property (nonatomic, weak, readonly, nullable) UIView *playerView;
@property (nonatomic, strong, readonly) UISlider *dragSlider;
@property (nonatomic, strong, readonly) UIView *cachedProgressView;
@property (nonatomic, strong, readonly) UIProgressView *trackProgressView;
@end
@interface JPVideoPlayerControlBar : UIView<JPVideoPlayerProtocol>
@property (nonatomic, strong, readonly) UIButton *playButton;
@property (nonatomic, strong, readonly) UIView<JPVideoPlayerControlProgressProtocol> *progressView;
@property (nonatomic, strong, readonly) UILabel *timeLabel;
@property (nonatomic, strong, readonly) UIButton *landscapeButton;
- (instancetype)initWithProgressView:(UIView<JPVideoPlayerControlProgressProtocol> *_Nullable)progressView NS_DESIGNATED_INITIALIZER;
@end
@interface JPVideoPlayerControlView : UIView<JPVideoPlayerProtocol>
@property (nonatomic, strong, readonly) UIView<JPVideoPlayerProtocol> *controlBar;
@property (nonatomic, strong, readonly) UIImage *blurImage;
/**
* A designated initializer.
*
* @param controlBar The view abide by the `JPVideoPlayerProgressProtocol`.
* @param blurImage A image on back of controlBar.
*
* @return The current instance.
*/
- (instancetype)initWithControlBar:(UIView<JPVideoPlayerProtocol> *_Nullable)controlBar
blurImage:(UIImage *_Nullable)blurImage NS_DESIGNATED_INITIALIZER;
@end
UIKIT_EXTERN const CGFloat JPVideoPlayerProgressViewElementHeight;
@interface JPVideoPlayerProgressView : UIView<JPVideoPlayerProtocol>
@property (nonatomic, strong, readonly) NSArray<NSValue *> *rangesValue;
@property (nonatomic, assign, readonly) NSUInteger fileLength;
@property (nonatomic, assign, readonly) NSTimeInterval totalSeconds;
@property (nonatomic, assign, readonly) NSTimeInterval elapsedSeconds;
@property (nonatomic, strong, readonly) UIProgressView *trackProgressView;
@property (nonatomic, strong, readonly) UIView *cachedProgressView;
@property (nonatomic, strong, readonly) UIProgressView *elapsedProgressView;
@end
@interface JPVideoPlayerBufferingIndicator : UIView<JPVideoPlayerBufferingProtocol>
@property (nonatomic, strong, readonly)UIActivityIndicatorView *activityIndicator;
@property (nonatomic, strong, readonly)UIVisualEffectView *blurView;
@property (nonatomic, assign, readonly, getter=isAnimating)BOOL animating;
@end
@interface JPVideoPlayerView : UIView
/**
* A placeholderView to custom your own business.
*/
@property (nonatomic, strong, readonly) UIView *placeholderView;
/**
* A layer to display video layer.
*/
@property (nonatomic, strong, readonly) CALayer *videoContainerLayer;
/**
* A placeholder view to display controlView
*/
@property (nonatomic, strong, readonly) UIView *controlContainerView;
/**
* A placeholder view to display progress view.
*/
@property (nonatomic, strong, readonly) UIView *progressContainerView;
/**
* A placeholder view to display buffering indicator view.
*/
@property (nonatomic, strong, readonly) UIView *bufferingIndicatorContainerView;
/**
* A view to receive user interaction.
*/
@property (nonatomic, strong, readonly) UIView *userInteractionContainerView;
/**
* To control need automatic hide controlView when user touched.
*/
@property (nonatomic, assign, readonly) BOOL needAutoHideControlViewWhenUserTapping;
- (instancetype)initWithNeedAutoHideControlViewWhenUserTapping:(BOOL)needAutoHideControlViewWhenUserTapping;
@end
NS_ASSUME_NONNULL_END
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import <Foundation/Foundation.h>
#import "JPVideoPlayerCompat.h"
NS_ASSUME_NONNULL_BEGIN
@class JPVideoPlayerDownloader, JPResourceLoadingRequestTask;
@class JPResourceLoadingRequestWebTask;
@protocol JPVideoPlayerDownloaderDelegate<NSObject>
@optional
/**
* This method will be called when received response from web,
* this method will execute on main-thread.
*
* @param downloader The current instance.
* @param response The response content.
*/
- (void)downloader:(JPVideoPlayerDownloader *)downloader
didReceiveResponse:(NSURLResponse *)response;
/**
* This method will be called when received data.
* this method will execute on any-thread.
*
* @param downloader The current instance.
* @param data The received new data.
* @param receivedSize The size of received data.
* @param expectedSize The expexted size of request.
*/
- (void)downloader:(JPVideoPlayerDownloader *)downloader
didReceiveData:(NSData *)data
receivedSize:(NSUInteger)receivedSize
expectedSize:(NSUInteger)expectedSize;
/**s
* This method will be called when request completed or some error happened other situations.
* this method will execute on main-thread.
*
* @param downloader The current instance.
* @param error The error when request, maybe nil if successed.
*/
- (void)downloader:(JPVideoPlayerDownloader *)downloader
didCompleteWithError:(NSError *)error;
@end
@interface JPVideoPlayerDownloader : NSObject
/**
* Set the default URL credential to be set for request operations.
*/
@property (strong, nonatomic, nullable) NSURLCredential *urlCredential;
/**
* Set username
*/
@property (strong, nonatomic, nullable) NSString *username;
/**
* Set password
*/
@property (strong, nonatomic, nullable) NSString *password;
/**
* The timeout value (in seconds) for the download operation. Default: 15.0s.
*/
@property (assign, nonatomic) NSTimeInterval downloadTimeout;
/**
* The current url, may nil if no download operation.
*/
@property (nonatomic, weak, readonly, nullable) JPResourceLoadingRequestWebTask *runningTask;
/**
* The current downloaderOptions, may nil if no download operation.
*/
@property (nonatomic, assign, readonly) JPVideoPlayerDownloaderOptions downloaderOptions;
@property (nonatomic, weak) id<JPVideoPlayerDownloaderDelegate> delegate;
/**
* @brief Customize acceptable response MIMETypes.
*
* @discussion
*
* Original acceptable MIMEType is just `audio` and `video`, but there are some
* other kind of MIMETypes, such as `application/oct-stream` and so on.
*
* @param types
*
* The supported MIMETypes.
*/
+ (void)registerSupportedMIMETypes:(NSArray<NSString *> *)types;
/**
* Creates an instance of a downloader with specified session configuration.
* *Note*: `timeoutIntervalForRequest` is going to be overwritten.
* @return new instance of downloader class
*/
- (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)sessionConfiguration NS_DESIGNATED_INITIALIZER;
/**
* Singleton method, returns the shared instance.
*
* @return global shared instance of downloader class.
*/
+ (nonnull instancetype)sharedDownloader;
/**
* Start download video data for given url.
*
* @param requestTask A abstract instance packageing the loading request.
* @param downloadOptions The options to be used for this download.
*/
- (void)downloadVideoWithRequestTask:(JPResourceLoadingRequestWebTask *)requestTask
downloadOptions:(JPVideoPlayerDownloaderOptions)downloadOptions;
/**
* Cancel current download task.
*/
- (void)cancel;
@end
NS_ASSUME_NONNULL_END
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import "UIView+WebVideoCache.h"
#import "JPVideoPlayerControlViews.h"
#import "UITableView+WebVideoCache.h"
#import "UITableViewCell+WebVideoCache.h"
#import "UICollectionViewCell+WebVideoCache.h"
#import "UICollectionView+WebVideoCache.h"
#import "JPVideoPlayerCache.h"
#import "JPVideoPlayerManager.h"
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import <UIKit/UIKit.h>
#import "JPVideoPlayerCompat.h"
#import <AVFoundation/AVFoundation.h>
NS_ASSUME_NONNULL_BEGIN
@protocol JPVideoPlayerLayoutProtocol<NSObject>
@required
/**
* This method called when need layout subviews, suggest you layout subviews in this method.
*
* @param constrainedRect The bounds of superview.
* @param nearestViewController The nearest `UIViewController` of view in view tree,
* it be use to fetch `safeAreaInsets` to help layout subviews.
* @param interfaceOrientation The current interface orientation of view.
*/
- (void)layoutThatFits:(CGRect)constrainedRect
nearestViewControllerInViewTree:(UIViewController *_Nullable)nearestViewController
interfaceOrientation:(JPVideoPlayViewInterfaceOrientation)interfaceOrientation;
@end
@protocol JPVideoPlayerProtocol<JPVideoPlayerLayoutProtocol>
@optional
/**
* This method will be called when the view as a subview be add to a view.
*
* @note User can hold this view to control player, but remember that do not retain this view, suggest to
* weak hold this view.
*
* @param view The view to control player.
*/
- (void)viewWillAddToSuperView:(UIView *)view;
/**
* This method will be call when the view be reuse in `UITableView`,
* you need reset progress value in this method for good user experience.
*/
- (void)viewWillPrepareToReuse;
/**
* This method will be called when the downloader fetched the file length or read from disk.
*
* @warning This method may be call repeatedly when download a video.
*
* @param videoLength The video file length.
* @param videoURL The URL of video.
*/
- (void)didFetchVideoFileLength:(NSUInteger)videoLength
videoURL:(NSURL *)videoURL;
/**
* This method will be called when received new video data from web.
*
* @param cacheRanges The ranges of video data cached in disk.
* @param videoURL The URL of video.
*/
- (void)cacheRangeDidChange:(NSArray<NSValue *> *)cacheRanges
videoURL:(NSURL *)videoURL;
/**
* This method will be called when play progress changed.
*
* @param elapsedSeconds The elapsed player time.
* @param totalSeconds The length of the video.
* @param videoURL The URL of video.
*/
- (void)playProgressDidChangeElapsedSeconds:(NSTimeInterval)elapsedSeconds
totalSeconds:(NSTimeInterval)totalSeconds
videoURL:(NSURL *)videoURL;
/**
* This method will be called when video player status did change.
*
* @param playerStatus The player status.
* @param videoURL The URL of video.
*/
- (void)videoPlayerStatusDidChange:(JPVideoPlayerStatus)playerStatus
videoURL:(NSURL *)videoURL;
/**
* This method will be called when the interfaceOrientation of player was changed.
*
* @param interfaceOrientation The interfaceOrientation of player.
* @param videoURL The URL of video.
*/
- (void)videoPlayerInterfaceOrientationDidChange:(JPVideoPlayViewInterfaceOrientation)interfaceOrientation
videoURL:(NSURL *)videoURL;
@end
@protocol JPVideoPlayerControlProgressProtocol<JPVideoPlayerProtocol>
/**
* Control progress must implement this method, and implement
*
* @code
* [self willChangeValueForKey:@"userDragging"];
* _userDragging = userDragging;
* [self didChangeValueForKey:@"userDragging"];
*@endcode
*/
@property(nonatomic) BOOL userDragging;
/**
* Control progress must implement this method, and implement
*
* @code
* [self willChangeValueForKey:@"userDragTimeInterval"];
* _userDragTimeInterval = userDragTimeInterval;
* [self didChangeValueForKey:@"userDragTimeInterval"];
*@endcode
*/
@property(nonatomic) NSTimeInterval userDragTimeInterval;
@end
@protocol JPVideoPlayerBufferingProtocol<JPVideoPlayerLayoutProtocol>
@optional
/**
* This method will be called when player buffering.
*
* @param videoURL The URL of video.
*/
- (void)didStartBufferingVideoURL:(NSURL *)videoURL;
/**
* This method will be called when player finish buffering and start play.
*
* @param videoURL The URL of video.
*/
- (void)didFinishBufferingVideoURL:(NSURL *)videoURL;
@end
@protocol JPVideoPlayerPlaybackProtocol<NSObject>
@required
/**
* The current playback rate.
*/
@property(nonatomic) float rate;
/**
* A Boolean value that indicates whether the audio output of the player is muted.
*/
@property(nonatomic) BOOL muted;
/**
* The audio playback volume for the player, ranging from 0.0 through 1.0 on a linear scale.
*/
@property(nonatomic) float volume;
/**
* Moves the playback cursor.
*
* @param time The time where seek to.
*
* @return The result of Moving the playback cursor, YES means succeed, NO means failure.
*/
- (BOOL)seekToTime:(CMTime)time;
/**
* Fetch the elapsed seconds of player.
*/
- (NSTimeInterval)elapsedSeconds;
/**
* Fetch the total seconds of player.
*/
- (NSTimeInterval)totalSeconds;
/**
* Call this method to pause playback.
*/
- (void)pause;
/**
* Call this method to resume playback.
*/
- (void)resume;
/**
* @return Returns the current time of the current player item.
*/
- (CMTime)currentTime;
/**
* Call this method to stop play video.
*/
- (void)stopPlay;
@end
NS_ASSUME_NONNULL_END
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
@class JPVideoPlayerResourceLoader,
JPResourceLoadingRequestWebTask,
JPVideoPlayerCacheFile;
NS_ASSUME_NONNULL_BEGIN
@protocol JPVideoPlayerResourceLoaderDelegate<NSObject>
@required
/**
* This method will be called when the current instance receive new loading request.
*
* @prama resourceLoader The current resource loader for videoURLAsset.
* @prama requestTask A abstract instance packaging the loading request.
*/
- (void)resourceLoader:(JPVideoPlayerResourceLoader *)resourceLoader
didReceiveLoadingRequestTask:(JPResourceLoadingRequestWebTask *)requestTask;
@end
@interface JPVideoPlayerResourceLoader : NSObject<AVAssetResourceLoaderDelegate>
@property (nonatomic, weak) id<JPVideoPlayerResourceLoaderDelegate> delegate;
/**
* The url custom passed in.
*/
@property (nonatomic, strong, readonly) NSURL *customURL;
/**
* The cache file take responsibility for save video data to disk and read cached video from disk.
*/
@property (nonatomic, strong, readonly) JPVideoPlayerCacheFile *cacheFile;
/**
* Convenience method to fetch instance of this class.
*
* @param customURL The url custom passed in.
*
* @return A instance of this class.
*/
+ (instancetype)resourceLoaderWithCustomURL:(NSURL *)customURL;
/**
* Designated initializer method.
*
* @param customURL The url custom passed in.
*
* @return A instance of this class.
*/
- (instancetype)initWithCustomURL:(NSURL *)customURL NS_DESIGNATED_INITIALIZER;
@end
NS_ASSUME_NONNULL_END
//
// Created by NewPan on 2019-02-25.
// Copyright (c) 2019 NewPan. All rights reserved.
//
#import "JPVideoPlayerCellProtocol.h"
typedef NS_ENUM(NSUInteger, JPScrollPlayStrategyType) {
/**
* `JPScrollFindBestCell` strategy mean find which cell need play video by the space from the center of cell
* to the center of `jp_tableViewVisibleFrame`, h1 on bottom picture.
*/
JPScrollPlayStrategyTypeBestCell = 0,
/**
* `JPScrollFindBestCell` strategy mean find which cell need play video by the space from the center of videoView
* to the center of `jp_tableViewVisibleFrame`, h2 on bottom picture.
*/
JPScrollPlayStrategyTypeBestVideoView,
};
typedef UIView<JPVideoPlayerCellProtocol> *_Nullable (^JPPlayVideoInVisibleCellsBlock)(NSArray<UIView<JPVideoPlayerCellProtocol> *> *_Nullable visibleCells);
NS_ASSUME_NONNULL_BEGIN
@protocol JPVideoPlayerScrollViewProtocol;
@protocol JPScrollViewPlayVideoDelegate<NSObject>
@optional
/**
* This method will be call when call `jp_playVideoInVisibleCellsIfNeed` and the find the best cell to play video when
* tableView or collectionView scroll end.
*
* @param tableView The tableView or collectionView.
* @param cell The cell ready to play video, you can call `[cell.jp_videoPlayView jp_playVideoMuteWithURL:cell.jp_videoURL progressView:nil]`
* or other method given to play video.
*/
- (void)scrollView:(UIScrollView<JPVideoPlayerScrollViewProtocol> *)scrollView willPlayVideoOnCell:(UIView<JPVideoPlayerCellProtocol> *)cell;
@end
@protocol JPVideoPlayerScrollViewProtocol <NSObject>
@property (nonatomic) id<JPScrollViewPlayVideoDelegate> jp_delegate;
/**
* The cell is playing video.
*/
@property(nonatomic, readonly, nullable) UIView<JPVideoPlayerCellProtocol> *jp_playingVideoCell;
/**
* The visible frame of tableView. `visible` mean when the tableView frame is {0, 0, screenWidth, screenHeight},
* but tableView is wrapped by `UITabBarController` and `UINavigationController`, `UINavigationBar` and `UITabBar`
* is visible, so the visible frame of tableView is {0, navigationBarHeight, screenWidth, screenHeight - navigationBarHeight - tabBarHeight}.
* {0, navigationBarHeight, screenWidth, screenHeight - navigationBarHeight} if `UITabBar` is hidden.
*
* @warning This value must be not empty.
*/
@property (nonatomic) CGRect jp_scrollViewVisibleFrame;
/**
* Display visible frame of the scrollView for debug, default is NO.
*/
@property(nonatomic, assign) BOOL jp_debugScrollViewVisibleFrame;
/**
* The play cell strategy when tableView stop scroll, `JPScrollFindStrategyBestCell` by default.
*
* @see `JPScrollFindStrategy`.
*
*
* ****************************** center of `jp_tableViewVisibleFrame`
* |h2 |h1
* ----------|-----|-------------
* | | | |
* | cell | | |
* | | | |
* | --------|-----|--- |
* | |videoView | | |
* | | | * <- cell center
* | | * <- videoView center
* | | | |
* | | | |
* | ------------------ |
* | |
* ------------------------------
*/
@property (nonatomic) JPScrollPlayStrategyType jp_scrollPlayStrategyType;
/**
* Because we play video on cell that stopped on screen center when the tableView was stopped scrolling,
* so some cell may can not stop in screen center, this type cell always is on top or bottom in tableView, we call this type cell `unreachableCell`.
* so we need handle this especially. but first we need do is to check the situation of this type cell appear.
*
* Here is the result of my measure on iPhone 6s(CH).
* The number of visible cells in screen: 4 3 2
* The number of cells cannot stop in screen center: 1 1 0
*
* The default dictionary content is: @{
* @"4" : @"1",
* @"3" : @"1",
* @"2" : @"0"
* };
*
* @warning you need to know that the mean of result, For example, when we got 4 cells in screen,
* this time we find 1 cell that can not stop in screen center on top, and we got the same cell that cannot stop in screen center on bottom at the same time.
* The cell of cannot stop in screen center only appear when the count of visible cells is greater than 3.
*
* @note You can custom this dictionary.
*/
@property (nonatomic) NSDictionary<NSString *, NSNumber *> *jp_unreachableCellDictionary;
/**
* Use this block to custom choosing cell process when call `jp_playVideoInVisibleCellsIfNeed`.
*/
@property(nonatomic) JPPlayVideoInVisibleCellsBlock jp_playVideoInVisibleCellsBlock;
/**
* Use this block to custom finding the best cell process when scrollView did stop scroll.
*/
@property(nonatomic) JPPlayVideoInVisibleCellsBlock jp_findBestCellInVisibleCellsBlock;
/**
* This method be used to find the first cell need to play video in visible cells.
* This method should be call after tableView is finished `-reloadData`.
* Suggest call this method in `-viewDidAppear:` method.
*/
- (void)jp_playVideoInVisibleCellsIfNeed;
/**
* Call this method to stop video play.
*/
- (void)jp_stopPlayIfNeed;
/**
* This method must be call after called `reloadData` for tableView.
*/
- (void)jp_handleCellUnreachableTypeInVisibleCellsAfterReloadData;
/**
* This method must be called in `-tableView:cellForRowAtIndexPath:`, and pass cell and indexPath in.
*
* @param cell A `UITableViewCell`.
* @param indexPath The indexPath of cell.
*
* @warning This method must be call in given method.
*/
- (void)jp_handleCellUnreachableTypeForCell:(UIView<JPVideoPlayerCellProtocol> *)cell
atIndexPath:(NSIndexPath *)indexPath;
/**
* This method must be call in `-scrollViewDidScroll:` method.
*
* * @warning This method must be call in given method.
*/
- (void)jp_scrollViewDidScroll;
/**
* This method must be call in `scrollViewDidEndDragging:willDecelerate:`.
*
* @param decelerate The tableView will decelerate or not.
*
* @warning This method must be call in given method.
*/
- (void)jp_scrollViewDidEndDraggingWillDecelerate:(BOOL)decelerate;
/**
* This method must be call in `scrollViewDidEndDecelerating:`.
*
* @warning This method must be call in given method.
*/
- (void)jp_scrollViewDidEndDecelerating;
/**
* You can use this method to judge a view is visible or not when scrollView did scroll.
*
* @param view The target view, the view must be a subview on this tableView.
*
* @return The result.
*/
- (BOOL)jp_viewIsVisibleInVisibleFrameAtScrollViewDidScroll:(UIView *)view;
@end
NS_ASSUME_NONNULL_END
\ No newline at end of file
//
// Created by NewPan on 2019-02-25.
// Copyright (c) 2019 NewPan. All rights reserved.
//
#import "JPVideoPlayerScrollViewProtocol.h"
#import "JPMethodInjecting.h"
#import "JPVideoPlayerSupportUtils.h"
@jp_concreteprotocol(JPVideoPlayerScrollViewProtocol)
- (void)setJp_delegate:(id <JPScrollViewPlayVideoDelegate>)jp_delegate {
self.helper.delegate = jp_delegate;
}
- (id <JPScrollViewPlayVideoDelegate>)jp_delegate {
return self.helper.delegate;
}
- (UITableViewCell *)jp_playingVideoCell {
return [self.helper playingVideoCell];
}
- (void)setJp_scrollViewVisibleFrame:(CGRect)jp_scrollViewVisibleFrame {
self.helper.scrollViewVisibleFrame = jp_scrollViewVisibleFrame;
}
- (CGRect)jp_scrollViewVisibleFrame {
return self.helper.scrollViewVisibleFrame;
}
- (void)setJp_debugScrollViewVisibleFrame:(BOOL)jp_debugScrollViewVisibleFrame {
self.helper.debugScrollViewVisibleFrame = jp_debugScrollViewVisibleFrame;
}
- (BOOL)jp_debugScrollViewVisibleFrame {
return self.helper.debugScrollViewVisibleFrame;
}
- (void)setJp_scrollPlayStrategyType:(JPScrollPlayStrategyType)jp_scrollPlayStrategyType {
self.helper.scrollPlayStrategyType = jp_scrollPlayStrategyType;
}
- (JPScrollPlayStrategyType)jp_scrollPlayStrategyType {
return self.helper.scrollPlayStrategyType;
}
- (void)setJp_unreachableCellDictionary:(NSDictionary<NSString *, NSString *> *)jp_unreachableCellDictionary {
self.helper.unreachableCellDictionary = jp_unreachableCellDictionary;
}
- (NSDictionary<NSString *, NSNumber *> *)jp_unreachableCellDictionary {
return self.helper.unreachableCellDictionary;
}
- (void)setJp_playVideoInVisibleCellsBlock:(JPPlayVideoInVisibleCellsBlock)jp_playVideoInVisibleCellsBlock {
self.helper.playVideoInVisibleCellsBlock = jp_playVideoInVisibleCellsBlock;
}
- (JPPlayVideoInVisibleCellsBlock)jp_playVideoInVisibleCellsBlock {
return self.helper.playVideoInVisibleCellsBlock;
}
- (void)setJp_findBestCellInVisibleCellsBlock:(JPPlayVideoInVisibleCellsBlock)jp_findBestCellInVisibleCellsBlock {
self.helper.findBestCellInVisibleCellsBlock = jp_findBestCellInVisibleCellsBlock;
}
- (JPPlayVideoInVisibleCellsBlock)jp_findBestCellInVisibleCellsBlock {
return self.helper.findBestCellInVisibleCellsBlock;
}
- (void)jp_playVideoInVisibleCellsIfNeed {
[self.helper playVideoInVisibleCellsIfNeed];
}
- (void)jp_stopPlayIfNeed {
[self.helper stopPlayIfNeed];
}
- (void)jp_handleCellUnreachableTypeInVisibleCellsAfterReloadData {
[self.helper handleCellUnreachableTypeInVisibleCellsAfterReloadData];
}
- (void)jp_handleCellUnreachableTypeForCell:(UIView<JPVideoPlayerCellProtocol> *)cell
atIndexPath:(NSIndexPath *)indexPath {
[self.helper handleCellUnreachableTypeForCell:cell
atIndexPath:indexPath];
}
- (void)jp_scrollViewDidEndDraggingWillDecelerate:(BOOL)decelerate {
[self.helper scrollViewDidEndDraggingWillDecelerate:decelerate];
}
- (void)jp_scrollViewDidEndDecelerating {
[self.helper scrollViewDidEndDecelerating];
}
- (void)jp_scrollViewDidScroll {
[self.helper scrollViewDidScroll];
}
- (BOOL)jp_viewIsVisibleInVisibleFrameAtScrollViewDidScroll:(UIView *)view {
return [self.helper viewIsVisibleInVisibleFrameAtScrollViewDidScroll:view];
}
#pragma mark - Private
- (JPVideoPlayerScrollViewInternalObject *)helper {
JPVideoPlayerScrollViewInternalObject *_helper = objc_getAssociatedObject(self, _cmd);
if(!_helper){
_helper = [[JPVideoPlayerScrollViewInternalObject alloc] initWithScrollView:self];
objc_setAssociatedObject(self, _cmd, _helper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return _helper;
}
@end
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import <UIKit/UIKit.h>
#import "JPResourceLoadingRequestTask.h"
#import "UITableViewCell+WebVideoCache.h"
#import "UITableView+WebVideoCache.h"
NS_ASSUME_NONNULL_BEGIN
@interface NSURL (cURL)
/**
* Returns a `curl` command string equivalent of the current object.
*
* @return The URL to format.
*/
- (NSString *)jp_cURLCommand;
@end
@interface NSHTTPURLResponse (JPVideoPlayer)
/**
* Fetch the file length of response.
*
* @return The file length of response.
*/
- (long long)jp_fileLength;
/**
* Check the response support streaming or not.
*
* @return The response support streaming or not.
*/
- (BOOL)jp_supportRange;
@end
@interface AVAssetResourceLoadingRequest (JPVideoPlayer)
/**
* Fill content information for current request use response conent.
*
* @param response A response.
*/
- (void)jp_fillContentInformationWithResponse:(NSHTTPURLResponse *)response;
@end
@interface NSFileHandle (JPVideoPlayer)
- (BOOL)jp_safeWriteData:(NSData *)data;
@end
@interface NSURLSessionTask(JPVideoPlayer)
@property(nonatomic) JPResourceLoadingRequestWebTask * webTask;
@end
@interface NSObject (JPSwizzle)
+ (BOOL)jp_swizzleMethod:(SEL)origSel withMethod:(SEL)altSel error:(NSError**)error;
+ (BOOL)jp_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError**)error_;
@end
@interface JPLog : NSObject
/**
* Output message to console.
*
* @param logLevel The log type.
* @param file The current file name.
* @param function The current function name.
* @param line The current line number.
* @param format The log format.
*/
+ (void)logWithFlag:(JPLogLevel)logLevel
file:(const char *)file
function:(const char *)function
line:(NSUInteger)line
format:(NSString *)format, ...;
@end
#ifdef __OBJC__
#define JP_LOG_MACRO(logFlag, frmt, ...) \
[JPLog logWithFlag:logFlag\
file:__FILE__ \
function:__FUNCTION__ \
line:__LINE__ \
format:(frmt), ##__VA_ARGS__]
#define JP_LOG_MAYBE(logFlag, frmt, ...) JP_LOG_MACRO(logFlag, frmt, ##__VA_ARGS__)
#if DEBUG
/**
* Log debug log.
*/
#define JPDebugLog(frmt, ...) JP_LOG_MAYBE(JPLogLevelDebug, frmt, ##__VA_ARGS__)
/**
* Log debug and warning log.
*/
#define JPWarningLog(frmt, ...) JP_LOG_MAYBE(JPLogLevelWarning, frmt, ##__VA_ARGS__)
/**
* Log debug, warning and error log.
*/
#define JPErrorLog(frmt, ...) JP_LOG_MAYBE(JPLogLevelError, frmt, ##__VA_ARGS__)
#else
#define JPDebugLog(frmt, ...)
#define JPWarningLog(frmt, ...)
#define JPErrorLog(frmt, ...)
#endif
#endif
typedef NS_ENUM(NSInteger, JPApplicationState) {
JPApplicationStateUnknown = 0,
JPApplicationStateWillResignActive,
JPApplicationStateDidEnterBackground,
JPApplicationStateWillEnterForeground,
JPApplicationStateDidBecomeActive
};
@class JPApplicationStateMonitor;
@protocol JPApplicationStateMonitorDelegate <NSObject>
@optional
/**
* This method will be called when application state changed.
*
* @param monitor The current object.
* @param applicationState The application state.
*/
- (void)applicationStateMonitor:(JPApplicationStateMonitor *)monitor
applicationStateDidChange:(JPApplicationState)applicationState;
/**
* This method will be called only when application become active from `Control Center`,
* `Notification Center`, `pop UIAlert`, `double click Home-Button`.
*
* @param monitor The current object.
*/
- (void)applicationDidBecomeActiveFromResignActive:(JPApplicationStateMonitor *)monitor;
/**
* This method will be called only when application become active from `Share to other application`,
* `Enter background`, `Lock screen`.
*
* @param monitor The current object.
*/
- (void)applicationDidBecomeActiveFromBackground:(JPApplicationStateMonitor *)monitor;
@end
@interface JPApplicationStateMonitor : NSObject
@property(nonatomic, weak) id<JPApplicationStateMonitorDelegate> delegate;
@property (nonatomic, assign, readonly) JPApplicationState applicationState;
@end
@protocol JPScrollViewPlayVideoDelegate;
@interface JPVideoPlayerScrollViewInternalObject : NSObject
@property (nonatomic, weak, readonly, nullable) UIScrollView<JPVideoPlayerScrollViewProtocol> *scrollView;
@property (nonatomic, weak, readonly, nullable) UIView<JPVideoPlayerCellProtocol> *playingVideoCell;
@property (nonatomic, assign) CGRect scrollViewVisibleFrame;
@property(nonatomic, assign) BOOL debugScrollViewVisibleFrame;
@property (nonatomic, assign) JPScrollPlayStrategyType scrollPlayStrategyType;
@property(nonatomic) JPPlayVideoInVisibleCellsBlock playVideoInVisibleCellsBlock;
@property(nonatomic) JPPlayVideoInVisibleCellsBlock findBestCellInVisibleCellsBlock;
@property (nonatomic, strong) NSDictionary<NSString *, NSNumber *> *unreachableCellDictionary;
@property (nonatomic, weak) id<JPScrollViewPlayVideoDelegate> delegate;
@property (nonatomic, assign) NSUInteger playVideoSection;
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithScrollView:(UIScrollView<JPVideoPlayerScrollViewProtocol> *)scrollView NS_DESIGNATED_INITIALIZER;
- (void)handleCellUnreachableTypeForCell:(UIView<JPVideoPlayerCellProtocol> *)cell
atIndexPath:(NSIndexPath *)indexPath;
- (void)handleCellUnreachableTypeInVisibleCellsAfterReloadData;
- (void)playVideoInVisibleCellsIfNeed;
- (void)stopPlayIfNeed;
- (void)scrollViewDidScroll;
- (void)scrollViewDidEndDraggingWillDecelerate:(BOOL)decelerate;
- (void)scrollViewDidEndDecelerating;
- (BOOL)viewIsVisibleInVisibleFrameAtScrollViewDidScroll:(UIView *)view;
@end
@interface JPMigration : NSObject
/**
* Executes a block of code for a specific version number and remembers this version as the latest migration done.
*
* @param version A string with a specific version number.
* @param migrationBlock A block object to be executed when the SDK version matches the string 'version'.
* This parameter can't be nil.
*/
+ (void)migrateToSDKVersion:(NSString *)version
block:(dispatch_block_t)migrationBlock;
@end
NS_ASSUME_NONNULL_END
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import "JPVideoPlayerScrollViewProtocol.h"
@interface UICollectionView (WebVideoCache) <JPVideoPlayerScrollViewProtocol>
@end
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import "UICollectionView+WebVideoCache.h"
@implementation UICollectionView (WebVideoCache)
@end
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import "JPVideoPlayerCellProtocol.h"
@interface UICollectionViewCell (WebVideoCache) <JPVideoPlayerCellProtocol>
@end
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import "UICollectionViewCell+WebVideoCache.h"
#import <objc/runtime.h>
@implementation UICollectionViewCell (WebVideoCache)
@end
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import "JPVideoPlayerScrollViewProtocol.h"
@interface UITableView (WebVideoCache) <JPVideoPlayerScrollViewProtocol>
@end
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import "UITableView+WebVideoCache.h"
@implementation UITableView (WebVideoCache)
@end
\ No newline at end of file
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import "JPVideoPlayerCellProtocol.h"
NS_ASSUME_NONNULL_BEGIN
@interface UITableViewCell (WebVideoCache) <JPVideoPlayerCellProtocol>
@end
NS_ASSUME_NONNULL_END
\ No newline at end of file
/*
* This file is part of the JPVideoPlayer package.
* (c) NewPan <13246884282@163.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Click https://github.com/newyjp
* or http://www.jianshu.com/users/e2f2d779c022/latest_articles to contact me.
*/
#import "UITableViewCell+WebVideoCache.h"
@implementation UITableViewCell (WebVideoCache)
@end
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment