产品文档 回放技术文档 iOS 回放 Core SDK

iOS 回放 Core SDK

git 链接

最新版1.3.2 changelog

主要功能

直播视频回放核心 SDK 提供在线及本地直播视频的回放功能(不包含 UI),可以完整重现直播时候的ppt,画笔, 以及老师视频. 支持回放下载. 功能包括:

模块 功能 对应文件
教室管理 在线/本地的回放教室进入/退出的监听 BJPRoom.h
聊天信息的监听
播放本地回放的时候获取权限
视频管理 记忆播放 BJPPlaybackVM.h
监听回放视频状态
视频控制(播放/暂停/停止/拖拽/切换清晰度/切换倍速)
下载和在线回放信息 下载管理 PMDownloadManager.h
回放视频信息 BJPlayerManager.h

另外还提供一个包含UI的回放UI SDK, 包含整套的直播回放UI, 集成工作量比较少, 便于快速开发.

Demo体验

Demo建议使用 BJPlaybackUI

  • demo 运行成功后将进入如下登录界面. 需要输入教室ID 和 sessionId(可以为空)及token才能进入教室。这些参数由客户服务端访问百家云服务端API获取, 然后传给移动端.

    回放UI

  • 进入后如下图, 支持倍速, 切换清晰度

    竖屏

    横屏

集成SDK

使用CocoaPods方式集成

  • Podfile 中设置 source
source 'https://github.com/CocoaPods/Specs.git'
source 'http://git.baijiashilian.com/open-ios/specs.git '
  • 配置ATS. 因为视频url是http的, 需要在info.plist里面增加 NSAllowsArbitraryLoads = true
  • 配置"Privacy - Media Library Usage Description" 媒体库权限
  • Podfile 中引入 BJPlaybackCore
pod 'BJPlaybackCore'

1. 引入头文件

#import <BJPlaybackCore/BJPlaybackCore.h>

2. 创建、进入回放

创建、进入教室的整体流程如下:

  • 定义一个BJPRoom的属性room,用于管理回放房间
  • 使用回放相关信息将room属性实例化
  • 为回放的进入, 退出, 聊天消息更新等事件添加监听
  • 调用 room 定义的 enter 进入教室, 监听到进入房间成功之后, 即可开始观看回放.

2.1 定义回放房间

@property (nonatomic) BJPRoom *room;

2.2 创建教室:分为在线回放和本地回放

  • 创建用于在线回放的房间
/**
 创建在线回放的room
 创建在线视频, 参数不可传空
 创建本地room的话, 两个参数传nil

 @param classId classId
 @param sessionId sessionId, 长期房间回放参数. 如果classId对应的课程不是长期房间,可不传;
                  如果classId对应的课程是长期房间, 不传则默认返回长期房间的第一个课程
 @param token token
 @param userName 第三方用户名, 不上报则传nil
 @param userNumber 第三方用户number号, 不上报则传0
 @return room
 */
+ (instancetype)onlineVideoCreateRoomWithClassId:(NSString *)classId
                                       sessionId:(nullable NSString *)sessionId
                                           token:(NSString *)token
                                        userName:(nullable NSString *)userName
                                      userNumber:(NSInteger)userNumber;
  • 创建用于本地回放的房间
/**
 创建本地视频  进入房间

 @param videoPath 本地视频的路径
 @param signalPath 本地信令, 根据isZip的值, 传信令文件的路径
 @param isZip 所传信令文件是否为压缩文件, YES:传压缩的信令文件的路径
 NO: 传解压后的信令文件, 且所传的路径的下一级目录即为all.json等数据
 @param handle 用于在iOS10以上的系统用户授权进入本地资料库, 如果版本低iOS10,不需要授权,
 即status = BJPMediaLibraryAuthorizationStatusAuthorized
 */
