SJVideoPlayer

iOS VideoPlayer MediaPlayer video player media player 短视频播放器 可接入 ijkplayer aliplayer alivodplayer plplayer

Github stars Tracking Chart

readme

Build Status
Version
Platform
License

Getting Started

wiki: https://github.com/changsanjiang/SJVideoPlayer/wiki

Installation

# 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/Queues', :git => 'https://gitee.com/changsanjiang/SJUIKit.git'
$ pod update --no-repo-update   (不要用 pod install 了, 用这个命令安装)

切换别的播放器SDK

本项目对播放控制默认封装的是 AVPlayer, 以下为切换别的播放器SDK:

Example

_player = [SJVideoPlayer player];
_player.view.frame = CGRectMake(0, 0, 200, 200);
[self.view addSubview:_player.view];

// 设置资源进行播放
_player.URLAsset = [[SJVideoPlayerURLAsset alloc] initWithURL:URL];

... 等等, 更多设置, 请查看头文件. 相应功能均为懒加载, 用到时才会创建. 

Author

Email: changsanjiang@gmail.com

QQGroup: 930508201 (iOS 开发)

大佬辛苦, 犒赏一下?

Documents

v2.6.5 往后的版本, 请配置旋转 !!

1. 视图层次结构

2. URLAsset

3. 播放控制

4. 控制层的显示和隐藏

5. 设备亮度和音量

6. 旋转

7. 直接全屏而不旋转

8. 镜像翻转

9. 网络状态

10. 手势

11. 占位图

12. 显示提示文本

13. 一些固定代码

14. 截屏

15. 导出视频或GIF

16. 滚动相关

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

18. 对控制层上的Item的操作

19. 对控制层上的Item的一些补充

20. SJEdgeControlLayer 的补充


以下为详细介绍:

_player = [SJVideoPlayer player];
_player.view.frame = ...;
[self.view addSubview:_player.view];

// 设置资源进行播放
SJVideoPlayerURLAsset *asset = [[SJVideoPlayerURLAsset alloc] initWithURL:URL];
_player.URLAsset = asset;

在普通视图中播放时, 不需要指定视图层次, 直接创建资源进行播放即可.


由于 UITableView 及 UICollectionView 的复用机制, 会导致播放器视图显示在错误的位置上, 为防止出现此种情况, 在创建资源时指定视图层次结构, 使得播放器能够定位具体的父视图, 依此来控制隐藏与显示.


--  UITableView
--  UITableViewCell
--  Player.superview
--  Player.view


_player = [SJVideoPlayer player];

UIView *playerSuperview = cell.coverImageView;
SJPlayModel *playModel = [SJPlayModel UITableViewCellPlayModelWithPlayerSuperviewTag:playerSuperview.tag atIndexPath:indexPath tableView:self.tableView];

_player.URLAsset = [[SJVideoPlayerURLAsset alloc] initWithURL:URL playModel:playModel];

在 UITableViewCell 中播放时, 需指定 Cell 所处的 indexPath 以及播放器父视图的 tag.

在滑动时, 管理类将会通过这两个参数控制播放器视图的显示与隐藏.


--  UITableView
--  UITableView.tableHeaderView 或者 UITableView.tableFooterView  
--  Player.superview
--  Player.view

UIView *playerSuperview = self.tableView.tableHeaderView;
// 也可以设置子视图
// playerSuperview = self.tableView.tableHeaderView.coverImageView;
SJPlayModel *playModel = [SJPlayModel UITableViewHeaderViewPlayModelWithPlayerSuperview:playerSuperview tableView:self.tableView];

--  UITableView
--  UITableView.tableHeaderView 或者 UITableView.tableFooterView  
--  Player.superview
--  Player.view

UIView *playerSuperview = self.tableView.tableFooterView;
// 也可以设置子视图
// playerSuperview = self.tableView.tableFooterView.coverImageView;
SJPlayModel *playModel = [SJPlayModel UITableViewHeaderViewPlayModelWithPlayerSuperview:playerSuperview tableView:self.tableView];

