基於AVPlayer.git
極速初始化, 不阻塞主線程.github
這個應該是目前基於AVPlayer的播放器中, 功能最全的一個吧.緩存
pod 'SJVideoPlayer' 複製代碼
只需pod到項目中便可帶全屏手勢ruby
在使用原生手勢返回時, 當前播放的視頻會出現卡幀的問題. 緣由我不太清楚. 我查看了騰訊視頻和愛奇藝等App均爲本身實現的手勢返回.bash
當時使用了一個全屏返回的三方庫. 是截屏返回的, 發現存在兩個問題: 一是截屏耗時, 二是視頻部分截取不了(黑色的) 因爲咱們起步8.0, 我便使用了新的APIsnapshotViewAfterScreenUpdates:
(這個接口其實7.0就有了)來替換截屏的方式, 直接使用視圖快照, 這個接口一箭雙鵰, 不只速度快, 並且視頻播放也能夠截取到. 可是快照是一個view, 並非image, 原來的三方庫截屏方式沒法使用, 因而我從新擼了一個全屏手勢.markdown
pod 'SJFullscreenPopGesture' 複製代碼
// default is `SJFullscreenPopGestureType_EdgeLeft`.
typedef NS_ENUM(NSUInteger, SJFullscreenPopGestureType) {
SJFullscreenPopGestureType_EdgeLeft, // 默認, 屏幕左邊緣觸發手勢
SJFullscreenPopGestureType_Full, // 全屏觸發手勢
};
複製代碼
可設置Pop返回時的動畫效果 目前有兩種:網絡
可在某個ViewController禁用手勢session
/// 將要拖拽
@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;
複製代碼
SingleTap 單擊手勢 當用戶單擊播放器時, 播放器會調用顯示或隱藏控制層的相關代理方法. 見 controlLayerDelegate
DoubleTap 雙擊手勢 雙擊會觸發暫停或播放的操做
Pan 移動手勢 當用戶水平滑動時, 會觸發控制層相應的代理方法. 見 controlLayerDelegate
當用戶垂直滑動時, 若是在屏幕左邊, 則會觸發調整亮度的操做, 並顯示亮度提示視圖. 若是在屏幕右邊, 則會觸發調整聲音的操做, 並顯示系統音量提示視圖
Pinch 捏合手勢 當用戶作放大或收縮觸發該手勢時, 會設置播放器顯示模式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:
控制層須要顯示, 代理能夠在這個方法裏面實現顯示控制層的UIcontrolLayerNeedDisappear:
控制層須要隱藏, 同上, 作一些隱藏工做- (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/changsanjiang/SJVideoPlayer
個人郵箱: changsanjiang@gmail.com
若是您有什麼建議, 望請聯繫我!