+ (instancetype)localVideoCreatRoomWithVideoPath:(NSString *)videoPath
                                      signalPath:(NSString *)signalPath
                                      definition:(PMVideoDefinitionType)definition
                                           isZip:(BOOL)isZip
                                          status:(void (^)(BJPMediaLibraryAuthorizationStatus status))handle;
  • 上报用户标识符:根据实际需要选择是否上报
// 如果用户标识符 _userInfo 存在,上报该标识符
[self.room.playbackVM setUserInfo:_userInfo];

2.3 准备进入回放: 添加方法监听

  • 监听进入、退出房间事件
// 监听刚进入教室成功/失败
@weakify(self);
[self bjl_observe:BJLMakeMethod(self.room, roomDidEnterWithError:)
         observer:^BOOL(BJLError *error) {
                @strongify(self);
             // error 为 nil 表示正常进入,否则输出错误信息
             if (error) {
                 NSLog(@"roomDidEnterWithError ==> error = %@", error);
             }
             return YES;
         }];
// 即将退出房间
@weakify(self);
[self bjl_observe:BJLMakeMethod(self.room, roomWillExitWithError:)
         observer:^BOOL(BJLError *error) {
                @strongify(self);
             // error 为 nil 表示主动退出,否则输出错误信息
             if (error) {
                 NSLog(@"roomWillExitWithError: ==> error = %@", error);
             }
             return YES;
         }];
// 退出房间
@weakify(self);
[self bjl_observe:BJLMakeMethod(self.room, roomDidExitWithError:)
         observer:^BOOL(BJLError *error) {
                @strongify(self);
             // error 为 nil 表示主动退出,否则输出错误信息
             if (error) { 
                 NSLog(@"roomDidExitWithError ==> error = %@", error);
             }
             return YES;
         }];

2.4 进出回放房间

// 进入回放
[self.room enter];

// 退出回放
[self.room exit];

3. 音视频管理

回放的音视频管理, 参考BJPRoom的属性playbackVM

创建房间之后,回放管理类 BJPPlaybackVM 的实例 playbackVM 也被初始化,可使用它进行回放管理

/** 播放器的view */
@property (nonatomic, readonly) UIView *playView;

播放信息可通过回放管理类的实例 playbackVM 直接获取,也可以通过 KVO 监听

  • 通过 playbackVM 获取
// 当前播放状态:枚举类型
PMPlayState playState = self.room.playbackVM.playbackState;

// 当前视频信息,包含视频 ID、视频名称、片头片尾、清晰度列表、选集信息列表等
PMVideoInfoModel *videoInfo = self.room.playbackVM.videoInfoModel;
NSArray <PMVideoDefinitionInfoModel*> *definitionList = videoInfo.definitionList; //支持的清晰度列表

// 当前播放清晰度,包含清晰度类型、CDN 列表等
PPMVideoDefinitionInfoModel *currDefinitionInfoModel;
NSArray<PMVideoCDNInfoModel *> *cdnList = definitionInfo.cdnList; // CDN列表

// 当前播放的 CDN
PMVideoCDNInfoModel *cdnInfo = self.room.playbackVM.currCDNInfoModel;
  • 通过 KVO 监听
[self bjl_kvo:BJLMakeProperty(self.room.playbackVM, currentTime) 
     observer:^BOOL(NSNumber * _Nullable old, NSNumber *  _Nullable now) {
          NSLog(@"currentTime: old = %@; now = %@", old, now);
//更新进度条
          return YES;
     }];

3.1 视频通知类型

// 即将播放的通知
PMPlayerWillPlayNotification

// 播放状态改变的通知
PKMoviePlayerPlaybackStateDidChangeNotification

// 播放结束的通知
PKMoviePlayerPlaybackDidFinishNotification
  • 以监听播放结束的通知为例
// 添加通知
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(playbackFinish:)
                                             name:PKMoviePlayerPlaybackDidFinishNotification
                                           object:nil];
