基于AVPlayer.git
极速初始化, 不阻塞主线程.github
这个应该是目前基于AVPlayer的播放器中, 功能最全的一个吧.
_缓存
pod 'SJVideoPlayer'
_ruby
_网络
只需pod到项目中便可带全屏手势session
在使用原生手势返回时, 当前播放的视频会出现卡帧的问题. 缘由我不太清楚. 我查看了腾讯视频和爱奇艺等App均为本身实现的手势返回. app
当时使用了一个全屏返回的三方库. 是截屏返回的, 发现存在两个问题: 一是截屏耗时, 二是视频部分截取不了(黑色的)
因为咱们起步8.0, 我便使用了新的APIsnapshotViewAfterScreenUpdates:
(这个接口其实7.0就有了)来替换截屏的方式, 直接使用视图快照, 这个接口一箭双雕, 不只速度快, 并且视频播放也能够截取到.
可是快照是一个view, 并非image, 原来的三方库截屏方式没法使用, 因而我从新撸了一个全屏手势.ide
pod 'SJFullscreenPopGesture'
// default is `SJFullscreenPopGestureType_EdgeLeft`. typedef NS_ENUM(NSUInteger, SJFullscreenPopGestureType) { SJFullscreenPopGestureType_EdgeLeft, // 默认, 屏幕左边缘触发手势 SJFullscreenPopGestureType_Full, // 全屏触发手势 };
目前有两种:post
- [相似腾讯视频返回.gif](https://upload-images.jianshu.io/upload_images/2318691-d5a992c40cfee5bb.gif?imageMogr2/auto-orient/strip) - [在1的基础上加上了一层遮罩.gif](https://upload-images.jianshu.io/upload_images/2318691-3dcd02f47b0dff4a.gif?imageMogr2/auto-orient/strip)
/// 将要拖拽 @property (nonatomic, copy, readwrite, nullable) void(^sj_viewWillBeginDragging)(__kindof UIViewController *vc); /// 拖拽中 @property (nonatomic, copy, readwrite, nullable) void(^sj_viewDidDrag)(__kindof UIViewController *vc); /// 结束拖拽 @property (nonatomic, copy, readwrite, nullable) void(^sj_viewDidEndDragging)(__kindof UIViewController *vc);
等 ...
_字体
网络状态提示就是当网络状态变动时, 播放器显示一行字或者图片等, 提示用户网络变动了.
对于默认提示的内容(可自定义), 我作了本地化处理, 根据 localLanguage 自动选择一种语言提示(中文/繁体/英文), 以下:
关于旋转我想吐槽一下: 大部分三方库的旋转是直接把播放器添加到window的中心, 再作一个transform动画.. .
旋转过程当中, 界面会闪一下, 这个体验几回眼就会累... 为了解决这个问题, 我又写了一个旋转的库在添加以前, 我作了相应的坐标转换, 使播放器添加到window上时, 还处于原始位置. 而后去作transform动画并更新bounds和center.
播放器除了自动旋转以外, 您能够随意的控制旋转. 支持的方向以下:
/// 竖屏 || (左右)横屏 /// Auto rotate supported orientation typedef NS_ENUM(NSUInteger, SJAutoRotateSupportedOrientation) { SJAutoRotateSupportedOrientation_All, SJAutoRotateSupportedOrientation_Portrait = 1 << 0, SJAutoRotateSupportedOrientation_LandscapeLeft = 1 << 1, // UIDeviceOrientationLandscapeLeft SJAutoRotateSupportedOrientation_LandscapeRight = 1 << 2, // UIDeviceOrientationLandscapeRight };
关于旋转的一些方法:
/** Autorotation. Animated. */ - (void)rotate; /** Rotate to the specified orientation. @param orientation Any value of SJOrientation. @param animated Whether or not animation. */ - (void)rotate:(SJOrientation)orientation animated:(BOOL)animated; /** Rotate to the specified orientation. @param orientation Any value of SJOrientation. @param animated Whether or not animation. @param block The block invoked when player rotated. */ - (void)rotate:(SJOrientation)orientation animated:(BOOL)animated completion:(void (^ _Nullable)(__kindof SJBaseVideoPlayer *player))block;
旋转的一些设置
/** The block invoked When player will rotate. readwrite. */ @property (nonatomic, copy, nullable) void(^willRotateScreen)(__kindof SJBaseVideoPlayer *player, BOOL isFullScreen); /** The block invoked when player rotated. readwrite. */ @property (nonatomic, copy, nullable) void(^rotatedScreen)(__kindof SJBaseVideoPlayer *player, BOOL isFullScreen);
这几个功能都已封装在默认的控制层中, 而且相应的提示文本也作了本地化处理
方法以下:
- (UIImage * __nullable)screenshot; - (void)screenshotWithTime:(NSTimeInterval)time completion:(void(^)(__kindof SJBaseVideoPlayer *videoPlayer, UIImage * __nullable image, NSError *__nullable error))block; - (void)screenshotWithTime:(NSTimeInterval)time size:(CGSize)size completion:(void(^)(__kindof SJBaseVideoPlayer *videoPlayer, UIImage * __nullable image, NSError *__nullable error))block; /** export session. @param beginTime unit is sec. @param endTime unit is sec. @param presetName default is `AVAssetExportPresetMediumQuality`. @param progressBlock progressBlock @param completion completion @param failure failure */ - (void)exportWithBeginTime:(NSTimeInterval)beginTime endTime:(NSTimeInterval)endTime presetName:(nullable NSString *)presetName progress:(void(^)(__kindof SJBaseVideoPlayer *videoPlayer, float progress))progressBlock completion:(void(^)(__kindof SJBaseVideoPlayer *videoPlayer, NSURL *fileURL, UIImage *thumbnailImage))completion failure:(void(^)(__kindof SJBaseVideoPlayer *videoPlayer, NSError *error))failure; - (void)cancelExportOperation; - (void)generateGIFWithBeginTime:(NSTimeInterval)beginTime duration:(NSTimeInterval)duration progress:(void(^)(__kindof SJBaseVideoPlayer *videoPlayer, float progress))progressBlock completion:(void(^)(__kindof SJBaseVideoPlayer *videoPlayer, UIImage *imageGIF, UIImage *thumbnailImage, NSURL *filePath))completion failure:(void(^)(__kindof SJBaseVideoPlayer *videoPlayer, NSError *error))failure; - (void)cancelGenerateGIFOperation;
_
目前提示文本支持 NSString(可自定义字体) 以及 NSAttributedString. 正如前面看到的网路状态提示, 就是使用的这些方法, 方法以下:
/** The middle of the player view shows the specified title. duration default is 1.0. @param title prompt. */ - (void)showTitle:(NSString *)title; /** The middle of the view shows the specified title. @param title prompt. @param duration prompt duration. duration if value set -1, prompt will always show. */ - (void)showTitle:(NSString *)title duration:(NSTimeInterval)duration; - (void)showTitle:(NSString *)title duration:(NSTimeInterval)duration hiddenExeBlock:(void(^__nullable)(__kindof SJBaseVideoPlayer *player))hiddenExeBlock; - (void)showAttributedString:(NSAttributedString *)attributedString duration:(NSTimeInterval)duration; - (void)showAttributedString:(NSAttributedString *)attributedString duration:(NSTimeInterval)duration hiddenExeBlock:(void(^__nullable)(__kindof SJBaseVideoPlayer *player))hiddenExeBlock; /** Hidden Prompt. */ - (void)hiddenTitle;
_
默认会存在四种手势: Single Tap, double Tap, Pan, Pinch.
单击手势
当用户单击播放器时, 播放器会调用显示或隐藏控制层的相关代理方法. 见 controlLayerDelegate
controlLayerDelegate
Aspect
或AspectFill
._
下面介绍如何自定义一个控制层, 相关的方法后面, 我都会跟上一个相应实现.
@protocol SJVideoPlayerControlLayerDataSource <NSObject> @required - (UIView *)controlView; - (BOOL)controlLayerDisappearCondition; - (BOOL)triggerGesturesCondition:(CGPoint)location; @optional - (void)installedControlViewToVideoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer; @end
- (UIView *)controlView;
请返回控制层的根视图, 这个视图将会添加的播放器视图中. - (BOOL)controlLayerDisappearCondition;
当控制层显示时, 播放器会在一段时间(默认3秒)后尝试隐藏控制层, 此时会调用该方法, 若是返回YES, 则隐藏控制层, 否之. - (BOOL)triggerGesturesCondition:(CGPoint)location;
播放器中的手势 触发手势以前会调用这个方法, 若是返回NO, 将不会触发手势. @required /**Objective-C This method will be called when the control layer needs to be appear. You should do some appear work here. */ - (void)controlLayerNeedAppear:(__kindof SJBaseVideoPlayer *)videoPlayer; /** This method will be called when the control layer needs to be disappear. You should do some disappear work here. */ - (void)controlLayerNeedDisappear:(__kindof SJBaseVideoPlayer *)videoPlayer;
播放器会接管控制层的显示与隐藏, 也就是当SingleTap时播放器会调用controlLayerNeedAppear:
播放器会在一段时间后调用隐藏controlLayerNeedDisappear:
.
controlLayerNeedAppear:
控制层须要显示, 代理能够在这个方法里面实现显示控制层的UI controlLayerNeedDisappear:
控制层须要隐藏, 同上, 作一些隐藏工做 - (void)videoPlayerWillAppearInScrollView:(__kindof SJBaseVideoPlayer *)videoPlayer; - (void)videoPlayerWillDisappearInScrollView:(__kindof SJBaseVideoPlayer *)videoPlayer;
videoPlayerWillAppearInScrollView:
播放器在滚动视图中将要显示时, 会调用它 videoPlayerWillDisappearInScrollView:
播放器在滚动视图中将要消失时, 会调用它 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer prepareToPlay:(SJVideoPlayerURLAsset *)asset; - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer stateChanged:(SJVideoPlayerPlayState)state; - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer currentTime:(NSTimeInterval)currentTime currentTimeStr:(NSString *)currentTimeStr totalTime:(NSTimeInterval)totalTime totalTimeStr:(NSString *)totalTimeStr;
videoPlayer:prepareToPlay:
当播放器播放一个新的资源时, 会调用这个方法 videoPlayer:stateChanged:
当播放状态变动时, 会调用这个方法 第三个方法
播放时的回调, 当前时间及视频的所有时长. 单位都是秒 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer loadedTimeProgress:(float)progress; - (void)startLoading:(__kindof SJBaseVideoPlayer *)videoPlayer; - (void)cancelLoading:(__kindof SJBaseVideoPlayer *)videoPlayer; - (void)loadCompletion:(__kindof SJBaseVideoPlayer *)videoPlayer;
videoPlayer:loadedTimeProgress:
缓冲进度的回调 startLoading:
开始缓冲的回调 cancelLoading:
取消缓冲的回调 loadCompletion:
缓冲完成的回调 - (void)lockedVideoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer; - (void)unlockedVideoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer; - (void)tappedPlayerOnTheLockedState:(__kindof SJBaseVideoPlayer *)videoPlayer;
lockedVideoPlayer:
当设置videoPlayer.lockedScreen = YES;
时, 会回调这个方法 unlockedVideoPlayer:
当设置videoPlayer.lockedScreen = NO;
时, 会回调这个方法 tappedPlayerOnTheLockedState:
当在锁屏状态下时, 单击播放器, 会回调这个方法 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer willRotateView:(BOOL)isFull; - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer didEndRotation:(BOOL)isFull;
videoPlayer:willRotateView:
当播放器 将要旋转时 会回调这个方法 videoPlayer:didEndRotation:
当播放器 旋转完成时 会回调这个方法 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer muteChanged:(BOOL)mute; - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer volumeChanged:(float)volume; - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer brightnessChanged:(float)brightness; - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer rateChanged:(float)rate;
videoPlayer:muteChanged:
设置静音时的回调 videoPlayer:volumeChanged:
声音改变的回调 videoPlayer:brightnessChanged:
亮度改变的回调 videoPlayer:rateChanged:
速率改变的回调 /// 水平方向开始拖动. - (void)horizontalDirectionWillBeginDragging:(__kindof SJBaseVideoPlayer *)videoPlayer; /** @param progress drag progress */ - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer horizontalDirectionDidMove:(CGFloat)progress; /// 水平方向拖动结束. - (void)horizontalDirectionDidEndDragging:(__kindof SJBaseVideoPlayer *)videoPlayer;
horizontalDirectionWillBeginDragging:
水平方向开始拖动时的回调 videoPlayer:horizontalDirectionDidMove:
水平方向拖动的进度的回调 horizontalDirectionDidEndDragging:
水平方向拖动结束的回调 /// 网络状态变动 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer reachabilityChanged:(SJNetworkStatus)status;
videoPlayer:reachabilityChanged:
当网络状态变动时, 会回调这个方法 项目地址: https://github.com/changsanji...
个人邮箱: changsanjiang@gmail.com
若是您有什么建议, 望请联系我!