This commit is contained in:
DDIsFriend
2023-08-28 18:29:21 +08:00
parent c73a6ff1d4
commit 8d563860f1
290 changed files with 9542 additions and 9626 deletions
@@ -0,0 +1,127 @@
//
// UIImageView+ZFCache.h
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
typedef void (^ZFDownLoadDataCallBack)(NSData *data, NSError *error);
typedef void (^ZFDownloadProgressBlock)(unsigned long long total, unsigned long long current);
@interface ZFImageDownloader : NSObject<NSURLSessionDownloadDelegate>
@property (nonatomic, strong) NSURLSession *session;
@property (nonatomic, strong) NSURLSessionDownloadTask *task;
@property (nonatomic, assign) unsigned long long totalLength;
@property (nonatomic, assign) unsigned long long currentLength;
@property (nonatomic, copy) ZFDownloadProgressBlock progressBlock;
@property (nonatomic, copy) ZFDownLoadDataCallBack callbackOnFinished;
- (void)startDownloadImageWithUrl:(NSString *)url
progress:(ZFDownloadProgressBlock)progress
finished:(ZFDownLoadDataCallBack)finished;
@end
typedef void (^ZFImageBlock)(UIImage *image);
@interface UIImageView (ZFCache)
/**
* Get/Set the callback block when download the image finished.
*
* The image object from network or from disk.
*/
@property (nonatomic, copy) ZFImageBlock completion;
/**
* Image downloader
*/
@property (nonatomic, strong) ZFImageDownloader *imageDownloader;
/**
* Specify the URL to download images fails, the number of retries, the default is 2
*/
@property (nonatomic, assign) NSUInteger attemptToReloadTimesForFailedURL;
/**
* Will automatically download to cutting for UIImageView size of image.The default value is NO.
* If set to YES, then the download after a successful store only after cutting the image
*/
@property (nonatomic, assign) BOOL shouldAutoClipImageToViewSize;
/**
* Set the imageView `image` with an `url` and a placeholder.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param placeholderImageName The image name to be set initially, until the image request finishes.
*/
- (void)setImageWithURLString:(NSString *)url placeholderImageName:(NSString *)placeholderImageName;
/**
* Set the imageView `image` with an `url` and a placeholder.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param placeholderImage The image to be set initially, until the image request finishes.
*/
- (void)setImageWithURLString:(NSString *)url placeholder:(UIImage *)placeholderImage;
/**
* Set the imageView `image` with an `url`, placeholder.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param placeholderImage The image to be set initially, until the image request finishes.
* @param completion A block called when operation has been completed. This block has no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrieved from the local cache or from the network.
* The fourth parameter is the original image url.
*/
- (void)setImageWithURLString:(NSString *)url
placeholder:(UIImage *)placeholderImage
completion:(void (^)(UIImage *image))completion;
/**
* Set the imageView `image` with an `url`, placeholder.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param placeholderImageName The image name to be set initially, until the image request finishes.
* @param completion A block called when operation has been completed. This block has no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrieved from the local cache or from the network.
* The fourth parameter is the original image url.
*/
- (void)setImageWithURLString:(NSString *)url
placeholderImageName:(NSString *)placeholderImageName
completion:(void (^)(UIImage *image))completion;
@end
@@ -0,0 +1,411 @@
//
// UIImageView+ZFCache.m
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "UIImageView+ZFCache.h"
#import <objc/runtime.h>
#import <CommonCrypto/CommonDigest.h>
@implementation ZFImageDownloader
- (void)startDownloadImageWithUrl:(NSString *)url
progress:(ZFDownloadProgressBlock)progress
finished:(ZFDownLoadDataCallBack)finished {
self.progressBlock = progress;
self.callbackOnFinished = finished;
if ([NSURL URLWithString:url] == nil) {
if (finished) { finished(nil, nil); }
return;
}
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]
cachePolicy:NSURLRequestReturnCacheDataElseLoad
timeoutInterval:60];
[request addValue:@"image/*" forHTTPHeaderField:@"Accept"];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
self.session = [NSURLSession sessionWithConfiguration:config
delegate:self
delegateQueue:queue];
NSURLSessionDownloadTask *task = [self.session downloadTaskWithRequest:request];
[task resume];
self.task = task;
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
NSData *data = [NSData dataWithContentsOfURL:location];
if (self.progressBlock) {
self.progressBlock(self.totalLength, self.currentLength);
}
if (self.callbackOnFinished) {
self.callbackOnFinished(data, nil);
// 防止重复调用
self.callbackOnFinished = nil;
}
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
self.currentLength = totalBytesWritten;
self.totalLength = totalBytesExpectedToWrite;
if (self.progressBlock) {
self.progressBlock(self.totalLength, self.currentLength);
}
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
if ([error code] != NSURLErrorCancelled) {
if (self.callbackOnFinished) {
self.callbackOnFinished(nil, error);
}
self.callbackOnFinished = nil;
}
}
@end
@interface NSString (md5)
+ (NSString *)cachedFileNameForKey:(NSString *)key;
+ (NSString *)zf_cachePath;
+ (NSString *)zf_keyForRequest:(NSURLRequest *)request;
@end
@implementation NSString (md5)
+ (NSString *)zf_keyForRequest:(NSURLRequest *)request {
return request.URL.absoluteString;
}
+ (NSString *)zf_cachePath {
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *directoryPath = [NSString stringWithFormat:@"%@/%@/%@",cachePath,@"default",@"com.hackemist.SDWebImageCache.default"];
return directoryPath;
}
+ (NSString *)cachedFileNameForKey:(NSString *)key {
const char *str = [key UTF8String];
if (str == NULL) {
str = "";
}
unsigned char r[CC_MD5_DIGEST_LENGTH];
CC_MD5(str, (CC_LONG)strlen(str), r);
NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%@",
r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10],
r[11], r[12], r[13], r[14], r[15], [[key pathExtension] isEqualToString:@""] ? @"" : [NSString stringWithFormat:@".%@", [key pathExtension]]];
return filename;
}
@end
@interface UIApplication (ZFCacheImage)
@property (nonatomic, strong, readonly) NSMutableDictionary *zf_cacheFaileTimes;
- (UIImage *)zf_cacheImageForRequest:(NSURLRequest *)request;
- (void)zf_cacheImage:(UIImage *)image forRequest:(NSURLRequest *)request;
- (void)zf_cacheFailRequest:(NSURLRequest *)request;
- (NSUInteger)zf_failTimesForRequest:(NSURLRequest *)request;
@end
@implementation UIApplication (ZFCacheImage)
- (NSMutableDictionary *)zf_cacheFaileTimes {
NSMutableDictionary *dict = objc_getAssociatedObject(self, _cmd);
if (!dict) {
dict = [[NSMutableDictionary alloc] init];
}
return dict;
}
- (void)setZf_cacheFaileTimes:(NSMutableDictionary *)zf_cacheFaileTimes {
objc_setAssociatedObject(self, @selector(zf_cacheFaileTimes), zf_cacheFaileTimes, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)zf_clearCache {
[self.zf_cacheFaileTimes removeAllObjects];
self.zf_cacheFaileTimes = nil;
}
- (void)zf_clearDiskCaches {
NSString *directoryPath = [NSString zf_cachePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:nil]) {
dispatch_queue_t ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL);
dispatch_async(ioQueue, ^{
[[NSFileManager defaultManager] removeItemAtPath:directoryPath error:nil];
[[NSFileManager defaultManager] createDirectoryAtPath:directoryPath
withIntermediateDirectories:YES
attributes:nil
error:nil];
});
}
[self zf_clearCache];
}
- (UIImage *)zf_cacheImageForRequest:(NSURLRequest *)request {
if (request) {
NSString *directoryPath = [NSString zf_cachePath];
NSString *path = [NSString stringWithFormat:@"%@/%@", directoryPath, [NSString cachedFileNameForKey:[NSString zf_keyForRequest:request]]];
return [UIImage imageWithContentsOfFile:path];
}
return nil;
}
- (NSUInteger)zf_failTimesForRequest:(NSURLRequest *)request {
NSNumber *faileTimes = [self.zf_cacheFaileTimes objectForKey:[NSString cachedFileNameForKey:[NSString zf_keyForRequest:request]]];
if (faileTimes && [faileTimes respondsToSelector:@selector(integerValue)]) {
return faileTimes.integerValue;
}
return 0;
}
- (void)zf_cacheFailRequest:(NSURLRequest *)request {
NSNumber *faileTimes = [self.zf_cacheFaileTimes objectForKey:[NSString cachedFileNameForKey:[NSString zf_keyForRequest:request]]];
NSUInteger times = 0;
if (faileTimes && [faileTimes respondsToSelector:@selector(integerValue)]) {
times = [faileTimes integerValue];
}
times++;
[self.zf_cacheFaileTimes setObject:@(times) forKey:[NSString cachedFileNameForKey:[NSString zf_keyForRequest:request]]];
}
- (void)zf_cacheImage:(UIImage *)image forRequest:(NSURLRequest *)request {
if (!image || !request) { return; }
NSString *directoryPath = [NSString zf_cachePath];
if (![[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:nil]) {
NSError *error = nil;
[[NSFileManager defaultManager] createDirectoryAtPath:directoryPath
withIntermediateDirectories:YES
attributes:nil
error:&error];
if (error) { return; }
}
NSString *path = [NSString stringWithFormat:@"%@/%@", directoryPath, [NSString cachedFileNameForKey:[NSString zf_keyForRequest:request]]];
NSData *data = UIImagePNGRepresentation(image);
if (data) {
[[NSFileManager defaultManager] createFileAtPath:path contents:data attributes:nil];
}
}
@end
@implementation UIImageView (ZFCache)
#pragma mark - getter
- (ZFImageBlock)completion {
return objc_getAssociatedObject(self, _cmd);
}
- (ZFImageDownloader *)imageDownloader {
return objc_getAssociatedObject(self, _cmd);
}
- (NSUInteger)attemptToReloadTimesForFailedURL {
NSUInteger count = [objc_getAssociatedObject(self, _cmd) integerValue];
if (count == 0) { count = 2; }
return count;
}
- (BOOL)shouldAutoClipImageToViewSize {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
#pragma mark - setter
- (void)setCompletion:(ZFImageBlock)completion {
objc_setAssociatedObject(self, @selector(completion), completion, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (void)setImageDownloader:(ZFImageDownloader *)imageDownloader {
objc_setAssociatedObject(self, @selector(imageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setAttemptToReloadTimesForFailedURL:(NSUInteger)attemptToReloadTimesForFailedURL {
objc_setAssociatedObject(self, @selector(attemptToReloadTimesForFailedURL), @(attemptToReloadTimesForFailedURL), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setShouldAutoClipImageToViewSize:(BOOL)shouldAutoClipImageToViewSize {
objc_setAssociatedObject(self, @selector(shouldAutoClipImageToViewSize), @(shouldAutoClipImageToViewSize), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#pragma mark - public method
- (void)setImageWithURLString:(NSString *)url
placeholderImageName:(NSString *)placeholderImageName {
return [self setImageWithURLString:url placeholderImageName:placeholderImageName completion:nil];
}
- (void)setImageWithURLString:(NSString *)url placeholder:(UIImage *)placeholderImage {
return [self setImageWithURLString:url placeholder:placeholderImage completion:nil];
}
- (void)setImageWithURLString:(NSString *)url
placeholderImageName:(NSString *)placeholderImage
completion:(void (^)(UIImage *image))completion {
NSString *path = [[NSBundle mainBundle] pathForResource:placeholderImage ofType:nil];
UIImage *image = [UIImage imageWithContentsOfFile:path];
if (image == nil) { image = [UIImage imageNamed:placeholderImage]; }
[self setImageWithURLString:url placeholder:image completion:completion];
}
- (void)setImageWithURLString:(NSString *)url
placeholder:(UIImage *)placeholderImageName
completion:(void (^)(UIImage *image))completion {
[self.layer removeAllAnimations];
self.completion = completion;
if (url == nil || [url isKindOfClass:[NSNull class]] || (![url hasPrefix:@"http://"] && ![url hasPrefix:@"https://"])) {
[self setImage:placeholderImageName isFromCache:YES];
if (completion) {
self.completion(self.image);
}
return;
}
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
[self downloadWithReqeust:request holder:placeholderImageName];
}
#pragma mark - private method
- (void)downloadWithReqeust:(NSURLRequest *)theRequest holder:(UIImage *)holder {
UIImage *cachedImage = [[UIApplication sharedApplication] zf_cacheImageForRequest:theRequest];
if (cachedImage) {
[self setImage:cachedImage isFromCache:YES];
if (self.completion) {
self.completion(cachedImage);
}
return;
}
[self setImage:holder isFromCache:YES];
if ([[UIApplication sharedApplication] zf_failTimesForRequest:theRequest] >= self.attemptToReloadTimesForFailedURL) {
return;
}
[self cancelRequest];
self.imageDownloader = nil;
__weak __typeof(self) weakSelf = self;
self.imageDownloader = [[ZFImageDownloader alloc] init];
[self.imageDownloader startDownloadImageWithUrl:theRequest.URL.absoluteString progress:nil finished:^(NSData *data, NSError *error) {
// success
if (data != nil && error == nil) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
UIImage *image = [UIImage imageWithData:data];
UIImage *finalImage = image;
if (image) {
if (weakSelf.shouldAutoClipImageToViewSize) {
// cutting
if (fabs(weakSelf.frame.size.width - image.size.width) != 0
&& fabs(weakSelf.frame.size.height - image.size.height) != 0) {
finalImage = [self clipImage:image toSize:weakSelf.frame.size isScaleToMax:YES];
}
}
[[UIApplication sharedApplication] zf_cacheImage:finalImage forRequest:theRequest];
} else {
[[UIApplication sharedApplication] zf_cacheFailRequest:theRequest];
}
dispatch_async(dispatch_get_main_queue(), ^{
if (finalImage) {
[weakSelf setImage:finalImage isFromCache:NO];
if (weakSelf.completion) {
weakSelf.completion(weakSelf.image);
}
} else {// error data
if (weakSelf.completion) {
weakSelf.completion(weakSelf.image);
}
}
});
});
} else { // error
[[UIApplication sharedApplication] zf_cacheFailRequest:theRequest];
if (weakSelf.completion) {
weakSelf.completion(weakSelf.image);
}
}
}];
}
- (void)setImage:(UIImage *)image isFromCache:(BOOL)isFromCache {
self.image = image;
if (!isFromCache) {
CATransition *animation = [CATransition animation];
[animation setDuration:0.6f];
[animation setType:kCATransitionFade];
animation.removedOnCompletion = YES;
[self.layer addAnimation:animation forKey:@"transition"];
}
}
- (void)cancelRequest {
[self.imageDownloader.task cancel];
}
- (UIImage *)clipImage:(UIImage *)image toSize:(CGSize)size isScaleToMax:(BOOL)isScaleToMax {
CGFloat scale = [UIScreen mainScreen].scale;
UIGraphicsBeginImageContextWithOptions(size, NO, scale);
CGSize aspectFitSize = CGSizeZero;
if (image.size.width != 0 && image.size.height != 0) {
CGFloat rateWidth = size.width / image.size.width;
CGFloat rateHeight = size.height / image.size.height;
CGFloat rate = isScaleToMax ? MAX(rateHeight, rateWidth) : MIN(rateHeight, rateWidth);
aspectFitSize = CGSizeMake(image.size.width * rate, image.size.height * rate);
}
[image drawInRect:CGRectMake(0, 0, aspectFitSize.width, aspectFitSize.height)];
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return finalImage;
}
@end
@@ -0,0 +1,45 @@
//
// UIView+ZFFrame.h
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
@interface UIView (ZFFrame)
@property (nonatomic) CGFloat zf_x;
@property (nonatomic) CGFloat zf_y;
@property (nonatomic) CGFloat zf_width;
@property (nonatomic) CGFloat zf_height;
@property (nonatomic) CGFloat zf_top;
@property (nonatomic) CGFloat zf_bottom;
@property (nonatomic) CGFloat zf_left;
@property (nonatomic) CGFloat zf_right;
@property (nonatomic) CGFloat zf_centerX;
@property (nonatomic) CGFloat zf_centerY;
@property (nonatomic) CGPoint zf_origin;
@property (nonatomic) CGSize zf_size;
@end
@@ -0,0 +1,149 @@
//
// UIView+ZFFrame.m
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "UIView+ZFFrame.h"
@implementation UIView (ZFFrame)
- (CGFloat)zf_x {
return self.frame.origin.x;
}
- (void)setZf_x:(CGFloat)zf_x {
CGRect newFrame = self.frame;
newFrame.origin.x = zf_x;
self.frame = newFrame;
}
- (CGFloat)zf_y {
return self.frame.origin.y;
}
- (void)setZf_y:(CGFloat)zf_y {
CGRect newFrame = self.frame;
newFrame.origin.y = zf_y;
self.frame = newFrame;
}
- (CGFloat)zf_width {
return CGRectGetWidth(self.bounds);
}
- (void)setZf_width:(CGFloat)zf_width {
CGRect newFrame = self.frame;
newFrame.size.width = zf_width;
self.frame = newFrame;
}
- (CGFloat)zf_height {
return CGRectGetHeight(self.bounds);
}
- (void)setZf_height:(CGFloat)zf_height {
CGRect newFrame = self.frame;
newFrame.size.height = zf_height;
self.frame = newFrame;
}
- (CGFloat)zf_top {
return self.frame.origin.y;
}
- (void)setZf_top:(CGFloat)zf_top {
CGRect newFrame = self.frame;
newFrame.origin.y = zf_top;
self.frame = newFrame;
}
- (CGFloat)zf_bottom {
return self.frame.origin.y + self.frame.size.height;
}
- (void)setZf_bottom:(CGFloat)zf_bottom {
CGRect newFrame = self.frame;
newFrame.origin.y = zf_bottom - self.frame.size.height;
self.frame = newFrame;
}
- (CGFloat)zf_left {
return self.frame.origin.x;
}
- (void)setZf_left:(CGFloat)zf_left {
CGRect newFrame = self.frame;
newFrame.origin.x = zf_left;
self.frame = newFrame;
}
- (CGFloat)zf_right {
return self.frame.origin.x + self.frame.size.width;
}
- (void)setZf_right:(CGFloat)zf_right {
CGRect newFrame = self.frame;
newFrame.origin.x = zf_right - self.frame.size.width;
self.frame = newFrame;
}
- (CGFloat)zf_centerX {
return self.center.x;
}
- (void)setZf_centerX:(CGFloat)zf_centerX {
CGPoint newCenter = self.center;
newCenter.x = zf_centerX;
self.center = newCenter;
}
- (CGFloat)zf_centerY {
return self.center.y;
}
- (void)setZf_centerY:(CGFloat)zf_centerY {
CGPoint newCenter = self.center;
newCenter.y = zf_centerY;
self.center = newCenter;
}
- (CGPoint)zf_origin {
return self.frame.origin;
}
- (void)setZf_origin:(CGPoint)zf_origin {
CGRect newFrame = self.frame;
newFrame.origin = zf_origin;
self.frame = newFrame;
}
- (CGSize)zf_size {
return self.frame.size;
}
- (void)setZf_size:(CGSize)zf_size {
CGRect newFrame = self.frame;
newFrame.size = zf_size;
self.frame = newFrame;
}
@end
@@ -0,0 +1,120 @@
//
// ZFLandScapeControlView.h
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import "ZFSliderView.h"
#if __has_include(<ZFPlayer/ZFPlayerController.h>)
#import <ZFPlayer/ZFPlayerController.h>
#else
#import "ZFPlayerController.h"
#endif
NS_ASSUME_NONNULL_BEGIN
@interface ZFLandScapeControlView : UIView
/// 顶部工具栏
@property (nonatomic, strong, readonly) UIView *topToolView;
/// 返回按钮
@property (nonatomic, strong, readonly) UIButton *backBtn;
/// 标题
@property (nonatomic, strong, readonly) UILabel *titleLabel;
/// 底部工具栏
@property (nonatomic, strong, readonly) UIView *bottomToolView;
/// 播放或暂停按钮
@property (nonatomic, strong, readonly) UIButton *playOrPauseBtn;
/// 播放的当前时间
@property (nonatomic, strong, readonly) UILabel *currentTimeLabel;
/// 滑杆
@property (nonatomic, strong, readonly) ZFSliderView *slider;
/// 视频总时间
@property (nonatomic, strong, readonly) UILabel *totalTimeLabel;
/// 锁定屏幕按钮
@property (nonatomic, strong, readonly) UIButton *lockBtn;
/// 播放器
@property (nonatomic, weak) ZFPlayerController *player;
/// slider滑动中
@property (nonatomic, copy, nullable) void(^sliderValueChanging)(CGFloat value,BOOL forward);
/// slider滑动结束
@property (nonatomic, copy, nullable) void(^sliderValueChanged)(CGFloat value);
/// 返回按钮点击回调
@property (nonatomic, copy) void(^backBtnClickCallback)(void);
/// 如果是暂停状态,seek完是否播放,默认YES
@property (nonatomic, assign) BOOL seekToPlay;
/// 全屏模式
@property (nonatomic, assign) ZFFullScreenMode fullScreenMode;
/// 重置控制层
- (void)resetControlView;
/// 显示控制层
- (void)showControlView;
/// 隐藏控制层
- (void)hideControlView;
/// 设置播放时间
- (void)videoPlayer:(ZFPlayerController *)videoPlayer currentTime:(NSTimeInterval)currentTime totalTime:(NSTimeInterval)totalTime;
/// 设置缓冲时间
- (void)videoPlayer:(ZFPlayerController *)videoPlayer bufferTime:(NSTimeInterval)bufferTime;
/// 是否响应该手势
- (BOOL)shouldResponseGestureWithPoint:(CGPoint)point withGestureType:(ZFPlayerGestureType)type touch:(nonnull UITouch *)touch;
/// 视频尺寸改变
- (void)videoPlayer:(ZFPlayerController *)videoPlayer presentationSizeChanged:(CGSize)size;
/// 标题和全屏模式
- (void)showTitle:(NSString *_Nullable)title fullScreenMode:(ZFFullScreenMode)fullScreenMode;
/// 根据当前播放状态取反
- (void)playOrPause;
/// 播放按钮状态
- (void)playBtnSelectedState:(BOOL)selected;
/// 调节播放进度slider和当前时间更新
- (void)sliderValueChanged:(CGFloat)value currentTimeString:(NSString *)timeString;
/// 滑杆结束滑动
- (void)sliderChangeEnded;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,462 @@
//
// ZFLandScapeControlView.m
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "ZFLandScapeControlView.h"
#import "UIView+ZFFrame.h"
#import "ZFUtilities.h"
#if __has_include(<ZFPlayer/ZFPlayer.h>)
#import <ZFPlayer/ZFPlayerConst.h>
#else
#import "ZFPlayerConst.h"
#endif
@interface ZFLandScapeControlView () <ZFSliderViewDelegate>
/// 顶部工具栏
@property (nonatomic, strong) UIView *topToolView;
/// 返回按钮
@property (nonatomic, strong) UIButton *backBtn;
/// 标题
@property (nonatomic, strong) UILabel *titleLabel;
/// 底部工具栏
@property (nonatomic, strong) UIView *bottomToolView;
/// 播放或暂停按钮
@property (nonatomic, strong) UIButton *playOrPauseBtn;
/// 播放的当前时间
@property (nonatomic, strong) UILabel *currentTimeLabel;
/// 滑杆
@property (nonatomic, strong) ZFSliderView *slider;
/// 视频总时间
@property (nonatomic, strong) UILabel *totalTimeLabel;
/// 锁定屏幕按钮
@property (nonatomic, strong) UIButton *lockBtn;
@property (nonatomic, assign) BOOL isShow;
@end
@implementation ZFLandScapeControlView
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self addSubview:self.topToolView];
[self.topToolView addSubview:self.backBtn];
[self.topToolView addSubview:self.titleLabel];
[self addSubview:self.bottomToolView];
[self.bottomToolView addSubview:self.playOrPauseBtn];
[self.bottomToolView addSubview:self.currentTimeLabel];
[self.bottomToolView addSubview:self.slider];
[self.bottomToolView addSubview:self.totalTimeLabel];
[self addSubview:self.lockBtn];
// 设置子控件的响应事件
[self makeSubViewsAction];
[self resetControlView];
/// statusBarFrame changed
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(layoutControllerViews) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat min_x = 0;
CGFloat min_y = 0;
CGFloat min_w = 0;
CGFloat min_h = 0;
CGFloat min_view_w = self.bounds.size.width;
CGFloat min_view_h = self.bounds.size.height;
CGFloat min_margin = 9;
min_x = 0;
min_y = 0;
min_w = min_view_w;
min_h = iPhoneX ? 110 : 80;
self.topToolView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = (iPhoneX && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) ? 44: 15;
if (@available(iOS 13.0, *)) {
min_y = UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation) ? 10 : (iPhoneX ? 40 : 20);
} else {
min_y = (iPhoneX && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) ? 10: (iPhoneX ? 40 : 20);
}
min_w = 40;
min_h = 40;
self.backBtn.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = self.backBtn.zf_right + 5;
min_y = 0;
min_w = min_view_w - min_x - 15 ;
min_h = 30;
self.titleLabel.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.titleLabel.zf_centerY = self.backBtn.zf_centerY;
min_h = iPhoneX ? 100 : 73;
min_x = 0;
min_y = min_view_h - min_h;
min_w = min_view_w;
self.bottomToolView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = (iPhoneX && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) ? 44: 15;
min_y = 32;
min_w = 30;
min_h = 30;
self.playOrPauseBtn.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = self.playOrPauseBtn.zf_right + 4;
min_y = 0;
min_w = 62;
min_h = 30;
self.currentTimeLabel.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.currentTimeLabel.zf_centerY = self.playOrPauseBtn.zf_centerY;
min_w = 62;
min_x = self.bottomToolView.zf_width - min_w - ((iPhoneX && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) ? 44: min_margin);
min_y = 0;
min_h = 30;
self.totalTimeLabel.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.totalTimeLabel.zf_centerY = self.playOrPauseBtn.zf_centerY;
min_x = self.currentTimeLabel.zf_right + 4;
min_y = 0;
min_w = self.totalTimeLabel.zf_left - min_x - 4;
min_h = 30;
self.slider.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.slider.zf_centerY = self.playOrPauseBtn.zf_centerY;
min_x = (iPhoneX && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) ? 50: 18;
min_y = 0;
min_w = 40;
min_h = 40;
self.lockBtn.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.lockBtn.zf_centerY = self.zf_centerY;
if (!self.isShow) {
self.topToolView.zf_y = -self.topToolView.zf_height;
self.bottomToolView.zf_y = self.zf_height;
self.lockBtn.zf_left = iPhoneX ? -82: -47;
} else {
self.lockBtn.zf_left = iPhoneX ? 50: 18;
if (self.player.isLockedScreen) {
self.topToolView.zf_y = -self.topToolView.zf_height;
self.bottomToolView.zf_y = self.zf_height;
} else {
self.topToolView.zf_y = 0;
self.bottomToolView.zf_y = self.zf_height - self.bottomToolView.zf_height;
}
}
}
- (void)makeSubViewsAction {
[self.backBtn addTarget:self action:@selector(backBtnClickAction:) forControlEvents:UIControlEventTouchUpInside];
[self.playOrPauseBtn addTarget:self action:@selector(playPauseButtonClickAction:) forControlEvents:UIControlEventTouchUpInside];
[self.lockBtn addTarget:self action:@selector(lockButtonClickAction:) forControlEvents:UIControlEventTouchUpInside];
}
#pragma mark - action
- (void)layoutControllerViews {
[self layoutIfNeeded];
[self setNeedsLayout];
}
- (void)backBtnClickAction:(UIButton *)sender {
self.lockBtn.selected = NO;
self.player.lockedScreen = NO;
self.lockBtn.selected = NO;
if (self.player.orientationObserver.supportInterfaceOrientation & ZFInterfaceOrientationMaskPortrait) {
[self.player enterFullScreen:NO animated:YES];
}
if (self.backBtnClickCallback) {
self.backBtnClickCallback();
}
}
- (void)playPauseButtonClickAction:(UIButton *)sender {
[self playOrPause];
}
/// 根据当前播放状态取反
- (void)playOrPause {
self.playOrPauseBtn.selected = !self.playOrPauseBtn.isSelected;
self.playOrPauseBtn.isSelected? [self.player.currentPlayerManager play]: [self.player.currentPlayerManager pause];
}
- (void)playBtnSelectedState:(BOOL)selected {
self.playOrPauseBtn.selected = selected;
}
- (void)lockButtonClickAction:(UIButton *)sender {
sender.selected = !sender.selected;
self.player.lockedScreen = sender.selected;
}
#pragma mark - ZFSliderViewDelegate
- (void)sliderTouchBegan:(float)value {
self.slider.isdragging = YES;
}
- (void)sliderTouchEnded:(float)value {
if (self.player.totalTime > 0) {
self.slider.isdragging = YES;
if (self.sliderValueChanging) self.sliderValueChanging(value, self.slider.isForward);
@zf_weakify(self)
[self.player seekToTime:self.player.totalTime*value completionHandler:^(BOOL finished) {
@zf_strongify(self)
if (finished) {
self.slider.isdragging = NO;
if (self.sliderValueChanged) self.sliderValueChanged(value);
if (self.seekToPlay) {
[self.player.currentPlayerManager play];
}
}
}];
} else {
self.slider.isdragging = NO;
self.slider.value = 0;
}
}
- (void)sliderValueChanged:(float)value {
if (self.player.totalTime == 0) {
self.slider.value = 0;
return;
}
self.slider.isdragging = YES;
NSString *currentTimeString = [ZFUtilities convertTimeSecond:self.player.totalTime*value];
self.currentTimeLabel.text = currentTimeString;
if (self.sliderValueChanging) self.sliderValueChanging(value,self.slider.isForward);
}
- (void)sliderTapped:(float)value {
[self sliderTouchEnded:value];
NSString *currentTimeString = [ZFUtilities convertTimeSecond:self.player.totalTime*value];
self.currentTimeLabel.text = currentTimeString;
}
#pragma mark - public method
/// 重置ControlView
- (void)resetControlView {
self.slider.value = 0;
self.slider.bufferValue = 0;
self.currentTimeLabel.text = @"00:00";
self.totalTimeLabel.text = @"00:00";
self.backgroundColor = [UIColor clearColor];
self.playOrPauseBtn.selected = YES;
self.titleLabel.text = @"";
self.topToolView.alpha = 1;
self.bottomToolView.alpha = 1;
self.isShow = NO;
}
- (void)showControlView {
self.lockBtn.alpha = 1;
self.isShow = YES;
if (self.player.isLockedScreen) {
self.topToolView.zf_y = -self.topToolView.zf_height;
self.bottomToolView.zf_y = self.zf_height;
} else {
self.topToolView.zf_y = 0;
self.bottomToolView.zf_y = self.zf_height - self.bottomToolView.zf_height;
}
self.lockBtn.zf_left = iPhoneX ? 50: 18;
self.player.statusBarHidden = NO;
if (self.player.isLockedScreen) {
self.topToolView.alpha = 0;
self.bottomToolView.alpha = 0;
} else {
self.topToolView.alpha = 1;
self.bottomToolView.alpha = 1;
}
}
- (void)hideControlView {
self.isShow = NO;
self.topToolView.zf_y = -self.topToolView.zf_height;
self.bottomToolView.zf_y = self.zf_height;
self.lockBtn.zf_left = iPhoneX ? -82: -47;
self.player.statusBarHidden = YES;
self.topToolView.alpha = 0;
self.bottomToolView.alpha = 0;
self.lockBtn.alpha = 0;
}
- (BOOL)shouldResponseGestureWithPoint:(CGPoint)point withGestureType:(ZFPlayerGestureType)type touch:(nonnull UITouch *)touch {
CGRect sliderRect = [self.bottomToolView convertRect:self.slider.frame toView:self];
if (CGRectContainsPoint(sliderRect, point)) {
return NO;
}
if (self.player.isLockedScreen && type != ZFPlayerGestureTypeSingleTap) { // 锁定屏幕方向后只相应tap手势
return NO;
}
return YES;
}
- (void)videoPlayer:(ZFPlayerController *)videoPlayer presentationSizeChanged:(CGSize)size {
self.lockBtn.hidden = self.player.orientationObserver.fullScreenMode == ZFFullScreenModePortrait;
}
- (void)videoPlayer:(ZFPlayerController *)videoPlayer currentTime:(NSTimeInterval)currentTime totalTime:(NSTimeInterval)totalTime {
if (!self.slider.isdragging) {
NSString *currentTimeString = [ZFUtilities convertTimeSecond:currentTime];
self.currentTimeLabel.text = currentTimeString;
NSString *totalTimeString = [ZFUtilities convertTimeSecond:totalTime];
self.totalTimeLabel.text = totalTimeString;
self.slider.value = videoPlayer.progress;
}
}
- (void)videoPlayer:(ZFPlayerController *)videoPlayer bufferTime:(NSTimeInterval)bufferTime {
self.slider.bufferValue = videoPlayer.bufferProgress;
}
- (void)showTitle:(NSString *)title fullScreenMode:(ZFFullScreenMode)fullScreenMode {
self.titleLabel.text = title;
self.player.orientationObserver.fullScreenMode = fullScreenMode;
self.lockBtn.hidden = fullScreenMode == ZFFullScreenModePortrait;
}
/// 调节播放进度slider和当前时间更新
- (void)sliderValueChanged:(CGFloat)value currentTimeString:(NSString *)timeString {
self.slider.value = value;
self.currentTimeLabel.text = timeString;
self.slider.isdragging = YES;
[UIView animateWithDuration:0.3 animations:^{
self.slider.sliderBtn.transform = CGAffineTransformMakeScale(1.2, 1.2);
}];
}
/// 滑杆结束滑动
- (void)sliderChangeEnded {
self.slider.isdragging = NO;
[UIView animateWithDuration:0.3 animations:^{
self.slider.sliderBtn.transform = CGAffineTransformIdentity;
}];
}
#pragma mark - setter
- (void)setFullScreenMode:(ZFFullScreenMode)fullScreenMode {
_fullScreenMode = fullScreenMode;
self.player.orientationObserver.fullScreenMode = fullScreenMode;
self.lockBtn.hidden = fullScreenMode == ZFFullScreenModePortrait;
}
#pragma mark - getter
- (UIView *)topToolView {
if (!_topToolView) {
_topToolView = [[UIView alloc] init];
UIImage *image = ZFPlayer_Image(@"ZFPlayer_top_shadow");
_topToolView.layer.contents = (id)image.CGImage;
}
return _topToolView;
}
- (UIButton *)backBtn {
if (!_backBtn) {
_backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[_backBtn setImage:ZFPlayer_Image(@"ZFPlayer_back_full") forState:UIControlStateNormal];
}
return _backBtn;
}
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] init];
_titleLabel.textColor = [UIColor whiteColor];
_titleLabel.font = [UIFont systemFontOfSize:15.0];
}
return _titleLabel;
}
- (UIView *)bottomToolView {
if (!_bottomToolView) {
_bottomToolView = [[UIView alloc] init];
UIImage *image = ZFPlayer_Image(@"ZFPlayer_bottom_shadow");
_bottomToolView.layer.contents = (id)image.CGImage;
}
return _bottomToolView;
}
- (UIButton *)playOrPauseBtn {
if (!_playOrPauseBtn) {
_playOrPauseBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[_playOrPauseBtn setImage:ZFPlayer_Image(@"ZFPlayer_play") forState:UIControlStateNormal];
[_playOrPauseBtn setImage:ZFPlayer_Image(@"ZFPlayer_pause") forState:UIControlStateSelected];
}
return _playOrPauseBtn;
}
- (UILabel *)currentTimeLabel {
if (!_currentTimeLabel) {
_currentTimeLabel = [[UILabel alloc] init];
_currentTimeLabel.textColor = [UIColor whiteColor];
_currentTimeLabel.font = [UIFont systemFontOfSize:14.0f];
_currentTimeLabel.textAlignment = NSTextAlignmentCenter;
}
return _currentTimeLabel;
}
- (ZFSliderView *)slider {
if (!_slider) {
_slider = [[ZFSliderView alloc] init];
_slider.delegate = self;
_slider.maximumTrackTintColor = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:0.8];
_slider.bufferTrackTintColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.5];
_slider.minimumTrackTintColor = [UIColor whiteColor];
[_slider setThumbImage:ZFPlayer_Image(@"ZFPlayer_slider") forState:UIControlStateNormal];
_slider.sliderHeight = 2;
}
return _slider;
}
- (UILabel *)totalTimeLabel {
if (!_totalTimeLabel) {
_totalTimeLabel = [[UILabel alloc] init];
_totalTimeLabel.textColor = [UIColor whiteColor];
_totalTimeLabel.font = [UIFont systemFontOfSize:14.0f];
_totalTimeLabel.textAlignment = NSTextAlignmentCenter;
}
return _totalTimeLabel;
}
- (UIButton *)lockBtn {
if (!_lockBtn) {
_lockBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[_lockBtn setImage:ZFPlayer_Image(@"ZFPlayer_unlock-nor") forState:UIControlStateNormal];
[_lockBtn setImage:ZFPlayer_Image(@"ZFPlayer_lock-nor") forState:UIControlStateSelected];
}
return _lockBtn;
}
@end
@@ -0,0 +1,66 @@
//
// ZFLoadingView.h
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, ZFLoadingType) {
ZFLoadingTypeKeep,
ZFLoadingTypeFadeOut,
};
@interface ZFLoadingView : UIView
/// default is ZFLoadingTypeKeep.
@property (nonatomic, assign) ZFLoadingType animType;
/// default is whiteColor.
@property (nonatomic, strong, null_resettable) UIColor *lineColor;
/// Sets the line width of the spinner's circle.
@property (nonatomic) CGFloat lineWidth;
/// Sets whether the view is hidden when not animating.
@property (nonatomic) BOOL hidesWhenStopped;
/// Property indicating the duration of the animation, default is 1.5s.
@property (nonatomic, readwrite) NSTimeInterval duration;
/// anima state
@property (nonatomic, assign, readonly, getter=isAnimating) BOOL animating;
/**
* Starts animation of the spinner.
*/
- (void)startAnimating;
/**
* Stops animation of the spinnner.
*/
- (void)stopAnimating;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,183 @@
//
// ZFLoadingView.m
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "ZFLoadingView.h"
@interface ZFLoadingView ()
@property (nonatomic, strong, readonly) CAShapeLayer *shapeLayer;
@property (nonatomic, assign, getter=isAnimating) BOOL animating;
@property (nonatomic, assign) BOOL strokeShow;
@end
@implementation ZFLoadingView
@synthesize lineColor = _lineColor;
@synthesize shapeLayer = _shapeLayer;
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self initialize];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self initialize];
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
[self initialize];
}
- (void)initialize {
[self.layer addSublayer:self.shapeLayer];
self.duration = 1;
self.lineWidth = 1;
self.lineColor = [UIColor whiteColor];
self.userInteractionEnabled = NO;
}
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat width = MIN(self.bounds.size.width, self.bounds.size.height);
CGFloat height = width;
self.shapeLayer.frame = CGRectMake(0, 0, width, height);
CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
CGFloat radius = MIN(CGRectGetWidth(self.bounds) / 2, CGRectGetHeight(self.bounds) / 2) - self.shapeLayer.lineWidth / 2;
CGFloat startAngle = (CGFloat)(0);
CGFloat endAngle = (CGFloat)(2*M_PI);
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
self.shapeLayer.path = path.CGPath;
}
- (void)startAnimating {
if (self.animating) return;
self.animating = YES;
if (self.animType == ZFLoadingTypeFadeOut) [self fadeOutShow];
CABasicAnimation *rotationAnim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotationAnim.toValue = [NSNumber numberWithFloat:2 * M_PI];
rotationAnim.duration = self.duration;
rotationAnim.repeatCount = CGFLOAT_MAX;
rotationAnim.removedOnCompletion = NO;
[self.shapeLayer addAnimation:rotationAnim forKey:@"rotation"];
if (self.hidesWhenStopped) {
self.hidden = NO;
}
}
- (void)stopAnimating {
if (!self.animating) return;
self.animating = NO;
[self.shapeLayer removeAllAnimations];
if (self.hidesWhenStopped) {
self.hidden = YES;
}
}
- (void)fadeOutShow {
CABasicAnimation *headAnimation = [CABasicAnimation animation];
headAnimation.keyPath = @"strokeStart";
headAnimation.duration = self.duration / 1.5f;
headAnimation.fromValue = @(0.f);
headAnimation.toValue = @(0.25f);
CABasicAnimation *tailAnimation = [CABasicAnimation animation];
tailAnimation.keyPath = @"strokeEnd";
tailAnimation.duration = self.duration / 1.5f;
tailAnimation.fromValue = @(0.f);
tailAnimation.toValue = @(1.f);
CABasicAnimation *endHeadAnimation = [CABasicAnimation animation];
endHeadAnimation.keyPath = @"strokeStart";
endHeadAnimation.beginTime = self.duration / 1.5f;
endHeadAnimation.duration = self.duration / 3.0f;
endHeadAnimation.fromValue = @(0.25f);
endHeadAnimation.toValue = @(1.f);
CABasicAnimation *endTailAnimation = [CABasicAnimation animation];
endTailAnimation.keyPath = @"strokeEnd";
endTailAnimation.beginTime = self.duration / 1.5f;
endTailAnimation.duration = self.duration / 3.0f;
endTailAnimation.fromValue = @(1.f);
endTailAnimation.toValue = @(1.f);
CAAnimationGroup *animations = [CAAnimationGroup animation];
[animations setDuration:self.duration];
[animations setAnimations:@[headAnimation, tailAnimation, endHeadAnimation, endTailAnimation]];
animations.repeatCount = INFINITY;
animations.removedOnCompletion = NO;
[self.shapeLayer addAnimation:animations forKey:@"strokeAnim"];
if (self.hidesWhenStopped) {
self.hidden = NO;
}
}
#pragma mark - setter and getter
- (CAShapeLayer *)shapeLayer {
if (!_shapeLayer) {
_shapeLayer = [CAShapeLayer layer];
_shapeLayer.strokeColor = self.lineColor.CGColor;
_shapeLayer.fillColor = [UIColor clearColor].CGColor;
_shapeLayer.strokeStart = 0.1;
_shapeLayer.strokeEnd = 1;
_shapeLayer.lineCap = @"round";
_shapeLayer.anchorPoint = CGPointMake(0.5, 0.5);
}
return _shapeLayer;
}
- (UIColor *)lineColor {
if (!_lineColor) {
return [UIColor whiteColor];
}
return _lineColor;
}
- (void)setLineWidth:(CGFloat)lineWidth {
_lineWidth = lineWidth;
self.shapeLayer.lineWidth = lineWidth;
}
- (void)setLineColor:(UIColor *)lineColor {
if (!lineColor) return;
_lineColor = lineColor;
self.shapeLayer.strokeColor = lineColor.CGColor;
}
- (void)setHidesWhenStopped:(BOOL)hidesWhenStopped {
_hidesWhenStopped = hidesWhenStopped;
self.hidden = !self.isAnimating && hidesWhenStopped;
}
@end
@@ -0,0 +1,39 @@
//
// ZFNetworkSpeedMonitor.h
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
extern NSString *const ZFDownloadNetworkSpeedNotificationKey;
extern NSString *const ZFUploadNetworkSpeedNotificationKey;
extern NSString *const ZFNetworkSpeedNotificationKey;
@interface ZFNetworkSpeedMonitor : NSObject
@property (nonatomic, copy, readonly) NSString *downloadNetworkSpeed;
@property (nonatomic, copy, readonly) NSString *uploadNetworkSpeed;
- (void)startNetworkSpeedMonitor;
- (void)stopNetworkSpeedMonitor;
@end
@@ -0,0 +1,167 @@
//
// ZFNetworkSpeedMonitor.m
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "ZFNetworkSpeedMonitor.h"
#if __has_include(<ZFPlayer/ZFPlayerLogManager.h>)
#import <ZFPlayer/ZFPlayerLogManager.h>
#else
#import "ZFPlayerLogManager.h"
#endif
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <net/if_dl.h>
NSString *const ZFDownloadNetworkSpeedNotificationKey = @"ZFDownloadNetworkSpeedNotificationKey";
NSString *const ZFUploadNetworkSpeedNotificationKey = @"ZFUploadNetworkSpeedNotificationKey";
NSString *const ZFNetworkSpeedNotificationKey = @"ZFNetworkSpeedNotificationKey";
@interface ZFNetworkSpeedMonitor () {
// 总网速
uint32_t _iBytes;
uint32_t _oBytes;
uint32_t _allFlow;
// wifi网速
uint32_t _wifiIBytes;
uint32_t _wifiOBytes;
uint32_t _wifiFlow;
// 3G网速
uint32_t _wwanIBytes;
uint32_t _wwanOBytes;
uint32_t _wwanFlow;
}
@property (nonatomic, strong) NSTimer *timer;
@end
@implementation ZFNetworkSpeedMonitor
- (instancetype)init {
if (self = [super init]) {
_iBytes = _oBytes = _allFlow = _wifiIBytes = _wifiOBytes = _wifiFlow = _wwanIBytes = _wwanOBytes = _wwanFlow = 0;
}
return self;
}
// 开始监听网速
- (void)startNetworkSpeedMonitor {
if (!_timer) {
_timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(checkNetworkSpeed) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
[_timer fire];
}
}
// 停止监听网速
- (void)stopNetworkSpeedMonitor {
if ([_timer isValid]) {
[_timer invalidate];
_timer = nil;
}
}
- (NSString *)stringWithbytes:(int)bytes {
if (bytes < 1024) { // B
return [NSString stringWithFormat:@"%dB", bytes];
} else if (bytes >= 1024 && bytes < 1024 * 1024) { // KB
return [NSString stringWithFormat:@"%.0fKB", (double)bytes / 1024];
} else if (bytes >= 1024 * 1024 && bytes < 1024 * 1024 * 1024) { // MB
return [NSString stringWithFormat:@"%.1fMB", (double)bytes / (1024 * 1024)];
} else { // GB
return [NSString stringWithFormat:@"%.1fGB", (double)bytes / (1024 * 1024 * 1024)];
}
}
- (void)checkNetworkSpeed {
struct ifaddrs *ifa_list = 0, *ifa;
if (getifaddrs(&ifa_list) == -1) return;
uint32_t iBytes = 0;
uint32_t oBytes = 0;
uint32_t allFlow = 0;
uint32_t wifiIBytes = 0;
uint32_t wifiOBytes = 0;
uint32_t wifiFlow = 0;
uint32_t wwanIBytes = 0;
uint32_t wwanOBytes = 0;
uint32_t wwanFlow = 0;
for (ifa = ifa_list; ifa; ifa = ifa->ifa_next) {
if (AF_LINK != ifa->ifa_addr->sa_family) continue;
if (!(ifa->ifa_flags & IFF_UP) && !(ifa->ifa_flags & IFF_RUNNING)) continue;
if (ifa->ifa_data == 0) continue;
// network
if (strncmp(ifa->ifa_name, "lo", 2)) {
struct if_data* if_data = (struct if_data*)ifa->ifa_data;
iBytes += if_data->ifi_ibytes;
oBytes += if_data->ifi_obytes;
allFlow = iBytes + oBytes;
}
//wifi
if (!strcmp(ifa->ifa_name, "en0")) {
struct if_data* if_data = (struct if_data*)ifa->ifa_data;
wifiIBytes += if_data->ifi_ibytes;
wifiOBytes += if_data->ifi_obytes;
wifiFlow = wifiIBytes + wifiOBytes;
}
//3G or gprs
if (!strcmp(ifa->ifa_name, "pdp_ip0")) {
struct if_data* if_data = (struct if_data*)ifa->ifa_data;
wwanIBytes += if_data->ifi_ibytes;
wwanOBytes += if_data->ifi_obytes;
wwanFlow = wwanIBytes + wwanOBytes;
}
}
freeifaddrs(ifa_list);
if (_iBytes != 0) {
_downloadNetworkSpeed = [[self stringWithbytes:iBytes - _iBytes] stringByAppendingString:@"/s"];
NSMutableDictionary *userInfo = @{}.mutableCopy;
userInfo[ZFNetworkSpeedNotificationKey] = _downloadNetworkSpeed;
[[NSNotificationCenter defaultCenter] postNotificationName:ZFDownloadNetworkSpeedNotificationKey object:nil userInfo:userInfo];
ZFPlayerLog(@"downloadNetworkSpeed : %@",_downloadNetworkSpeed);
}
_iBytes = iBytes;
if (_oBytes != 0) {
_uploadNetworkSpeed = [[self stringWithbytes:oBytes - _oBytes] stringByAppendingString:@"/s"];
NSMutableDictionary *userInfo = @{}.mutableCopy;
userInfo[ZFNetworkSpeedNotificationKey] = _uploadNetworkSpeed;
[[NSNotificationCenter defaultCenter] postNotificationName:ZFUploadNetworkSpeedNotificationKey object:nil userInfo:userInfo];
ZFPlayerLog(@"uploadNetworkSpeed :%@",_uploadNetworkSpeed);
}
_oBytes = oBytes;
}
@end
Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 761 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 834 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 772 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 585 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