// 响应方法
- (void)playbackFinish:(NSNotification *)noti {
    // 获取结束原因
    PKMovieFinishReason reason = [[noti.userInfo objectForKey:PKMoviePlayerPlaybackDidFinishReasonUserInfoKey] integerValue];
    // 获取错误信息
    NSString *error = [noti.userInfo objectForKey:@"error"];
    switch (reason) {
        case PKMovieFinishReasonPlaybackEnded: // 回放结束
            [MBProgressHUD bjp_showMessageThenHide:@"Playback Ended ==> video finish" toView:self.view onHide:nil];
            break;

        case PKMovieFinishReasonPlaybackError: // 回放出错
            [MBProgressHUD bjp_showMessageThenHide:[NSString stringWithFormat:@"playback error ==> video finish, error:%@", error] toView:self.view onHide:nil];
            break;

        case PKMovieFinishReasonUserExited: // 用户退出
            [MBProgressHUD bjp_showMessageThenHide:@"User Exited ==> video finish" toView:self.view onHide:nil];
            break;

        default:
            break;
    }
}

3.2 视频播放控制

// 播放视频
[self.room.playbackVM playerPlay];

// 暂停播放
[self.room.playbackVM playerPause];

// 停止播放
[self.room.playbackVM playerStop];

3.3 跳转到指定时刻

// 以5.0秒为例
[self.room.playbackVM playerSeekToTime:5.0];

3.4 改变播放倍速

// 以2.0倍为例
[self.room.playbackVM changeRate:2.0];

3.5 切换清晰度

/** 切换换清晰度
typedef NS_ENUM(NSInteger, PMVideoDefinitionType){
    DT_Unknown  = -1, //未知
    DT_LOW      = 0, //标清
    DT_HIGH     = 1, //高清
    DT_SUPPERHD = 2, //超清
    DT_720p     = 3, //720p
    DT_1080p    = 4, //1080p
};
*/
[self.room.playbackVM changeDefinition:DT_HIGH];

4. 回放协议

协议为 BJPMProtocol

  • 设置当前 viewController 为代理
self.room.playbackVM.playerControl.delegate = self;
  • 代理方法
// 播放过程中出错(@required)
- (void)videoplayer:(BJPlayerManager *)playerManager throwPlayError:(NSError *)error {
    NSLog(@"video play ended with error: %@", error);
}

5. 显示 PPT、画笔

/** 课件显示 */
@property (nonatomic, readonly, nullable) UIViewController<BJLSlideshowUI> *slideshowViewController;
  • 添加 PPT、画笔视图
// 将 BJPRoom 的课件视图添加到当前 viewController 的对应视图
UIView *roomSlideshowView = self.room.slideshowViewController.view;
[self.slideshowView addSubview:roomSlideshowView];
[roomSlideshowView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(self.slideshowView);
}];
  • 设置显示模式
/** 设置显示模式
 BJLContentMode_scaleAspectFit  完整
 BJLContentMode_scaleAspectFill 铺满
 BJLContentMode_scaleToFill     拉伸
*/
self.room.slideshowViewController.contentMode = BJLContentMode_scaleAspectFit;

6. 获取聊天消息列表

  • SDK 传出来的聊天信息列表是增量的, 需要创建一个可变数组(比如:chatList)来接收消息, 同时需要监听'didReceiveMessageList:'方法
// 接收增量消息
@weakify(self);
[self bjl_observe:BJLMakeMethod(self.room, didReceiveMessageList:)
         observer:^BOOL(NSArray<BJPMessage *> *messageArray){
             @strongify(self);
             if (messageArray.count > 0) {
                 for (BJPMessage *msg in messageArray) {
                     [self.chatCtrl.chatList addObject:msg];
                 }
             }
             [self.chatCtrl.tableView reloadData];
             return YES;
         }];
  • 用户回退视频时,需要将当前消息列表清空,重新添加增量数组