--  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];

在 UICollectionView 中播放时, 同 UITableView 中一样, 需指定视图层次, 使得播放器能够定位具体的父视图, 依此来控制隐藏与显示.


--  UICollectionView
--  UICollectionViewCell
--  Player.superview
--  Player.view

SJPlayModel *playModel = [SJPlayModel UICollectionViewCellPlayModelWithPlayerSuperviewTag:cell.coverImageView.tag atIndexPath:indexPath collectionView:self.collectionView];

嵌套的情况下, 传递的参数比较多, 不过熟悉了前面的套路, 下面的这些也不成问题. (会被复用的视图, 传 tag. 如果不会被复用, 则直接传视图)


--  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];

--  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];

--  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];

播放器 播放的资源是通过 SJVideoPlayerURLAsset 创建的. SJVideoPlayerURLAsset 由两部分组成:

视图层次 (第一部分中的SJPlayModel)
资源地址 (可以是本地资源/URL/AVAsset)

默认情况下, 创建了 SJVideoPlayerURLAsset , 赋值给播放器后即可播放.


NSURL *URL = [NSURL URLWithString:@"https://...example.mp4"];
_player.URLAsset = [[SJVideoPlayerURLAsset alloc] initWithURL:URL];
_player.URLAsset = [[SJVideoPlayerURLAsset alloc] initWithAVAsset:avAsset];
NSTimeInterval secs = 20.0;
_player.URLAsset = [[SJVideoPlayerURLAsset alloc] initWithURL:URL specifyStartTime:secs]; // 直接从20秒处开始播放

我们可能需要切换界面时, 希望视频能够在下一个界面无缝的进行播放. 使用如下方法, 传入正在播放的资源, 将新的资源赋值给播放器播放即可.

// otherAsset 即为上一个页面播放的Asset
// 除了需要一个otherAsset, 其他方面同以上的示例一模一样
_player.URLAsset = [SJVideoPlayerURLAsset.alloc initWithOtherAsset:otherAsset]; 
// 每个资源dealloc时的回调
_player.assetDeallocExeBlock = ^(__kindof SJBaseVideoPlayer * _Nonnull videoPlayer) {
// .....
};

当资源销毁时, 播放器将会回调该 block.


大多数对播放进行的操作, 均在协议 SJMediaPlaybackController 进行了声明.

正常来说实现了此协议的任何对象, 均可赋值给 player.playbackController 来替换原始实现.

[_player play];
[_player pause];

此时当用户点击刷新按钮, 我们需要对当前的资源(Asset)进行刷新.

SJBaseVideoPlayer提供了直接的方法去刷新, 不需要开发者再重复的去创建新的Asset.

[_player refresh];
[_player replay];
[_player stop];
_player.muted = YES;
// 默认值为 1.0
_player.rate = 1.0;

当播放发生错误时, 可以通过它来获取错误信息

_player.error
///
/// 是否精确跳转, default value is NO.
///
@property (nonatomic) BOOL accurateSeeking;

///
/// 跳转到指定位置播放
///
- (void)seekToTime:(NSTimeInterval)secs completionHandler:(void (^ __nullable)(BOOL finished))completionHandler;
- (void)seekToTime:(CMTime)time toleranceBefore:(CMTime)toleranceBefore toleranceAfter:(CMTime)toleranceAfter completionHandler:(void (^ __nullable)(BOOL finished))completionHandler;
///
/// 切换清晰度
///
- (void)switchVideoDefinition:(SJVideoPlayerURLAsset *)URLAsset;

///
/// 当前清晰度切换的信息
///
@property (nonatomic, strong, readonly) SJVideoDefinitionSwitchingInfo *definitionSwitchingInfo;