@@ -0,0 +1,151 @@
//
// ZFPlayerControlView.h
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import "ZFPortraitControlView.h"
#import "ZFLandScapeControlView.h"
#import "ZFSpeedLoadingView.h"
#import "ZFSmallFloatControlView.h"
#if __has_include(<ZFPlayer/ZFPlayerMediaControl.h>)
#import <ZFPlayer/ZFPlayerMediaControl.h>
#else
#import "ZFPlayerMediaControl.h"
#endif
@interface ZFPlayerControlView : UIView <ZFPlayerMediaControl>
/// 竖屏控制层的View
@property (nonatomic, strong, readonly) ZFPortraitControlView *portraitControlView;
/// 横屏控制层的View
@property (nonatomic, strong, readonly) ZFLandScapeControlView *landScapeControlView;
/// 加载loading
@property (nonatomic, strong, readonly) ZFSpeedLoadingView *activity;
/// 快进快退View
@property (nonatomic, strong, readonly) UIView *fastView;
/// 快进快退进度progress
@property (nonatomic, strong, readonly) ZFSliderView *fastProgressView;
/// 快进快退时间
@property (nonatomic, strong, readonly) UILabel *fastTimeLabel;
/// 快进快退ImageView
@property (nonatomic, strong, readonly) UIImageView *fastImageView;
/// 加载失败按钮
@property (nonatomic, strong, readonly) UIButton *failBtn;
/// 底部播放进度
@property (nonatomic, strong, readonly) ZFSliderView *bottomPgrogress;
/// 封面图
@property (nonatomic, strong, readonly) UIImageView *coverImageView;
/// 高斯模糊的背景图
@property (nonatomic, strong, readonly) UIImageView *bgImgView;
/// 高斯模糊视图
@property (nonatomic, strong, readonly) UIView *effectView;
/// 小窗口控制层
@property (nonatomic, strong, readonly) ZFSmallFloatControlView *floatControlView;
/// 快进视图是否显示动画,默认NO.
@property (nonatomic, assign) BOOL fastViewAnimated;
/// 视频之外区域是否高斯模糊显示,默认YES.
@property (nonatomic, assign) BOOL effectViewShow;
/// 如果是暂停状态,seek完是否播放,默认YES
@property (nonatomic, assign) BOOL seekToPlay;
/// 返回按钮点击回调
@property (nonatomic, copy) void(^backBtnClickCallback)(void);
/// 控制层显示或者隐藏
@property (nonatomic, readonly) BOOL controlViewAppeared;
/// 控制层显示或者隐藏的回调
@property (nonatomic, copy) void(^controlViewAppearedCallback)(BOOL appeared);
/// 控制层自动隐藏的时间,默认2.5秒
@property (nonatomic, assign) NSTimeInterval autoHiddenTimeInterval;
/// 控制层显示、隐藏动画的时长,默认0.25秒
@property (nonatomic, assign) NSTimeInterval autoFadeTimeInterval;
/// 横向滑动控制播放进度时是否显示控制层,默认 YES.
@property (nonatomic, assign) BOOL horizontalPanShowControlView;
/// prepare时候是否显示控制层,默认 NO.
@property (nonatomic, assign) BOOL prepareShowControlView;
/// prepare时候是否显示loading,默认 NO.
@property (nonatomic, assign) BOOL prepareShowLoading;
/// 是否自定义禁止pan手势,默认 NO.
@property (nonatomic, assign) BOOL customDisablePanMovingDirection;
/// 全屏模式
@property (nonatomic, assign) ZFFullScreenMode fullScreenMode;
/**
设置标题、封面、全屏模式
@param title 视频的标题
@param coverUrl 视频的封面,占位图默认是灰色的
@param fullScreenMode 全屏模式
*/
- (void)showTitle:(NSString *)title coverURLString:(NSString *)coverUrl fullScreenMode:(ZFFullScreenMode)fullScreenMode;
/**
设置标题、封面、默认占位图、全屏模式
@param title 视频的标题
@param coverUrl 视频的封面
@param placeholder 指定封面的placeholder
@param fullScreenMode 全屏模式
*/
- (void)showTitle:(NSString *)title coverURLString:(NSString *)coverUrl placeholderImage:(UIImage *)placeholder fullScreenMode:(ZFFullScreenMode)fullScreenMode;
/**
设置标题、UIImage封面、全屏模式
@param title 视频的标题
@param image 视频的封面UIImage
@param fullScreenMode 全屏模式
*/
- (void)showTitle:(NSString *)title coverImage:(UIImage *)image fullScreenMode:(ZFFullScreenMode)fullScreenMode;
//- (void)showFullScreen
/**
重置控制层
*/
- (void)resetControlView;
@end
@@ -0,0 +1,836 @@
//
// ZFPlayerControlView.m
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "ZFPlayerControlView.h"
#import <AVKit/AVKit.h>
#import <AVFoundation/AVFoundation.h>
#import "UIView+ZFFrame.h"
#import "ZFSliderView.h"
#import "ZFUtilities.h"
#import "UIImageView+ZFCache.h"
#import <MediaPlayer/MediaPlayer.h>
#import "ZFVolumeBrightnessView.h"
#if __has_include(<ZFPlayer/ZFPlayer.h>)
#import <ZFPlayer/ZFPlayerConst.h>
#else
#import "ZFPlayerConst.h"
#endif
@interface ZFPlayerControlView () <ZFSliderViewDelegate>
/// 竖屏控制层的View
@property (nonatomic, strong) ZFPortraitControlView *portraitControlView;
/// 横屏控制层的View
@property (nonatomic, strong) ZFLandScapeControlView *landScapeControlView;
/// 加载loading
@property (nonatomic, strong) ZFSpeedLoadingView *activity;
/// 快进快退View
@property (nonatomic, strong) UIView *fastView;
/// 快进快退进度progress
@property (nonatomic, strong) ZFSliderView *fastProgressView;
/// 快进快退时间
@property (nonatomic, strong) UILabel *fastTimeLabel;
/// 快进快退ImageView
@property (nonatomic, strong) UIImageView *fastImageView;
/// 加载失败按钮
@property (nonatomic, strong) UIButton *failBtn;
/// 底部播放进度
@property (nonatomic, strong) ZFSliderView *bottomPgrogress;
/// 是否显示了控制层
@property (nonatomic, assign, getter=isShowing) BOOL showing;
/// 是否播放结束
@property (nonatomic, assign, getter=isPlayEnd) BOOL playeEnd;
@property (nonatomic, assign) BOOL controlViewAppeared;
@property (nonatomic, assign) NSTimeInterval sumTime;
@property (nonatomic, strong) dispatch_block_t afterBlock;
@property (nonatomic, strong) ZFSmallFloatControlView *floatControlView;
@property (nonatomic, strong) ZFVolumeBrightnessView *volumeBrightnessView;
@property (nonatomic, strong) UIImageView *bgImgView;
@property (nonatomic, strong) UIView *effectView;
@end
@implementation ZFPlayerControlView
@synthesize player = _player;
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self addAllSubViews];
self.landScapeControlView.hidden = YES;
self.floatControlView.hidden = YES;
self.seekToPlay = YES;
self.effectViewShow = YES;
self.horizontalPanShowControlView = YES;
self.autoFadeTimeInterval = 0.25;
self.autoHiddenTimeInterval = 2.5;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(volumeChanged:)
name:@"AVSystemController_SystemVolumeDidChangeNotification"
object:nil];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat min_x = 0;
CGFloat min_y = 0;
CGFloat min_w = 0;
CGFloat min_h = 0;
CGFloat min_view_w = self.zf_width;
CGFloat min_view_h = self.zf_height;
self.portraitControlView.frame = self.bounds;
self.landScapeControlView.frame = self.bounds;
self.floatControlView.frame = self.bounds;
self.coverImageView.frame = self.bounds;
self.bgImgView.frame = self.bounds;
self.effectView.frame = self.bounds;
min_w = 80;
min_h = 80;
self.activity.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.activity.zf_centerX = self.zf_centerX;
self.activity.zf_centerY = self.zf_centerY + 10;
min_w = 150;
min_h = 30;
self.failBtn.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.failBtn.center = self.center;
min_w = 140;
min_h = 80;
self.fastView.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.fastView.center = self.center;
min_w = 32;
min_x = (self.fastView.zf_width - min_w) / 2;
min_y = 5;
min_h = 32;
self.fastImageView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = 0;
min_y = self.fastImageView.zf_bottom + 2;
min_w = self.fastView.zf_width;
min_h = 20;
self.fastTimeLabel.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = 12;
min_y = self.fastTimeLabel.zf_bottom + 5;
min_w = self.fastView.zf_width - 2 * min_x;
min_h = 10;
self.fastProgressView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = 0;
min_y = min_view_h - 1;
min_w = min_view_w;
min_h = 1;
self.bottomPgrogress.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = 0;
min_y = iPhoneX ? 54 : 30;
min_w = 170;
min_h = 35;
self.volumeBrightnessView.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.volumeBrightnessView.zf_centerX = self.zf_centerX;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"AVSystemController_SystemVolumeDidChangeNotification" object:nil];
[self cancelAutoFadeOutControlView];
}
/// 添加所有子控件
- (void)addAllSubViews {
[self addSubview:self.portraitControlView];
[self addSubview:self.landScapeControlView];
[self addSubview:self.floatControlView];
[self addSubview:self.activity];
[self addSubview:self.failBtn];
[self addSubview:self.fastView];
[self.fastView addSubview:self.fastImageView];
[self.fastView addSubview:self.fastTimeLabel];
[self.fastView addSubview:self.fastProgressView];
[self addSubview:self.bottomPgrogress];
[self addSubview:self.volumeBrightnessView];
}
- (void)autoFadeOutControlView {
self.controlViewAppeared = YES;
[self cancelAutoFadeOutControlView];
@zf_weakify(self)
self.afterBlock = dispatch_block_create(0, ^{
@zf_strongify(self)
[self hideControlViewWithAnimated:YES];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.autoHiddenTimeInterval * NSEC_PER_SEC)), dispatch_get_main_queue(),self.afterBlock);
}
/// 取消延时隐藏controlView的方法
- (void)cancelAutoFadeOutControlView {
if (self.afterBlock) {
dispatch_block_cancel(self.afterBlock);
self.afterBlock = nil;
}
}
/// 隐藏控制层
- (void)hideControlViewWithAnimated:(BOOL)animated {
self.controlViewAppeared = NO;
if (self.controlViewAppearedCallback) {
self.controlViewAppearedCallback(NO);
}
[UIView animateWithDuration:animated ? self.autoFadeTimeInterval : 0 animations:^{
if (self.player.isFullScreen) {
[self.landScapeControlView hideControlView];
} else {
if (!self.player.isSmallFloatViewShow) {
[self.portraitControlView hideControlView];
}
}
} completion:^(BOOL finished) {
self.bottomPgrogress.hidden = NO;
}];
}
/// 显示控制层
- (void)showControlViewWithAnimated:(BOOL)animated {
self.controlViewAppeared = YES;
if (self.controlViewAppearedCallback) {
self.controlViewAppearedCallback(YES);
}
[self autoFadeOutControlView];
[UIView animateWithDuration:animated ? self.autoFadeTimeInterval : 0 animations:^{
if (self.player.isFullScreen) {
[self.landScapeControlView showControlView];
} else {
if (!self.player.isSmallFloatViewShow) {
[self.portraitControlView showControlView];
}
}
} completion:^(BOOL finished) {
self.bottomPgrogress.hidden = YES;
}];
}
/// 音量改变的通知
- (void)volumeChanged:(NSNotification *)notification {
NSDictionary *userInfo = notification.userInfo;
NSString *reasonstr = userInfo[@"AVSystemController_AudioVolumeChangeReasonNotificationParameter"];
if ([reasonstr isEqualToString:@"ExplicitVolumeChange"]) {
float volume = [ userInfo[@"AVSystemController_AudioVolumeNotificationParameter"] floatValue];
if (self.player.isFullScreen) {
[self.volumeBrightnessView updateProgress:volume withVolumeBrightnessType:ZFVolumeBrightnessTypeVolume];
} else {
[self.volumeBrightnessView addSystemVolumeView];
}
}
}
#pragma mark - Public Method
/// 重置控制层
- (void)resetControlView {
[self.portraitControlView resetControlView];
[self.landScapeControlView resetControlView];
[self cancelAutoFadeOutControlView];
self.bottomPgrogress.value = 0;
self.bottomPgrogress.bufferValue = 0;
self.floatControlView.hidden = YES;
self.failBtn.hidden = YES;
self.volumeBrightnessView.hidden = YES;
self.portraitControlView.hidden = self.player.isFullScreen;
self.landScapeControlView.hidden = !self.player.isFullScreen;
if (self.controlViewAppeared) {
[self showControlViewWithAnimated:NO];
} else {
[self hideControlViewWithAnimated:NO];
}
}
/// 设置标题、封面、全屏模式
- (void)showTitle:(NSString *)title coverURLString:(NSString *)coverUrl fullScreenMode:(ZFFullScreenMode)fullScreenMode {
UIImage *placeholder = [ZFUtilities imageWithColor:[UIColor colorWithRed:220/255.0 green:220/255.0 blue:220/255.0 alpha:1] size:self.bgImgView.bounds.size];
[self showTitle:title coverURLString:coverUrl placeholderImage:placeholder fullScreenMode:fullScreenMode];
}
/// 设置标题、封面、默认占位图、全屏模式
- (void)showTitle:(NSString *)title coverURLString:(NSString *)coverUrl placeholderImage:(UIImage *)placeholder fullScreenMode:(ZFFullScreenMode)fullScreenMode {
[self resetControlView];
[self layoutIfNeeded];
[self setNeedsDisplay];
[self.portraitControlView showTitle:title fullScreenMode:fullScreenMode];
[self.landScapeControlView showTitle:title fullScreenMode:fullScreenMode];
/// 这里直接设置播放器视图里的coverImageView
[self.player.currentPlayerManager.view.coverImageView setImageWithURLString:coverUrl placeholder:placeholder];
[self.bgImgView setImageWithURLString:coverUrl placeholder:placeholder];
if (self.prepareShowControlView) {
[self showControlViewWithAnimated:NO];
} else {
[self hideControlViewWithAnimated:NO];
}
}
/// 设置标题、UIImage封面、全屏模式
- (void)showTitle:(NSString *)title coverImage:(UIImage *)image fullScreenMode:(ZFFullScreenMode)fullScreenMode {
[self resetControlView];
[self layoutIfNeeded];
[self setNeedsDisplay];
[self.portraitControlView showTitle:title fullScreenMode:fullScreenMode];
[self.landScapeControlView showTitle:title fullScreenMode:fullScreenMode];
self.coverImageView.image = image;
self.bgImgView.image = image;
if (self.prepareShowControlView) {
[self showControlViewWithAnimated:NO];
} else {
[self hideControlViewWithAnimated:NO];
}
}
#pragma mark - ZFPlayerControlViewDelegate
/// 手势筛选,返回NO不响应该手势
- (BOOL)gestureTriggerCondition:(ZFPlayerGestureControl *)gestureControl gestureType:(ZFPlayerGestureType)gestureType gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer touch:(nonnull UITouch *)touch {
CGPoint point = [touch locationInView:self];
if (self.player.isSmallFloatViewShow && !self.player.isFullScreen && gestureType != ZFPlayerGestureTypeSingleTap) {
return NO;
}
if (self.player.isFullScreen) {
if (!self.customDisablePanMovingDirection) {
/// 不禁用滑动方向
self.player.disablePanMovingDirection = ZFPlayerDisablePanMovingDirectionNone;
}
return [self.landScapeControlView shouldResponseGestureWithPoint:point withGestureType:gestureType touch:touch];
} else {
if (!self.customDisablePanMovingDirection) {
if (self.player.scrollView) { /// 列表时候禁止上下滑动(防止和列表滑动冲突)
self.player.disablePanMovingDirection = ZFPlayerDisablePanMovingDirectionVertical;
} else { /// 不禁用滑动方向
self.player.disablePanMovingDirection = ZFPlayerDisablePanMovingDirectionNone;
}
}
return [self.portraitControlView shouldResponseGestureWithPoint:point withGestureType:gestureType touch:touch];
}
}
/// 单击手势事件
- (void)gestureSingleTapped:(ZFPlayerGestureControl *)gestureControl {
if (!self.player) return;
if (self.player.isSmallFloatViewShow && !self.player.isFullScreen) {
[self.player enterFullScreen:YES animated:YES];
} else {
if (self.controlViewAppeared) {
[self hideControlViewWithAnimated:YES];
} else {
/// 显示之前先把控制层复位,先隐藏后显示
[self hideControlViewWithAnimated:NO];
[self showControlViewWithAnimated:YES];
}
}
}
/// 双击手势事件
- (void)gestureDoubleTapped:(ZFPlayerGestureControl *)gestureControl {
if (self.player.isFullScreen) {
[self.landScapeControlView playOrPause];
} else {
[self.portraitControlView playOrPause];
}
}
/// 开始滑动手势事件
- (void)gestureBeganPan:(ZFPlayerGestureControl *)gestureControl panDirection:(ZFPanDirection)direction panLocation:(ZFPanLocation)location {
if (direction == ZFPanDirectionH) {
self.sumTime = self.player.currentTime;
}
}
/// 滑动中手势事件
- (void)gestureChangedPan:(ZFPlayerGestureControl *)gestureControl panDirection:(ZFPanDirection)direction panLocation:(ZFPanLocation)location withVelocity:(CGPoint)velocity {
if (direction == ZFPanDirectionH) {
// 每次滑动需要叠加时间
self.sumTime += velocity.x / 200;
// 需要限定sumTime的范围
NSTimeInterval totalMovieDuration = self.player.totalTime;
if (totalMovieDuration == 0) return;
if (self.sumTime > totalMovieDuration) self.sumTime = totalMovieDuration;
if (self.sumTime < 0) self.sumTime = 0;
BOOL style = NO;
if (velocity.x > 0) style = YES;
if (velocity.x < 0) style = NO;
if (velocity.x == 0) return;
[self sliderValueChangingValue:self.sumTime/totalMovieDuration isForward:style];
} else if (direction == ZFPanDirectionV) {
if (location == ZFPanLocationLeft) { /// 调节亮度
self.player.brightness -= (velocity.y) / 10000;
[self.volumeBrightnessView updateProgress:self.player.brightness withVolumeBrightnessType:ZFVolumeBrightnessTypeumeBrightness];
} else if (location == ZFPanLocationRight) { /// 调节声音
self.player.volume -= (velocity.y) / 10000;
if (self.player.isFullScreen) {
[self.volumeBrightnessView updateProgress:self.player.volume withVolumeBrightnessType:ZFVolumeBrightnessTypeVolume];
}
}
}
}
/// 滑动结束手势事件
- (void)gestureEndedPan:(ZFPlayerGestureControl *)gestureControl panDirection:(ZFPanDirection)direction panLocation:(ZFPanLocation)location {
@zf_weakify(self)
if (direction == ZFPanDirectionH && self.sumTime >= 0 && self.player.totalTime > 0) {
[self.player seekToTime:self.sumTime completionHandler:^(BOOL finished) {
if (finished) {
@zf_strongify(self)
/// 左右滑动调节播放进度
[self.portraitControlView sliderChangeEnded];
[self.landScapeControlView sliderChangeEnded];
self.bottomPgrogress.isdragging = NO;
if (self.controlViewAppeared) {
[self autoFadeOutControlView];
}
}
}];
if (self.seekToPlay) {
[self.player.currentPlayerManager play];
}
self.sumTime = 0;
}
}
/// 捏合手势事件,这里改变了视频的填充模式
- (void)gesturePinched:(ZFPlayerGestureControl *)gestureControl scale:(float)scale {
if (scale > 1) {
self.player.currentPlayerManager.scalingMode = ZFPlayerScalingModeAspectFill;
} else {
self.player.currentPlayerManager.scalingMode = ZFPlayerScalingModeAspectFit;
}
}
/// 准备播放
- (void)videoPlayer:(ZFPlayerController *)videoPlayer prepareToPlay:(NSURL *)assetURL {
[self hideControlViewWithAnimated:NO];
}
/// 播放状态改变
- (void)videoPlayer:(ZFPlayerController *)videoPlayer playStateChanged:(ZFPlayerPlaybackState)state {
if (state == ZFPlayerPlayStatePlaying) {
[self.portraitControlView playBtnSelectedState:YES];
[self.landScapeControlView playBtnSelectedState:YES];
self.failBtn.hidden = YES;
/// 开始播放时候判断是否显示loading
if (videoPlayer.currentPlayerManager.loadState == ZFPlayerLoadStateStalled && !self.prepareShowLoading) {
[self.activity startAnimating];
} else if ((videoPlayer.currentPlayerManager.loadState == ZFPlayerLoadStateStalled || videoPlayer.currentPlayerManager.loadState == ZFPlayerLoadStatePrepare) && self.prepareShowLoading) {
[self.activity startAnimating];
}
} else if (state == ZFPlayerPlayStatePaused) {
[self.portraitControlView playBtnSelectedState:NO];
[self.landScapeControlView playBtnSelectedState:NO];
/// 暂停的时候隐藏loading
[self.activity stopAnimating];
self.failBtn.hidden = YES;
} else if (state == ZFPlayerPlayStatePlayFailed) {
self.failBtn.hidden = NO;
[self.activity stopAnimating];
}
}
/// 加载状态改变
- (void)videoPlayer:(ZFPlayerController *)videoPlayer loadStateChanged:(ZFPlayerLoadState)state {
if (state == ZFPlayerLoadStatePrepare) {
self.coverImageView.hidden = NO;
[self.portraitControlView playBtnSelectedState:videoPlayer.currentPlayerManager.shouldAutoPlay];
[self.landScapeControlView playBtnSelectedState:videoPlayer.currentPlayerManager.shouldAutoPlay];
} else if (state == ZFPlayerLoadStatePlaythroughOK || state == ZFPlayerLoadStatePlayable) {
self.coverImageView.hidden = YES;
if (self.effectViewShow) {
self.effectView.hidden = NO;
} else {
self.effectView.hidden = YES;
self.player.currentPlayerManager.view.backgroundColor = [UIColor blackColor];
}
}
if (state == ZFPlayerLoadStateStalled && videoPlayer.currentPlayerManager.isPlaying && !self.prepareShowLoading) {
[self.activity startAnimating];
} else if ((state == ZFPlayerLoadStateStalled || state == ZFPlayerLoadStatePrepare) && videoPlayer.currentPlayerManager.isPlaying && self.prepareShowLoading) {
[self.activity startAnimating];
} else {
[self.activity stopAnimating];
}
}
/// 播放进度改变回调
- (void)videoPlayer:(ZFPlayerController *)videoPlayer currentTime:(NSTimeInterval)currentTime totalTime:(NSTimeInterval)totalTime {
[self.portraitControlView videoPlayer:videoPlayer currentTime:currentTime totalTime:totalTime];
[self.landScapeControlView videoPlayer:videoPlayer currentTime:currentTime totalTime:totalTime];
if (!self.bottomPgrogress.isdragging) {
self.bottomPgrogress.value = videoPlayer.progress;
}
}
/// 缓冲改变回调
- (void)videoPlayer:(ZFPlayerController *)videoPlayer bufferTime:(NSTimeInterval)bufferTime {
[self.portraitControlView videoPlayer:videoPlayer bufferTime:bufferTime];
[self.landScapeControlView videoPlayer:videoPlayer bufferTime:bufferTime];
self.bottomPgrogress.bufferValue = videoPlayer.bufferProgress;
}
- (void)videoPlayer:(ZFPlayerController *)videoPlayer presentationSizeChanged:(CGSize)size {
[self.landScapeControlView videoPlayer:videoPlayer presentationSizeChanged:size];
}
/// 视频view即将旋转
- (void)videoPlayer:(ZFPlayerController *)videoPlayer orientationWillChange:(ZFOrientationObserver *)observer {
self.portraitControlView.hidden = observer.isFullScreen;
self.landScapeControlView.hidden = !observer.isFullScreen;
if (videoPlayer.isSmallFloatViewShow) {
self.floatControlView.hidden = observer.isFullScreen;
self.portraitControlView.hidden = YES;
if (observer.isFullScreen) {
self.controlViewAppeared = NO;
[self cancelAutoFadeOutControlView];
}
}
if (self.controlViewAppeared) {
[self showControlViewWithAnimated:NO];
} else {
[self hideControlViewWithAnimated:NO];
}
if (observer.isFullScreen) {
[self.volumeBrightnessView removeSystemVolumeView];
} else {
[self.volumeBrightnessView addSystemVolumeView];
}
}
/// 视频view已经旋转
- (void)videoPlayer:(ZFPlayerController *)videoPlayer orientationDidChanged:(ZFOrientationObserver *)observer {
if (self.controlViewAppeared) {
[self showControlViewWithAnimated:NO];
} else {
[self hideControlViewWithAnimated:NO];
}
}
/// 锁定旋转方向
- (void)lockedVideoPlayer:(ZFPlayerController *)videoPlayer lockedScreen:(BOOL)locked {
[self showControlViewWithAnimated:YES];
}
/// 列表滑动时视频view已经显示
- (void)playerDidAppearInScrollView:(ZFPlayerController *)videoPlayer {
if (!self.player.stopWhileNotVisible && !videoPlayer.isFullScreen) {
self.floatControlView.hidden = YES;
self.portraitControlView.hidden = NO;
}
}
/// 列表滑动时视频view已经消失
- (void)playerDidDisappearInScrollView:(ZFPlayerController *)videoPlayer {
if (!self.player.stopWhileNotVisible && !videoPlayer.isFullScreen) {
self.floatControlView.hidden = NO;
self.portraitControlView.hidden = YES;
}
}
- (void)videoPlayer:(ZFPlayerController *)videoPlayer floatViewShow:(BOOL)show {
self.floatControlView.hidden = !show;
self.portraitControlView.hidden = show;
}
#pragma mark - Private Method
- (void)sliderValueChangingValue:(CGFloat)value isForward:(BOOL)forward {
if (self.horizontalPanShowControlView) {
/// 显示控制层
[self showControlViewWithAnimated:NO];
[self cancelAutoFadeOutControlView];
}
self.fastProgressView.value = value;
self.fastView.hidden = NO;
self.fastView.alpha = 1;
if (forward) {
self.fastImageView.image = ZFPlayer_Image(@"ZFPlayer_fast_forward");
} else {
self.fastImageView.image = ZFPlayer_Image(@"ZFPlayer_fast_backward");
}
NSString *draggedTime = [ZFUtilities convertTimeSecond:self.player.totalTime*value];
NSString *totalTime = [ZFUtilities convertTimeSecond:self.player.totalTime];
self.fastTimeLabel.text = [NSString stringWithFormat:@"%@ / %@",draggedTime,totalTime];
/// 更新滑杆
[self.portraitControlView sliderValueChanged:value currentTimeString:draggedTime];
[self.landScapeControlView sliderValueChanged:value currentTimeString:draggedTime];
self.bottomPgrogress.isdragging = YES;
self.bottomPgrogress.value = value;
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideFastView) object:nil];
[self performSelector:@selector(hideFastView) withObject:nil afterDelay:0.1];
if (self.fastViewAnimated) {
[UIView animateWithDuration:0.4 animations:^{
self.fastView.transform = CGAffineTransformMakeTranslation(forward?8:-8, 0);
}];
}
}
/// 隐藏快进视图
- (void)hideFastView {
[UIView animateWithDuration:0.4 animations:^{
self.fastView.transform = CGAffineTransformIdentity;
self.fastView.alpha = 0;
} completion:^(BOOL finished) {
self.fastView.hidden = YES;
}];
}
/// 加载失败
- (void)failBtnClick:(UIButton *)sender {
[self.player.currentPlayerManager reloadPlayer];
}
#pragma mark - setter
- (void)setPlayer:(ZFPlayerController *)player {
_player = player;
self.landScapeControlView.player = player;
self.portraitControlView.player = player;
/// 解决播放时候黑屏闪一下问题
[player.currentPlayerManager.view insertSubview:self.bgImgView atIndex:0];
[self.bgImgView addSubview:self.effectView];
self.bgImgView.frame = player.currentPlayerManager.view.bounds;
self.bgImgView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.effectView.frame = self.bgImgView.bounds;
}
- (void)setSeekToPlay:(BOOL)seekToPlay {
_seekToPlay = seekToPlay;
self.portraitControlView.seekToPlay = seekToPlay;
self.landScapeControlView.seekToPlay = seekToPlay;
}
- (void)setEffectViewShow:(BOOL)effectViewShow {
_effectViewShow = effectViewShow;
if (effectViewShow) {
self.bgImgView.hidden = NO;
} else {
self.bgImgView.hidden = YES;
}
}
- (void)setFullScreenMode:(ZFFullScreenMode)fullScreenMode {
_fullScreenMode = fullScreenMode;
self.portraitControlView.fullScreenMode = fullScreenMode;
self.landScapeControlView.fullScreenMode = fullScreenMode;
self.player.orientationObserver.fullScreenMode = fullScreenMode;
}
#pragma mark - getter
- (UIImageView *)bgImgView {
if (!_bgImgView) {
_bgImgView = [[UIImageView alloc] init];
_bgImgView.userInteractionEnabled = YES;
}
return _bgImgView;
}
- (UIView *)effectView {
if (!_effectView) {
if (@available(iOS 8.0, *)) {
UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
_effectView = [[UIVisualEffectView alloc] initWithEffect:effect];
} else {
UIToolbar *effectView = [[UIToolbar alloc] init];
effectView.barStyle = UIBarStyleBlackTranslucent;
_effectView = effectView;
}
}
return _effectView;
}
- (ZFPortraitControlView *)portraitControlView {
if (!_portraitControlView) {
@zf_weakify(self)
_portraitControlView = [[ZFPortraitControlView alloc] init];
_portraitControlView.sliderValueChanging = ^(CGFloat value, BOOL forward) {
@zf_strongify(self)
NSString *draggedTime = [ZFUtilities convertTimeSecond:self.player.totalTime*value];
/// 更新滑杆和时间
[self.landScapeControlView sliderValueChanged:value currentTimeString:draggedTime];
self.fastProgressView.value = value;
self.bottomPgrogress.isdragging = YES;
self.bottomPgrogress.value = value;
[self cancelAutoFadeOutControlView];
};
_portraitControlView.sliderValueChanged = ^(CGFloat value) {
@zf_strongify(self)
[self.landScapeControlView sliderChangeEnded];
self.fastProgressView.value = value;
self.bottomPgrogress.isdragging = NO;
self.bottomPgrogress.value = value;
[self autoFadeOutControlView];
};
}
return _portraitControlView;
}
- (ZFLandScapeControlView *)landScapeControlView {
if (!_landScapeControlView) {
@zf_weakify(self)
_landScapeControlView = [[ZFLandScapeControlView alloc] init];
_landScapeControlView.sliderValueChanging = ^(CGFloat value, BOOL forward) {
@zf_strongify(self)
NSString *draggedTime = [ZFUtilities convertTimeSecond:self.player.totalTime*value];
/// 更新滑杆和时间
[self.portraitControlView sliderValueChanged:value currentTimeString:draggedTime];
self.fastProgressView.value = value;
self.bottomPgrogress.isdragging = YES;
self.bottomPgrogress.value = value;
[self cancelAutoFadeOutControlView];
};
_landScapeControlView.sliderValueChanged = ^(CGFloat value) {
@zf_strongify(self)
[self.portraitControlView sliderChangeEnded];
self.fastProgressView.value = value;
self.bottomPgrogress.isdragging = NO;
self.bottomPgrogress.value = value;
[self autoFadeOutControlView];
};
}
return _landScapeControlView;
}
- (ZFSpeedLoadingView *)activity {
if (!_activity) {
_activity = [[ZFSpeedLoadingView alloc] init];
}
return _activity;
}
- (UIView *)fastView {
if (!_fastView) {
_fastView = [[UIView alloc] init];
_fastView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.7];
_fastView.layer.cornerRadius = 4;
_fastView.layer.masksToBounds = YES;
_fastView.hidden = YES;
}
return _fastView;
}
- (UIImageView *)fastImageView {
if (!_fastImageView) {
_fastImageView = [[UIImageView alloc] init];
}
return _fastImageView;
}
- (UILabel *)fastTimeLabel {
if (!_fastTimeLabel) {
_fastTimeLabel = [[UILabel alloc] init];
_fastTimeLabel.textColor = [UIColor whiteColor];
_fastTimeLabel.textAlignment = NSTextAlignmentCenter;
_fastTimeLabel.font = [UIFont systemFontOfSize:14.0];
_fastTimeLabel.adjustsFontSizeToFitWidth = YES;
}
return _fastTimeLabel;
}
- (ZFSliderView *)fastProgressView {
if (!_fastProgressView) {
_fastProgressView = [[ZFSliderView alloc] init];
_fastProgressView.maximumTrackTintColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.4];
_fastProgressView.minimumTrackTintColor = [UIColor whiteColor];
_fastProgressView.sliderHeight = 2;
_fastProgressView.isHideSliderBlock = NO;
}
return _fastProgressView;
}
- (UIButton *)failBtn {
if (!_failBtn) {
_failBtn = [UIButton buttonWithType:UIButtonTypeSystem];
[_failBtn setTitle:@"加载失败,点击重试" forState:UIControlStateNormal];
[_failBtn addTarget:self action:@selector(failBtnClick:) forControlEvents:UIControlEventTouchUpInside];
[_failBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
_failBtn.titleLabel.font = [UIFont systemFontOfSize:14.0];
_failBtn.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.7];
_failBtn.hidden = YES;
}
return _failBtn;
}
- (ZFSliderView *)bottomPgrogress {
if (!_bottomPgrogress) {
_bottomPgrogress = [[ZFSliderView alloc] init];
_bottomPgrogress.maximumTrackTintColor = [UIColor clearColor];
_bottomPgrogress.minimumTrackTintColor = [UIColor whiteColor];
_bottomPgrogress.bufferTrackTintColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.5];
_bottomPgrogress.sliderHeight = 1;
_bottomPgrogress.isHideSliderBlock = NO;
}
return _bottomPgrogress;
}
- (ZFSmallFloatControlView *)floatControlView {
if (!_floatControlView) {
_floatControlView = [[ZFSmallFloatControlView alloc] init];
@zf_weakify(self)
_floatControlView.closeClickCallback = ^{
@zf_strongify(self)
if (self.player.containerType == ZFPlayerContainerTypeCell) {
[self.player stopCurrentPlayingCell];
} else if (self.player.containerType == ZFPlayerContainerTypeView) {
[self.player stopCurrentPlayingView];
}
[self resetControlView];
};
}
return _floatControlView;
}
- (ZFVolumeBrightnessView *)volumeBrightnessView {
if (!_volumeBrightnessView) {
_volumeBrightnessView = [[ZFVolumeBrightnessView alloc] init];
_volumeBrightnessView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.7];
_volumeBrightnessView.hidden = YES;
}
return _volumeBrightnessView;
}
- (void)setBackBtnClickCallback:(void (^)(void))backBtnClickCallback {
_backBtnClickCallback = [backBtnClickCallback copy];
self.landScapeControlView.backBtnClickCallback = _backBtnClickCallback;
}
@end
@@ -0,0 +1,111 @@
//
// ZFPortraitControlView.h
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
#import "ZFSliderView.h"
#if __has_include(<ZFPlayer/ZFPlayerController.h>)
#import <ZFPlayer/ZFPlayerController.h>
#else
#import "ZFPlayerController.h"
#endif
NS_ASSUME_NONNULL_BEGIN
@interface ZFPortraitControlView : UIView
/// 底部工具栏
@property (nonatomic, strong, readonly) UIView *bottomToolView;
/// 顶部工具栏
@property (nonatomic, strong, readonly) UIView *topToolView;
/// 标题
@property (nonatomic, strong, readonly) UILabel *titleLabel;
/// 播放或暂停按钮
@property (nonatomic, strong, readonly) UIButton *playOrPauseBtn;
/// 播放的当前时间
@property (nonatomic, strong, readonly) UILabel *currentTimeLabel;
/// 滑杆
@property (nonatomic, strong, readonly) ZFSliderView *slider;
/// 视频总时间
@property (nonatomic, strong, readonly) UILabel *totalTimeLabel;
/// 全屏按钮
@property (nonatomic, strong, readonly) UIButton *fullScreenBtn;
/// 播放器
@property (nonatomic, weak) ZFPlayerController *player;
/// slider滑动中
@property (nonatomic, copy, nullable) void(^sliderValueChanging)(CGFloat value,BOOL forward);
/// slider滑动结束
@property (nonatomic, copy, nullable) void(^sliderValueChanged)(CGFloat value);
/// 如果是暂停状态,seek完是否播放,默认YES
@property (nonatomic, assign) BOOL seekToPlay;
/// 全屏模式
@property (nonatomic, assign) ZFFullScreenMode fullScreenMode;
/// 重置控制层
- (void)resetControlView;
/// 显示控制层
- (void)showControlView;
/// 隐藏控制层
- (void)hideControlView;
/// 设置播放时间
- (void)videoPlayer:(ZFPlayerController *)videoPlayer currentTime:(NSTimeInterval)currentTime totalTime:(NSTimeInterval)totalTime;
/// 设置缓冲时间
- (void)videoPlayer:(ZFPlayerController *)videoPlayer bufferTime:(NSTimeInterval)bufferTime;
/// 是否响应该手势
- (BOOL)shouldResponseGestureWithPoint:(CGPoint)point withGestureType:(ZFPlayerGestureType)type touch:(nonnull UITouch *)touch;
/// 标题和全屏模式
- (void)showTitle:(NSString *_Nullable)title fullScreenMode:(ZFFullScreenMode)fullScreenMode;
/// 根据当前播放状态取反
- (void)playOrPause;
/// 播放按钮状态
- (void)playBtnSelectedState:(BOOL)selected;
/// 调节播放进度slider和当前时间更新
- (void)sliderValueChanged:(CGFloat)value currentTimeString:(NSString *)timeString;
/// 滑杆结束滑动
- (void)sliderChangeEnded;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,387 @@
//
// ZFPortraitControlView.m
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "ZFPortraitControlView.h"
#import "UIView+ZFFrame.h"
#import "ZFUtilities.h"
#if __has_include(<ZFPlayer/ZFPlayer.h>)
#import <ZFPlayer/ZFPlayerConst.h>
#else
#import "ZFPlayerConst.h"
#endif
@interface ZFPortraitControlView () <ZFSliderViewDelegate>
/// 底部工具栏
@property (nonatomic, strong) UIView *bottomToolView;
/// 顶部工具栏
@property (nonatomic, strong) UIView *topToolView;
/// 标题
@property (nonatomic, strong) UILabel *titleLabel;
/// 播放或暂停按钮
@property (nonatomic, strong) UIButton *playOrPauseBtn;
/// 播放的当前时间
@property (nonatomic, strong) UILabel *currentTimeLabel;
/// 滑杆
@property (nonatomic, strong) ZFSliderView *slider;
/// 视频总时间
@property (nonatomic, strong) UILabel *totalTimeLabel;
/// 全屏按钮
@property (nonatomic, strong) UIButton *fullScreenBtn;
@property (nonatomic, assign) BOOL isShow;
@end
@implementation ZFPortraitControlView
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// 添加子控件
[self addSubview:self.topToolView];
[self addSubview:self.bottomToolView];
[self addSubview:self.playOrPauseBtn];
[self.topToolView addSubview:self.titleLabel];
[self.bottomToolView addSubview:self.currentTimeLabel];
[self.bottomToolView addSubview:self.slider];
[self.bottomToolView addSubview:self.totalTimeLabel];
[self.bottomToolView addSubview:self.fullScreenBtn];
// 设置子控件的响应事件
[self makeSubViewsAction];
[self resetControlView];
self.clipsToBounds = YES;
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat min_x = 0;
CGFloat min_y = 0;
CGFloat min_w = 0;
CGFloat min_h = 0;
CGFloat min_view_w = self.bounds.size.width;
CGFloat min_view_h = self.bounds.size.height;
CGFloat min_margin = 9;
min_x = 0;
min_y = 0;
min_w = min_view_w;
min_h = 40;
self.topToolView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = 15;
min_y = 5;
min_w = min_view_w - min_x - 15;
min_h = 30;
self.titleLabel.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_h = 40;
min_x = 0;
min_y = min_view_h - min_h;
min_w = min_view_w;
self.bottomToolView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = 0;
min_y = 0;
min_w = 44;
min_h = min_w;
self.playOrPauseBtn.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.playOrPauseBtn.center = self.center;
min_x = min_margin;
min_w = 62;
min_h = 28;
min_y = (self.bottomToolView.zf_height - min_h)/2;
self.currentTimeLabel.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_w = 28;
min_h = min_w;
min_x = self.bottomToolView.zf_width - min_w - min_margin;
min_y = 0;
self.fullScreenBtn.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.fullScreenBtn.zf_centerY = self.currentTimeLabel.zf_centerY;
min_w = 62;
min_h = 28;
min_x = self.fullScreenBtn.zf_left - min_w - 4;
min_y = 0;
self.totalTimeLabel.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.totalTimeLabel.zf_centerY = self.currentTimeLabel.zf_centerY;
min_x = self.currentTimeLabel.zf_right + 4;
min_y = 0;
min_w = self.totalTimeLabel.zf_left - min_x - 4;
min_h = 30;
self.slider.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.slider.zf_centerY = self.currentTimeLabel.zf_centerY;
if (!self.isShow) {
self.topToolView.zf_y = -self.topToolView.zf_height;
self.bottomToolView.zf_y = self.zf_height;
self.playOrPauseBtn.alpha = 0;
} else {
self.topToolView.zf_y = 0;
self.bottomToolView.zf_y = self.zf_height - self.bottomToolView.zf_height;
self.playOrPauseBtn.alpha = 1;
}
}
- (void)makeSubViewsAction {
[self.playOrPauseBtn addTarget:self action:@selector(playPauseButtonClickAction:) forControlEvents:UIControlEventTouchUpInside];
[self.fullScreenBtn addTarget:self action:@selector(fullScreenButtonClickAction:) forControlEvents:UIControlEventTouchUpInside];
}
#pragma mark - action
- (void)playPauseButtonClickAction:(UIButton *)sender {
[self playOrPause];
}
- (void)fullScreenButtonClickAction:(UIButton *)sender {
[self.player enterFullScreen:YES animated:YES];
}
/// 根据当前播放状态取反
- (void)playOrPause {
self.playOrPauseBtn.selected = !self.playOrPauseBtn.isSelected;
self.playOrPauseBtn.isSelected? [self.player.currentPlayerManager play]: [self.player.currentPlayerManager pause];
}
- (void)playBtnSelectedState:(BOOL)selected {
self.playOrPauseBtn.selected = selected;
}
#pragma mark - ZFSliderViewDelegate
- (void)sliderTouchBegan:(float)value {
self.slider.isdragging = YES;
}
- (void)sliderTouchEnded:(float)value {
if (self.player.totalTime > 0) {
self.slider.isdragging = YES;
if (self.sliderValueChanging) self.sliderValueChanging(value, self.slider.isForward);
@zf_weakify(self)
[self.player seekToTime:self.player.totalTime*value completionHandler:^(BOOL finished) {
@zf_strongify(self)
if (finished) {
self.slider.isdragging = NO;
if (self.sliderValueChanged) self.sliderValueChanged(value);
}
}];
if (self.seekToPlay) {
[self.player.currentPlayerManager play];
}
} else {
self.slider.isdragging = NO;
self.slider.value = 0;
}
}
- (void)sliderValueChanged:(float)value {
if (self.player.totalTime == 0) {
self.slider.value = 0;
return;
}
self.slider.isdragging = YES;
NSString *currentTimeString = [ZFUtilities convertTimeSecond:self.player.totalTime*value];
self.currentTimeLabel.text = currentTimeString;
if (self.sliderValueChanging) self.sliderValueChanging(value,self.slider.isForward);
}
- (void)sliderTapped:(float)value {
[self sliderTouchEnded:value];
NSString *currentTimeString = [ZFUtilities convertTimeSecond:self.player.totalTime*value];
self.currentTimeLabel.text = currentTimeString;
}
#pragma mark - public method
/** 重置ControlView */
- (void)resetControlView {
self.bottomToolView.alpha = 1;
self.slider.value = 0;
self.slider.bufferValue = 0;
self.currentTimeLabel.text = @"00:00";
self.totalTimeLabel.text = @"00:00";
self.backgroundColor = [UIColor clearColor];
self.playOrPauseBtn.selected = YES;
self.titleLabel.text = @"";
}
- (void)showControlView {
self.topToolView.alpha = 1;
self.bottomToolView.alpha = 1;
self.isShow = YES;
self.topToolView.zf_y = 0;
self.bottomToolView.zf_y = self.zf_height - self.bottomToolView.zf_height;
self.playOrPauseBtn.alpha = 1;
self.player.statusBarHidden = NO;
}
- (void)hideControlView {
self.isShow = NO;
self.topToolView.zf_y = -self.topToolView.zf_height;
self.bottomToolView.zf_y = self.zf_height;
self.player.statusBarHidden = NO;
self.playOrPauseBtn.alpha = 0;
self.topToolView.alpha = 0;
self.bottomToolView.alpha = 0;
}
- (BOOL)shouldResponseGestureWithPoint:(CGPoint)point withGestureType:(ZFPlayerGestureType)type touch:(nonnull UITouch *)touch {
CGRect sliderRect = [self.bottomToolView convertRect:self.slider.frame toView:self];
if (CGRectContainsPoint(sliderRect, point)) {
return NO;
}
return YES;
}
- (void)videoPlayer:(ZFPlayerController *)videoPlayer currentTime:(NSTimeInterval)currentTime totalTime:(NSTimeInterval)totalTime {
if (!self.slider.isdragging) {
NSString *currentTimeString = [ZFUtilities convertTimeSecond:currentTime];
self.currentTimeLabel.text = currentTimeString;
NSString *totalTimeString = [ZFUtilities convertTimeSecond:totalTime];
self.totalTimeLabel.text = totalTimeString;
self.slider.value = videoPlayer.progress;
}
}
- (void)videoPlayer:(ZFPlayerController *)videoPlayer bufferTime:(NSTimeInterval)bufferTime {
self.slider.bufferValue = videoPlayer.bufferProgress;
}
- (void)showTitle:(NSString *)title fullScreenMode:(ZFFullScreenMode)fullScreenMode {
self.titleLabel.text = title;
self.player.orientationObserver.fullScreenMode = fullScreenMode;
}
/// 调节播放进度slider和当前时间更新
- (void)sliderValueChanged:(CGFloat)value currentTimeString:(NSString *)timeString {
self.slider.value = value;
self.currentTimeLabel.text = timeString;
self.slider.isdragging = YES;
[UIView animateWithDuration:0.3 animations:^{
self.slider.sliderBtn.transform = CGAffineTransformMakeScale(1.2, 1.2);
}];
}
/// 滑杆结束滑动
- (void)sliderChangeEnded {
self.slider.isdragging = NO;
[UIView animateWithDuration:0.3 animations:^{
self.slider.sliderBtn.transform = CGAffineTransformIdentity;
}];
}
#pragma mark - setter
- (void)setFullScreenMode:(ZFFullScreenMode)fullScreenMode {
_fullScreenMode = fullScreenMode;
self.player.orientationObserver.fullScreenMode = fullScreenMode;
}
#pragma mark - getter
- (UIView *)topToolView {
if (!_topToolView) {
_topToolView = [[UIView alloc] init];
UIImage *image = ZFPlayer_Image(@"ZFPlayer_top_shadow");
_topToolView.layer.contents = (id)image.CGImage;
}
return _topToolView;
}
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] init];
_titleLabel.textColor = [UIColor whiteColor];
_titleLabel.font = [UIFont systemFontOfSize:15.0];
}
return _titleLabel;
}
- (UIView *)bottomToolView {
if (!_bottomToolView) {
_bottomToolView = [[UIView alloc] init];
UIImage *image = ZFPlayer_Image(@"ZFPlayer_bottom_shadow");
_bottomToolView.layer.contents = (id)image.CGImage;
}
return _bottomToolView;
}
- (UIButton *)playOrPauseBtn {
if (!_playOrPauseBtn) {
_playOrPauseBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[_playOrPauseBtn setImage:ZFPlayer_Image(@"new_allPlay_44x44_") forState:UIControlStateNormal];
[_playOrPauseBtn setImage:ZFPlayer_Image(@"new_allPause_44x44_") forState:UIControlStateSelected];
}
return _playOrPauseBtn;
}
- (UILabel *)currentTimeLabel {
if (!_currentTimeLabel) {
_currentTimeLabel = [[UILabel alloc] init];
_currentTimeLabel.textColor = [UIColor whiteColor];
_currentTimeLabel.font = [UIFont systemFontOfSize:14.0f];
_currentTimeLabel.textAlignment = NSTextAlignmentCenter;
}
return _currentTimeLabel;
}
- (ZFSliderView *)slider {
if (!_slider) {
_slider = [[ZFSliderView alloc] init];
_slider.delegate = self;
_slider.maximumTrackTintColor = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:0.8];
_slider.bufferTrackTintColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.5];
_slider.minimumTrackTintColor = [UIColor whiteColor];
[_slider setThumbImage:ZFPlayer_Image(@"ZFPlayer_slider") forState:UIControlStateNormal];
_slider.sliderHeight = 2;
}
return _slider;
}
- (UILabel *)totalTimeLabel {
if (!_totalTimeLabel) {
_totalTimeLabel = [[UILabel alloc] init];
_totalTimeLabel.textColor = [UIColor whiteColor];
_totalTimeLabel.font = [UIFont systemFontOfSize:14.0f];
_totalTimeLabel.textAlignment = NSTextAlignmentCenter;
}
return _totalTimeLabel;
}
- (UIButton *)fullScreenBtn {
if (!_fullScreenBtn) {
_fullScreenBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[_fullScreenBtn setImage:ZFPlayer_Image(@"ZFPlayer_fullscreen") forState:UIControlStateNormal];
}
return _fullScreenBtn;
}
@end
@@ -0,0 +1,118 @@
//
// ZFSliderView.h
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
@protocol ZFSliderViewDelegate <NSObject>
@optional
// 滑块滑动开始
- (void)sliderTouchBegan:(float)value;
// 滑块滑动中
- (void)sliderValueChanged:(float)value;
// 滑块滑动结束
- (void)sliderTouchEnded:(float)value;
// 滑杆点击
- (void)sliderTapped:(float)value;
@end
@interface ZFSliderButton : UIButton
@end
@interface ZFSliderView : UIView
@property (nonatomic, weak) id<ZFSliderViewDelegate> delegate;
/** 滑块 */
@property (nonatomic, strong, readonly) ZFSliderButton *sliderBtn;
/** 默认滑杆的颜色 */
@property (nonatomic, strong) UIColor *maximumTrackTintColor;
/** 滑杆进度颜色 */
@property (nonatomic, strong) UIColor *minimumTrackTintColor;
/** 缓存进度颜色 */
@property (nonatomic, strong) UIColor *bufferTrackTintColor;
/** loading进度颜色 */
@property (nonatomic, strong) UIColor *loadingTintColor;
/** 默认滑杆的图片 */
@property (nonatomic, strong) UIImage *maximumTrackImage;
/** 滑杆进度的图片 */
@property (nonatomic, strong) UIImage *minimumTrackImage;
/** 缓存进度的图片 */
@property (nonatomic, strong) UIImage *bufferTrackImage;
/** 滑杆进度 */
@property (nonatomic, assign) float value;
/** 缓存进度 */
@property (nonatomic, assign) float bufferValue;
/** 是否允许点击,默认是YES */
@property (nonatomic, assign) BOOL allowTapped;
/** 是否允许点击,默认是YES */
@property (nonatomic, assign) BOOL animate;
/** 设置滑杆的高度 */
@property (nonatomic, assign) CGFloat sliderHeight;
/** 设置滑杆的圆角 */
@property (nonatomic, assign) CGFloat sliderRadius;
/** 是否隐藏滑块(默认为NO) */
@property (nonatomic, assign) BOOL isHideSliderBlock;
/// 是否正在拖动
@property (nonatomic, assign) BOOL isdragging;
/// 向前还是向后拖动
@property (nonatomic, assign) BOOL isForward;
@property (nonatomic, assign) CGSize thumbSize;
/**
* Starts animation of the spinner.
*/
- (void)startAnimating;
/**
* Stops animation of the spinnner.
*/
- (void)stopAnimating;
// 设置滑块背景色
- (void)setBackgroundImage:(UIImage *)image forState:(UIControlState)state;
// 设置滑块图片
- (void)setThumbImage:(UIImage *)image forState:(UIControlState)state;
@end
@@ -0,0 +1,426 @@
//
// ZFSliderView.m
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "ZFSliderView.h"
#import "UIView+ZFFrame.h"
/** 滑块的大小 */
static const CGFloat kSliderBtnWH = 19.0;
/** 进度的高度 */
static const CGFloat kProgressH = 1.0;
/** 拖动slider动画的时间*/
static const CGFloat kAnimate = 0.3;
@implementation ZFSliderButton
// 重写此方法将按钮的点击范围扩大
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
CGRect bounds = self.bounds;
// 扩大点击区域
bounds = CGRectInset(bounds, -20, -20);
// 若点击的点在新的bounds里面。就返回yes
return CGRectContainsPoint(bounds, point);
}
@end
@interface ZFSliderView ()
/** 进度背景 */
@property (nonatomic, strong) UIImageView *bgProgressView;
/** 缓存进度 */
@property (nonatomic, strong) UIImageView *bufferProgressView;
/** 滑动进度 */
@property (nonatomic, strong) UIImageView *sliderProgressView;
/** 滑块 */
@property (nonatomic, strong) ZFSliderButton *sliderBtn;
@property (nonatomic, strong) UIView *loadingBarView;
@property (nonatomic, assign) BOOL isLoading;
@property (nonatomic, strong) UITapGestureRecognizer *tapGesture;
@end
@implementation ZFSliderView
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.allowTapped = YES;
self.animate = YES;
[self addSubViews];
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
self.allowTapped = YES;
self.animate = YES;
[self addSubViews];
}
- (void)layoutSubviews {
[super layoutSubviews];
if (isnan(self.value) || isnan(self.bufferValue)) return;
CGFloat min_x = 0;
CGFloat min_y = 0;
CGFloat min_w = 0;
CGFloat min_h = 0;
CGFloat min_view_w = self.bounds.size.width;
CGFloat min_view_h = self.bounds.size.height;
min_x = 0;
min_w = min_view_w;
min_y = 0;
min_h = self.sliderHeight;
self.bgProgressView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = 0;
min_y = 0;
min_w = self.thumbSize.width;
min_h = self.thumbSize.height;
self.sliderBtn.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.sliderBtn.zf_centerX = self.bgProgressView.zf_width * self.value;
min_x = 0;
min_y = 0;
if (self.sliderBtn.hidden) {
min_w = self.bgProgressView.zf_width * self.value;
} else {
min_w = self.sliderBtn.zf_centerX;
}
min_h = self.sliderHeight;
self.sliderProgressView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = 0;
min_y = 0;
min_w = self.bgProgressView.zf_width * self.bufferValue;
min_h = self.sliderHeight;
self.bufferProgressView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_w = 0.1;
min_h = self.sliderHeight;
min_x = (min_view_w - min_w)/2;
min_y = (min_view_h - min_h)/2;
self.loadingBarView.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.bgProgressView.zf_centerY = min_view_h * 0.5;
self.bufferProgressView.zf_centerY = min_view_h * 0.5;
self.sliderProgressView.zf_centerY = min_view_h * 0.5;
self.sliderBtn.zf_centerY = min_view_h * 0.5;
}
/**
添加子视图
*/
- (void)addSubViews {
self.thumbSize = CGSizeMake(kSliderBtnWH, kSliderBtnWH);
self.sliderHeight = kProgressH;
self.backgroundColor = [UIColor clearColor];
[self addSubview:self.bgProgressView];
[self addSubview:self.bufferProgressView];
[self addSubview:self.sliderProgressView];
[self addSubview:self.sliderBtn];
[self addSubview:self.loadingBarView];
// 添加点击手势
self.tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped:)];
[self addGestureRecognizer:self.tapGesture];
// 添加滑动手势
UIPanGestureRecognizer *sliderGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(sliderGesture:)];
[self addGestureRecognizer:sliderGesture];
}
#pragma mark - Setter
- (void)setMaximumTrackTintColor:(UIColor *)maximumTrackTintColor {
_maximumTrackTintColor = maximumTrackTintColor;
self.bgProgressView.backgroundColor = maximumTrackTintColor;
}
- (void)setMinimumTrackTintColor:(UIColor *)minimumTrackTintColor {
_minimumTrackTintColor = minimumTrackTintColor;
self.sliderProgressView.backgroundColor = minimumTrackTintColor;
}
- (void)setBufferTrackTintColor:(UIColor *)bufferTrackTintColor {
_bufferTrackTintColor = bufferTrackTintColor;
self.bufferProgressView.backgroundColor = bufferTrackTintColor;
}
- (void)setLoadingTintColor:(UIColor *)loadingTintColor {
_loadingTintColor = loadingTintColor;
self.loadingBarView.backgroundColor = loadingTintColor;
}
- (void)setMaximumTrackImage:(UIImage *)maximumTrackImage {
_maximumTrackImage = maximumTrackImage;
self.bgProgressView.image = maximumTrackImage;
self.maximumTrackTintColor = [UIColor clearColor];
}
- (void)setMinimumTrackImage:(UIImage *)minimumTrackImage {
_minimumTrackImage = minimumTrackImage;
self.sliderProgressView.image = minimumTrackImage;
self.minimumTrackTintColor = [UIColor clearColor];
}
- (void)setBufferTrackImage:(UIImage *)bufferTrackImage {
_bufferTrackImage = bufferTrackImage;
self.bufferProgressView.image = bufferTrackImage;
self.bufferTrackTintColor = [UIColor clearColor];
}
- (void)setBackgroundImage:(UIImage *)image forState:(UIControlState)state {
[self.sliderBtn setBackgroundImage:image forState:state];
}
- (void)setThumbImage:(UIImage *)image forState:(UIControlState)state {
[self.sliderBtn setImage:image forState:state];
}
- (void)setValue:(float)value {
if (isnan(value)) return;
value = MIN(1.0, value);
_value = value;
if (self.sliderBtn.hidden) {
self.sliderProgressView.zf_width = self.bgProgressView.zf_width * value;
} else {
self.sliderBtn.zf_centerX = self.bgProgressView.zf_width * value;
self.sliderProgressView.zf_width = self.sliderBtn.zf_centerX;
}
}
- (void)setBufferValue:(float)bufferValue {
if (isnan(bufferValue)) return;
bufferValue = MIN(1.0, bufferValue);
_bufferValue = bufferValue;
self.bufferProgressView.zf_width = self.bgProgressView.zf_width * bufferValue;
}
- (void)setAllowTapped:(BOOL)allowTapped {
_allowTapped = allowTapped;
if (!allowTapped) {
[self removeGestureRecognizer:self.tapGesture];
}
}
- (void)setSliderHeight:(CGFloat)sliderHeight {
if (isnan(sliderHeight)) return;
_sliderHeight = sliderHeight;
self.bgProgressView.zf_height = sliderHeight;
self.bufferProgressView.zf_height = sliderHeight;
self.sliderProgressView.zf_height = sliderHeight;
}
- (void)setSliderRadius:(CGFloat)sliderRadius {
if (isnan(sliderRadius)) return;
_sliderRadius = sliderRadius;
self.bgProgressView.layer.cornerRadius = sliderRadius;
self.bufferProgressView.layer.cornerRadius = sliderRadius;
self.sliderProgressView.layer.cornerRadius = sliderRadius;
self.bgProgressView.layer.masksToBounds = YES;
self.bufferProgressView.layer.masksToBounds = YES;
self.sliderProgressView.layer.masksToBounds = YES;
}
- (void)setIsHideSliderBlock:(BOOL)isHideSliderBlock {
_isHideSliderBlock = isHideSliderBlock;
// 隐藏滑块,滑杆不可点击
if (isHideSliderBlock) {
self.sliderBtn.hidden = YES;
self.bgProgressView.zf_left = 0;
self.bufferProgressView.zf_left = 0;
self.sliderProgressView.zf_left = 0;
self.allowTapped = NO;
}
}
/**
* Starts animation of the spinner.
*/
- (void)startAnimating {
if (self.isLoading) return;
self.isLoading = YES;
self.bufferProgressView.hidden = YES;
self.sliderProgressView.hidden = YES;
self.sliderBtn.hidden = YES;
self.loadingBarView.hidden = NO;
[self.loadingBarView.layer removeAllAnimations];
CAAnimationGroup *animationGroup = [[CAAnimationGroup alloc] init];
animationGroup.duration = 0.4;
animationGroup.beginTime = CACurrentMediaTime() + 0.4;
animationGroup.repeatCount = MAXFLOAT;
animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
CABasicAnimation *scaleAnimation = [CABasicAnimation animation];
scaleAnimation.keyPath = @"transform.scale.x";
scaleAnimation.fromValue = @(1000.0f);
scaleAnimation.toValue = @(self.zf_width * 10);
CABasicAnimation *alphaAnimation = [CABasicAnimation animation];
alphaAnimation.keyPath = @"opacity";
alphaAnimation.fromValue = @(1.0f);
alphaAnimation.toValue = @(0.0f);
[animationGroup setAnimations:@[scaleAnimation, alphaAnimation]];
[self.loadingBarView.layer addAnimation:animationGroup forKey:@"loading"];
}
/**
* Stops animation of the spinnner.
*/
- (void)stopAnimating {
self.isLoading = NO;
self.bufferProgressView.hidden = NO;
self.sliderProgressView.hidden = NO;
self.sliderBtn.hidden = self.isHideSliderBlock;
self.loadingBarView.hidden = YES;
[self.loadingBarView.layer removeAllAnimations];
}
#pragma mark - User Action
- (void)sliderGesture:(UIGestureRecognizer *)gesture {
switch (gesture.state) {
case UIGestureRecognizerStateBegan: {
[self sliderBtnTouchBegin:self.sliderBtn];
}
break;
case UIGestureRecognizerStateChanged: {
[self sliderBtnDragMoving:self.sliderBtn point:[gesture locationInView:self.bgProgressView]];
}
break;
case UIGestureRecognizerStateEnded: {
[self sliderBtnTouchEnded:self.sliderBtn];
}
break;
default:
break;
}
}
- (void)sliderBtnTouchBegin:(UIButton *)btn {
if ([self.delegate respondsToSelector:@selector(sliderTouchBegan:)]) {
[self.delegate sliderTouchBegan:self.value];
}
if (self.animate) {
[UIView animateWithDuration:kAnimate animations:^{
btn.transform = CGAffineTransformMakeScale(1.2, 1.2);
}];
}
}
- (void)sliderBtnTouchEnded:(UIButton *)btn {
if ([self.delegate respondsToSelector:@selector(sliderTouchEnded:)]) {
[self.delegate sliderTouchEnded:self.value];
}
if (self.animate) {
[UIView animateWithDuration:kAnimate animations:^{
btn.transform = CGAffineTransformIdentity;
}];
}
}
- (void)sliderBtnDragMoving:(UIButton *)btn point:(CGPoint)touchPoint {
// 点击的位置
CGPoint point = touchPoint;
// 获取进度值 由于btn是从 0-(self.width - btn.width)
CGFloat value = (point.x - btn.zf_width * 0.5) / self.bgProgressView.zf_width;
// value的值需在0-1之间
value = value >= 1.0 ? 1.0 : value <= 0.0 ? 0.0 : value;
if (self.value == value) return;
self.isForward = self.value < value;
self.value = value;
if ([self.delegate respondsToSelector:@selector(sliderValueChanged:)]) {
[self.delegate sliderValueChanged:value];
}
}
- (void)tapped:(UITapGestureRecognizer *)tap {
CGPoint point = [tap locationInView:self.bgProgressView];
// 获取进度
CGFloat value = (point.x - self.sliderBtn.zf_width * 0.5) * 1.0 / self.bgProgressView.zf_width;
value = value >= 1.0 ? 1.0 : value <= 0 ? 0 : value;
self.value = value;
if ([self.delegate respondsToSelector:@selector(sliderTapped:)]) {
[self.delegate sliderTapped:value];
}
}
#pragma mark - getter
- (UIView *)bgProgressView {
if (!_bgProgressView) {
_bgProgressView = [UIImageView new];
_bgProgressView.backgroundColor = [UIColor grayColor];
_bgProgressView.contentMode = UIViewContentModeScaleAspectFill;
_bgProgressView.clipsToBounds = YES;
}
return _bgProgressView;
}
- (UIView *)bufferProgressView {
if (!_bufferProgressView) {
_bufferProgressView = [UIImageView new];
_bufferProgressView.backgroundColor = [UIColor whiteColor];
_bufferProgressView.contentMode = UIViewContentModeScaleAspectFill;
_bufferProgressView.clipsToBounds = YES;
}
return _bufferProgressView;
}
- (UIView *)sliderProgressView {
if (!_sliderProgressView) {
_sliderProgressView = [UIImageView new];
_sliderProgressView.backgroundColor = [UIColor redColor];
_sliderProgressView.contentMode = UIViewContentModeScaleAspectFill;
_sliderProgressView.clipsToBounds = YES;
}
return _sliderProgressView;
}
- (ZFSliderButton *)sliderBtn {
if (!_sliderBtn) {
_sliderBtn = [ZFSliderButton buttonWithType:UIButtonTypeCustom];
[_sliderBtn setAdjustsImageWhenHighlighted:NO];
}
return _sliderBtn;
}
- (UIView *)loadingBarView {
if (!_loadingBarView) {
_loadingBarView = [[UIView alloc] init];
_loadingBarView.backgroundColor = [UIColor whiteColor];
_loadingBarView.hidden = YES;
}
return _loadingBarView;
}
@end
@@ -0,0 +1,31 @@
//
// ZFSmallFloatControlView.h
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
@interface ZFSmallFloatControlView : UIView
@property (nonatomic, copy, nullable) void(^closeClickCallback)(void);
@end
@@ -0,0 +1,72 @@
//
// ZFSmallFloatControlView.m
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "ZFSmallFloatControlView.h"
#import "ZFUtilities.h"
@interface ZFSmallFloatControlView ()
@property (nonatomic, strong) UIButton *closeBtn;
@end
@implementation ZFSmallFloatControlView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self addSubview:self.closeBtn];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat min_x = 0;
CGFloat min_y = 0;
CGFloat min_w = 0;
CGFloat min_h = 0;
CGFloat min_view_w = self.bounds.size.width;
min_x = min_view_w-20;
min_y = -10;
min_w = 30;
min_h = min_w;
self.closeBtn.frame = CGRectMake(min_x, min_y, min_w, min_h);
}
- (void)closeBtnClick:(UIButton *)sender {
if (self.closeClickCallback) self.closeClickCallback();
}
- (UIButton *)closeBtn {
if (!_closeBtn) {
_closeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[_closeBtn setImage:ZFPlayer_Image(@"ZFPlayer_close") forState:UIControlStateNormal];
[_closeBtn addTarget:self action:@selector(closeBtnClick:) forControlEvents:UIControlEventTouchUpInside];
}
return _closeBtn;
}
@end
@@ -0,0 +1,27 @@
//
// ZFSpeedLoadingView.h
// Pods-ZFPlayer_Example
//
// Created by 紫枫 on 2018/6/27.
//
#import <UIKit/UIKit.h>
#import "ZFLoadingView.h"
@interface ZFSpeedLoadingView : UIView
@property (nonatomic, strong) ZFLoadingView *loadingView;
@property (nonatomic, strong) UILabel *speedTextLabel;
/**
* Starts animation of the spinner.
*/
- (void)startAnimating;
/**
* Stops animation of the spinnner.
*/
- (void)stopAnimating;
@end
@@ -0,0 +1,117 @@
//
// ZFSpeedLoadingView.m
// Pods-ZFPlayer_Example
//
// Created by 紫枫 on 2018/6/27.
//
#import "ZFSpeedLoadingView.h"
#import "ZFNetworkSpeedMonitor.h"
#import "UIView+ZFFrame.h"
@interface ZFSpeedLoadingView ()
@property (nonatomic, strong) ZFNetworkSpeedMonitor *speedMonitor;
@end
@implementation ZFSpeedLoadingView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self initialize];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self initialize];
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
[self initialize];
}
- (void)initialize {
self.userInteractionEnabled = NO;
[self addSubview:self.loadingView];
[self addSubview:self.speedTextLabel];
[self.speedMonitor startNetworkSpeedMonitor];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkSpeedChanged:) name:ZFDownloadNetworkSpeedNotificationKey object:nil];
}
- (void)dealloc {
[self.speedMonitor stopNetworkSpeedMonitor];
[[NSNotificationCenter defaultCenter] removeObserver:self name:ZFDownloadNetworkSpeedNotificationKey object:nil];
}
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat min_x = 0;
CGFloat min_y = 0;
CGFloat min_w = 0;
CGFloat min_h = 0;
CGFloat min_view_w = self.zf_width;
CGFloat min_view_h = self.zf_height;
min_w = 44;
min_h = min_w;
min_x = (min_view_w - min_w) / 2;
min_y = (min_view_h - min_h) / 2 - 10;
self.loadingView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = 0;
min_y = self.loadingView.zf_bottom+5;
min_w = min_view_w;
min_h = 20;
self.speedTextLabel.frame = CGRectMake(min_x, min_y, min_w, min_h);
}
- (void)networkSpeedChanged:(NSNotification *)sender {
NSString *downloadSpped = [sender.userInfo objectForKey:ZFNetworkSpeedNotificationKey];
self.speedTextLabel.text = downloadSpped;
}
- (void)startAnimating {
[self.loadingView startAnimating];
self.hidden = NO;
}
- (void)stopAnimating {
[self.loadingView stopAnimating];
self.hidden = YES;
}
- (UILabel *)speedTextLabel {
if (!_speedTextLabel) {
_speedTextLabel = [UILabel new];
_speedTextLabel.textColor = [UIColor whiteColor];
_speedTextLabel.font = [UIFont systemFontOfSize:12.0];
_speedTextLabel.textAlignment = NSTextAlignmentCenter;
}
return _speedTextLabel;
}
- (ZFNetworkSpeedMonitor *)speedMonitor {
if (!_speedMonitor) {
_speedMonitor = [[ZFNetworkSpeedMonitor alloc] init];
}
return _speedMonitor;
}
- (ZFLoadingView *)loadingView {
if (!_loadingView) {
_loadingView = [[ZFLoadingView alloc] init];
_loadingView.lineWidth = 0.8;
_loadingView.duration = 1;
_loadingView.hidesWhenStopped = YES;
}
return _loadingView;
}
@end
@@ -0,0 +1,47 @@
//
// ZFUtilities.m
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
/// iPhoneX iPhoneXS iPhoneXS Max iPhoneXR 机型判断
#define iPhoneX ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? ((NSInteger)(([[UIScreen mainScreen] currentMode].size.height/[[UIScreen mainScreen] currentMode].size.width)*100) == 216) : NO)
#define ZFPlayer_Image(file) [ZFUtilities imageNamed:file]
// 屏幕的宽
#define ZFPlayer_ScreenWidth [[UIScreen mainScreen] bounds].size.width
// 屏幕的高
#define ZFPlayer_ScreenHeight [[UIScreen mainScreen] bounds].size.height
@interface ZFUtilities : NSObject
+ (NSString *)convertTimeSecond:(NSInteger)timeSecond;
+ (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size;
+ (UIImage *)imageNamed:(NSString *)name;
@end
@@ -0,0 +1,74 @@
//
// ZFUtilities.m
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "ZFUtilities.h"
@implementation ZFUtilities
+ (NSString *)convertTimeSecond:(NSInteger)timeSecond {
NSString *theLastTime = nil;
long second = timeSecond;
if (timeSecond < 60) {
theLastTime = [NSString stringWithFormat:@"00:%02zd", second];
} else if(timeSecond >= 60 && timeSecond < 3600){
theLastTime = [NSString stringWithFormat:@"%02zd:%02zd", second/60, second%60];
} else if(timeSecond >= 3600){
theLastTime = [NSString stringWithFormat:@"%02zd:%02zd:%02zd", second/3600, second%3600/60, second%60];
}
return theLastTime;
}
+ (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size {
if (!color || size.width <= 0 || size.height <= 0) return nil;
CGRect rect = CGRectMake(0.0f, 0.0f, size.width, size.height);
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextFillRect(context, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
+ (NSBundle *)bundle {
static NSBundle *bundle = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
bundle = [NSBundle bundleWithPath:[[NSBundle bundleForClass:[self class]] pathForResource:@"ZFPlayer" ofType:@"bundle"]];
});
return bundle;
}
+ (UIImage *)imageNamed:(NSString *)name {
if (name.length == 0) return nil;
int scale = (int)UIScreen.mainScreen.scale;
if (scale < 2) scale = 2;
else if (scale > 3) scale = 3;
NSString *n = [NSString stringWithFormat:@"%@@%dx", name, scale];
UIImage *image = [UIImage imageWithContentsOfFile:[self.bundle pathForResource:n ofType:@"png"]];
if (!image) image = [UIImage imageWithContentsOfFile:[self.bundle pathForResource:name ofType:@"png"]];
return image;
}
@end
@@ -0,0 +1,46 @@
//
// ZFVolumeBrightnessView.h
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSInteger, ZFVolumeBrightnessType) {
ZFVolumeBrightnessTypeVolume, // volume
ZFVolumeBrightnessTypeumeBrightness // brightness
};
@interface ZFVolumeBrightnessView : UIView
@property (nonatomic, assign, readonly) ZFVolumeBrightnessType volumeBrightnessType;
@property (nonatomic, strong, readonly) UIProgressView *progressView;
@property (nonatomic, strong, readonly) UIImageView *iconImageView;
- (void)updateProgress:(CGFloat)progress withVolumeBrightnessType:(ZFVolumeBrightnessType)volumeBrightnessType;
/// 添加系统音量view
- (void)addSystemVolumeView;
/// 移除系统音量view
- (void)removeSystemVolumeView;
@end
@@ -0,0 +1,162 @@
//
// ZFVolumeBrightnessView.m
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "ZFVolumeBrightnessView.h"
#import <MediaPlayer/MediaPlayer.h>
#import "ZFUtilities.h"
@interface ZFVolumeBrightnessView ()
@property (nonatomic, strong) UIProgressView *progressView;
@property (nonatomic, strong) UIImageView *iconImageView;
@property (nonatomic, assign) ZFVolumeBrightnessType volumeBrightnessType;
@property (nonatomic, strong) MPVolumeView *volumeView;
@end
@implementation ZFVolumeBrightnessView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self addSubview:self.iconImageView];
[self addSubview:self.progressView];
[self hideTipView];
}
return self;
}
- (void)dealloc {
[self addSystemVolumeView];
}
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat min_x = 0;
CGFloat min_y = 0;
CGFloat min_w = 0;
CGFloat min_h = 0;
CGFloat min_view_w = self.frame.size.width;
CGFloat min_view_h = self.frame.size.height;
CGFloat margin = 10;
min_x = margin;
min_w = 20;
min_h = min_w;
min_y = (min_view_h-min_h)/2;
self.iconImageView.frame = CGRectMake(min_x, min_y, min_w, min_h);
min_x = CGRectGetMaxX(self.iconImageView.frame) + margin;
min_h = 2;
min_y = (min_view_h-min_h)/2;
min_w = min_view_w - min_x - margin;
self.progressView.frame = CGRectMake(min_x, min_y, min_w, min_h);
self.layer.cornerRadius = min_view_h/2;
self.layer.masksToBounds = YES;
}
- (void)hideTipView {
[UIView animateWithDuration:0.5 animations:^{
self.alpha = 0;
} completion:^(BOOL finished) {
self.hidden = YES;
}];
}
/// 添加系统音量view
- (void)addSystemVolumeView {
[self.volumeView removeFromSuperview];
}
/// 移除系统音量view
- (void)removeSystemVolumeView {
[[UIApplication sharedApplication].keyWindow addSubview:self.volumeView];
}
- (void)updateProgress:(CGFloat)progress withVolumeBrightnessType:(ZFVolumeBrightnessType)volumeBrightnessType {
if (progress >= 1) {
progress = 1;
} else if (progress <= 0) {
progress = 0;
}
self.progressView.progress = progress;
self.volumeBrightnessType = volumeBrightnessType;
UIImage *playerImage = nil;
if (volumeBrightnessType == ZFVolumeBrightnessTypeVolume) {
if (progress == 0) {
playerImage = ZFPlayer_Image(@"ZFPlayer_muted");
} else if (progress > 0 && progress < 0.5) {
playerImage = ZFPlayer_Image(@"ZFPlayer_volume_low");
} else {
playerImage = ZFPlayer_Image(@"ZFPlayer_volume_high");
}
} else if (volumeBrightnessType == ZFVolumeBrightnessTypeumeBrightness) {
if (progress >= 0 && progress < 0.5) {
playerImage = ZFPlayer_Image(@"ZFPlayer_brightness_low");
} else {
playerImage = ZFPlayer_Image(@"ZFPlayer_brightness_high");
}
}
self.iconImageView.image = playerImage;
self.hidden = NO;
self.alpha = 1;
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideTipView) object:nil];
[self performSelector:@selector(hideTipView) withObject:nil afterDelay:1.5];
}
- (void)setVolumeBrightnessType:(ZFVolumeBrightnessType)volumeBrightnessType {
_volumeBrightnessType = volumeBrightnessType;
if (volumeBrightnessType == ZFVolumeBrightnessTypeVolume) {
self.iconImageView.image = ZFPlayer_Image(@"ZFPlayer_volume");
} else {
self.iconImageView.image = ZFPlayer_Image(@"ZFPlayer_brightness");
}
}
- (UIProgressView *)progressView {
if (!_progressView) {
_progressView = [[UIProgressView alloc] init];
_progressView.progressTintColor = [UIColor whiteColor];
_progressView.trackTintColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.4];;
}
return _progressView;
}
- (UIImageView *)iconImageView {
if (!_iconImageView) {
_iconImageView = [UIImageView new];
}
return _iconImageView;
}
- (MPVolumeView *)volumeView {
if (!_volumeView) {
_volumeView = [[MPVolumeView alloc] init];
_volumeView.frame = CGRectMake(-1000, -1000, 100, 100);
}
return _volumeView;
}
@end
@@ -0,0 +1,79 @@
/*
* IJKAVMoviePlayerController.h
*
* Copyright (c) 2014 Bilibili
* Copyright (c) 2014 Zhang Rui <bbcallen@gmail.com>
*
* This file is part of ijkPlayer.
*
* ijkPlayer is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* ijkPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with ijkPlayer; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
File: AVPlayerDemoPlaybackViewController.h
Abstract: UIViewController managing a playback view, thumbnail view, and associated playback UI.
Version: 1.3
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
Inc. ("Apple") in consideration of your agreement to the following
terms, and your use, installation, modification or redistribution of
this Apple software constitutes acceptance of these terms. If you do
not agree with these terms, please do not use, install, modify or
redistribute this Apple software.
In consideration of your agreement to abide by the following terms, and
subject to these terms, Apple grants you a personal, non-exclusive
license, under Apple's copyrights in this original Apple software (the
"Apple Software"), to use, reproduce, modify and redistribute the Apple
Software, with or without modifications, in source and/or binary forms;
provided that if you redistribute the Apple Software in its entirety and
without modifications, you must retain this notice and the following
text and disclaimers in all such redistributions of the Apple Software.
Neither the name, trademarks, service marks or logos of Apple Inc. may
be used to endorse or promote products derived from the Apple Software
without specific prior written permission from Apple. Except as
expressly stated in this notice, no other rights or licenses, express or
implied, are granted by Apple herein, including but not limited to any
patent rights that may be infringed by your derivative works or by other
works in which the Apple Software may be incorporated.
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Copyright (C) 2014 Apple Inc. All Rights Reserved.
*/
#import "IJKMediaPlayback.h"
@interface IJKAVMoviePlayerController : NSObject <IJKMediaPlayback>
- (id)initWithContentURL:(NSURL *)aUrl;
- (id)initWithContentURLString:(NSString *)aUrl;
+ (id)getInstance:(NSString *)aUrl;
@end
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2016 Bilibili
* Copyright (c) 2016 Zhang Rui <bbcallen@gmail.com>
*
* This file is part of ijkPlayer.
*
* ijkPlayer is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* ijkPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with ijkPlayer; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import <Foundation/Foundation.h>
@interface IJKFFMonitor : NSObject
- (instancetype)init;
@property(nonatomic) NSDictionary *mediaMeta;
@property(nonatomic) NSDictionary *videoMeta;
@property(nonatomic) NSDictionary *audioMeta;
@property(nonatomic, readonly) int64_t duration; // milliseconds
@property(nonatomic, readonly) int64_t bitrate; // bit / sec
@property(nonatomic, readonly) float fps; // frame / sec
@property(nonatomic, readonly) int width; // width
@property(nonatomic, readonly) int height; // height
@property(nonatomic, readonly) NSString *vcodec; // video codec
@property(nonatomic, readonly) NSString *acodec; // audio codec
@property(nonatomic, readonly) int sampleRate;
@property(nonatomic, readonly) int64_t channelLayout;
@property(nonatomic) NSString *vdecoder;
@property(nonatomic) int tcpError;
@property(nonatomic) NSString *remoteIp;
@property(nonatomic) int httpError;
@property(nonatomic) NSString *httpUrl;
@property(nonatomic) NSString *httpHost;
@property(nonatomic) int httpCode;
@property(nonatomic) int64_t httpOpenTick;
@property(nonatomic) int64_t httpSeekTick;
@property(nonatomic) int httpOpenCount;
@property(nonatomic) int httpSeekCount;
@property(nonatomic) int64_t lastHttpOpenDuration;
@property(nonatomic) int64_t lastHttpSeekDuration;
@property(nonatomic) int64_t filesize;
@property(nonatomic) int64_t prepareStartTick;
@property(nonatomic) int64_t prepareDuration;
@property(nonatomic) int64_t firstVideoFrameLatency;
@property(nonatomic) int64_t lastPrerollStartTick;
@property(nonatomic) int64_t lastPrerollDuration;
@end
@@ -0,0 +1,155 @@
/*
* IJKFFMoviePlayerController.h
*
* Copyright (c) 2013 Bilibili
* Copyright (c) 2013 Zhang Rui <bbcallen@gmail.com>
*
* This file is part of ijkPlayer.
*
* ijkPlayer is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* ijkPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with ijkPlayer; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import "IJKMediaPlayback.h"
#import "IJKFFMonitor.h"
#import "IJKFFOptions.h"
#import "IJKSDLGLViewProtocol.h"
// media meta
#define k_IJKM_KEY_FORMAT @"format"
#define k_IJKM_KEY_DURATION_US @"duration_us"
#define k_IJKM_KEY_START_US @"start_us"
#define k_IJKM_KEY_BITRATE @"bitrate"
// stream meta
#define k_IJKM_KEY_TYPE @"type"
#define k_IJKM_VAL_TYPE__VIDEO @"video"
#define k_IJKM_VAL_TYPE__AUDIO @"audio"
#define k_IJKM_VAL_TYPE__UNKNOWN @"unknown"
#define k_IJKM_KEY_CODEC_NAME @"codec_name"
#define k_IJKM_KEY_CODEC_PROFILE @"codec_profile"
#define k_IJKM_KEY_CODEC_LONG_NAME @"codec_long_name"
// stream: video
#define k_IJKM_KEY_WIDTH @"width"
#define k_IJKM_KEY_HEIGHT @"height"
#define k_IJKM_KEY_FPS_NUM @"fps_num"
#define k_IJKM_KEY_FPS_DEN @"fps_den"
#define k_IJKM_KEY_TBR_NUM @"tbr_num"
#define k_IJKM_KEY_TBR_DEN @"tbr_den"
#define k_IJKM_KEY_SAR_NUM @"sar_num"
#define k_IJKM_KEY_SAR_DEN @"sar_den"
// stream: audio
#define k_IJKM_KEY_SAMPLE_RATE @"sample_rate"
#define k_IJKM_KEY_CHANNEL_LAYOUT @"channel_layout"
#define kk_IJKM_KEY_STREAMS @"streams"
typedef enum IJKLogLevel {
k_IJK_LOG_UNKNOWN = 0,
k_IJK_LOG_DEFAULT = 1,
k_IJK_LOG_VERBOSE = 2,
k_IJK_LOG_DEBUG = 3,
k_IJK_LOG_INFO = 4,
k_IJK_LOG_WARN = 5,
k_IJK_LOG_ERROR = 6,
k_IJK_LOG_FATAL = 7,
k_IJK_LOG_SILENT = 8,
} IJKLogLevel;
@interface IJKFFMoviePlayerController : NSObject <IJKMediaPlayback>
- (id)initWithContentURL:(NSURL *)aUrl
withOptions:(IJKFFOptions *)options;
- (id)initWithContentURLString:(NSString *)aUrlString
withOptions:(IJKFFOptions *)options;
- (id)initWithMoreContent:(NSURL *)aUrl
withOptions:(IJKFFOptions *)options
withGLView:(UIView<IJKSDLGLViewProtocol> *)glView;
- (id)initWithMoreContentString:(NSString *)aUrlString
withOptions:(IJKFFOptions *)options
withGLView:(UIView<IJKSDLGLViewProtocol> *)glView;
- (void)prepareToPlay;
- (void)play;
- (void)pause;
- (void)stop;
- (BOOL)isPlaying;
- (int64_t)trafficStatistic;
- (float)dropFrameRate;
- (void)setPauseInBackground:(BOOL)pause;
- (BOOL)isVideoToolboxOpen;
- (void)setHudValue:(NSString *)value forKey:(NSString *)key;
+ (void)setLogReport:(BOOL)preferLogReport;
+ (void)setLogLevel:(IJKLogLevel)logLevel;
+ (BOOL)checkIfFFmpegVersionMatch:(BOOL)showAlert;
+ (BOOL)checkIfPlayerVersionMatch:(BOOL)showAlert
version:(NSString *)version;
@property(nonatomic, readonly) CGFloat fpsInMeta;
@property(nonatomic, readonly) CGFloat fpsAtOutput;
@property(nonatomic) BOOL shouldShowHudView;
- (void)setOptionValue:(NSString *)value
forKey:(NSString *)key
ofCategory:(IJKFFOptionCategory)category;
- (void)setOptionIntValue:(int64_t)value
forKey:(NSString *)key
ofCategory:(IJKFFOptionCategory)category;
- (void)setFormatOptionValue: (NSString *)value forKey:(NSString *)key;
- (void)setCodecOptionValue: (NSString *)value forKey:(NSString *)key;
- (void)setSwsOptionValue: (NSString *)value forKey:(NSString *)key;
- (void)setPlayerOptionValue: (NSString *)value forKey:(NSString *)key;
- (void)setFormatOptionIntValue: (int64_t)value forKey:(NSString *)key;
- (void)setCodecOptionIntValue: (int64_t)value forKey:(NSString *)key;
- (void)setSwsOptionIntValue: (int64_t)value forKey:(NSString *)key;
- (void)setPlayerOptionIntValue: (int64_t)value forKey:(NSString *)key;
@property (nonatomic, retain) id<IJKMediaUrlOpenDelegate> segmentOpenDelegate;
@property (nonatomic, retain) id<IJKMediaUrlOpenDelegate> tcpOpenDelegate;
@property (nonatomic, retain) id<IJKMediaUrlOpenDelegate> httpOpenDelegate;
@property (nonatomic, retain) id<IJKMediaUrlOpenDelegate> liveOpenDelegate;
@property (nonatomic, retain) id<IJKMediaNativeInvokeDelegate> nativeInvokeDelegate;
- (void)didShutdown;
#pragma mark KVO properties
@property (nonatomic, readonly) IJKFFMonitor *monitor;
@end
#define IJK_FF_IO_TYPE_READ (1)
void IJKFFIOStatDebugCallback(const char *url, int type, int bytes);
void IJKFFIOStatRegister(void (*cb)(const char *url, int type, int bytes));
void IJKFFIOStatCompleteDebugCallback(const char *url,
int64_t read_bytes, int64_t total_size,
int64_t elpased_time, int64_t total_duration);
void IJKFFIOStatCompleteRegister(void (*cb)(const char *url,
int64_t read_bytes, int64_t total_size,
int64_t elpased_time, int64_t total_duration));
@@ -0,0 +1,75 @@
/*
* IJKFFOptions.h
*
* Copyright (c) 2013-2015 Bilibili
* Copyright (c) 2013-2015 Zhang Rui <bbcallen@gmail.com>
*
* This file is part of ijkPlayer.
*
* ijkPlayer is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* ijkPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with ijkPlayer; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import <Foundation/Foundation.h>
typedef enum IJKFFOptionCategory {
kIJKFFOptionCategoryFormat = 1,
kIJKFFOptionCategoryCodec = 2,
kIJKFFOptionCategorySws = 3,
kIJKFFOptionCategoryPlayer = 4,
kIJKFFOptionCategorySwr = 5,
} IJKFFOptionCategory;
// for codec option 'skip_loop_filter' and 'skip_frame'
typedef enum IJKAVDiscard {
/* We leave some space between them for extensions (drop some
* keyframes for intra-only or drop just some bidir frames). */
IJK_AVDISCARD_NONE =-16, ///< discard nothing
IJK_AVDISCARD_DEFAULT = 0, ///< discard useless packets like 0 size packets in avi
IJK_AVDISCARD_NONREF = 8, ///< discard all non reference
IJK_AVDISCARD_BIDIR = 16, ///< discard all bidirectional frames
IJK_AVDISCARD_NONKEY = 32, ///< discard all frames except keyframes
IJK_AVDISCARD_ALL = 48, ///< discard all
} IJKAVDiscard;
struct IjkMediaPlayer;
@interface IJKFFOptions : NSObject
+(IJKFFOptions *)optionsByDefault;
-(void)applyTo:(struct IjkMediaPlayer *)mediaPlayer;
- (void)setOptionValue:(NSString *)value
forKey:(NSString *)key
ofCategory:(IJKFFOptionCategory)category;
- (void)setOptionIntValue:(int64_t)value
forKey:(NSString *)key
ofCategory:(IJKFFOptionCategory)category;
-(void)setFormatOptionValue: (NSString *)value forKey:(NSString *)key;
-(void)setCodecOptionValue: (NSString *)value forKey:(NSString *)key;
-(void)setSwsOptionValue: (NSString *)value forKey:(NSString *)key;
-(void)setPlayerOptionValue: (NSString *)value forKey:(NSString *)key;
-(void)setFormatOptionIntValue: (int64_t)value forKey:(NSString *)key;
-(void)setCodecOptionIntValue: (int64_t)value forKey:(NSString *)key;
-(void)setSwsOptionIntValue: (int64_t)value forKey:(NSString *)key;
-(void)setPlayerOptionIntValue: (int64_t)value forKey:(NSString *)key;
@property(nonatomic) BOOL showHudView;
@end
@@ -0,0 +1,39 @@
/*
* IJKKVOController.h
*
* Copyright (c) 2014 Bilibili
* Copyright (c) 2014 Zhang Rui <bbcallen@gmail.com>
*
* This file is part of ijkPlayer.
*
* ijkPlayer is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* ijkPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with ijkPlayer; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import <Foundation/Foundation.h>
@interface IJKKVOController : NSObject
- (id)initWithTarget:(NSObject *)target;
- (void)safelyAddObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(void *)context;
- (void)safelyRemoveObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath;
- (void)safelyRemoveAllObservers;
@end
@@ -0,0 +1,32 @@
/*
* IJKMPMoviePlayerController.h
*
* Copyright (c) 2013 Bilibili
* Copyright (c) 2013 Zhang Rui <bbcallen@gmail.com>
*
* This file is part of ijkPlayer.
*
* ijkPlayer is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* ijkPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with ijkPlayer; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import "IJKMediaPlayback.h"
#import <MediaPlayer/MediaPlayer.h>
@interface IJKMPMoviePlayerController : MPMoviePlayerController <IJKMediaPlayback>
- (id)initWithContentURL:(NSURL *)aUrl;
- (id)initWithContentURLString:(NSString *)aUrl;
@end
@@ -0,0 +1,63 @@
/*
* IJKMediaFramework.h
*
* Copyright (c) 2013 Bilibili
* Copyright (c) 2013 Zhang Rui <bbcallen@gmail.com>
*
* This file is part of ijkPlayer.
*
* ijkPlayer is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* ijkPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with ijkPlayer; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import <UIKit/UIKit.h>
//! Project version number for IJKMediaFramework.
FOUNDATION_EXPORT double IJKMediaFrameworkVersionNumber;
//! Project version string for IJKMediaFramework.
FOUNDATION_EXPORT const unsigned char IJKMediaFrameworkVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <IJKMediaFrameworkWithSSL/PublicHeader.h>
#import <IJKMediaFramework/IJKMediaPlayback.h>
#import <IJKMediaFramework/IJKMPMoviePlayerController.h>
#import <IJKMediaFramework/IJKFFOptions.h>
#import <IJKMediaFramework/IJKFFMoviePlayerController.h>
#import <IJKMediaFramework/IJKAVMoviePlayerController.h>
#import <IJKMediaFramework/IJKMediaModule.h>
#import <IJKMediaFramework/IJKMediaPlayer.h>
#import <IJKMediaFramework/IJKNotificationManager.h>
#import <IJKMediaFramework/IJKKVOController.h>
#import <IJKMediaFramework/IJKSDLGLViewProtocol.h>
// backward compatible for old names
#define IJKMediaPlaybackIsPreparedToPlayDidChangeNotification IJKMPMediaPlaybackIsPreparedToPlayDidChangeNotification
#define IJKMoviePlayerLoadStateDidChangeNotification IJKMPMoviePlayerLoadStateDidChangeNotification
#define IJKMoviePlayerPlaybackDidFinishNotification IJKMPMoviePlayerPlaybackDidFinishNotification
#define IJKMoviePlayerPlaybackDidFinishReasonUserInfoKey IJKMPMoviePlayerPlaybackDidFinishReasonUserInfoKey
#define IJKMoviePlayerPlaybackStateDidChangeNotification IJKMPMoviePlayerPlaybackStateDidChangeNotification
#define IJKMoviePlayerIsAirPlayVideoActiveDidChangeNotification IJKMPMoviePlayerIsAirPlayVideoActiveDidChangeNotification
#define IJKMoviePlayerVideoDecoderOpenNotification IJKMPMoviePlayerVideoDecoderOpenNotification
#define IJKMoviePlayerFirstVideoFrameRenderedNotification IJKMPMoviePlayerFirstVideoFrameRenderedNotification
#define IJKMoviePlayerFirstAudioFrameRenderedNotification IJKMPMoviePlayerFirstAudioFrameRenderedNotification
#define IJKMoviePlayerFirstAudioFrameDecodedNotification IJKMPMoviePlayerFirstAudioFrameDecodedNotification
#define IJKMoviePlayerFirstVideoFrameDecodedNotification IJKMPMoviePlayerFirstVideoFrameDecodedNotification
#define IJKMoviePlayerOpenInputNotification IJKMPMoviePlayerOpenInputNotification
#define IJKMoviePlayerFindStreamInfoNotification IJKMPMoviePlayerFindStreamInfoNotification
#define IJKMoviePlayerComponentOpenNotification IJKMPMoviePlayerComponentOpenNotification
#define IJKMPMoviePlayerAccurateSeekCompleteNotification IJKMPMoviePlayerAccurateSeekCompleteNotification
#define IJKMoviePlayerSeekAudioStartNotification IJKMPMoviePlayerSeekAudioStartNotification
#define IJKMoviePlayerSeekVideoStartNotification IJKMPMoviePlayerSeekVideoStartNotification
@@ -0,0 +1,33 @@
/*
* IJKMediaModule.h
*
* Copyright (c) 2013 Bilibili
* Copyright (c) 2013 Zhang Rui <bbcallen@gmail.com>
*
* This file is part of ijkPlayer.
*
* ijkPlayer is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* ijkPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with ijkPlayer; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import <Foundation/Foundation.h>
@interface IJKMediaModule : NSObject
+ (IJKMediaModule *)sharedModule;
@property(atomic, getter=isAppIdleTimerDisabled) BOOL appIdleTimerDisabled;
@property(atomic, getter=isMediaModuleIdleTimerDisabled) BOOL mediaModuleIdleTimerDisabled;
@end
@@ -0,0 +1,227 @@
/*
* IJKMediaPlayback.h
*
* Copyright (c) 2013 Bilibili
* Copyright (c) 2013 Zhang Rui <bbcallen@gmail.com>
*
* This file is part of ijkPlayer.
*
* ijkPlayer is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* ijkPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with ijkPlayer; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSInteger, IJKMPMovieScalingMode) {
IJKMPMovieScalingModeNone, // No scaling
IJKMPMovieScalingModeAspectFit, // Uniform scale until one dimension fits
IJKMPMovieScalingModeAspectFill, // Uniform scale until the movie fills the visible bounds. One dimension may have clipped contents
IJKMPMovieScalingModeFill // Non-uniform scale. Both render dimensions will exactly match the visible bounds
};
typedef NS_ENUM(NSInteger, IJKMPMoviePlaybackState) {
IJKMPMoviePlaybackStateStopped,
IJKMPMoviePlaybackStatePlaying,
IJKMPMoviePlaybackStatePaused,
IJKMPMoviePlaybackStateInterrupted,
IJKMPMoviePlaybackStateSeekingForward,
IJKMPMoviePlaybackStateSeekingBackward
};
typedef NS_OPTIONS(NSUInteger, IJKMPMovieLoadState) {
IJKMPMovieLoadStateUnknown = 0,
IJKMPMovieLoadStatePlayable = 1 << 0,
IJKMPMovieLoadStatePlaythroughOK = 1 << 1, // Playback will be automatically started in this state when shouldAutoplay is YES
IJKMPMovieLoadStateStalled = 1 << 2, // Playback will be automatically paused in this state, if started
};
typedef NS_ENUM(NSInteger, IJKMPMovieFinishReason) {
IJKMPMovieFinishReasonPlaybackEnded,
IJKMPMovieFinishReasonPlaybackError,
IJKMPMovieFinishReasonUserExited
};
// -----------------------------------------------------------------------------
// Thumbnails
typedef NS_ENUM(NSInteger, IJKMPMovieTimeOption) {
IJKMPMovieTimeOptionNearestKeyFrame,
IJKMPMovieTimeOptionExact
};
@protocol IJKMediaPlayback;
#pragma mark IJKMediaPlayback
@protocol IJKMediaPlayback <NSObject>
- (void)prepareToPlay;
- (void)play;
- (void)pause;
- (void)stop;
- (BOOL)isPlaying;
- (void)shutdown;
- (void)setPauseInBackground:(BOOL)pause;
@property(nonatomic, readonly) UIView *view;
@property(nonatomic) NSTimeInterval currentPlaybackTime;
@property(nonatomic, readonly) NSTimeInterval duration;
@property(nonatomic, readonly) NSTimeInterval playableDuration;
@property(nonatomic, readonly) NSInteger bufferingProgress;
@property(nonatomic, readonly) BOOL isPreparedToPlay;
@property(nonatomic, readonly) IJKMPMoviePlaybackState playbackState;
@property(nonatomic, readonly) IJKMPMovieLoadState loadState;
@property(nonatomic, readonly) int isSeekBuffering;
@property(nonatomic, readonly) int isAudioSync;
@property(nonatomic, readonly) int isVideoSync;
@property(nonatomic, readonly) int64_t numberOfBytesTransferred;
@property(nonatomic, readonly) CGSize naturalSize;
@property(nonatomic) IJKMPMovieScalingMode scalingMode;
@property(nonatomic) BOOL shouldAutoplay;
@property (nonatomic) BOOL allowsMediaAirPlay;
@property (nonatomic) BOOL isDanmakuMediaAirPlay;
@property (nonatomic, readonly) BOOL airPlayMediaActive;
@property (nonatomic) float playbackRate;
@property (nonatomic) float playbackVolume;
- (UIImage *)thumbnailImageAtCurrentTime;
#pragma mark Notifications
#ifdef __cplusplus
#define IJK_EXTERN extern "C" __attribute__((visibility ("default")))
#else
#define IJK_EXTERN extern __attribute__((visibility ("default")))
#endif
// -----------------------------------------------------------------------------
// MPMediaPlayback.h
// Posted when the prepared state changes of an object conforming to the MPMediaPlayback protocol changes.
// This supersedes MPMoviePlayerContentPreloadDidFinishNotification.
IJK_EXTERN NSString *const IJKMPMediaPlaybackIsPreparedToPlayDidChangeNotification;
// -----------------------------------------------------------------------------
// MPMoviePlayerController.h
// Movie Player Notifications
// Posted when the scaling mode changes.
IJK_EXTERN NSString* const IJKMPMoviePlayerScalingModeDidChangeNotification;
// Posted when movie playback ends or a user exits playback.
IJK_EXTERN NSString* const IJKMPMoviePlayerPlaybackDidFinishNotification;
IJK_EXTERN NSString* const IJKMPMoviePlayerPlaybackDidFinishReasonUserInfoKey; // NSNumber (IJKMPMovieFinishReason)
// Posted when the playback state changes, either programatically or by the user.
IJK_EXTERN NSString* const IJKMPMoviePlayerPlaybackStateDidChangeNotification;
// Posted when the network load state changes.
IJK_EXTERN NSString* const IJKMPMoviePlayerLoadStateDidChangeNotification;
// Posted when the movie player begins or ends playing video via AirPlay.
IJK_EXTERN NSString* const IJKMPMoviePlayerIsAirPlayVideoActiveDidChangeNotification;
// -----------------------------------------------------------------------------
// Movie Property Notifications
// Calling -prepareToPlay on the movie player will begin determining movie properties asynchronously.
// These notifications are posted when the associated movie property becomes available.
IJK_EXTERN NSString* const IJKMPMovieNaturalSizeAvailableNotification;
// -----------------------------------------------------------------------------
// Extend Notifications
IJK_EXTERN NSString *const IJKMPMoviePlayerVideoDecoderOpenNotification;
IJK_EXTERN NSString *const IJKMPMoviePlayerFirstVideoFrameRenderedNotification;
IJK_EXTERN NSString *const IJKMPMoviePlayerFirstAudioFrameRenderedNotification;
IJK_EXTERN NSString *const IJKMPMoviePlayerFirstAudioFrameDecodedNotification;
IJK_EXTERN NSString *const IJKMPMoviePlayerFirstVideoFrameDecodedNotification;
IJK_EXTERN NSString *const IJKMPMoviePlayerOpenInputNotification;
IJK_EXTERN NSString *const IJKMPMoviePlayerFindStreamInfoNotification;
IJK_EXTERN NSString *const IJKMPMoviePlayerComponentOpenNotification;
IJK_EXTERN NSString *const IJKMPMoviePlayerDidSeekCompleteNotification;
IJK_EXTERN NSString *const IJKMPMoviePlayerDidSeekCompleteTargetKey;
IJK_EXTERN NSString *const IJKMPMoviePlayerDidSeekCompleteErrorKey;
IJK_EXTERN NSString *const IJKMPMoviePlayerDidAccurateSeekCompleteCurPos;
IJK_EXTERN NSString *const IJKMPMoviePlayerAccurateSeekCompleteNotification;
IJK_EXTERN NSString *const IJKMPMoviePlayerSeekAudioStartNotification;
IJK_EXTERN NSString *const IJKMPMoviePlayerSeekVideoStartNotification;
@end
#pragma mark IJKMediaUrlOpenDelegate
// Must equal to the defination in ijkavformat/ijkavformat.h
typedef NS_ENUM(NSInteger, IJKMediaEvent) {
// Notify Events
IJKMediaEvent_WillHttpOpen = 1, // attr: url
IJKMediaEvent_DidHttpOpen = 2, // attr: url, error, http_code
IJKMediaEvent_WillHttpSeek = 3, // attr: url, offset
IJKMediaEvent_DidHttpSeek = 4, // attr: url, offset, error, http_code
// Control Message
IJKMediaCtrl_WillTcpOpen = 0x20001, // IJKMediaUrlOpenData: no args
IJKMediaCtrl_DidTcpOpen = 0x20002, // IJKMediaUrlOpenData: error, family, ip, port, fd
IJKMediaCtrl_WillHttpOpen = 0x20003, // IJKMediaUrlOpenData: url, segmentIndex, retryCounter
IJKMediaCtrl_WillLiveOpen = 0x20005, // IJKMediaUrlOpenData: url, retryCounter
IJKMediaCtrl_WillConcatSegmentOpen = 0x20007, // IJKMediaUrlOpenData: url, segmentIndex, retryCounter
};
#define IJKMediaEventAttrKey_url @"url"
#define IJKMediaEventAttrKey_host @"host"
#define IJKMediaEventAttrKey_error @"error"
#define IJKMediaEventAttrKey_time_of_event @"time_of_event"
#define IJKMediaEventAttrKey_http_code @"http_code"
#define IJKMediaEventAttrKey_offset @"offset"
#define IJKMediaEventAttrKey_file_size @"file_size"
// event of IJKMediaUrlOpenEvent_xxx
@interface IJKMediaUrlOpenData: NSObject
- (id)initWithUrl:(NSString *)url
event:(IJKMediaEvent)event
segmentIndex:(int)segmentIndex
retryCounter:(int)retryCounter;
@property(nonatomic, readonly) IJKMediaEvent event;
@property(nonatomic, readonly) int segmentIndex;
@property(nonatomic, readonly) int retryCounter;
@property(nonatomic, retain) NSString *url;
@property(nonatomic, assign) int fd;
@property(nonatomic, strong) NSString *msg;
@property(nonatomic) int error; // set a negative value to indicate an error has occured.
@property(nonatomic, getter=isHandled) BOOL handled; // auto set to YES if url changed
@property(nonatomic, getter=isUrlChanged) BOOL urlChanged; // auto set to YES by url changed
@end
@protocol IJKMediaUrlOpenDelegate <NSObject>
- (void)willOpenUrl:(IJKMediaUrlOpenData*) urlOpenData;
@end
@protocol IJKMediaNativeInvokeDelegate <NSObject>
- (int)invoke:(IJKMediaEvent)event attributes:(NSDictionary *)attributes;
@end
@@ -0,0 +1,32 @@
/*
* IJKMediaPlayer.h
*
* Copyright (c) 2013 Bilibili
* Copyright (c) 2013 Zhang Rui <bbcallen@gmail.com>
*
* This file is part of ijkPlayer.
*
* ijkPlayer is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* ijkPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with ijkPlayer; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import "IJKMediaPlayback.h"
#import "IJKMPMoviePlayerController.h"
#import "IJKFFOptions.h"
#import "IJKFFMoviePlayerController.h"
#import "IJKAVMoviePlayerController.h"
#import "IJKMediaModule.h"
@@ -0,0 +1,37 @@
/*
* IJKNotificationManager.h
*
* Copyright (c) 2016 Bilibili
* Copyright (c) 2016 Zhang Rui <bbcallen@gmail.com>
*
* This file is part of ijkPlayer.
*
* ijkPlayer is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* ijkPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with ijkPlayer; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import <Foundation/Foundation.h>
@interface IJKNotificationManager : NSObject
- (nullable instancetype)init;
- (void)addObserver:(nonnull id)observer
selector:(nonnull SEL)aSelector
name:(nullable NSString *)aName
object:(nullable id)anObject;
- (void)removeAllObservers:(nonnull id)observer;
@end
@@ -0,0 +1,50 @@
/*
* IJKSDLGLViewProtocol.h
*
* Copyright (c) 2017 Bilibili
* Copyright (c) 2017 raymond <raymondzheng1412@gmail.com>
*
* This file is part of ijkPlayer.
*
* ijkPlayer is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* ijkPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with ijkPlayer; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef IJKSDLGLViewProtocol_h
#define IJKSDLGLViewProtocol_h
#import <UIKit/UIKit.h>
typedef struct IJKOverlay IJKOverlay;
struct IJKOverlay {
int w;
int h;
UInt32 format;
int planes;
UInt16 *pitches;
UInt8 **pixels;
int sar_num;
int sar_den;
CVPixelBufferRef pixel_buffer;
};
@protocol IJKSDLGLViewProtocol <NSObject>
- (UIImage*) snapshot;
@property(nonatomic, readonly) CGFloat fps;
@property(nonatomic) CGFloat scaleFactor;
@property(nonatomic) BOOL isThirdGLView;
- (void) display_pixels: (IJKOverlay *) overlay;
@end
#endif /* IJKSDLGLViewProtocol_h */
@@ -0,0 +1,6 @@
framework module IJKMediaFramework {
umbrella header "IJKMediaFramework.h"
export *
module * { export * }
}
@@ -0,0 +1,45 @@
//
// ZFIJKPlayerManager.h
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#if __has_include(<ZFPlayer/ZFPlayerMediaPlayback.h>)
#import <ZFPlayer/ZFPlayerMediaPlayback.h>
#else
#import "ZFPlayerMediaPlayback.h"
#endif
#if __has_include(<IJKMediaFramework/IJKMediaFramework.h>)
#import <IJKMediaFramework/IJKMediaFramework.h>
@interface ZFIJKPlayerManager : NSObject <ZFPlayerMediaPlayback>
@property (nonatomic, strong, readonly) IJKFFMoviePlayerController *player;
@property (nonatomic, strong, readonly) IJKFFOptions *options;
@property (nonatomic, assign) NSTimeInterval timeRefreshInterval;
@end
#endif
@@ -0,0 +1,458 @@
//
// ZFIJKPlayerManager.m
// ZFPlayer
//
// Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "ZFIJKPlayerManager.h"
#if __has_include(<ZFPlayer/ZFPlayer.h>)
#import <ZFPlayer/ZFPlayer.h>
#import <ZFPlayer/ZFPlayerConst.h>
#else
#import "ZFPlayer.h"
#import "ZFPlayerConst.h"
#endif
#if __has_include(<IJKMediaFramework/IJKMediaFramework.h>)
@interface ZFIJKPlayerManager ()
@property (nonatomic, strong) IJKFFMoviePlayerController *player;
@property (nonatomic, strong) IJKFFOptions *options;
@property (nonatomic, assign) CGFloat lastVolume;
@property (nonatomic, strong) NSTimer *timer;
@property (nonatomic, assign) BOOL isReadyToPlay;
@end
@implementation ZFIJKPlayerManager
@synthesize view = _view;
@synthesize currentTime = _currentTime;
@synthesize totalTime = _totalTime;
@synthesize playerPlayTimeChanged = _playerPlayTimeChanged;
@synthesize playerBufferTimeChanged = _playerBufferTimeChanged;
@synthesize playerDidToEnd = _playerDidToEnd;
@synthesize bufferTime = _bufferTime;
@synthesize playState = _playState;
@synthesize loadState = _loadState;
@synthesize assetURL = _assetURL;
@synthesize playerPrepareToPlay = _playerPrepareToPlay;
@synthesize playerReadyToPlay = _playerReadyToPlay;
@synthesize playerPlayStateChanged = _playerPlayStateChanged;
@synthesize playerLoadStateChanged = _playerLoadStateChanged;
@synthesize seekTime = _seekTime;
@synthesize muted = _muted;
@synthesize volume = _volume;
@synthesize presentationSize = _presentationSize;
@synthesize isPlaying = _isPlaying;
@synthesize rate = _rate;
@synthesize isPreparedToPlay = _isPreparedToPlay;
@synthesize shouldAutoPlay = _shouldAutoPlay;
@synthesize scalingMode = _scalingMode;
@synthesize playerPlayFailed = _playerPlayFailed;
@synthesize presentationSizeChanged = _presentationSizeChanged;
- (void)dealloc {
[self stop];
}
- (instancetype)init {
self = [super init];
if (self) {
_scalingMode = ZFPlayerScalingModeAspectFit;
_shouldAutoPlay = YES;
}
return self;
}
- (void)prepareToPlay {
if (!_assetURL) return;
_isPreparedToPlay = YES;
[self initializePlayer];
if (self.shouldAutoPlay) {
[self play];
}
self.loadState = ZFPlayerLoadStatePrepare;
if (self.playerPrepareToPlay) self.playerPrepareToPlay(self, self.assetURL);
}
- (void)reloadPlayer {
[self prepareToPlay];
}
- (void)play {
if (!_isPreparedToPlay) {
[self prepareToPlay];
} else {
[self.player play];
if (self.timer) [self.timer setFireDate:[NSDate date]];
self.player.playbackRate = self.rate;
_isPlaying = YES;
self.playState = ZFPlayerPlayStatePlaying;
}
}
- (void)pause {
if (self.timer) [self.timer setFireDate:[NSDate distantFuture]];
[self.player pause];
_isPlaying = NO;
self.playState = ZFPlayerPlayStatePaused;
}
- (void)stop {
[self removeMovieNotificationObservers];
[self.player shutdown];
[self.player.view removeFromSuperview];
self.player = nil;
_assetURL = nil;
[self.timer invalidate];
self.presentationSize = CGSizeZero;
self.timer = nil;
_isPlaying = NO;
_isPreparedToPlay = NO;
self->_currentTime = 0;
self->_totalTime = 0;
self->_bufferTime = 0;
self.isReadyToPlay = NO;
self.playState = ZFPlayerPlayStatePlayStopped;
}
- (void)replay {
@zf_weakify(self)
[self seekToTime:0 completionHandler:^(BOOL finished) {
@zf_strongify(self)
if (finished) {
[self play];
}
}];
}
- (void)seekToTime:(NSTimeInterval)time completionHandler:(void (^ __nullable)(BOOL finished))completionHandler {
if (self.player.duration > 0) {
self.player.currentPlaybackTime = time;
if (completionHandler) completionHandler(YES);
} else {
self.seekTime = time;
}
}
- (UIImage *)thumbnailImageAtCurrentTime {
return [self.player thumbnailImageAtCurrentTime];
}
#pragma mark - private method
- (void)initializePlayer {
// IJKFFMoviePlayerController 初始化后,必须手动进行释放,否则会依然存在内存中对资源进行播放。
if (self.player) {
[self removeMovieNotificationObservers];
[self.player shutdown];
[self.player.view removeFromSuperview];
self.player = nil;
}
self.player = [[IJKFFMoviePlayerController alloc] initWithContentURL:self.assetURL withOptions:self.options];
self.player.shouldAutoplay = self.shouldAutoPlay;
[self.player prepareToPlay];
self.view.playerView = self.player.view;
self.scalingMode = self->_scalingMode;
[self addPlayerNotificationObservers];
}
- (void)addPlayerNotificationObservers {
/// 准备播放
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(loadStateDidChange:)
name:IJKMPMoviePlayerLoadStateDidChangeNotification
object:_player];
/// 播放完成或者用户退出
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(moviePlayBackFinish:)
name:IJKMPMoviePlayerPlaybackDidFinishNotification
object:_player];
/// 准备开始播放了
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(mediaIsPreparedToPlayDidChange:)
name:IJKMPMediaPlaybackIsPreparedToPlayDidChangeNotification
object:_player];
/// 播放状态改变了
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(moviePlayBackStateDidChange:)
name:IJKMPMoviePlayerPlaybackStateDidChangeNotification
object:_player];
/// 视频的尺寸变化了
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(sizeAvailableChange:)
name:IJKMPMovieNaturalSizeAvailableNotification
object:self.player];
}
- (void)removeMovieNotificationObservers {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:IJKMPMoviePlayerLoadStateDidChangeNotification
object:_player];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:IJKMPMoviePlayerPlaybackDidFinishNotification
object:_player];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:IJKMPMediaPlaybackIsPreparedToPlayDidChangeNotification
object:_player];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:IJKMPMoviePlayerPlaybackStateDidChangeNotification
object:_player];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:IJKMPMovieNaturalSizeAvailableNotification
object:_player];
}
- (void)timerUpdate {
if (self.player.currentPlaybackTime > 0 && !self.isReadyToPlay) {
self.isReadyToPlay = YES;
self.loadState = ZFPlayerLoadStatePlaythroughOK;
}
self->_currentTime = self.player.currentPlaybackTime > 0 ? self.player.currentPlaybackTime : 0;
self->_totalTime = self.player.duration;
self->_bufferTime = self.player.playableDuration;
if (self.playerPlayTimeChanged) self.playerPlayTimeChanged(self, self.currentTime, self.totalTime);
if (self.playerBufferTimeChanged) self.playerBufferTimeChanged(self, self.bufferTime);
}
#pragma - notification
/// 播放完成
- (void)moviePlayBackFinish:(NSNotification *)notification {
int reason = [[[notification userInfo] valueForKey:IJKMPMoviePlayerPlaybackDidFinishReasonUserInfoKey] intValue];
switch (reason) {
case IJKMPMovieFinishReasonPlaybackEnded: {
ZFPlayerLog(@"playbackStateDidChange: 播放完毕: %d\n", reason);
self.playState = ZFPlayerPlayStatePlayStopped;
if (self.playerDidToEnd) self.playerDidToEnd(self);
}
break;
case IJKMPMovieFinishReasonUserExited: {
ZFPlayerLog(@"playbackStateDidChange: 用户退出播放: %d\n", reason);
}
break;
case IJKMPMovieFinishReasonPlaybackError: {
ZFPlayerLog(@"playbackStateDidChange: 播放出现错误: %d\n", reason);
self.playState = ZFPlayerPlayStatePlayFailed;
if (self.playerPlayFailed) self.playerPlayFailed(self, @(reason));
}
break;
default:
ZFPlayerLog(@"playbackPlayBackDidFinish: ???: %d\n", reason);
break;
}
}
// 准备开始播放了
- (void)mediaIsPreparedToPlayDidChange:(NSNotification *)notification {
ZFPlayerLog(@"加载状态变成了已经缓存完成,如果设置了自动播放, 会自动播放");
// 视频开始播放的时候开启计时器
if (!self.timer) {
self.timer = [NSTimer scheduledTimerWithTimeInterval:self.timeRefreshInterval > 0 ? self.timeRefreshInterval : 0.1 target:self selector:@selector(timerUpdate) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
if (self.isPlaying) {
[self play];
self.muted = self.muted;
if (self.seekTime > 0) {
[self seekToTime:self.seekTime completionHandler:nil];
self.seekTime = 0; // 滞空, 防止下次播放出错
[self play];
}
}
if (self.playerReadyToPlay) self.playerReadyToPlay(self, self.assetURL);
}
#pragma mark - 加载状态改变
/**
视频加载状态改变了
IJKMPMovieLoadStateUnknown == 0
IJKMPMovieLoadStatePlayable == 1
IJKMPMovieLoadStatePlaythroughOK == 2
IJKMPMovieLoadStateStalled == 4
*/
- (void)loadStateDidChange:(NSNotification*)notification {
IJKMPMovieLoadState loadState = self.player.loadState;
if ((loadState & IJKMPMovieLoadStatePlayable)) {
ZFPlayerLog(@"加载状态变成了缓存数据足够开始播放,但是视频并没有缓存完全");
if (self.player.currentPlaybackTime > 0) {
self.loadState = ZFPlayerLoadStatePlayable;
}
} else if ((loadState & IJKMPMovieLoadStatePlaythroughOK)) {
// 加载完成,即将播放,停止加载的动画,并将其移除
ZFPlayerLog(@"加载状态变成了已经缓存完成,如果设置了自动播放, 会自动播放");
} else if ((loadState & IJKMPMovieLoadStateStalled)) {
// 可能由于网速不好等因素导致了暂停,重新添加加载的动画
ZFPlayerLog(@"网速不好等因素导致了暂停");
self.loadState = ZFPlayerLoadStateStalled;
} else {
ZFPlayerLog(@"加载状态变成了未知状态");
self.loadState = ZFPlayerLoadStateUnknown;
}
}
// 播放状态改变
- (void)moviePlayBackStateDidChange:(NSNotification *)notification {
switch (self.player.playbackState) {
case IJKMPMoviePlaybackStateStopped: {
ZFPlayerLog(@"播放器的播放状态变了,现在是停止状态 %d: stoped", (int)_player.playbackState);
// 这里的回调也会来多次(一次播放完成, 会回调三次), 所以, 这里不设置
self.playState = ZFPlayerPlayStatePlayStopped;
}
break;
case IJKMPMoviePlaybackStatePlaying: {
ZFPlayerLog(@"播放器的播放状态变了,现在是播放状态 %d: playing", (int)_player.playbackState);
}
break;
case IJKMPMoviePlaybackStatePaused: {
ZFPlayerLog(@"播放器的播放状态变了,现在是暂停状态 %d: paused", (int)_player.playbackState);
}
break;
case IJKMPMoviePlaybackStateInterrupted: {
ZFPlayerLog(@"播放器的播放状态变了,现在是中断状态 %d: interrupted", (int)_player.playbackState);
}
break;
case IJKMPMoviePlaybackStateSeekingForward: {
ZFPlayerLog(@"播放器的播放状态变了,现在是向前拖动状态:%d forward",(int)self.player.playbackState);
}
break;
case IJKMPMoviePlaybackStateSeekingBackward: {
ZFPlayerLog(@"放器的播放状态变了,现在是向后拖动状态 %d: backward", (int)_player.playbackState);
}
break;
default: {
ZFPlayerLog(@"播放器的播放状态变了,现在是未知状态 %d: unknown", (int)_player.playbackState);
}
break;
}
}
/// 视频的尺寸变化了
- (void)sizeAvailableChange:(NSNotification *)notify {
self.presentationSize = self.player.naturalSize;
self.view.presentationSize = self.presentationSize;
if (self.presentationSizeChanged) {
self.presentationSizeChanged(self, self->_presentationSize);
}
}
#pragma mark - getter
- (ZFPlayerView *)view {
if (!_view) {
_view = [[ZFPlayerView alloc] init];
}
return _view;
}
- (float)rate {
return _rate == 0 ?1:_rate;
}
- (IJKFFOptions *)options {
if (!_options) {
_options = [IJKFFOptions optionsByDefault];
/// 精准seek
[_options setPlayerOptionIntValue:1 forKey:@"enable-accurate-seek"];
/// 解决http播放不了
[_options setOptionIntValue:1 forKey:@"dns_cache_clear" ofCategory:kIJKFFOptionCategoryFormat];
}
return _options;
}
#pragma mark - setter
- (void)setPlayState:(ZFPlayerPlaybackState)playState {
_playState = playState;
if (self.playerPlayStateChanged) self.playerPlayStateChanged(self, playState);
}
- (void)setLoadState:(ZFPlayerLoadState)loadState {
_loadState = loadState;
if (self.playerLoadStateChanged) self.playerLoadStateChanged(self, loadState);
}
- (void)setAssetURL:(NSURL *)assetURL {
if (self.player) [self stop];
_assetURL = assetURL;
[self prepareToPlay];
}
- (void)setRate:(float)rate {
_rate = rate;
if (self.player && fabsf(_player.playbackRate) > 0.00001f) {
self.player.playbackRate = rate;
}
}
- (void)setMuted:(BOOL)muted {
_muted = muted;
if (muted) {
self.lastVolume = self.player.playbackVolume;
self.player.playbackVolume = 0;
} else {
/// Fix first called the lastVolume is 0.
if (self.lastVolume == 0) self.lastVolume = self.player.playbackVolume;
self.player.playbackVolume = self.lastVolume;
}
}
- (void)setScalingMode:(ZFPlayerScalingMode)scalingMode {
_scalingMode = scalingMode;
self.view.scalingMode = scalingMode;
switch (scalingMode) {
case ZFPlayerScalingModeNone:
self.player.scalingMode = IJKMPMovieScalingModeNone;
break;
case ZFPlayerScalingModeAspectFit:
self.player.scalingMode = IJKMPMovieScalingModeAspectFit;
break;
case ZFPlayerScalingModeAspectFill:
self.player.scalingMode = IJKMPMovieScalingModeAspectFill;
break;
case ZFPlayerScalingModeFill:
self.player.scalingMode = IJKMPMovieScalingModeFill;
break;
default:
break;
}
}
- (void)setVolume:(float)volume {
_volume = MIN(MAX(0, volume), 1);
self.player.playbackVolume = volume;
}
@end
#endif