# SJVideoPlayer **Repository Path**: lijunchengit/SJVideoPlayer ## Basic Information - **Project Name**: SJVideoPlayer - **Description**: 功能齐全的视频播放器 - **Primary Language**: Objective-C - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 13 - **Created**: 2019-08-28 - **Last Updated**: 2025-02-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ![readme](https://user-images.githubusercontent.com/37614260/43947531-922a0712-9cb2-11e8-8f8d-4823a21308d3.png) [![Build Status](https://travis-ci.org/changsanjiang/SJVideoPlayer.svg?branch=master)](https://travis-ci.org/changsanjiang/SJVideoPlayer) [![Version](https://img.shields.io/cocoapods/v/SJVideoPlayer.svg?style=flat)](https://cocoapods.org/pods/SJVideoPlayer) [![Platform](https://img.shields.io/badge/platform-iOS-blue.svg)](https://github.com/changsanjiang) [![License](https://img.shields.io/github/license/changsanjiang/SJVideoPlayer.svg)](https://github.com/changsanjiang/SJVideoPlayer/blob/master/LICENSE.md) ### Installation ```ruby # Player with default control layer. pod 'SJVideoPlayer' # The base player, without the control layer, can be used if you need a custom control layer. pod 'SJBaseVideoPlayer' # 天朝 # 如果网络不行安装不了, 可改成以下方式进行安装 pod 'SJBaseVideoPlayer', :git => 'https://gitee.com/changsanjiang/SJBaseVideoPlayer.git' pod 'SJVideoPlayer', :git => 'https://gitee.com/changsanjiang/SJVideoPlayer.git' pod 'SJUIKit/AttributesFactory', :git => 'https://gitee.com/changsanjiang/SJUIKit.git' pod 'SJUIKit/ObserverHelper', :git => 'https://gitee.com/changsanjiang/SJUIKit.git' pod 'SJUIKit/Queues', :git => 'https://gitee.com/changsanjiang/SJUIKit.git' $ pod update --no-repo-update (不要用 pod install 了, 用这个命令安装) ``` - [Base Video Player](https://github.com/changsanjiang/SJBaseVideoPlayer) ___ ## Contact * Email: changsanjiang@gmail.com ___ ## License SJVideoPlayer is available under the MIT license. See the LICENSE file for more info. ___ ## 最近更新 * 适配 iOS 13.0. * v2.6.0 开始 旋转的配置已从播放器内部移出, 现在需开发者自己添加配置, 代码如下: ```Objective-C static BOOL _iPhone_shouldAutorotate(UIViewController *vc) { NSString *class = NSStringFromClass(vc.class); // 禁止哪些控制器旋转. // - 如果返回 NO, 则只旋转`播放器`. // - 如果返回 YES, 则`所有控制器`同`播放器`一起旋转. // // return NO; // - 为了避免控制器同播放器一起旋转, 此处禁止Demo中SJ前缀的控制器旋转. if ( [class hasPrefix:@"SJ"] ) { return NO; } // 其余情况 return YES. 此时系统的播放器(如在网页播放全屏后)可以触发旋转. return YES; } @implementation UIViewController (RotationControl) /// 该控制器是否可以旋转 - (BOOL)shouldAutorotate { // 此处为设置 iPhone 哪些控制器可以旋转 if ( UIUserInterfaceIdiomPhone == UI_USER_INTERFACE_IDIOM() ) return _iPhone_shouldAutorotate(self); return NO; } /// 旋转支持的方向 - (UIInterfaceOrientationMask)supportedInterfaceOrientations { // 此处为设置 iPhone 某个控制器旋转支持的方向 // - 请根据实际情况进行修改. if ( UIUserInterfaceIdiomPhone == UI_USER_INTERFACE_IDIOM() ) { // 如果self不支持旋转, 返回仅支持竖屏 if ( _iPhone_shouldAutorotate(self) == NO ) return UIInterfaceOrientationMaskPortrait; } return UIInterfaceOrientationMaskAllButUpsideDown; } @end @implementation UITabBarController (RotationControl) - (UIViewController *)sj_topViewController { if ( self.selectedIndex == NSNotFound ) return self.viewControllers.firstObject; return self.selectedViewController; } - (BOOL)shouldAutorotate { return [[self sj_topViewController] shouldAutorotate]; } - (UIInterfaceOrientationMask)supportedInterfaceOrientations { return [[self sj_topViewController] supportedInterfaceOrientations]; } - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { return [[self sj_topViewController] preferredInterfaceOrientationForPresentation]; } @end @implementation UINavigationController (RotationControl) - (BOOL)shouldAutorotate { return self.topViewController.shouldAutorotate; } - (UIInterfaceOrientationMask)supportedInterfaceOrientations { return self.topViewController.supportedInterfaceOrientations; } - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { return self.topViewController.preferredInterfaceOrientationForPresentation; } - (nullable UIViewController *)childViewControllerForStatusBarStyle { return self.topViewController; } - (nullable UIViewController *)childViewControllerForStatusBarHidden { return self.topViewController; } @end ``` * 新增 切换清晰度的控制层. 开启如下: ```Objective-C SJVideoPlayerURLAsset *asset1 = [[SJVideoPlayerURLAsset alloc] initWithURL:VideoURL_Level4]; asset1.definition_fullName = @"超清 1080P"; asset1.definition_lastName = @"超清"; SJVideoPlayerURLAsset *asset2 = [[SJVideoPlayerURLAsset alloc] initWithURL:VideoURL_Level3]; asset2.definition_fullName = @"高清 720P"; asset2.definition_lastName = @"AAAAAAA"; SJVideoPlayerURLAsset *asset3 = [[SJVideoPlayerURLAsset alloc] initWithURL:VideoURL_Level2]; asset3.definition_fullName = @"清晰 480P"; asset3.definition_lastName = @"480P"; _player.definitionURLAssets = @[asset1, asset2, asset3]; // 先播放asset1. (asset2 和 asset3 将会在用户选择后进行切换) _player.URLAsset = asset1; ``` * 新增 左右边缘快进快退. 开启如下: ```Objective-C // 开启左右边缘快进快退. 如需进行更多配置, 请查看`fastForwardViewController` _player.fastForwardViewController.enabled = YES; ``` * 新增 小浮窗播放. 开启如下: ```Objective-C // 开启小浮窗. 如需进行更多配置, 请查看`floatSmallViewController` _player.floatSmallViewController.enabled = YES; ``` ___ ## Documents #### [1 视图层次](#1) * [1.1 在普通 View 上播放](#1.1) * [1.2 在 TableViewCell 上播放](#1.2) * [1.3 在 TableHeaderView 或者 TableFooterView 上播放](#1.3) * [1.4 在 CollectionViewCell 上播放](#1.4) * [1.5 CollectionView 嵌套在 TableViewHeaderView 中, 在 CollectionViewCell 上播放](#1.5) * [1.6 CollectionView 嵌套在 TableViewCell 中, 在 CollectionViewCell 上播放](#1.6) * [1.7 CollectionView 嵌套在 CollectionViewCell 中, 在 CollectionViewCell 上播放](#1.7) * [1.8 在 UITableViewHeaderFooterView 上播放](#1.8) #### [2. 创建资源进行播放](#2) * [2.1 通过 URL 创建资源进行播放](#2.1) * [2.2 通过 AVAsset 或其子类进行播放](#2.2) * [2.3 指定开始播放的时间](#2.3) * [2.4 续播. 进入下个页面时, 继续播放](#2.4) * [2.5 销毁时的回调. 可在此时做一些记录工作, 如播放位置](#2.5) #### [3. 播放控制](#3) * [3.1 当前时间和时长](#3.1) * [3.2 时间改变时的回调](#3.2) * [3.3 播放结束后的回调](#3.3) * [3.4 播放状态 - 未知/准备/准备就绪/播放中/暂停的/不活跃的](#3.4) * [3.5 暂停的原因 - 缓冲/跳转/暂停](#3.5) * [3.6 不活跃的原因 - 加载失败/播放完毕](#3.6) * [3.7 播放状态改变的回调](#3.7) * [3.8 是否自动播放 - 当资源初始化完成后](#3.8) * [3.9 刷新 ](#3.9) * [3.10 播放器的声音设置 & 静音](#3.10) * [3.11 播放](#3.11) * [3.12 暂停](#3.12) * [3.13 是否暂停 - 当App进入后台后](#3.13) * [3.14 停止](#3.14) * [3.15 重播](#3.15) * [3.16 跳转到指定的时间播放](#3.16) * [3.17 调速 & 速率改变时的回调](#3.17) * [3.18 接入别的视频 SDK, 自己动手撸一个 SJMediaPlaybackController, 替换作者原始实现](#3.18) #### [4. 控制层的显示和隐藏](#4) * [4.1 让控制层显示](#4.1) * [4.2 让控制层隐藏](#4.2) * [4.3 控制层是否显示中](#4.3) * [4.4 是否在暂停时保持控制层显示](#4.4) * [4.5 是否自动显示控制层 - 资源初始化完成后](#4.5) * [4.6 控制层显示状态改变的回调](#4.6) * [4.7 禁止管理控制层的显示和隐藏](#4.7) * [4.8 自己动手撸一个 SJControlLayerAppearManager, 替换作者原始实现](#4.8) #### [5. 设备亮度和音量](#5) * [5.1 调整设备亮度](#5.1) * [5.2 调整设备声音](#5.2) * [5.3 亮度 & 声音改变后的回调](#5.3) * [5.4 禁止播放器设置](#5.4) * [5.5 自己动手撸一个 SJDeviceVolumeAndBrightnessManager, 替换作者原始实现](#5.5) #### [6. 旋转](#6) * [6.1 自动旋转](#6.1) * [6.2 设置自动旋转支持的方向](#6.2) * [6.3 禁止自动旋转](#6.3) * [6.4 主动调用旋转](#6.4) * [6.5 是否全屏](#6.5) * [6.6 是否正在旋转](#6.6) * [6.7 当前旋转的方向 ](#6.7) * [6.8 旋转开始和结束的回调](#6.8) * [6.9 自己动手撸一个 SJRotationManager, 替换作者原始实现](#6.9) #### [7. 直接全屏而不旋转](#7) * [7.1 全屏和恢复](#7.1) * [7.2 开始和结束的回调](#7.2) * [7.3 是否是全屏](#7.3) * [7.4 自己动手撸一个 SJFitOnScreenManager, 替换作者原始实现](#7.4) #### [8. 镜像翻转](#8) * [8.1 翻转和恢复](#8.1) * [8.2 开始和结束的回调](#8.2) * [8.3 自己动手撸一个 SJFlipTransitionManager, 替换作者原始实现](#8.3) #### [9. 网络状态](#9) * [9.1 当前的网络状态](#9.1) * [9.2 网络状态改变的回调](#9.2) * [9.3 自己动手撸一个 SJReachability, 替换作者原始实现](#9.3) #### [10. 手势](#10) * [10.1 单击手势](#10.1) * [10.2 双击手势](#10.2) * [10.3 移动手势](#10.3) * [10.4 捏合手势](#10.4) * [10.5 禁止某些手势](#10.5) * [10.6 自定义某个手势的处理](#10.6) * [10.7 自己动手撸一个 SJPlayerGestureControl, 替换作者原始实现](#10.7) #### [11. 占位图](#11) * [11.1 设置本地占位图](#11.1) * [11.2 设置网络占位图](#11.2) * [11.3 是否隐藏占位图 - 播放器准备好显示时](#11.3) #### [12. 显示提示文本](#12) * [12.1 显示文本及持续时间 - (NSString or NSAttributedString)](#12.1) * [12.2 配置提示文本](#12.2) #### [13. 一些固定代码](#13) * [13.1 - (void)vc_viewDidAppear; ](#13.1) * [13.2 - (void)vc_viewWillDisappear;](#13.2) * [13.3 - (void)vc_viewDidDisappear;](#13.3) * [13.4 - (BOOL)vc_prefersStatusBarHidden;](#13.4) * [13.5 - (UIStatusBarStyle)vc_preferredStatusBarStyle;](#13.5) * [13.6 - 临时显示状态栏](#13.6) * [13.7 - 临时隐藏状态栏](#13.7) #### [14. 截屏](#14) * [14.1 当前时间截图](#14.1) * [14.2 指定时间截图](#14.2) * [14.3 生成预览视图, 大约20张](#14.3) #### [15. 导出视频或GIF](#15) * [15.1 导出视频](#15.1) * [15.2 导出GIF](#15.2) * [15.3 取消操作](#15.3) #### [16. 滚动相关](#16) * [16.1 是否在 UICollectionView 或者 UITableView 中播放](#16.1) * [16.2 是否滚动显示](#16.2) * [16.3 播放器视图将要滚动显示和消失的回调](#16.3) #### [17. 自动播放 - 在 UICollectionView 或者 UITableView 中](#17) * [17.1 开启](#17.1) * [17.2 配置](#17.2) * [17.3 关闭](#17.3) * [17.4 主动调用播放下一个资源](#17.4) #### [18. 控制层数据源, 每个方法介绍](#18) * [18.1 - (UIView *)controlView;](#18.1) * [18.2 - (void)installedControlViewToVideoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer;](#18.2) #### [19. 控制层代理, 每个方法介绍](#19) * [19.1 - (void)controlLayerNeedAppear:(__kindof SJBaseVideoPlayer *)videoPlayer;](#19.1) * [19.2 - (void)controlLayerNeedDisappear:(__kindof SJBaseVideoPlayer *)videoPlayer;](#19.2) * [19.3 - (BOOL)controlLayerOfVideoPlayerCanAutomaticallyDisappear:(__kindof SJBaseVideoPlayer *)videoPlayer;](#19.3) * [19.4 - (void)videoPlayerWillAppearInScrollView:(__kindof SJBaseVideoPlayer *)videoPlayer;](#19.4) * [19.5 - (void)videoPlayerWillDisappearInScrollView:(__kindof SJBaseVideoPlayer *)videoPlayer;](#19.5)
* [SJPlayStatusControlDelegate](#SJPlayStatusControlDelegate) * [19.6 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer prepareToPlay:(SJVideoPlayerURLAsset *)asset;](#19.6) * [19.7 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer statusDidChanged:(SJVideoPlayerPlayStatus)status;](#19.7) * [19.8 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayercurrentTime:(NSTimeInterval)currentTime currentTimeStr:(NSString *)currentTimeStr totalTime:(NSTimeInterval)totalTime totalTimeStr:(NSString *)totalTimeStr;](#19.8) * [19.9 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer presentationSize:(CGSize)size;](#19.9)
* [SJVolumeBrightnessRateControlDelegate](#SJVolumeBrightnessRateControlDelegate) * [19.10 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer muteChanged:(BOOL)mute;](#19.10) * [19.11 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer volumeChanged:(float)volume;](#19.11) * [19.12 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer brightnessChanged:(float)brightness;](#19.12) * [19.13 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer rateChanged:(float)rate;](#19.13)
* [SJBufferControlDelegate](#SJBufferControlDelegate) * [19.14 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer bufferTimeDidChange:(NSTimeInterval)bufferTime;](#19.14 ) * [19.15 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer bufferStatusDidChange:(SJPlayerBufferStatus)bufferStatus;](#19.15)
* [SJRotationControlDelegate](#SJRotationControlDelegate) * [19.16 - (BOOL)canTriggerRotationOfVideoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer;](#19.16) * [19.17 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer willRotateView:(BOOL)isFull;](#19.17) * [19.18 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer didEndRotation:(BOOL)isFull;](#19.18)
* [SJFitOnScreenControlDelegate](#SJFitOnScreenControlDelegate) * [19.22 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer willFitOnScreen:(BOOL)isFitOnScreen;](#19.22) * [19.23 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer didCompleteFitOnScreen:(BOOL)isFitOnScreen;](#19.23)
* [SJGestureControlDelegate](#SJGestureControlDelegate) * [19.24 - (BOOL)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer gestureRecognizerShouldTrigger:(SJPlayerGestureType)type location:(CGPoint)location;](#19.24) * [19.25 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer panGestureTriggeredInTheHorizontalDirection:(SJPanGestureRecognizerState)state progressTime:(NSTimeInterval)progressTime;](#19.25)
* [SJNetworkStatusControlDelegate](#SJNetworkStatusControlDelegate) * [19.26 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer reachabilityChanged:(SJNetworkStatus)status;](#19.26)
* [SJLockScreenStateControlDelegate](#SJLockScreenStateControlDelegate) * [19.27 - (void)tappedPlayerOnTheLockedState:(__kindof SJBaseVideoPlayer *)videoPlayer;](#19.27) * [19.28 - (void)lockedVideoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer;](#19.28) * [19.29 - (void)unlockedVideoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer;](#19.29)
* [SJSwitchVideoDefinitionControlDelegate](#SJSwitchVideoDefinitionControlDelegate) * [19.30 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer switchVideoDefinitionByURL:(NSURL *)URL statusDidChange:(SJMediaPlaybackSwitchDefinitionStatus)status;](#19.30) ___

1. 视图层次

SJBaseVideoPlayer 播放的视频资源是通过 SJVideoPlayerURLAsset 进行初始化的. SJVideoPlayerURLAsset 由两部分组成: - 视图层次 (SJPlayModel) - 资源地址 (可以是本地资源/URL/AVAsset) 默认情况下, 创建了 SJVideoPlayerURLAsset , 赋值给播放器后即可播放. 如下示例:

```Objective-C SJVideoPlayerURLAsset *asset = [[SJVideoPlayerURLAsset alloc] initWithURL:URL playModel:playModel]; _player.URLAsset = asset; ```

我们先来看 SJPlayModel, 我将以下视图层次封装到了 SJPlayModel 中, 使用它初始化对应层次即可.

1.1 在普通 View 上播放

在普通视图中播放时, 直接创建PlayModel即可. ```Objective-C SJPlayModel *playModel = [SJPlayModel new]; ```

1.2 在 TableViewCell 上播放

```Objective-C -- UITableView -- UITableViewCell -- Player.superview -- Player.view SJPlayModel *playModel = [SJPlayModel UITableViewCellPlayModelWithPlayerSuperviewTag:cell.coverImageView.tag atIndexPath:indexPath tableView:self.tableView]; ```

1.3 在 TableHeaderView 或者 TableFooterView 上播放

```Objective-C -- UITableView -- UITableView.tableHeaderView 或者 UITableView.tableFooterView -- Player.superview -- Player.view SJPlayModel *playModel = [SJPlayModel UITableViewHeaderViewPlayModelWithPlayerSuperview:view.coverImageView tableView:self.tableView]; ```

1.4 在 CollectionViewCell 上播放

```Objective-C -- UICollectionView -- UICollectionViewCell -- Player.superview -- Player.view SJPlayModel *playModel = [SJPlayModel UICollectionViewCellPlayModelWithPlayerSuperviewTag:cell.coverImageView.tag atIndexPath:indexPath collectionView:self.collectionView]; ```

1.5 CollectionView 嵌套在 TableViewHeaderView 中, 在 CollectionViewCell 上播放

```Objective-C -- UITableView -- UITableView.tableHeaderView 或者 UITableView.tableFooterView -- tableHeaderView.UICollectionView -- UICollectionViewCell -- Player.superview -- Player.view SJPlayModel *playModel = [SJPlayModel UICollectionViewNestedInUITableViewHeaderViewPlayModelWithPlayerSuperviewTag:cell.coverImageView.tag atIndexPath:indexPath collectionView:tableHeaderView.collectionView tableView:self.tableView]; ```

1.6 CollectionView 嵌套在 TableViewCell 中, 在 CollectionViewCell 上播放

```Objective-C -- UITableView -- UITableViewCell -- UITableViewCell.UICollectionView -- UICollectionViewCell -- Player.superview -- Player.view SJPlayModel *playModel = [SJPlayModel UICollectionViewNestedInUITableViewCellPlayModelWithPlayerSuperviewTag:collectionViewCell.coverImageView.tag atIndexPath:collectionViewCellAtIndexPath collectionViewTag:tableViewCell.collectionView.tag collectionViewAtIndexPath:tableViewCellAtIndexPath tableView:self.tableView]; ```

1.7 CollectionView 嵌套在 CollectionViewCell 中, 在 CollectionViewCell 上播放

```Objective-C -- UICollectionView -- UICollectionViewCell -- UICollectionViewCell.UICollectionView -- UICollectionViewCell -- Player.superview -- Player.view SJPlayModel *playModel = [SJPlayModel UICollectionViewNestedInUICollectionViewCellPlayModelWithPlayerSuperviewTag:collectionViewCell.coverImageView.tag atIndexPath:collectionViewCellAtIndexPath collectionViewTag:rootCollectionViewCell.collectionView.tag collectionViewAtIndexPath:collectionViewAtIndexPath rootCollectionView:self.collectionView]; ```

1.8 在 UITableViewHeaderFooterView 上播放

```Objective-C -- UITableView -- UITableViewHeaderFooterView -- Player.superview -- Player.view /// isHeader: 当在header中播放时, 传YES, 在footer时, 传NO. SJPlayModel *playModel = [SJPlayModel UITableViewHeaderFooterViewPlayModelWithPlayerSuperviewTag:sectionHeaderView.coverImageView.tag inSection:section isHeader:YES tableView:self.tableView]; ``` ___

2. 创建资源进行播放

SJBaseVideoPlayer 播放的视频资源是通过 SJVideoPlayerURLAsset 进行初始化的. SJVideoPlayerURLAsset 由两部分组成: - 视图层次 (第一部分中的SJPlayModel) - 资源地址 (可以是本地资源/URL/AVAsset) 默认情况下, 创建了 SJVideoPlayerURLAsset , 赋值给播放器后即可播放. 如下示例:

```Objective-C SJVideoPlayerURLAsset *asset = [[SJVideoPlayerURLAsset alloc] initWithURL:URL playModel:playModel]; _player.URLAsset = asset; ```

2.1 通过 URL 创建资源进行播放

```Objective-C _player.URLAsset = [[SJVideoPlayerURLAsset alloc] initWithURL:URL playModel:playModel]; ```

2.2 通过 AVAsset 或其子类进行播放

```Objective-C _player.URLAsset = [[SJVideoPlayerURLAsset alloc] initWithAVAsset:avAsset playModel:playModel]; ```

2.3 指定开始播放的时间

```Objective-C _player.URLAsset = [[SJVideoPlayerURLAsset alloc] initWithURL:URL playModel:playModel]; NSTimeInterval secs = 20.0; _player.URLAsset.specifyStartTime = secs; ```

2.4 续播. 进入下个页面时, 继续播放

在播放时, 我们可能需要切换界面, 而希望视频能够在下一个界面无缝的进行播放. 针对此种情况 SJVideoPlayerURLAsset 提供了便利的初始化方法. 请看片段:

```Objective-C /// otherAsset即为上一个页面播放的Asset /// 除了需要一个otherAsset, 其他方面同以上的示例一模一样 _player.URLAsset = [[SJVideoPlayerURLAsset alloc] initWithOtherAsset:otherAsset playModel:playModel]; ```

2.5 销毁时的回调. 可在此时做一些记录工作, 如播放位置

我们有时候想存储某个视频的播放记录, 以便下次, 能够从指定的位置进行播放. 那什么时候存储合适呢? 最好的时机就是资源被释放时. SJBaseVideoPlayer 提供了每个资源在 Dealloc 时的回调, 如下:

```Objective-C // 每个资源dealloc时的回调 _player.assetDeallocExeBlock = ^(__kindof SJBaseVideoPlayer * _Nonnull videoPlayer) { // ..... }; ``` ___

3. 播放控制

播放控制: 对播放进行的操作. 此部分的内容由 "id <SJMediaPlaybackController> playbackController" 提供支持. 大多数对播放进行的操作, 均在协议 SJMediaPlaybackController 进行了声明. 正常来说实现了此协议的任何对象, 均可赋值给 player.playbackController 来替换原始实现.

3.1 当前时间和时长

```Objective-C /// 当前时间 _player.currentTime /// 时长 _player.totalTime /// 字符串化, /// - 格式为 00:00(小于 1 小时) 或者 00:00:00 (大于 1 小时) _player.currentTimeStr _player.totalTimeStr ```

3.2 时间改变时的回调

```Objective-C _player.playTimeDidChangeExeBlok = ^(__kindof SJBaseVideoPlayer * _Nonnull videoPlayer) { /// ... }; ```

3.3 播放结束后的回调

```Objective-C _player.playDidToEndExeBlock = ^(__kindof SJBaseVideoPlayer * _Nonnull videoPlayer) { /// ... }; ```

3.4 播放状态 - 未知/准备/准备就绪/播放中/暂停的/不活跃的

播放状态有两个状态需要注意一下, 分别是 暂停和不活跃状态 当状态为暂停时, 目前有3种可能: - 正在缓冲 - 主动暂停 - 正在跳转 当状态为不活跃时, 目前有2种可能: - 播放完毕 - 播放失败

```Objective-C /** 当前播放的状态 - SJVideoPlayerPlayStatusUnknown: 未播放任何资源时的状态 - SJVideoPlayerPlayStatusPrepare: 准备播放一个资源 - SJVideoPlayerPlayStatusReadyToPlay: 准备就绪, 可以播放 - SJVideoPlayerPlayStatusPlaying: 播放中 - SJVideoPlayerPlayStatusPaused: 暂停状态, 请通过`SJVideoPlayerPausedReason`, 查看暂停原因 - SJVideoPlayerPlayStatusInactivity: 不活跃状态, 请通过`SJVideoPlayerInactivityReason`, 查看暂停原因 */ typedef NS_ENUM(NSUInteger, SJVideoPlayerPlayStatus) { SJVideoPlayerPlayStatusUnknown, SJVideoPlayerPlayStatusPrepare, SJVideoPlayerPlayStatusReadyToPlay, SJVideoPlayerPlayStatusPlaying, SJVideoPlayerPlayStatusPaused, SJVideoPlayerPlayStatusInactivity, }; ```

3.5 暂停的原因 - 缓冲/跳转/暂停

```Objective-C /** 暂停的理由 - SJVideoPlayerPausedReasonBuffering: 正在缓冲 - SJVideoPlayerPausedReasonPause: 被暂停 - SJVideoPlayerPausedReasonSeeking: 正在跳转(调用seekToTime:时) */ typedef NS_ENUM(NSUInteger, SJVideoPlayerPausedReason) { SJVideoPlayerPausedReasonBuffering, SJVideoPlayerPausedReasonPause, SJVideoPlayerPausedReasonSeeking, }; ```

3.6 不活跃的原因 - 加载失败/播放完毕

```Objective-C /** 不活跃的原因 - SJVideoPlayerInactivityReasonPlayEnd: 播放完毕 - SJVideoPlayerInactivityReasonPlayFailed: 播放失败 */ typedef NS_ENUM(NSUInteger, SJVideoPlayerInactivityReason) { SJVideoPlayerInactivityReasonPlayEnd, SJVideoPlayerInactivityReasonPlayFailed, }; ```

3.7 播放状态改变的回调

对播放状态的判断我添加了一个便利的分类 `SJBaseVideoPlayer (PlayStatus)` 如需判断状态, 可导入头文件 `#import "SJBaseVideoPlayer+PlayStatus.h"` 使用.

```Objective-C /// 播放状态改变的回调 _player.playStatusDidChangeExeBlock = ^(__kindof SJBaseVideoPlayer * _Nonnull videoPlayer) { }; /// 对播放状态的判断我添加了一个便利的分类 @interface SJBaseVideoPlayer (PlayStatus) - (NSString *)getPlayStatusStr:(SJVideoPlayerPlayStatus)status; - (BOOL)playStatus_isUnknown; - (BOOL)playStatus_isPrepare; - (BOOL)playStatus_isReadyToPlay; - (BOOL)playStatus_isPlaying; - (BOOL)playStatus_isPaused; - (BOOL)playStatus_isPaused_ReasonBuffering; - (BOOL)playStatus_isPaused_ReasonPause; - (BOOL)playStatus_isPaused_ReasonSeeking; - (BOOL)playStatus_isInactivity; - (BOOL)playStatus_isInactivity_ReasonPlayEnd; - (BOOL)playStatus_isInactivity_ReasonPlayFailed; @end ```

3.8 是否自动播放 - 当资源初始化完成后

```Objective-C _player.autoPlayWhenPlayStatusIsReadyToPlay = YES; ```

3.9 刷新

在播放一个资源时, 可能有一些意外情况导致播放失败(如网络环境差). 此时当用户点击刷新按钮, 我们需要对当前的资源(Asset)进行刷新. SJBaseVideoPlayer提供了直接的方法去刷新, 不需要开发者再重复的去创建新的Asset.

```Objective-C [_player refresh]; ```

3.10 播放器的声音设置 & 静音

```Objective-C /// 默认值为 1.0, 最小为 0.0 _player.playerVolume = 1.0; /// 设置静音 _player.mute = YES; ```

3.11 播放

```Objective-C [_player play]; ```

3.12 暂停

```Objective-C [_player pause]; ```

3.13 是否暂停 - 当App进入后台后

关于后台播放视频, 引用自: https://juejin.im/post/5a38e1a0f265da4327185a26 当您想在后台播放视频时: 1. 需要设置 videoPlayer.pauseWhenAppDidEnterBackground = NO; (该值默认为YES, 即App进入后台默认暂停). 2. 前往 `TARGETS` -> `Capability` -> enable `Background Modes` -> select this mode `Audio, AirPlay, and Picture in Picture`

```Objective-C _player.pauseWhenAppDidEnterBackground = NO; // 默认值为 YES, 即进入后台后 暂停. ```

3.14 停止

注意, 调用此方法后, 当前的 asset 将会被清空. 也就是说, 调用 play等播放操作将会无效.

```Objective-C [_player stop]; ```

3.15 重播

从头开始重新播放

```Objective-C [_player replay]; ```

3.16 跳转到指定的时间播放

```Objective-C NSTimeInterval secs = 20.0; [_player seekToTime:secs completionHandler:^(BOOL finished) { // .... }]; ```

3.17 调速 & 速率改变时的回调

```Objective-C /// 默认值为 1.0 _player.rate = 1.0; _player.rateDidChangeExeBlock = ^(__kindof SJBaseVideoPlayer * _Nonnull player) { /// .. } ```

3.18 接入别的视频 SDK, 自己动手撸一个 SJMediaPlaybackController, 替换作者原始实现

某些时候, 我们需要接入第三方的视频SDK, 但是又想使用 SJBaseVideoPlayer 封装的其他的功能. 这个时候, 我们可以自己动手, 将第三方的SDK封装一下, 实现 SJMediaPlaybackController 协议, 管理 SJBaseVideoPlayer 中的播放操作. 示例: - 可以参考 SJAVMediaPlaybackController 中的实现. - 封装 ijkplayer 的示例: https://gitee.com/changsanjiang/SJIJKMediaPlaybackController

```Objective-C _player.playbackController = Your PlaybackController. ``` ___

4. 控制层的显示和隐藏

控制层的显示和隐藏, 此部分的内容由 "id <SJControlLayerAppearManager> controlLayerAppearManager" 提供支持. controlLayerAppearManager 内部存在一个定时器, 当控制层显示时, 会开启此定时器. 一定间隔后, 会尝试隐藏控制层. 其他相关操作, 请见以下内容.

4.1 让控制层显示

当控制层需要显示时, 可以调用下面方法. 此方法将会回调控制层的代理方法: "- (void)controlLayerNeedAppear:(__kindof SJBaseVideoPlayer *)videoPlayer;" 代理将会对当前的控制层进行显示处理.

```Objective-C [_player controlLayerNeedAppear]; ```

4.2 让控制层隐藏

当控制层需要隐藏时, 可以调用下面方法. 此方法将会回调控制层的代理方法: "- (void)controlLayerNeedDisappear:(__kindof SJBaseVideoPlayer *)videoPlayer;" 代理将会对当前的控制层进行隐藏处理.

```Objective-C [_player controlLayerNeedDisappear]; ```

4.3 控制层是否显示中

```Objective-C /// 是否显示, YES为显示, NO为隐藏 _player.controlLayerIsAppeared ```

4.4 是否在暂停时保持控制层显示

```Objective-C /// 默认为 NO, 即不保持显示 _player.pausedToKeepAppearState = YES; ```

4.5 是否自动显示控制层 - 资源初始化完成后

```Objective-C /// 默认为 NO, 即不显示 _player.controlLayerAutoAppearWhenAssetInitialized = YES; ```

4.6 控制层显示状态改变的回调

```Objective-C @property (nonatomic, copy, nullable) void(^controlLayerAppearStateDidChangeExeBlock)(__kindof SJBaseVideoPlayer *player, BOOL state); ```

4.7 禁止管理控制层的显示和隐藏

有时候, 我们可能不需要对控制层的显示和隐藏进行管理. 这个时候可以设置如下属性, 来禁止管理类的操作.

```Objective-C @property (nonatomic) BOOL disabledControlLayerAppearManager; // default value is NO. ```

4.8 自己动手撸一个 SJControlLayerAppearManager, 替换作者原始实现

同样的, 协议 "SJControlLayerAppearManager" 定义了一系列的操作, 只要实现了这些协议方法的对象, 就可以管理控制层的显示和隐藏.

```Objective-C _player.controlLayerAppearManager = Your controlLayerAppearManager; ``` ___

5. 设备亮度和音量

设备亮度和音量的调整, 此部分的内容由 "id <SJDeviceVolumeAndBrightnessManager> deviceVolumeAndBrightnessManager" 提供支持.

5.1 调整设备亮度

```Objective-C _player.deviceBrightness = 1.0; ```

5.2 调整设备声音

```Objective-C _player.deviceVolume = 1.0; ```

5.3 亮度 & 声音改变后的回调

```Objective-C _observer = [_player.deviceVolumeAndBrightnessManager getObserver]; observer.volumeDidChangeExeBlock = ...; observer.brightnessDidChangeExeBlock = ...; ```

5.4 禁止播放器设置

```Objective-C _player.disableBrightnessSetting = YES; _player.disableVolumeSetting = YES; ```

5.5 自己动手撸一个 SJDeviceVolumeAndBrightnessManager, 替换作者原始实现

当需要对设备音量视图进行自定义时, 可以自己动手撸一个 SJDeviceVolumeAndBrightnessManager.

```Objective-C _player.deviceVolumeAndBrightnessManager = Your deviceVolumeAndBrightnessManager; ``` ___

6. 旋转

此部分的内容由 "id <SJRotationManagerProtocol> rotationManager" 提供支持. 对于旋转, 我们开发者肯定需要绝对的控制, 例如: 设置自动旋转所支持方向. 能够主动+自动旋转, 而且还需要能在适当的时候禁止自动旋转. 旋转前后的回调等等... 放心这些功能都有, 我挨个给大家介绍. 另外旋转有两种方式: - 仅旋转播放器视图 (默认情况下) - 使 ViewController 也一起旋转 具体请看下面介绍.

6.1 自动旋转

先说说何为自动旋转. 其实就是当设备方向变更时, 播放器根据设备方向进行自动旋转.

6.2 设置自动旋转支持的方向

```Objective-C /// 设置自动旋转支持的方向 _player.supportedOrientation = SJAutoRotateSupportedOrientation_LandscapeLeft | SJAutoRotateSupportedOrientation_LandscapeRight; /** 自动旋转支持的方向 - SJAutoRotateSupportedOrientation_Portrait: 竖屏 - SJAutoRotateSupportedOrientation_LandscapeLeft: 支持全屏, Home键在右侧 - SJAutoRotateSupportedOrientation_LandscapeRight: 支持全屏, Home键在左侧 - SJAutoRotateSupportedOrientation_All: 全部方向 */ typedef NS_ENUM(NSUInteger, SJAutoRotateSupportedOrientation) { SJAutoRotateSupportedOrientation_Portrait = 1 << 0, SJAutoRotateSupportedOrientation_LandscapeLeft = 1 << 1, SJAutoRotateSupportedOrientation_LandscapeRight = 1 << 2, SJAutoRotateSupportedOrientation_All = SJAutoRotateSupportedOrientation_Portrait | SJAutoRotateSupportedOrientation_LandscapeLeft | SJAutoRotateSupportedOrientation_LandscapeRight, }; ```

6.3 禁止自动旋转

这里有两点需要注意: - 合适的时候要记得恢复自动旋转. - 禁止自动旋转后, 主动调用旋转, 还是可以旋转的.

```Objective-C _player.disableAutoRotation = YES; ```

6.4 主动调用旋转

主动旋转. 当我们想主动旋转时, 大概分为以下三点: - 播放器旋转到用户当前的设备方向或恢复小屏. - 主动旋转到指定方向. - 主动旋转完成后的回调. 请看以下方法, 分别对应以上三点:

```Objective-C - (void)rotate; - (void)rotate:(SJOrientation)orientation animated:(BOOL)animated; - (void)rotate:(SJOrientation)orientation animated:(BOOL)animated completion:(void (^ _Nullable)(__kindof SJBaseVideoPlayer *player))block; ```

6.5 是否全屏

```Objective-C /// 如果为YES, 表示全屏 _player.isFullScreen ```

6.6 是否正在旋转

```Objective-C /// 如果为YES, 表示正在旋转中 _player.isTransitioning ```

6.7 当前旋转的方向

```Objective-C _player.orientation ```

6.8 旋转开始和结束的回调

```Objective-C _observer = [self.rotationManager getObserver]; _observer.rotationDidStartExeBlock = ^(id _Nonnull mgr) { /// ... }; _observer.rotationDidEndExeBlock = ^(id _Nonnull mgr) { /// ... }; ```

6.9 自己动手撸一个 SJRotationManager, 替换作者原始实现

当你想替换原始实现时, 可以实现 SJRotationManagerProtocol 中定义的方法. ___

7. 直接全屏而不旋转

直接全屏, 或者说充满屏幕, 但不旋转.

7.1 全屏和恢复

```Objective-C _player.fitOnScreen = YES; [_player setFitOnScreen:NO animated:NO]; [_player setFitOnScreen:YES animated:YES completionHandler:^(__kindof SJBaseVideoPlayer * _Nonnull player) { /// ... }]; ```

7.2 开始和结束的回调

```Objective-C @property (nonatomic, copy, nullable) void(^fitOnScreenWillBeginExeBlock)(__kindof SJBaseVideoPlayer *player); @property (nonatomic, copy, nullable) void(^fitOnScreenDidEndExeBlock)(__kindof SJBaseVideoPlayer *player);; ```

7.3 是否是全屏

```Objective-C /// YES 为充满屏幕 _player.isFitOnScreen ```

7.4 自己动手撸一个 SJFitOnScreenManager, 替换作者原始实现

该部分管理类的协议定义在 SJFitOnScreenManagerProtocol 中, 实现该协议的任何对象, 均可赋值给播放器, 替换原始实现. ___

8. 镜像翻转

此部分内容由 id<SJFlipTransitionManager> flipTransitionManager 提供支持 目前镜像翻转只写了 水平翻转, 未来可能会加入更多的翻转类型.

```Objective-C typedef enum : NSUInteger { SJViewFlipTransition_Identity, SJViewFlipTransition_Horizontally, // 水平翻转 } SJViewFlipTransition; ```

8.1 翻转和恢复

```Objective-C /// 当前的翻转类型 _player.flipTransition /// 翻转相关方法 [_player setFlipTransition:SJViewFlipTransition_Horizontally]; [_player setFlipTransition:SJViewFlipTransition_Horizontally animated:YES]; [_player setFlipTransition:SJViewFlipTransition_Identity animated:YES completionHandler:^(__kindof SJBaseVideoPlayer * _Nonnull player) { /// ... }]; ```

8.2 开始和结束的回调

```Objective-C @property (nonatomic, copy, nullable) void(^flipTransitionDidStartExeBlock)(__kindof SJBaseVideoPlayer *player); @property (nonatomic, copy, nullable) void(^flipTransitionDidStopExeBlock)(__kindof SJBaseVideoPlayer *player); ```

8.3 自己动手撸一个 SJFlipTransitionManager, 替换作者原始实现

该部分管理类的协议定义在 SJFlipTransitionManagerProtocol 中, 实现该协议的任何对象, 均可赋值给播放器, 替换原始实现.

___

9. 网络状态

此部分内容由 id<SJReachability> reachability 提供支持 默认的 reachability 是个单例, 在App生命周期中, 仅创建一次. 因此每个播放器对象持有的 reachability 都是相同的.

9.1 当前的网络状态

```Objective-C @property (nonatomic, readonly) SJNetworkStatus networkStatus; ```

9.2 网络状态改变的回调

```Objective-C @property (nonatomic, copy, nullable) void(^networkStatusDidChangeExeBlock)(__kindof SJBaseVideoPlayer *player); ```

9.3 自己动手撸一个 SJReachability, 替换作者原始实现

该部分管理类的协议定义在 SJNetworkStatus 中, 实现该协议的任何对象, 均可赋值给播放器, 替换原始实现.

___

10. 手势

此部分内容由 id<SJPlayerGestureControl> gestureControl 提供支持 播放器默认存在四种手势, 每个手势触发的回调均定义在 SJPlayerGestureControl 中, 当想改变某个手势的处理时, 可以直接修改对应手势触发的 block 即可. 具体请看以下部分.

10.1 单击手势

当用户单击播放器时, 播放器会调用 [显示或隐藏控制层的操作](#4) 以下为默认实现: ```Objective-C __weak typeof(self) _self = self; _gestureControl.singleTapHandler = ^(id _Nonnull control, CGPoint location) { __strong typeof(_self) self = _self; if ( !self ) return ; /// 让控制层显示或隐藏 [self.controlLayerAppearManager switchAppearState]; }; ```

10.2 双击手势

双击会触发暂停或播放的操作

```Objective-C __weak typeof(self) _self = self; _gestureControl.doubleTapHandler = ^(id _Nonnull control, CGPoint location) { __strong typeof(_self) self = _self; if ( !self ) return ; if ( [self playStatus_isPlaying] ) [self pause]; else [self play]; }; ```

10.3 移动手势

- 垂直滑动时, 默认情况下如果在屏幕左边, 则会触发调整亮度的操作, 并显示亮度提示视图. 如果在屏幕右边, 则会触发调整声音的操作, 并显示系统音量提示视图 - 水平滑动时, 会触发控制层相应的代理方法 ```Objective-C __weak typeof(self) _self = self; _gestureControl.panHandler = ^(id _Nonnull control, SJPanGestureTriggeredPosition position, SJPanGestureMovingDirection direction, SJPanGestureRecognizerState state, CGPoint translate) { __strong typeof(_self) self = _self; if ( !self ) return ; /// .... }; ```

10.4 捏合手势

当用户做放大或收缩触发该手势时, 会设置播放器显示模式`Aspect`或`AspectFill`.

```Objective-C __weak typeof(self) _self = self; _gestureControl.pinchHandler = ^(id _Nonnull control, CGFloat scale) { __strong typeof(_self) self = _self; if ( !self ) return ; self.playbackController.videoGravity = scale > 1 ?AVLayerVideoGravityResizeAspectFill:AVLayerVideoGravityResizeAspect; }; ```

10.5 禁止某些手势

当需要禁止某个手势时, 可以像如下设置:

```Objective-C /// 此处为禁止单击和双击手势 _player.disabledGestures = SJPlayerGestureType_SingleTap | SJPlayerGestureType_DoubleTap; typedef enum : NSUInteger { SJPlayerGestureType_SingleTap, SJPlayerGestureType_DoubleTap, SJPlayerGestureType_Pan, SJPlayerGestureType_Pinch, } SJPlayerGestureType; ```

10.6 自定义某个手势的处理

```Objective-C /// 例如 替换单击手势的处理 __weak typeof(self) _self = self; _player.gestureControl.singleTapHandler = ^(id _Nonnull control, CGPoint location) { __strong typeof(_self) self = _self; if ( !self ) return ; /// .....你的处理 }; ```

10.7 自己动手撸一个 SJPlayerGestureControl, 替换作者原始实现

该部分管理类的协议定义在 SJPlayerGestureControlProtocol 中, 实现该协议的任何对象, 均可赋值给播放器, 替换原始实现.

___

11. 占位图

资源在初始化时, 由于暂时没有画面可以呈现, 会出现短暂的黑屏. 在此期间, 建议大家设置一下占位图.

11.1 设置本地占位图

```Objective-C _player.placeholderImageView.image = [UIImage imageNamed:@"..."]; ```

11.2 设置网络占位图

```Objective-C [_player.placeholderImageView sd_setImageWithURL:URL placeholderImage:img]; ```

11.3 是否隐藏占位图 - 播放器准备好显示时

```Objective-C /// 播放器准备好显示时, 是否隐藏占位图 /// - 默认为YES @property (nonatomic) BOOL hiddenPlaceholderImageViewWhenPlayerIsReadyForDisplay; ``` ___

12. 显示提示文本

目前提示文本支持 NSString 以及 NSAttributedString.

12.1 显示文本及持续时间 - (NSString or NSAttributedString)

```Objective-C /// duration 如果为 -1, 则会一直显示 - (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; /// 隐藏 - (void)hiddenTitle; ```

12.2 配置提示文本

```Objective-C /// Update _player.prompt.update(^(SJPromptConfig * _Nonnull config) { config.font = [UIFont systemFontOfSize:12]; }); /// 所有属性如下: @interface SJPromptConfig : NSObject /// default is UIEdgeInsetsMake( 8, 8, 8, 8 ). @property (nonatomic, assign) UIEdgeInsets insets; /// default is 8. @property (nonatomic, assign) CGFloat cornerRadius; /// default is black. @property (nonatomic, strong) UIColor *backgroundColor; /// default is systemFont( 14 ). @property (nonatomic, assign) UIFont *font; /// default is white. @property (nonatomic, strong) UIColor *fontColor; /// default is ( superview.width * 0.6 ). @property (nonatomic, assign) CGFloat maxWidth; - (void)reset; @end ``` ___

13. 一些固定代码

接入播放器的 ViewController 中, 会写一些固定的代码, 我将这些固定代码(例如 进入下个页面时, 需要当前页面的播放器暂停), 都封装在了以下方法中. ```Objective-C - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [_player vc_viewDidAppear]; } ``` 在适当的时候直接调用即可, 以下为内部实现:

13.1 - (void)vc_viewDidAppear;

当 ViewController 的 viewDidAppear 调用时, 恢复播放 实现如下:

```Objective-C - (void)vc_viewDidAppear { if ( !self.isPlayOnScrollView || (self.isPlayOnScrollView && self.isScrollAppeared) ) { /// 恢复播放 [self play]; } /// 标识vc已显示 /// vc_isDisappeared 是自动旋转触发的条件之一, 如果控制器 disappear 了, 就不会触发旋转 self.vc_isDisappeared = NO; } ```

13.2 - (void)vc_viewWillDisappear;

当 ViewController 的 viewWillDisappear 调用时, 设置标识为YES 实现如下:

```Objective-C - (void)vc_viewWillDisappear { /// 标识vc已显示 /// vc_isDisappeared 是自动旋转触发的条件之一, 如果控制器 disappear 了, 就不会触发旋转 self.vc_isDisappeared = YES; } ```

13.3 - (void)vc_viewDidDisappear;

当 ViewController 的 viewDidDisappear 调用时, 暂停播放 实现如下:

```Objective-C - (void)vc_viewDidDisappear { [self pause]; } ```

13.4 - (BOOL)vc_prefersStatusBarHidden;

状态栏是否可以隐藏 实现如下:

```Objective-C - (BOOL)vc_prefersStatusBarHidden { if ( _tmpShowStatusBar ) return NO; // 临时显示 if ( _tmpHiddenStatusBar ) return YES; // 临时隐藏 if ( self.lockedScreen ) return YES; // 锁屏时, 不显示 if ( self.rotationManager.isTransitioning ) { // 旋转时, 不显示 if ( !self.disabledControlLayerAppearManager && self.controlLayerIsAppeared ) return NO; return YES; } // 全屏播放时, 使状态栏根据控制层显示或隐藏 if ( self.isFullScreen ) return !self.controlLayerIsAppeared; return NO; } ```

13.5 - (UIStatusBarStyle)vc_preferredStatusBarStyle;

状态栏显示白色还是黑色 实现如下:

```Objective-C - (UIStatusBarStyle)vc_preferredStatusBarStyle { // 全屏播放时, 使状态栏变成白色 if ( self.isFullScreen || self.fitOnScreen ) return UIStatusBarStyleLightContent; return UIStatusBarStyleDefault; } ```

13.6 - 临时显示状态栏

有时候, 可能会希望临时显示状态栏, 例如全屏转回小屏时, 旋转之前, 需要将状态栏显示.

```Objective-C [_player needShowStatusBar]; ```

13.7 - 临时隐藏状态栏

有时候, 可能会希望临时隐藏状态栏, 例如某个播放器控制层不需要显示状态栏.

```Objective-C [_player needHiddenStatusBar]; ``` ___

14. 截屏

14.1 当前时间截图

```Objective-C UIImage *img = [_player screenshot]; ```

14.2 指定时间截图

```Objective-C - (void)screenshotWithTime:(NSTimeInterval)secs completion:(void(^)(__kindof SJBaseVideoPlayer *videoPlayer, UIImage * __nullable image, NSError *__nullable error))block; /// 可以通过 _player.playbackController.presentationSize 来获取当前视频宽高 - (void)screenshotWithTime:(NSTimeInterval)secs size:(CGSize)size completion:(void(^)(__kindof SJBaseVideoPlayer *videoPlayer, UIImage * __nullable image, NSError *__nullable error))block; ```

14.3 生成预览视图, 大约20张

```Objective-C /// 可以通过 _player.playbackController.presentationSize 来获取当前视频宽高 /// itemSize 应该尽可能的小一点, 这样处理的效率会更快 - (void)generatedPreviewImagesWithMaxItemSize:(CGSize)itemSize completion:(void(^)(__kindof SJBaseVideoPlayer *player, NSArray> *__nullable images, NSError *__nullable error))block; ```

15. 导出视频或GIF

15.1 导出视频

```Objective-C /** export session. @param beginTime 开始的位置, 单位是秒 @param endTime 结束的位置, 单位是秒 @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; ```

15.2 导出GIF

```Objective-C /** 生成GIF @param beginTime 开始的位置, 单位是秒 @param duration 时长 @param progressBlock 进度回调 @param completion 完成的回调 @param failure 失败的回调 */ - (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; ```

15.3 取消操作

```Objective-C /// 取消导出操作 /// 播放器 dealloc 时, 会调用一次 - (void)cancelExportOperation; /// 取消GIF操作 /// 播放器 dealloc 时, 会调用一次 - (void)cancelGenerateGIFOperation; ```

16. 滚动相关

此部分的内容由 SJPlayModelPropertiesObserver 提供支持.

16.1 是否在 UICollectionView 或者 UITableView 中播放

```Objective-C /// 是否是在 UICollectionView 或者 UITableView 中播放 _player.isPlayOnScrollView ```

16.2 是否滚动显示

```Objective-C /// 是否滚动显示 _player.isScrollAppeared ```

16.3 播放器视图将要滚动显示和消失的回调

```Objective-C @property (nonatomic, copy, nullable) void(^playerViewWillAppearExeBlock)(__kindof SJBaseVideoPlayer *videoPlayer); @property (nonatomic, copy, nullable) void(^playerViewWillDisappearExeBlock)(__kindof SJBaseVideoPlayer *videoPlayer); ```

17. 自动播放 - 在 UICollectionView 或者 UITableView 中

目前支持在 UICollectionViewCell 和 UITableViewCell 中自动播放. 使用之前, 请导入头文件 `#import "UIScrollView+ListViewAutoplaySJAdd.h"`

17.1 开启

```Objective-C /// 配置列表自动播放 [_tableView sj_enableAutoplayWithConfig:[SJPlayerAutoplayConfig configWithPlayerSuperviewTag:101 autoplayDelegate:self]]; /// Delegate method - (void)sj_playerNeedPlayNewAssetAtIndexPath:(NSIndexPath *)indexPath { } ```

17.2 配置

```Objective-C typedef NS_ENUM(NSUInteger, SJAutoplayScrollAnimationType) { SJAutoplayScrollAnimationTypeNone, SJAutoplayScrollAnimationTypeTop, SJAutoplayScrollAnimationTypeMiddle, }; @interface SJPlayerAutoplayConfig : NSObject + (instancetype)configWithPlayerSuperviewTag:(NSInteger)playerSuperviewTag autoplayDelegate:(id)autoplayDelegate; /// 滚动的动画类型 /// default is .Middle; @property (nonatomic) SJAutoplayScrollAnimationType animationType; @property (nonatomic, readonly) NSInteger playerSuperviewTag; @property (nonatomic, weak, nullable, readonly) id autoplayDelegate; @end @protocol SJPlayerAutoplayDelegate - (void)sj_playerNeedPlayNewAssetAtIndexPath:(NSIndexPath *)indexPath; @end ```

17.3 关闭

```Objective-C [_tableView sj_disenableAutoplay]; ```

17.4 主动调用播放下一个资源

```Objective-C [_tableView sj_needPlayNextAsset]; ``` ___

18. 控制层数据源, 每个方法介绍

18.1 - (UIView *)controlView;

controlView 为控制层的视图, 它将会被添加到播放器中

18.2 - (void)installedControlViewToVideoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer;

当播放器将[controlView](#18.1)添加到播放器视图中后, 会回调这个方法. ___

19. 控制层代理, 每个方法介绍

19.1 - (void)controlLayerNeedAppear:(__kindof SJBaseVideoPlayer *)videoPlayer;

控制层需要显示的时候, 会回调这个方法. 你应该在这里做一些显示的工作. 当调用 `[_player controlLayerNeedAppear]` 时, 此时会立即回调这个方法

19.2 - (void)controlLayerNeedDisappear:(__kindof SJBaseVideoPlayer *)videoPlayer;

当控制层需要隐藏的时候, 会回调这个方法. 你应该在这里做一些隐藏的工作. 关于控制层的隐藏: 默认情况下(videoPlayer.enableControlLayerDisplayController==YES) - 当调用[videoPlayer controlLayerNeedDisappear]时, 此时会立即回调这个方法 - 当控制层显示时, 默认会在3秒后, 自动调用这个方法, 隐藏控制层

19.3 - (BOOL)controlLayerOfVideoPlayerCanAutomaticallyDisappear:(__kindof SJBaseVideoPlayer *)videoPlayer;

控制层是否可以自动隐藏, 返回NO, 播放器将不会调用[隐藏的代理方法](#19.2).

19.4 - (void)videoPlayerWillAppearInScrollView:(__kindof SJBaseVideoPlayer *)videoPlayer;

滚动 scrollView 时, 播放器即将出现时会回调这个方法.

19.5 - (void)videoPlayerWillDisappearInScrollView:(__kindof SJBaseVideoPlayer *)videoPlayer;

滚动scrollView时, 播放器即将消失时会回调这个方法.

SJPlayStatusControlDelegate

* [19.9 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer presentationSize:(CGSize)size;](#19.9)

19.6 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer prepareToPlay:(SJVideoPlayerURLAsset *)asset;

当播放器播放一个新的资源时, 会回调这个方法

19.7 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer statusDidChanged:(SJVideoPlayerPlayStatus)status;

当播放状态改变时, 会回调这个方法

19.8 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer currentTime:(NSTimeInterval)currentTime currentTimeStr:(NSString *)currentTimeStrtotalTime:(NSTimeInterval)totalTime totalTimeStr:(NSString *)totalTimeStr;

播放时间改变的回调

19.9 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer presentationSize:(CGSize)size;

播放器获取到视频宽高后, 会回调这个方法

SJVolumeBrightnessRateControlDelegate

19.10 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer muteChanged:(BOOL)mute;

设置静音时, 会回调这个方法

19.11 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer volumeChanged:(float)volume;

设置系统音量时, 会回调这个方法

19.12 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer brightnessChanged:(float)brightness;

设置系统亮度时, 会回调这个方法

19.13 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer rateChanged:(float)rate;

设置速率时, 会回调这个方法

SJBufferControlDelegate

19.14 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer bufferTimeDidChange:(NSTimeInterval)bufferTime;

缓冲时间改变的回调.

19.15 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer bufferStatusDidChange:(SJPlayerBufferStatus)bufferStatus;

缓冲状态改变的回调

SJRotationControlDelegate

19.16 - (BOOL)canTriggerRotationOfVideoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer;

播放器是否可以出发自动旋转.

19.17 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer willRotateView:(BOOL)isFull;

开始旋转的回调

19.18 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer didEndRotation:(BOOL)isFull;

结束旋转的回调

SJFitOnScreenControlDelegate

19.19 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer willFitOnScreen:(BOOL)isFitOnScreen;

将要充满屏幕的回调

19.20 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer didCompleteFitOnScreen:(BOOL)isFitOnScreen;

完成后的回调

SJGestureControlDelegate

19.24 - (BOOL)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer gestureRecognizerShouldTrigger:(SJPlayerGestureType)type location:(CGPoint)location;

是否可以出发某个手势

19.25 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer panGestureTriggeredInTheHorizontalDirection:(SJPanGestureRecognizerState)state progressTime:(NSTimeInterval)progressTime;

水平方向拖动

SJNetworkStatusControlDelegate

19.26 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer reachabilityChanged:(SJNetworkStatus)status;

网络状态变更的回调.

SJLockScreenStateControlDelegate

19.27 - (void)tappedPlayerOnTheLockedState:(__kindof SJBaseVideoPlayer *)videoPlayer;

这是一个只有在播放器锁屏状态下, 才会回调的方法 当播放器锁屏后, 用户每次点击都会回调这个方法

19.28 - (void)lockedVideoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer;

锁屏后的回调

19.29 - (void)unlockedVideoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer;

解锁后的回调

SJSwitchVideoDefinitionControlDelegate

19.30 - (void)videoPlayer:(__kindof SJBaseVideoPlayer *)videoPlayer switchVideoDefinitionByURL:(NSURL *)URL statusDidChange:(SJMediaPlaybackSwitchDefinitionStatus)status;

切换分辨率的回调