8.1 概述
三分屏是一种支持 ppt 或 pdf 文档与视频同步播放的双窗口播放模式。使用一大一小两个播放窗口,布局灵活,能自由切换主屏播放内容,支持为 ppt 或 pdf 文档的每一页设定播放时间点来实现自动翻页功能,支持点击课件目录上的课件进行播放进度跳转,支持下载到本机离线播放。为客户更丰富的授课场景提供了可能。
8.2 快速集成
8.2.1 SDK 版本
如果想使用点播的三分屏功能,请将 PolyvVodSDK 升级到 2.6.5 以上,Podfile 设置如下:
Copy pod 'PolyvVodSDK' , '~> 2.6.5'
8.2.2 开源代码
其次,大部分代码我们在 demo 项目的文件夹 PolyvOpenSourceModule/PPT 中提供,请把 demo 的代码更新到版本 2.6.5 以上,然后把这个文件夹下的代码拖到你的项目中。该文件夹下的文件目录如下:
└── PPT ├── Controller │ ├── PLVPPTBaseViewController.h │ ├── PLVPPTBaseViewController.m │ ├── PLVPPTBaseViewControllerInternal.h │ ├── PLVPPTVideoViewController.h │ ├── PLVPPTVideoViewController.m │ ├── PLVPPTViewController.h │ └── PLVPPTViewController.m └── View ├── PLVFloatingView.h ├── PLVFloatingView.m ├── PLVPPTActionView.h ├── PLVPPTActionView.m ├── PLVPPTActionViewCell.h ├── PLVPPTActionViewCell.m ├── PLVPPTControllerSkinView.h ├── PLVPPTControllerSkinView.m ├── PLVPPTFailView.h ├── PLVPPTFailView.m ├── PLVPPTLoadFailAlertView.h ├── PLVPPTLoadFailAlertView.m ├── PLVPPTSkinProgressView.h └── PLVPPTSkinProgressView.m
8.2.3 demo 示例
在 demo 项目中 Classes 路径下,我们提供了三分屏播放页 PLVPPTSimpleDetailController,可使用该页面进行三分屏播放。使用代码如下:
Copy PLVPPTSimpleDetailController * vctrl = [[PLVPPTSimpleDetailController alloc ] init ];
vctrl.vid = @"准备播放的视频 vid" ;
vctrl.isOffline = NO ;
[self.navigationController pushViewController : vctrl animated : YES ];
属性 isOffline
默认为 NO,表示调用接口获取视频资源,没有网络时即使本地有缓存也无法播放,如果设置为 YES,则从本地获取视频资源,没有网络时只要本地有缓存就可以播放。
8.2.4 项目配置
由于 ppt 文档的图片链接使用 http 协议,需要在项目的 Info.plist 文件中的 App Transport Security Settings / Exception Domains 中增加 Key-Value 如下:
Copy < key >doc.polyv.net</ key >
< dict >
< key >NSExceptionAllowsInsecureHTTPLoads</ key >
< true />
</ dict >
效果如下图所示:
8.3 视频播放器
8.3.1 三分屏开关
点播 SDK 中的视频播放器 PLVVodPlayerViewController
增加布尔属性 enablePPT
,表示是否启动 ppt 功能,默认为 NO,此时无论该视频有无 ppt 均不显示。可在 PLVPPTVideoViewController.m
的方法 -player
进行设置,demo 中的代码如下:
Copy - (PLVVodSkinPlayerController * )player {
if ( ! _player){
_player = [[PLVVodSkinPlayerController alloc ] init ];
_player . enablePPT = YES ;
_player . enableBackgroundPlayback = YES ;
_player . autoplay = YES ;
_player . enableAd = YES ;
}
return _player;
}
除了设置 enablePPT
,还可以在此处进行播放器的其他属性设置,譬如是否允许后台播放enableBackgroundPlayback
、是否自动播放 autoplay
、是否开启广告 enableAd
等,更多参数详见点播文档 4 视频播放 - 4.5 播放器配置 。
8.3.2 播放器皮肤
demo 中开源组件 PolyvOpenSourceModule
中提供的播放器皮肤 PLVVodPlayerSkin
新增【开/关三分屏小窗】按钮、全屏时新增【课件】按钮。点击【开/关三分屏小窗】可以打开或关闭小窗,点击【课件】按钮可在全屏时弹出课件目录列表。开源组件中播放器皮肤新增按钮代码如下:
Copy /// 竖屏播放器皮肤
@interface PLVVodShrinkscreenView : UIView
@property ( weak , nonatomic ) IBOutlet UIButton * subScreenButton; // 关闭三分屏按钮
@end
Copy /// 全屏播放器皮肤
@interface PLVVodFullscreenView : UIView
@property ( weak , nonatomic ) IBOutlet UIButton * subScreenButton; // 关闭三分屏按钮
@property ( weak , nonatomic ) IBOutlet UIButton * pptCatalogButton; // 显示课件目录按钮
@end
按钮的显示与隐藏在 PLVVodPlayerSkin.m
中进行控制。只有当 enablePPT
为YES,且播放的音/视频包含 ppt 或 pdf 文档,这两个按钮才会显示。
8.3.3 播放进度回调
PLVVodSkinPlayerController
新增播放器回调,代码如下:
Copy @interface PLVVodSkinPlayerController : PLVVodPlayerViewController
// 播放进度回调
@property ( nonatomic , copy ) void ( ^ playbackTimeHandler)( NSTimeInterval currentPlaybackTime);
@end
三分屏功能将通过这个回调,实现文档与视频播放的同步。PLVPPTBaseViewController
中的代码示例如下:
Copy __weak typeof (self) weakSelf = self;
self.videoController.player.playbackTimeHandler = ^ ( NSTimeInterval currentPlaybackTime) {
[weakSelf.pptController playAtCurrentSecond : ( int )currentPlaybackTime ];
};
其中,self.videoController.player
为 PLVVodSkinPlayerController 的实例,weakSelf.pptController
为下文即将提到的 ppt 播放器。
8.3.4 播放器容器
PLVPPTVideoViewController
是播放器的视图容器,用于配置视频播放器、控制播放器皮肤、视频播放业务逻辑代码等,PLVPPTBaseViewController
中的代码示例如下:
Copy #import "PLVPPTBaseViewController.h"
#import "PLVPPTVideoViewController.h"
#import <PLVVodSDK/PLVVodSDK.h>
@interface PLVPPTBaseViewController ()<
PLVPPTVideoViewControllerProtocol
>
@property ( nonatomic , strong ) UIView * mainView; // 大屏视图
@property ( nonatomic , strong ) PLVPPTVideoViewController * videoController;
@end
@implementation PLVPPTBaseViewController
#pragma mark - Life Cycle
- ( void ) viewDidLoad {
[super viewDidLoad ];
[self.view addSubview : self.mainView ];
[self.mainView addSubview : self.videoController.view ]; // 添加播放器视图到大屏视图
}
- ( void ) dealloc {
_videoController . delegate = nil ;
}
#pragma mark - Getter & Setter
- (PLVPPTVideoViewController * ) videoController {
if ( ! _videoController){
_videoController = [[PLVPPTVideoViewController alloc ] init ];
_videoController . delegate = self;
}
return _videoController;
}
#pragma mark - PLVPPTVideoViewControllerProtocol
- ( void ) videoWithVid : ( NSString * ) vid title : ( NSString * ) title hasPPT : ( BOOL ) hasPPT localPlay : ( BOOL ) localPlay {
// 视频播放回调,获取到视频资源后调用
// vid:当前播放视频的 vid
// title:当前播放视频的标题
// hasPPT:当前播放视频是否包含文档
// localPlay:播放资源是否为本地缓存,YES 为是,NO 为否
}
- (PLVVodPlaybackMode) currenPlaybackMode {
// 当前播放音视频模式:
// PLVVodPlaybackModeDefault
// PLVVodPlaybackModeVideo
// PLVVodPlaybackModeAudio
}
@end
“8.3.2 播放器皮肤” 一节中提到的新增按钮的响应事件,也可以在 PLVPPTBaseViewController.m
中进行设置:
Copy - (PLVPPTVideoViewController * )videoController {
if ( ! _videoController){
_videoController = [[PLVPPTVideoViewController alloc ] init ];
_videoController . delegate = self;
__weak typeof(self) weakSelf = self;
_videoController . closeSubscreenButtonActionHandler = ^ {
// 打开/关闭小屏
};
_videoController . pptCatalogButtonActionHandler = ^ {
// 打开课件列表
};
}
return _videoController;
}
8.4 PPT 播放器
demo 中的开源组件 PolyvOpenSourceModule
提供了 PPT 播放器 PLVPPTViewController
用于播放 ppt 或 pdf 文档,并支持后台自定义文档翻页的时间点,实现文档与视频同步播放。
8.4.1 PPT 加载
PPT 播放器简单使用示例如下:
Copy self.pptController = [[PLVPPTViewController alloc ] init ];
self.pptController.ppt = ppt;
其中属性 ppt
是 PLVVodPPT
模型,表示一个文档数据模型。使用 PLVVodPPT
的以下方法获取 ppt 文档在线数据(离线数据的获取见 8.7):
Copy // 获取在线数据
+ (void)requestPPTWithVid:(NSString *)vid completion:(void (^)(PLVVodPPT * _Nullable ppt, NSError * _Nullable error))completion;
PLVPPTBaseViewController.m
的代码示例如下:
Copy - ( void )getPPTJson {
[PLVVodPPT requestPPTWithVid : self.vid completion : ^ (PLVVodPPT * _Nullable ppt , NSError * _Nullable error) {
if (error == nil && ppt) {
// 获取 ppt 数据成功
} else {
// 获取 ppt 数据失败
}
} ];
}
8.4.2 PPT 翻页
PPT 播放器提供了以下两个方法,实现文档的翻页:
Copy @interface PLVPPTViewController : UIViewController
/**
将文档切换到特定播放时间点的特定页
@param second 当前视频播放时间点,单位:秒
*/
- ( void ) playAtCurrentSecond : ( NSInteger ) second ;
/**
将文档切换到特定页
@param index 文档的第 index 页
*/
- ( void ) playPPTAtIndex : ( NSInteger ) index ;
@end
在 PLVPPTBaseViewController
中,方法 -playAtCurrentSecond:
在视频播放进度回调中调用,实现与视频播放的同步。方法 -playPPTAtIndex:
用于手动选择播放某一页文档时调用。
8.4.3 PPT 播放器皮肤
PPT 播放器 PLVPPTViewController
提供了简单的皮肤,一个是加载失败时的文本显示“暂无课件”,一个是加载中的 loading 控件,还有一个是下载课件时的下载进度控件。这几个皮肤可通过以下四个方法进行控制:
Copy @interface PLVPPTViewController (PLVPPTSkin)
/**
开始加载 ppt
*/
- ( void ) startLoading ;
/**
加载 ppt 失败
*/
- ( void ) loadPPTFail ;
/**
开始下载 ppt
*/
- ( void ) startDownloading ;
/**
下载 ppt 进度变化
@param progress ppt 下载进度
*/
- ( void ) setDownloadProgress : ( CGFloat ) progress ;
@end
加载/下载 ppt 成功时,即 ppt 属性被赋值(非 nil)时,这些控件会自动隐藏,无需调用任何接口。
8.5 三分屏小窗
demo 中的开源组件 PolyvOpenSourceModule
提供了自定义 UIView
的子类 PLVFloatingView
作为三分屏播放时的(悬浮)小窗。
小窗大小固定,由类方法 +viewSize
定义:
Copy + ( CGSize )viewSize {
return CGSizeMake( 125 , 70 ) ;
}
提供协议 PLVFloatingViewProtocol
和代理方法 -tapAtFloatingView:
用于响应窗口点击事件:
Copy @protocol PLVFloatingViewProtocol < NSObject >
- ( void ) tapAtFloatingView : (PLVFloatingView * ) floatingView ;
@end
三分屏小窗 PLVFloatingView
支持通过手势拖动改变窗口位置,如果想固定位置,只需将 PLVFloatingView.m
文件中的以下几行代码注释掉即可:
Copy @implementation PLVFloatingView
- ( instancetype )init {
self = [super init ];
if (self) {
……
// 如果想固定窗口,注释掉下面这两行代码
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGestureRecognizer:)];
[self addGestureRecognizer : panGestureRecognizer ];
}
return self;
}
@end
8.6 三分屏播放页
demo 中的开源组件 PolyvOpenSourceModule
提供了 PLVPPTBaseViewController
作为三分屏播放页。
PLVPPTBaseViewController
提供对外属性 vid
、isOffline
、playbackMode
:
Copy @interface PLVPPTBaseViewController : UIViewController
/**
播放视频的 vid
*/
@property ( nonatomic , copy ) NSString * vid;
/**
是否离线播放,默认为 NO
YES: 从本地获取视频资源,没有网络时只要本地有缓存就可以播放
NO: 调用接口获取视频资源,没有网络时即使本地有缓存也无法播放
*/
@property ( nonatomic , assign ) BOOL isOffline;
/**
播放模式:默认、视频、音频三种
在线播放时会自动设置播放模式
离线时需根据本地资源类型手动设置播放模式
*/
@property ( nonatomic , assign ) PLVVodPlaybackMode playbackMode;
@end
demo 中的 PLVPPTSimpleDetailController
是 PLVPPTBaseViewController
的子类,三分屏等业务逻辑代码都封装在父类中,使用 PLVPPTSimpleDetailController
进行视频播放的代码示例如下:
Copy PLVPPTSimpleDetailController * vctrl = [[PLVPPTSimpleDetailController alloc ] init ];
vctrl.vid = vid;
[self.navigationController pushViewController : vctrl animated : YES ];
PLVPPTBaseViewController
还提供了以下几个空方法,供子类覆写:
Copy // 获取课件异常时,会执行这个方法,子类需要时可覆写
- ( void )getPPTFail;
// ppt 的值更新时,获得 ppt 模型,或者置 nil 会执行这个方法,子类需要时可覆写
- ( void )getPPTSuccess;
// 横竖屏切换时会执行这个方法,子类需要时可覆写
- ( void )interfaceOrientationDidChange;
文件 PLVPPTBaseViewControllerInternal.h
定义了子类可见的其他属性跟方法。
8.7 下载器
SDK 2.6.5 之后的下载器 PLVVodDownloadManager
支持三分屏模式的视频、文档打包下载。调用方法不变:
Copy PLVVodVideo * video;
[[PLVVodDownloadManager sharedManager ] downloadVideo : video ];
视频模型 PLVVodVideo
新增属性 hasPPT
、ppt_link
,PLVVodDownloadManager
会在下载视频时,根据该属性判断是否需要下载 ppt 文档,从属性 ppt_link
获取文档的下载链接。
下载器 PLVVodDownloadManager
新增 ppt 下载接口 -downloadPPTWithVideo:completion:
:
Copy /**
下载PPT 文件
@param video PLVVodVideo 视频对象
*/
- ( void )downloadPPTWithVideo:(PLVVodVideo * )video completion:( void ( ^ )(PLVVodDownloadInfo * info))completion;
可通过这个接口单独下载某个视频对应的 ppt。
下载成功后,通过类 PLVVodPPT
提供的方法 -requestCachePPTWithVid:completion:
获取离线 ppt 文档数据:
Copy + (void)requestCachePPTWithVid:(NSString *)vid completion:(void (^)(PLVVodPPT * _Nullable ppt, NSError * _Nullable error))completion;
PLVPPTBaseViewController.m
还提供了 ppt 的下载进度回调、下载状态变化回调示例,示例代码如下:
Copy @implementation PLVPPTBaseViewController (PPT)
- ( void ) downloadPPT {
PLVVodVideo * video = self . video;
[[PLVVodDownloadManager sharedManager ] downloadPPTWithVideo : video completion : ^ (PLVVodDownloadInfo * info) {
[self handlePPTDownload : info];
} ];
}
- ( void ) handlePPTDownload : (PLVVodDownloadInfo * ) info {
__weak typeof(self) weakSelf = self;
PLVVodDownloadInfo * downloadInfo = info;
downloadInfo . progressDidChangeBlock = ^ (PLVVodDownloadInfo * info) {
NSLog ( @"下载进度: %f " , info . progress);
};
downloadInfo . stateDidChangeBlock = ^ (PLVVodDownloadInfo * info) {
if ( info . state == PLVVodDownloadStateSuccess) {
NSLog ( @"下载成功" );
} else if ( info . state == PLVVodDownloadStateFailed) {
NSLog ( @"下载失败" );
}
};
}
@end