/// 以下为设置 SJVideoPlayer.definitionURLAssets, 将会在清晰度切换控制层中显示这些资源项. 

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;
@property (nonatomic, readonly) NSTimeInterval currentTime;                         ///< 当前播放到的时间
@property (nonatomic, readonly) NSTimeInterval duration;                            ///< 总时长
@property (nonatomic, readonly) NSTimeInterval playableDuration;                    ///< 缓冲到的时间
@property (nonatomic, readonly) BOOL isPlayedToEndTime;                             ///< 当前资源是否已播放结束
@property (nonatomic, readonly) BOOL isPlayed;                                      ///< 是否播放过当前的资源
@property (nonatomic, readonly) BOOL isReplayed;                                    ///< 是否重播过当前的资源
@property (nonatomic) BOOL autoplayWhenSetNewAsset;                    ///< 设置新的资源后, 是否自动调用播放. 默认为 YES

当您想在后台播放视频时:

  1. 需要设置 videoPlayer.pauseWhenAppDidEnterBackground = NO; (该值默认为YES, 即App进入后台默认暂停).

  2. 前往 TARGETS -> Capability -> enable Background Modes -> select this mode Audio, AirPlay, and Picture in Picture

_player.pauseWhenAppDidEnterBackground = NO; // 默认值为 YES, 即进入后台后 暂停.
@property (nonatomic) BOOL resumePlaybackWhenAppDidEnterForeground;    ///< 进入前台时, 是否恢复播放. 默认为 NO
@property (nonatomic) BOOL resumePlaybackWhenPlayerHasFinishedSeeking; ///< 当`seekToTime:`操作完成后, 是否恢复播放. 默认为 YES

资源准备(或初始化)的状态

当未设置资源时, 此时 player.assetStatus = .unknown
当设置新资源时, 此时 player.assetStatus = .preparing
当准备好播放时, 此时 player.assetStatus = .readyToPlay
当初始化失败时, 此时 player.assetStatus = .failed

typedef NS_ENUM(NSInteger, SJAssetStatus) {
///
/// 未知状态
///
SJAssetStatusUnknown,

///
/// 准备中
///
SJAssetStatusPreparing,

///
/// 当前资源可随时进行播放(播放控制请查看`timeControlStatus`)
///
SJAssetStatusReadyToPlay,

///
/// 发生错误
///
SJAssetStatusFailed
};

暂停或播放的控制状态

当调用了暂停时, 此时 player.timeControlStatus = .paused

当调用了播放时, 此时 将可能处于以下两种状态中的任意一个:

  • player.timeControlStatus = .playing
    正在播放中.

  • player.timeControlStatus = .waitingToPlay
    等待播放, 等待的原因请查看 player.reasonForWaitingToPlay

typedef NS_ENUM(NSInteger, SJPlaybackTimeControlStatus) {
///
/// 暂停状态(已调用暂停或未执行任何操作的状态)
///
SJPlaybackTimeControlStatusPaused,

///
/// 播放状态(已调用播放), 当前正在缓冲或正在评估能否播放. 可以通过`reasonForWaitingToPlay`来获取原因, UI层可以根据原因来控制loading视图的状态.
///
SJPlaybackTimeControlStatusWaitingToPlay,

///
/// 播放状态(已调用播放), 当前播放器正在播放
///
SJPlaybackTimeControlStatusPlaying
};

当调用了播放, 播放器未能播放处于等待状态时的原因

等待原因有以下3种状态:
1.未设置资源, 此时设置资源后, 当player.assetStatus = .readyToPlay, 播放器将自动进行播放.
2.可能是由于缓冲不足, 播放器在等待缓存足够时自动恢复播放, 此时可以显示loading视图.
3.可能是正在评估缓冲中, 这个过程会进行的很快, 不需要显示loading视图.

///
/// 缓冲中, UI层建议显示loading视图 
///
extern SJWaitingReason const SJWaitingToMinimizeStallsReason;

///
/// 正在评估能否播放, 处于此状态时, 不建议UI层显示loading视图
///
extern SJWaitingReason const SJWaitingWhileEvaluatingBufferingRateReason;