// 监听 PKMoviePlayerPlaybackStateDidChangeNotification, 
// 当 state == PKMoviePlaybackStateSeekingBackward时, 清空这个可变数组
- (void)playStatusChange:(NSNotification *)noti {
    if (self.room.playbackVM.playbackState == PKMoviePlaybackStateSeekingBackward) {
        [self.chatCtrl.chatList removeAllObjects];
        [self.chatCtrl.tableView reloadData];
    }
}

7. 下载

7.1 初始设置

注意:下载前需要先设置根路径, 然后调用单例方法, 否则代码抛出异常
  /* 下载根路径, folder需要是一个 存在的 文件夹, 如果不存在, 需要上层创建 */
  NSString *path = @"xxx/folder";
    [PMDownloadManager downloadManagerWithRootPath:path];

7.2 下载方法

参考头文件:PMDownloadManager.h

//回放下载
    [[PMDownloadManager downloadManager] addDownloadWithClass:classID seesionID:sessionID token:token definionArray:defiArr showFileName:showname creatTime:@"creatTime"];

需要注意的是,由于我们使用了ID+token的视频下载方式, 因此是存在token过期的现象, 发生token失效或者下载url失效的问题时 , 会向上层抛出BJPMErrorCodeDownloadInvalid (1010)的错误码,此时需要上层重新获取token,并调用下面的方法来重新设置token

/**
  下载中的任务发生了下载的url失效的错误,需要调用此方法,内部重新请求下载地址

 @param downloader 下载的任务
 @param token 新的token
 */
- (void)resetDownloadWithDownloader:(PMDownloader *)downloader token:(NSString *)token;

7.3 主要属性

/** 下载事件相关的代理 */
@property (nonatomic, weak  ) id<PMDownloadDelegate> downloadDelegate;

//下载过程中的数据 和 已经完成的数据
@property (nonatomic, readonly) NSArray <PMDownloader *> *downloadingList;
@property (nonatomic, readonly) NSArray <PMDownloadModel *> *finishedList;

7.4 下载代理方法

- (void)startDownload:(PMDownloader *)downloader;//开始下载
- (void)updateProgress:(PMDownloader *)downloader;//获取下载进度
- (void)finishedDownload:(PMDownloader *)downloader;//下载完成

/**
 下载时的错误回调, 包括开始下载之前和下载过程中的错误

 @param downloader 下载过程中的下载器实例, 可能为空
 @param beforeDownloadModel 下载开始前的错误model, 可能为空
 */
- (void)downloadFail:(nullable PMDownloader *)downloader beforeDownloadError:(nullable PMBeforeDownloadModel *)beforeDownloadModel;

7.5 分账户下载

  • 在当前账户登录成功后, 首先释放当前的下载管理的单例

    [PMDownloadManager downloadManagerDestory];
    
  • 设置根路径, 如上, 传进来的文件夹以当前userId为命名的文件夹
  • 调用 +downloadManager

7.6 下载中的错误回调方法处理

详细参考demo中的对(void)downloadFail:(nullable PMDownloader *)downloader beforeDownloadError:(nullable PMBeforeDownloadModel *)beforeDownloadModel;代理方法的处理

8. 错误码

    BJPMErrorCodeLoading           = 1000,    //加载中
    BJPMErrorCodeLoadingEnd        = 1001,    //加载完成
    BJPMErrorCodeParse             = 1002,    //视频解析错误
    BJPMErrorCodeNetwork           = 1003,    //网络错误, 没有网络或是未知网络
    BJPMErrorCodeWWAN              = 1004,    //非WIFI环境,这时要暂停,并提示
    BJPMErrorCodeWIFI              = 1005,    //wifi
    BJPMErrorCodeServer            = 1006,    //server端返回的错误
    BJPMErrorCodeApp               = 1007,    //app端的错误
    BJPMErrorCodeDownloadInvalid   = 1010,    //下载的url失效