本文介绍 iOS 观播 SDK 的基础功能接入方法,包括如何接入完整直播间、完整播放器、独立播放器以及浮窗播放器。
说明
目前仅支持同时进入一个直播间。
您已完成集成 iOS 观播 SDK 的前 5 步。
本文介绍以下功能的接入方法。
完整直播间包含企业直播直播间的所有功能模块,相较于模块化接入,更为简单、便捷。如无特殊需求,推荐接入完整直播间。
有关完整直播间支持的具体功能,详见 功能列表。
您可以通过以下示例代码,让观众进入完整直播间并在 App 内显示完整直播间页面。
... [[BDLLiveEngine sharedInstance] joinLiveRoomWithActivity:activity success:^{ // 1. 获取完整直播间实例。 BDLLivePullViewController *livePullVC = [[BDLLiveEngine sharedInstance] getLivePullViewController]; // 2.(可选)设置 modalPresentationStyle,即直播间的显示方式。此处以全屏显示直播间为例。 // 如选择通过 UINavigationController 的 push 方式显示直播间,或者选择系统默认方式显示直播间,则无需额外配置。 livePullVC.modalPresentationStyle = UIModalPresentationFullScreen; // 3. 设置 actionProvider。此处以 self 为例,用以响应直播间的显示、隐藏等事件。 livePullVC.actionProvider = self; // 4. (可选)配置直播间。 [self configLivePullViewController:livePullVC]; // 5. 显示直播间。此处以 present 方式显示直播间为例。 // 如需通过 UINavigationController 的 push 方式显示直播间,详见以 push 方式显示直播间。 [self showLivePullViewController:livePullVC]; } failure:^(NSError * _Nonnull error) { // 处理错误信息。在该示例中,错误信息将打印至控制台,以便调试与排查问题。 NSLog(@"%@", error.localizedDescription); }]; /// 配置直播间,包括定制 UI。您可将功能代码复制到该方法内运行。 - (void)configLivePullViewController:(BDLLivePullViewController *)livePullVC { } /// 6. 实现 actionProvider 所需的方法。 /// 6.1 实现直播间显示的方法。 /// 显示直播间。当需要显示完整直播间时,SDK 会调用该方法。此处以 present 方式显示直播间为例。 /// 如需通过 UINavigationController 的 push 方式显示直播间,详见以 push 方式显示直播间。 - (void)showLivePullViewController:(BDLLivePullViewController *)livePullVC { [self presentViewController:livePullVC animated:NO completion:nil]; } /// 6.2 实现直播间隐藏的方法。 /// 隐藏直播间。当需要隐藏完整直播间时,SDK 会调用该方法。 - (void)hideLivePullViewController:(BDLLivePullViewController *)livePullVC { [self dismissViewControllerAnimated:YES completion:nil]; }
如需通过 UINavigationController
的 push
方式显示完整直播间,可参考以下流程。
说明
仅 SDK 1.29.1 及以上版本支持 push
方式。
在获取完整直播间实例后,配置浮窗播放器(即 InApp 画中画)的显示逻辑。
配置是否显示浮窗播放器。
/// 在直播状态发生变化时,触发该回调并向 App 传入完整直播间实例、直播状态、是否关闭直播间页面。 livePullVC.config.shouldShowInAppPipIfAvailable = ^BOOL(BDLLivePullViewController * _Nonnull viewController, BDLActivityStatus status, BOOL isClose) { /// 根据直播状态和是否关闭直播间页面,判断是否显示浮窗播放器。 switch (status) { case BDLActivityStatusLive: // 如果直播状态是直播中,执行以下逻辑。 return isClose ? YES : NO; // 此处以通过 pop 或 dismiss 操作关闭直播间页面时显示浮窗播放器(isClose 为 YES 时返回 YES)、通过 push 或 present 方式显示商品详情页等新页面时不显示浮窗播放器(isClose 为 NO 时返回 NO)为例。 case BDLActivityStatusReplay: // 如果直播状态是回放,执行以下逻辑。 return isClose ? YES : NO; // 此处以通过 pop 或 dismiss 操作关闭直播间页面时显示浮窗播放器(isClose 为 YES 时返回 YES)、通过 push 或 present 方式显示商品详情页等新页面时不显示浮窗播放器(isClose 为 NO 时返回 NO)为例。 case BDLActivityStatusPreview: // 如果直播状态是预告且在播放预告片,执行以下逻辑。 return isClose ? YES : NO; // 此处以通过 pop 或 dismiss 操作关闭直播间页面时显示浮窗播放器(isClose 为 YES 时返回 YES)、通过 push 或 present 方式显示商品详情页等新页面时不显示浮窗播放器(isClose 为 NO 时返回 NO)为例。 default: return NO; // 不显示浮窗播放器。正常情况下不会触发该逻辑,但为避免报错,需设置该默认返回值。 } };
配置在 viewDidAppear
时(即完整直播间对应的 View 已经显示时)是否自动关闭正在显示的浮窗播放器。
以下示例代码将自动关闭正在显示的浮窗播放器:
livePullVC.config.autoCloseFloatingPlayerWhenAppear = YES;
如果取值为 NO
,则需在合适的时机调用关闭浮窗播放器的方法。
以下示例代码在关闭浮窗播放器的同时,显示完整直播间。
[livePullVC hideFloatingPlayerIfAvailable:YES]
显示完整直播间。
示例代码如下所示。
- (void)showLivePullViewController:(BDLLivePullViewController *)livePullVC { if (self.livePullVC.navigationController) { // 已通过 push 方式显示完整直播间,但完整直播间上存在其他页面(ViewController)。 [self.navigationController popToViewController:livePullVC animated:YES]; // 通过 pop 操作移除完整直播间上的其他页面(ViewController)。 return; } [self.navigationController pushViewController:livePullVC animated:YES]; // 以 push 方式显示完整直播间。 }
(可选)如果您在步骤 1.a 设置 isClose
为 NO
时返回 YES
,即通过 push
或 present
方式显示商品详情页等新页面时显示浮窗播放器,需要修改是否执行浮窗播放器关闭按钮的默认点击行为。
示例代码如下所示。
__weak typeof(self) weakSelf = self; livePullVC.onFloatingPlayerCloseTapped = ^BOOL(BDLLivePullViewController * _Nonnull viewController, BDLFloatingPlayer * _Nonnull floatingPlayer) { if (viewController.navigationController) { // 通过 push 或 present 方式显示商品详情页等新页面时,点击浮窗播放器的关闭按钮,SDK 关闭浮窗播放器、不显示完整直播间,但不离开直播间。 [viewController hideFloatingPlayerIfAvailable:NO]; return NO; } else { // 通过 pop 或 dismiss 操作关闭直播间页面时,点击浮窗播放器的关闭按钮,SDK 会执行默认的点击行为,即关闭浮窗播放器并离开直播间。 return YES; } };
(可选)由于完整直播间除全屏模式外仅支持竖屏显示,如果 App 页面支持横屏显示,请完成以下配置:
确保在显示完整直播间页面时,前置页面(进入直播间页面前的 App 页面)仅支持竖屏显示。
示例代码如下所示。
// 在 ViewController 中添加以下代码。 @interface ViewController : UIViewController @property (nonatomic, assign) BOOL visible; @end // 在 viewDidAppear 时(即前置页面对应的 View 已经显示时),将 visible 设置为 YES,表示当前视图可见。 - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; self.visible = YES; } // 在 viewDidDisappear 时(即前置页面对应的 View 已经消失时),将 visible 设置为 NO,表示当前视图不可见。 - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; self.visible = NO; } // 指定当前视图支持的页面方向。 - (UIInterfaceOrientationMask)supportedInterfaceOrientations { if (!self.visible) { // 当前视图不可见时,直播间内仅支持竖屏,避免侧滑返回前置页面导致显示异常。 return UIInterfaceOrientationMaskPortrait; } // 当前视图可见时,前置页面支持的页面方向。此处以前置页面支持除 UpsideDown 以外的所有方向为例。 return UIInterfaceOrientationMaskAllButUpsideDown; }
在横屏展示直播间页面时(即全屏模式时)禁用侧滑返回,防止页面显示异常。
// 在 ViewController 中添加以下代码。 // 在横竖屏即将切换时,系统会调用该方法。 - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator { if (size.width > size.height) { // 宽大于高,即横屏显示页面。 self.navigationController.interactivePopGestureRecognizer.enabled = NO; // 禁用侧滑返回。 } else { self.navigationController.interactivePopGestureRecognizer.enabled = YES; // 启用侧滑返回。 } }
确保在横竖屏切换时,将浮窗播放器的显示方向与 App 的当前页面方向保持一致。
// 在 NavigationController 中或者 UIViewController 中添加以下代码。 #import <BDLive/BDLLiveEngine.h> @interface ViewController : UIViewController //NOTE: 需要在获取完整直播间实例时对 livePullViewController 赋值。 @property (nonatomic, weak) BDLLivePullViewController *livePullViewController; @end // 在横竖屏即将切换时,系统会调用该方法。 - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator { [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; // 获取 App 的当前页面方向。 UIInterfaceOrientation orientation = UIInterfaceOrientationUnknown; if (@available(iOS 13, *)) { // 如果使用 self.view.window,则会在 ViewController 不可见时返回 nil,因此请使用 self.navigationController.view.window。 orientation = self.navigationController.view.window.windowScene.interfaceOrientation; } else { orientation = UIApplication.sharedApplication.statusBarOrientation; } // 先显示完整直播间再显示浮窗播放器,需要调整 livePullViewController.floatingPlayerOrientation,将浮窗播放器的显示方向与 App 的当前页面方向保持一致。 if (self.livePullViewController.isFloating) { self.livePullViewController.floatingPlayerOrientation = orientation; } // 直接显示浮窗播放器时,需要调整 floatingPlayer,将浮窗播放器的显示方向与 App 的当前页面方向保持一致。 if (self.floatingPlayer) { [self.floatingPlayer setUIOrientation:orientation]; } }
当需要让观众离开直播间页面时,例如观众观看权限不足时,可通过以下示例代码实现。
- (void)leaveLiveRoom { [[BDLLiveEngine sharedInstance] leaveLiveRoom]; }
以下示例展示了以 present
的方式显示直播间活动 ID(activityId
)为 167808997736****
、token
为 JQ****
、鉴权模式(authMode
)为公开模式的竖屏直播间。您可以按需修改相关参数值。
说明
Mode
=1
)的授权 Token。- (void)joinLiveRoom { BDLActivity *activity = [[BDLActivity alloc] init]; activity.activityId = @(167808997736****); activity.token = @"JQ****"; activity.isPortrait = YES; activity.authMode = BDLActivityAuthModePublic; [[BDLLiveEngine sharedInstance] joinLiveRoomWithActivity:activity success:^{ BDLLivePullViewController *livePullVC = [[BDLLiveEngine sharedInstance] getLivePullViewController]; livePullVC.modalPresentationStyle = UIModalPresentationFullScreen; livePullVC.actionProvider = self; [self configLivePullViewController:livePullVC]; [self showLivePullViewController:livePullVC]; } failure:^(NSError * _Nonnull error) { NSLog(@"%@", error.localizedDescription); }]; } - (void)leaveLiveRoom { [[BDLLiveEngine sharedInstance] leaveLiveRoom]; } - (void)configLivePullViewController:(BDLLivePullViewController *)livePullVC { } - (void)showLivePullViewController:(BDLLivePullViewController *)livePullVC { [self presentViewController:livePullVC animated:NO completion:nil]; } - (void)hideLivePullViewController:(BDLLivePullViewController *)livePullVC { [self dismissViewControllerAnimated:YES completion:nil]; }
如果您具备自有的观看页面,并且对播放器的控制界面无自定义需求,可以选择只接入完整播放器。
您可以通过以下示例代码,进入直播间并创建完整播放器。
@interface ViewController () @property (nonatomic, strong) BDLPlayerView *playerView; @end ... [[BDLLiveEngine sharedInstance] joinLiveRoomWithActivity:activity success:^{ // 创建一个带控制界面的播放器,即播放器内包含播放按钮等互动按钮。此处以竖屏模式初始化播放器为例。 self.playerView =[[BDLPlayerView alloc] initWithPortrait:YES]; // 将 popupSuperView 设置为当前 view。SDK 内部默认实现了倍速、清晰度选择等弹窗效果,弹窗会加到 popupSuperView 上面。 self.playerView.popupSuperView = self.view; // 将 playerView 作为子 view 添加到当前 view 上。 [self.view addSubview:self.playerView]; // 设置 playerView 充满其父 view,即当前 view。 [self.playerView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self.view); }]; } failure:^(NSError * _Nonnull error) { // 处理错误信息。在该示例中,错误信息将打印至控制台,以便调试与排查问题。 NSLog(@"%@", error.localizedDescription); }];
在观众退出您的观看页面时,您可以通过以下示例代码,离开直播间并释放完整播放器的引用。
- (void)leaveLiveRoom { [[BDLLiveEngine sharedInstance] leaveLiveRoom]; [self.playerView removeFromSuperview]; self.playerView = nil; }
以下示例以直播间的活动 ID(activityId
)为 167808997736****
、token
为 JQ****
、鉴权模式(authMode
)为公开模式的竖屏直播间为例。您可以按需修改相关参数值。
说明
您可以通过调用 CreateActivityAPIV2 或 ListActivityAPI 接口获取直播间的活动 ID,调用 GetSDKTokenAPI 接口获取公开模式(Mode
=1
)的授权 Token。
@interface ViewController () @property (nonatomic, strong) BDLPlayerView *playerView; @end - (void)joinLiveRoom { BDLActivity *activity = [[BDLActivity alloc] init]; activity.activityId = @(167808997736****); activity.token = @"JQ****"; activity.isPortrait = YES; activity.authMode = BDLActivityAuthModePublic; [[BDLLiveEngine sharedInstance] joinLiveRoomWithActivity:activity success:^{ self.playerView =[[BDLPlayerView alloc] initWithPortrait:YES]; self.playerView.popupSuperView = self.view; [self.view addSubview:self.playerView]; [self.playerView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self.view); }]; } failure:^(NSError * _Nonnull error) { NSLog(@"%@", error.localizedDescription); }]; } - (void)leaveLiveRoom { [[BDLLiveEngine sharedInstance] leaveLiveRoom]; [self.playerView removeFromSuperview]; self.playerView = nil; }
独立播放器不包含控制界面,即播放器内无播放按钮等互动按钮。如果您具备自有的观看页面,且对播放器的控制界面有自定义需求,可选择只接入独立播放器。在接入独立播放器后,您可自行添加自定义的控制界面。
您可以通过以下示例代码,进入直播间并创建独立播放器。
@interface ViewController () <BDLBasePlayerViewDelegate> @property (nonatomic, strong) BDLBasePlayerView *playerView; @end ... [[BDLLiveEngine sharedInstance] joinLiveRoomWithActivity:activity success:^{ // 创建独立播放器。 self.playerView =[[BDLBasePlayerView alloc] init]; // 设置当前 ViewController 为 playerView 的代理,用于接收和处理 playerView 的事件。 self.playerView.delegate = self; // 将 playerView 作为子 view 添加到 ViewController 的 view 中。 [self.view addSubview:self.playerView]; // 设置 playerView 充满其父 view,即 ViewController 的 view。 [self.playerView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self.view); }]; } failure:^(NSError * _Nonnull error) { // 处理错误信息。在该示例中,错误信息将打印至控制台,以便调试与排查问题。 NSLog(@"%@", error.localizedDescription); }]; ...
您可以通过处理 Delegate,在以下情况发生时,向您的 App 传入相关回调信息,用于定制控制界面。
以下示例代码中,当视频卡顿状态发生变化时会打印日志信息。
// 视频开始卡顿 - (void)basePlayerViewStallStart:(BDLBasePlayerView *)basePlayerView { NSLog(@"%s", __func__); } // 视频结束卡顿 - (void)basePlayerViewStallEnd:(BDLBasePlayerView *)basePlayerView { NSLog(@"%s", __func__); }
在观众退出您的观看页面时,您可以通过以下示例代码,离开直播间并释放独立播放器的引用。
- (void)leaveLiveRoom { [[BDLLiveEngine sharedInstance] leaveLiveRoom]; [self.playerView removeFromSuperview]; self.playerView = nil; }
以下示例以直播间的活动 ID(activityId
)为 167808997736****
、token
为 JQ****
、鉴权模式(authMode
)为公开模式的竖屏直播间为例。您可以按需修改相关参数值。
说明
您可以通过调用 CreateActivityAPIV2 或 ListActivityAPI 接口获取直播间的活动 ID,调用 GetSDKTokenAPI 接口获取公开模式(Mode
=1
)的授权 Token。
@interface ViewController () <BDLBasePlayerViewDelegate> @property (nonatomic, strong) BDLBasePlayerView *playerView; @end - (void)joinLiveRoom { BDLActivity *activity = [[BDLActivity alloc] init]; activity.activityId = @(167808997736****); activity.token = @"JQ****"; activity.isPortrait = YES; activity.authMode = BDLActivityAuthModePublic; [[BDLLiveEngine sharedInstance] joinLiveRoomWithActivity:activity success:^{ self.playerView =[[BDLBasePlayerView alloc] init]; self.playerView.delegate = self; [self.view addSubview:self.playerView]; [self.playerView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self.view); }]; } failure:^(NSError * _Nonnull error) { NSLog(@"%@", error.localizedDescription); }]; } - (void)leaveLiveRoom { [[BDLLiveEngine sharedInstance] leaveLiveRoom]; [self.playerView removeFromSuperview]; self.playerView = nil; } - (void)basePlayerViewStallStart:(BDLBasePlayerView *)basePlayerView { NSLog(@"%s", __func__); } - (void)basePlayerViewStallEnd:(BDLBasePlayerView *)basePlayerView { NSLog(@"%s", __func__); }
浮窗播放器,即 InApp 画中画。通过 pop
或 dismiss
操作关闭直播间页面后,即可显示浮窗播放器。
接入完整直播间后,即可同时具备浮窗播放器功能。但如需先展示浮窗播放器,再展示完整直播间,例如观众先进入您的商品详情页查看商品,再点击浮窗播放器进入完整直播间,则需接入浮窗播放器。
您可以通过以下示例代码,进入直播间并显示浮窗播放器(InApp 画中画)。
@interface ViewController () @property (nonatomic, strong) BDLFloatingPlayer *floatingPlayer; @end ... [[BDLLiveEngine sharedInstance] joinLiveRoomWithActivity:activity success:^{ // 创建浮窗播放器。此处以竖屏模式初始化浮窗播放器为例。 // 参数值仅与获取浮窗播放器加载中图片及其大小的方法有关,不影响浮窗的展示。浮窗的宽高比与视频的宽高比一致。 self.floatingPlayer = [[BDLFloatingPlayer alloc] initWithPortrait:YES]; // 显示浮窗播放器。此处以显示浮窗播放器右上方的关闭按钮为例。 self.floatingPlayer.delegate = self; [self.floatingPlayer showWithCloseButton:YES]; } failure:^(NSError * _Nonnull error) { // 处理错误信息。在该示例中,错误信息将打印至控制台,以便调试与排查问题。 NSLog(@"%@", error.localizedDescription); }];
您可以通过以下示例代码,关闭浮窗播放器并离开直播间。
// 在点击浮窗播放器的关闭按钮时,关闭浮窗播放器并离开直播间。 - (void)floatingPlayerWillClose:(BDLFloatingPlayer *)floatingPlayer { [self leaveLiveRoom] } // 在需要关闭浮窗播放器时,关闭浮窗播放器、离开直播间并释放浮窗播放器的引用。 - (void)leaveLiveRoom { [[BDLLiveEngine sharedInstance] leaveLiveRoom]; [self.floatingPlayer hide]; self.floatingPlayer = nil; }
您可以通过以下示例代码,在横竖屏切换时,将浮窗播放器的显示方向与 App 的当前页面方向保持一致。
// 在横竖屏即将切换时,系统会调用该方法。 - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator { [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; // 获取 App 的当前页面方向。 UIInterfaceOrientation orientation = UIInterfaceOrientationUnknown; if (@available(iOS 13, *)) { // 如果使用 self.view.window,则会在 ViewController 不可见时返回 nil,因此请使用 self.navigationController.view.window。 orientation = self.navigationController.view.window.windowScene.interfaceOrientation; } else { orientation = UIApplication.sharedApplication.statusBarOrientation; } // 将浮窗播放器的显示方向与 App 的当前页面方向保持一致。 [self.floatingPlayer setUIOrientation:orientation]; }
为避免在观众点击浮窗播放器返回完整直播间后视频播放出现中断,您可以通过以下示例代码,确保视频内容能够继续播放。
// MARK: - BDLFloatingPlayerDelegate // 点击浮窗播放器时触发该回调。 - (void)floatingPlayerDidSingleTap:(BDLFloatingPlayer *)floatingPlayer { // 隐藏浮窗播放器。 [self.floatingPlayer hide]; // 获取浮窗播放器中的 basePlayerView。 BDLBasePlayerView *basePlayerView = [floatingPlayer removeBasePlayerView]; // 创建完整直播间并传入获取的 basePlayerView。 BDLLivePullViewController *vc = [[BDLLiveEngine sharedInstance] getLivePullViewControllerWithBasePlayerView:basePlayerView]; vc.delegate = self; vc.actionProvider = self; self.livePullViewController = vc; self.floatingPlayer = nil; // 按需选择以 push 或 present 的方式显示完整直播间。 [self.navigationController pushViewController:self.livePullViewController animated:YES]; }
以下示例以直播间的活动 ID(activityId
)为 167808997736****
、token
为 JQ****
、鉴权模式(authMode
)为公开模式的竖屏直播间为例。您可以按需修改相关参数值。
说明
您可以通过调用 CreateActivityAPIV2 或 ListActivityAPI 接口获取直播间的活动 ID,调用 GetSDKTokenAPI 接口获取公开模式(Mode
=1
)的授权 Token。
@interface ViewController () @property (nonatomic, strong) BDLFloatingPlayer *floatingPlayer; @end - (void)joinLiveRoom { BDLActivity *activity = [[BDLActivity alloc] init]; activity.activityId = @(167808997736****); activity.token = @"JQ****"; activity.isPortrait = YES; activity.authMode = BDLActivityAuthModePublic; [[BDLLiveEngine sharedInstance] joinLiveRoomWithActivity:activity success:^{ self.floatingPlayer = [[BDLFloatingPlayer alloc] initWithPortrait:YES]; self.floatingPlayer.delegate = self; [self.floatingPlayer showWithCloseButton:YES]; } failure:^(NSError * _Nonnull error) { NSLog(@"%@", error.localizedDescription); }]; } - (void)leaveLiveRoom { [[BDLLiveEngine sharedInstance] leaveLiveRoom]; [self.playerView removeFromSuperview]; self.playerView = nil; } - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator { [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; UIInterfaceOrientation orientation = UIInterfaceOrientationUnknown; if (@available(iOS 13, *)) { orientation = self.navigationController.view.window.windowScene.interfaceOrientation; } else { orientation = UIApplication.sharedApplication.statusBarOrientation; } [self.floatingPlayer setUIOrientation:orientation]; } - (void)floatingPlayerDidSingleTap:(BDLFloatingPlayer *)floatingPlayer { [self.floatingPlayer hide]; BDLBasePlayerView *basePlayerView = [floatingPlayer removeBasePlayerView]; BDLLivePullViewController *vc = [[BDLLiveEngine sharedInstance] getLivePullViewControllerWithBasePlayerView:basePlayerView]; vc.delegate = self; vc.actionProvider = self; self.livePullViewController = vc; self.floatingPlayer = nil; [self.navigationController pushViewController:self.livePullViewController animated:YES]; }