Latest 2.2.3
Homepage https://github.com/changsanjiang/SJBaseVideoPlayer
License MIT
Platforms ios 8.0, requires ARC
Dependencies Masonry, SJObserverHelper, Reachability
Frameworks UIKit, AVFoundation
Authors

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'

Contact


License

SJBaseVideoPlayer is available under the MIT license. See the LICENSE file for more info.


Documents

1 视图层次

2. 创建资源进行播放

3. 播放控制

4. 控制层的显示和隐藏

5. 设备亮度和音量

6. 旋转

7. 直接全屏而不旋转

8. 镜像翻转

9. 网络状态

10. 手势

11. 占位图

12. 显示提示文本

13. 一些固定代码

14. 截屏

15. 导出视频或GIF

16. 滚动相关

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

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

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











1. 视图层次

SJBaseVideoPlayer 播放的视频资源是通过 SJVideoPlayerURLAsset 进行初始化的. SJVideoPlayerURLAsset 由两部分组成:

– 视图层次 (SJPlayModel)
– 资源地址 (可以是本地资源/URL/AVAsset)

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

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

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

1.1 在普通 View 上播放

在普通视图中播放时, 直接创建PlayModel即可.

SJPlayModel *playModel = [SJPlayModel new];

1.2 在 TableViewCell 上播放

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

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

1.3 在 TableHeaderView 或者 TableFooterView 上播放

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

SJPlayModel *playModel = [SJPlayModel UITableViewHeaderViewPlayModelWithPlayerSuperview:view.coverImageView tableView:self.tableView];

1.4 在 CollectionViewCell 上播放

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

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

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

--  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 上播放

--  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 上播放

--  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 上播放

--  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 , 赋值给播放器后即可播放. 如下示例:

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

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

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

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

_player.URLAsset = [[SJVideoPlayerURLAsset alloc] initWithAVAsset:avAsset playModel:playModel];

2.3 指定开始播放的时间

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

NSTimeInterval secs = 20.0;
_player.URLAsset.specifyStartTime = secs;

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

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

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

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

我们有时候想存储某个视频的播放记录, 以便下次, 能够从指定的位置进行播放.

那什么时候存储合适呢? 最好的时机就是资源被释放时.

SJBaseVideoPlayer 提供了每个资源在 Dealloc 时的回调, 如下:

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

3. 播放控制

播放控制: 对播放进行的操作. 此部分的内容由 “id <SJMediaPlaybackController> playbackController” 提供支持.

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

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

3.1 当前时间和时长

/// 当前时间
_player.currentTime

/// 时长
_player.totalTime

/// 字符串化, 
/// - 格式为 00:00(小于 1 小时) 或者 00:00:00 (大于 1 小时)
_player.currentTimeStr
_player.totalTimeStr

3.2 时间改变时的回调

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

3.3 播放结束后的回调

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

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

播放状态有两个状态需要注意一下, 分别是 暂停和不活跃状态

当状态为暂停时, 目前有3种可能:

– 正在缓冲
– 主动暂停
– 正在跳转

当状态为不活跃时, 目前有2种可能:

– 播放完毕
– 播放失败

/**
 当前播放的状态

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

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

/**
 暂停的理由

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

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

/**
 不活跃的原因

 - SJVideoPlayerInactivityReasonPlayEnd:    播放完毕
 - SJVideoPlayerInactivityReasonPlayFailed: 播放失败
 */
typedef NS_ENUM(NSUInteger, SJVideoPlayerInactivityReason) {
    SJVideoPlayerInactivityReasonPlayEnd,
    SJVideoPlayerInactivityReasonPlayFailed,
};

3.7 播放状态改变的回调

对播放状态的判断我添加了一个便利的分类 `SJBaseVideoPlayer (PlayStatus)`

如需判断状态, 可导入头文件 `#import “SJBaseVideoPlayer+PlayStatus.h”` 使用.

/// 播放状态改变的回调
_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 是否自动播放 – 当资源初始化完成后

_player.autoPlayWhenPlayStatusIsReadyToPlay = YES;

3.9 刷新

