SJVideoPlayer
基於AVPlayer.
極速初始化, 不阻塞主執行緒.
這個應該是目前基於AVPlayer的播放器中, 功能最全的一個吧.
使用
pod `SJVideoPlayer`
複製程式碼
功能
- 支援 全屏手勢返回
- 支援 網路狀態變更提示
- 支援 隨心所欲的控制旋轉
- 支援 匯出片段 + 截圖 + 生成GIF
- 支援 自定義控制層(實現協議方法即可)
- 支援 顯示提示文字(如: 現有的網路狀態切換提示)
- 支援 調整亮度/聲音/調速/靜音/捏合, 播放 本地/網路視訊
- 支援 在TableView的HeaderView或者Cell上播放, 在CollectionView的Cell中播放
- 支援 續播, 跳入下一個介面可以使用上一個介面的資源初始化
- 本地化處理. 支援簡體/繁體/英文
- 可以 在後臺播放視訊
- 無縫切換您自定義的控制層
全屏手勢返回
只需pod到專案中即可帶全屏手勢
為啥自定義手勢?
在使用原生手勢返回時, 當前播放的視訊會出現卡幀的問題. 原因我不太清楚. 我檢視了騰訊視訊和愛奇藝等App均為自己實現的手勢返回.
當時使用了一個全屏返回的三方庫. 是截圖返回的, 發現存在兩個問題: 一是截圖耗時, 二是視訊部分擷取不了(黑色的)
由於我們起步8.0, 我便使用了新的APIsnapshotViewAfterScreenUpdates:
(這個介面其實7.0就有了)來替換截圖的方式, 直接使用檢視快照, 這個介面一舉兩得, 不僅速度快, 而且視訊播放也可以擷取到.
但是快照是一個view, 並不是image, 原來的三方庫截圖方式無法使用, 於是我重新擼了一個全屏手勢.
手勢使用
pod `SJFullscreenPopGesture`
複製程式碼
手勢功能介紹
// default is `SJFullscreenPopGestureType_EdgeLeft`.
typedef NS_ENUM(NSUInteger, SJFullscreenPopGestureType) {
SJFullscreenPopGestureType_EdgeLeft, // 預設, 螢幕左邊緣觸發手勢
SJFullscreenPopGestureType_Full, // 全屏觸發手勢
};
複製程式碼
-
可設定Pop返回時的動畫效果
目前有兩種:
/// 將要拖拽
@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);
複製程式碼
匯出片段 + 截圖 + 生成GIF
這幾個功能都已封裝在預設的控制層中, 並且相應的提示文字也做了本地化處理
方法如下:
- (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.
-
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:
控制層需要隱藏, 同上, 做一些隱藏工作
在UITableView或者CollectionView中播放時的代理回撥
- (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:
水平方向拖動結束的回撥
Network
/// 網路狀態變更
- (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer reachabilityChanged:(SJNetworkStatus)status;
複製程式碼
videoPlayer:reachabilityChanged:
當網路狀態變更時, 會回撥這個方法
Demo
專案地址: https://github.com/changsanjiang/SJVideoPlayer
我的郵箱: changsanjiang@gmail.com
如果您有什麼建議, 望請聯絡我!