///
/// 未设置资源
///
extern SJWaitingReason const SJWaitingWithNoAssetToPlayReason;
///
/// 观察者
///
///         可以如下设置block, 来监听某个状态的改变
///         了解更多请前往头文件查看
///         player.playbackObserver.currentTimeDidChangeExeBlock = ...;
///         player.playbackObserver.durationDidChangeExeBlock = ...;
///         player.playbackObserver.timeControlStatusDidChangeExeBlock = ...;
///
@property (nonatomic, strong, readonly) SJPlaybackObservation *playbackObserver;
@property (nonatomic, readonly) NSTimeInterval durationWatched;                     ///< 已观看的时长(当前资源)

这个时候, 我们可以自己动手, 将第三方的SDK封装一下, 实现 SJVideoPlayerPlaybackController 协议, 管理 SJBaseVideoPlayer 中的播放操作.

示例:

_player.playbackController = Your PlaybackController.

controlLayerAppearManager 内部存在一个定时器, 当控制层显示时, 会开启此定时器. 一定间隔后, 会尝试隐藏控制层.

其他相关操作, 请见以下内容.

[_player controlLayerNeedAppear];

此方法将会回调控制层的代理方法:

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

代理将会对当前的控制层进行显示处理.

[_player controlLayerNeedDisappear];

此方法将会回调控制层的代理方法:

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

代理将会对当前的控制层进行隐藏处理.

///
/// 控制层的显示状态(是否已显示)
///
@property (nonatomic, getter=isControlLayerAppeared) BOOL controlLayerAppeared;
///
/// 暂停的时候是否保持控制层显示
///
///         default value is NO
///
@property (nonatomic) BOOL pausedToKeepAppearState;
///
/// 观察者
///
///         当需要监听控制层的显示和隐藏时, 可以设置`player.controlLayerAppearObserver.appearStateDidChangeExeBlock = ...;`
///
@property (nonatomic, strong, readonly) id<SJControlLayerAppearManagerObserver> controlLayerAppearObserver;
_player.controlLayerAppearManager = Your controlLayerAppearManager; 

// 0 到 1
_player.deviceVolumeAndBrightnessManager.brightness = 1.0;
// 0 到 1
_player.deviceVolumeAndBrightnessManager.volume = 1.0;
///
/// 观察者
///
@property (nonatomic, strong, readonly) id<SJDeviceVolumeAndBrightnessManagerObserver> deviceVolumeAndBrightnessObserver;
_player.disableBrightnessSetting = YES;
_player.disableVolumeSetting = YES;
_player.deviceVolumeAndBrightnessManager = Your deviceVolumeAndBrightnessManager;

对于旋转, 我们开发者肯定需要绝对的控制, 例如: 设置自动旋转所支持方向. 能够主动+自动旋转, 而且还需要能在适当的时候禁止自动旋转. 旋转前后的回调等等... 放心这些功能都有, 我挨个给大家介绍.

具体请看下面介绍.

请查看该指南: https://github.com/changsanjiang/SJVideoPlayer/wiki/Getting-Started#configure-rotation

/// 设置自动旋转支持的方向
_player.supportedOrientations = SJOrientationMaskLandscapeLeft

Overview

Name With Ownerchangsanjiang/SJVideoPlayer
Primary LanguageObjective-C
Program languageObjective-C (Language Count: 3)
PlatformiOS
License:MIT License
Release Count174
Last Release Namev3.4.3 (Posted on 2022-11-02 19:06:52)
First Release Namev0.0.1 (Posted on )
Created At2017-08-18 08:56:30
Pushed At2022-12-10 08:57:54
Last Commit At2022-12-10 16:57:46
Stargazers Count2.4k
Watchers Count38
Fork Count471
Commits Count1.1k
Has Issues Enabled
Issues Count615
Issue Open Count172
Pull Requests Count14
Pull Requests Open Count4
Pull Requests Close Count1
Has Wiki Enabled
Is Archived
Is Fork
Is Locked
Is Mirror
Is Private
To the top