在播放一个资源时, 可能有一些意外情况导致播放失败(如网络环境差).

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

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

[_player refresh];

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

/// 默认值为 1.0, 最小为 0.0
_player.playerVolume = 1.0;

/// 设置静音
_player.mute = YES;

3.11 播放

[_player play];

3.12 暂停

[_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`

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

3.14 停止

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

[_player stop];

3.15 重播

从头开始重新播放

[_player replay];

3.16 跳转到指定的时间播放

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

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


/// 默认值为 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

_player.playbackController = Your PlaybackController.

4. 控制层的显示和隐藏

_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, 替换作者原始实现

Latest podspec

{
    "name": "SJBaseVideoPlayer",
    "version": "2.2.3",
    "summary": "video player.",
    "description": "https://github.com/changsanjiang/SJBaseVideoPlayer/blob/master/README.md",
    "homepage": "https://github.com/changsanjiang/SJBaseVideoPlayer",
    "license": {
        "type": "MIT",
        "file": "LICENSE.md"
    },
    "authors": {
        "SanJiang": "[email protected]"
    },
    "platforms": {
        "ios": "8.0"
    },
    "source": {
        "git": "https://github.com/changsanjiang/SJBaseVideoPlayer.git",
        "tag": "v2.2.3"
    },
    "frameworks": [
        "UIKit",
        "AVFoundation"
    ],
    "requires_arc": true,
    "dependencies": {
        "Masonry": [],
        "SJObserverHelper": [],
        "Reachability": []
    },
    "source_files": "SJBaseVideoPlayer/*.{h,m}",
    "subspecs": [
        {
            "name": "Header",
            "source_files": "SJBaseVideoPlayer/Header/*.{h}"
        },
        {
            "name": "Tool",
            "source_files": "SJBaseVideoPlayer/Tool/*.{h,m}",
            "dependencies": {
                "SJBaseVideoPlayer/Header": [],
                "SJBaseVideoPlayer/Model": []
            }
        },
        {
            "name": "Model",
            "source_files": "SJBaseVideoPlayer/Model/*.{h,m}",
            "dependencies": {
                "SJBaseVideoPlayer/Header": []
            }
        },
        {
            "name": "SJAVMediaPlaybackController",
            "source_files": "SJBaseVideoPlayer/SJAVMediaPlaybackController/*.{h,m}",
            "dependencies": {
                "SJBaseVideoPlayer/Tool": []
            },
            "subspecs": [
                {
                    "name": "Core",
                    "source_files": "SJBaseVideoPlayer/SJAVMediaPlaybackController/Core/*.{h,m}"
                }
            ]
        },
        {
            "name": "SJPrompt",
            "source_files": "SJBaseVideoPlayer/SJPrompt/*.{h,m}"
        },
        {
            "name": "SJRotationManager",
            "source_files": "SJBaseVideoPlayer/SJRotationManager/*.{h,m}",
            "dependencies": {
                "SJBaseVideoPlayer/Header": []
            }
        },
        {
            "name": "SJDeviceVolumeAndBrightnessManager",
            "dependencies": {
                "SJBaseVideoPlayer/Header": []
            },
            "source_files": "SJBaseVideoPlayer/SJDeviceVolumeAndBrightnessManager/*.{h,m}",
            "subspecs": [
                {
                    "name": "Core",
                    "source_files": "SJBaseVideoPlayer/SJDeviceVolumeAndBrightnessManager/Core/*.{h,m}",
                    "dependencies": {
                        "SJBaseVideoPlayer/SJDeviceVolumeAndBrightnessManager/ResourceLoader": []
                    }
                },
                {
                    "name": "ResourceLoader",
                    "source_files": "SJBaseVideoPlayer/SJDeviceVolumeAndBrightnessManager/ResourceLoader/*.{h,m}",
                    "resources": "SJBaseVideoPlayer/SJDeviceVolumeAndBrightnessManager/ResourceLoader/SJDeviceVolumeAndBrightnessManager.bundle"
                }
            ]
        }
    ]
}

Pin It on Pinterest

Share This