You need to enable JavaScript to run this app.
导航
iOS 端集成
最近更新时间:2024.11.07 17:09:25首次发布时间:2024.11.01 10:20:19

本文提供视频点播 iOS 端集成短剧场景的方案概览,并为您详细介绍如何快速集成短剧 Demo。

集成方案概览

火山引擎视频点播针对短剧场景提供四种客户端集成方案。下表列出这四种解决方案的详细区别。

集成方案

集成短剧 Demo

集成播放控件 PlayerKit

集成播放器 SDK

方案说明

基于短视频场景控件,提供支持防录屏、剧集切换、 切换页面无缝续播、付费内容解锁播放等短剧场景特色功能。

基于播放器 SDK,提供 View 层播放能力,屏蔽播放器使用细节。

直接集成播放器 SDK。

适用客户

适用于开发新 App、可切换业务接口层逻辑的客户。

适用于开发新 App 或者已有 App 但可切换业务播放架构的客户。

  • 适用于已有 App、希望复用已有业务播放代码场景、替换播放内核即可快速上线的客户。
  • 适用于开发能力强、播放器经验丰富、拥有充裕的时间开发新 App 的客户。

开发工作

仅需切换业务接口层逻辑和转换数据结构。

客户需自行实现网络数据交互、短剧播放控件 UI 浮层、短剧播放页面切换、短剧支付等功能。

客户自行实现播放器功能以及短剧场景特色功能。

上线时间

最快一周上线

两周至一个月

一个月左右

相关链接

本文

集成播放控件 (iOS)

集成 iOS 点播 SDK

跑通 Demo

在集成短剧 Demo 前,建议您先参考文档跑通 Demo,体验短剧场景的功能。

集成短剧 Demo

短剧 Demo 源码位于在 VEVodDemo-iOS 仓库 VESceneModule 文件夹内,其目录结构说明如下:

|--VOLCDemo
|--|--VOLCDemo          // 主 app(壳工程)
|--|--VEVodMain         // Demo 入口+资源文件
|--|--VEBaseKit         // 依赖的基础组建
|--|--VEPlayerKit       // 播放控件层
|--|--VEPlayerUIModule  // 播放控件使用的 UI 组件(计划废弃)
|--|--VESceneModule     // 各种场景源码
|--|--|--ShortDrama     // 短剧场景源码
|--|--|--ShortVideo     // 短视频场景源码(类似抖音推荐页面)
|--|--|--FeedVideo      // 中视频场景源码(类似西瓜推荐页面)
|--|--|--LongVideo      // 长视频场景源码
|--|--|--CustomPlayVideo// 自定义 URL 方式播放场景源码
|--|--|--Data           // API 请求+ViewModel 
|--|--|--Setting        // Demo setting 页面配置
|--|--|--UIComponents   // 通用 UI 组件

步骤 1:下载源码

运行以下命令将源码下载至本地:

git clone https://github.com/volcengine/VEVodDemo-iOS
cd VEVodDemo-iOS/VOLCDemo

步骤 2:拷贝代码

将以下文件夹拷贝至您的项目根目录下:

VEBaseKit
VEPlayerKit
VEPlayerUIModule
VESceneModule
VEVodMain // 这里存放资源文件

说明

复制完成后,建议运行一次 git commit,并在 commit message 中记录 VEVodDemo-iOS 当前最新的 Commit ID。因为根据业务需要,源码可能会有变动,这次 commit 可以帮您追溯。

步骤 3:配置 Podfile 文件

参考 VEVodDemo-iOS/VOLCDemo/Podfile 文件将下列的依赖拷贝到您自己的 Podfile 文件中。对于 VEBaseKitVESceneModuleVEPlayerKitVEPlayerUIModule,请根据您项目中实际存放依赖库的路径进行相应的修改。如果源码依赖的第三方开源库与业务代码中实际使用的版本存在冲突,以您使用的版本为准。

# TTSDK-Player
pod 'TTSDKFramework', '1.41.3.5-premium', :subspecs => ['Player-SR']  
# vod main
pod 'VEVodMain', :path=> './VEVodMain/'
# vod base kit
pod 'VEBaseKit', :path=> './VEBaseKit/'
# vod all scene exhibition
pod 'VESceneModule', :path=> './VESceneModule/'
# vod player kit
pod 'VEPlayerKit', :path=> './VEPlayerKit/'
# vod player control UI kit
pod 'VEPlayerUIModule', :path=> './VEPlayerUIModule/'
  
# Third libray
pod 'Masonry'
pod 'SDWebImage'
pod 'MBProgressHUD', '~> 1.2.0'
pod 'Reachability'
pod 'MJRefresh'
pod 'JSONModel'

步骤 4:引入短剧模块

短剧场景源码位于 VOLCDemo/VESceneModule/ShortDrama 文件夹。如果您只需要集成短剧场景,可以删除 VOLCDemo/VESceneModule 文件夹下其他场景的代码。

步骤 5:初始化播放器 SDK

参考 AppDelegate.m 文件初始化播放器 SDK。

- (void)initTTSDK {
#ifdef DEBUG
    /// 建议 Debug 期间打开 Log 开关
    [TTVideoEngine setLogFlag:TTVideoEngineLogFlagAll];
#endif
    
    /// appid 和 license 不能为空,详见[应用管理](https://www.volcengine.com/docs/4/79594)和 [License 包管理](https://www.volcengine.com/docs/4/65772)
    NSString *appId = @"";
    NSString *licenseName = @"";
    
    // 以下是 Demo 检查有没有指定 appid 和 license,您可删除 
    //if (appId.length == 0 || licenseName.length == 0) {
    //    UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:NSLocalizedStringFromTable(@"tip_license_required", @"VodLocalizable", nil) preferredStyle:UIAlertControllerStyleAlert];
    //    UIAlertAction *vidSource = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
    //        exit(0);
    //    }];
    //    [alert addAction:vidSource];
    //    [self.window.rootViewController presentViewController:alert animated:YES completion:nil];
    //    return;
    //}
    
    /// initialize ttsdk, configure Liscene ,this step cannot be skipped !!!!!
    TTSDKConfiguration *configuration = [TTSDKConfiguration defaultConfigurationWithAppID:appId licenseName:licenseName];
    /// 播放器 CacheSize,默认 100M,建议设置 300M
    TTSDKVodConfiguration *vodConfig = [[TTSDKVodConfiguration alloc] init];
    vodConfig.cacheMaxSize = 300 * 1024 * 1024; 
    configuration.vodConfiguration = vodConfig;
    [TTSDKManager startWithConfiguration:configuration];
}

步骤 6:集成短剧页面

VEShortDramaPagingViewController 是短剧场景的主页面对象,包含默认的“剧场”和“推荐”两个标签页。您可以根据实际业务需求来扩展更多标签页。您可以将 VEShortDramaPagingViewController 添加到业务 UINavigationControllerrootViewController 中进行集成。

适配业务逻辑

参考以下步骤快速将短剧 Demo 与您的业务逻辑进行适配:

  1. 数据层适配,以快速接入短剧场景的核心功能。您需要:
    1. 替换业务接口:App 需要实现 ShortDrama/Data/DataManager/VEDramaDataManager.h 中定义的 API。
    2. 转换数据结构:App 将业务 AppServer 返回的数据结构转换成短剧 Demo 中定义的数据结构,以便快速复用 Demo 中的现有业务逻辑。
  2. 适配付费内容解锁播放等短剧场景特色功能。

短剧 Demo 源码结构说明

├ VESceneModule
│   └ ShortDrama
│       └ VEShortDramaPagingViewController.h         // 短剧主页面 UI
│       └ Data
│           ├ DataManager
│               └ VEDramaDataManager.h         // 短剧 HTTP API 接口类
│           ├ Model     
│               └ VEDramaInfoModel.h           // 短剧信息
│               └ VEDramaEpisodeInfoModel.h    // 单集信息
│               └ VEDramaVideoInfoModel.h      // 单集视频
│               └ VEDramaPayInfoModel.h        // 单集付费信息
│       ├ ShortDramaListController
│           └ VEShortDramaListViewController.h      // 剧场页面
│       ├ ShortDramaFeedController
│           ├ DramaRecommod
│               └ VEShortDramaVideoFeedViewController.h      // 推荐页面
│               └ ShortDramaRecommodPlayerModuleLoader.h     // 推荐页面 Modules Loader 组件
│           ├ DramaDetail
│               └ VEShortDramaDetailFeedViewController.h     // 剧集详情页
│               └ ShortDramaDetailPlayerModuleLoader.h       // 剧集详情页 Modules Loader 组件
│           ├ DramaCollect
│               └ ShortDramaCollectViewController.h          // 收藏组件,业务可以对该组件修改实际业务逻辑
│           ├ DramaPraise
│               └ ShortDramaPraiseViewController.h           // 点赞组件,业务可以对该组件修改实际业务逻辑
│           ├ DramaPay
│               └ ShortDramaPayViewController.h              // 支付组件,业务可以对该组件修改实际支付逻辑
│               └ ShortDramaCachePayManager.h                // Demo 管理支持缓存的对象,实际业务开发一般用不到,应该以Server返回的支付状态为准
│           ├ DramaSelection
│               └ ShortDramaSelectionViewController.h        // 剧集选集的组件
│   └ PlayerModules    // 短剧播控相关所有 Modules 组件,通过 Modules Loader 加载
│       └ ShortDramaPlayButtonModule.h        // 播放/暂停 Module
│       └ ShortDramaPlayerSpeedModule.h       // 倍速 Module
│       └ //... 更多 Module 不一一列举,可自行阅读源码
│   └ ShortDramaUIView
│       └ ShortDramaIntroduceView.h        // 剧集介绍 View
│       └ ShortDramaSelectionView.h        // 选集介绍 View
│       └ //... 更多 View 不一一列举,可自行阅读源码

数据层适配

数据结构介绍

为构建短剧业务逻辑,短剧 Demo 层定义了 VEDramaInfoModel(短剧信息)、VEDramaEpisodeInfoModel(单集视频)、VEDramaEpisodeInfoModel(单集信息)、VEDramaPayInfoModel(单集付费解锁信息)数据结构。您需要将你自己的应用服务端返回的数据结构转换为上述数据结构。数据结构具体说明如下表所示。

字段

类型

是否必需

描述

VEDramaInfoModel
短剧信息

dramaId

String

必需

短剧 ID

dramaTitle

String

必需

短剧名

coverUrl

String

必需

短剧封面

totalEpisodeNumber

Int

必需

短剧总集数

latestEpisodeNumber

Int

可选

短剧最新集数

VEDramaVideoInfoModel
单集视频

videoId

Int

必需

视频 ID

title

String

可选

视频标题

duration

Double

必需

视频时长(单位:秒)

coverUrl

String

必需

封面地址

videoUrl

String

使用 DirectUrl 数据源必需

视频播放地址

playAuthToken

String

用 Vid 数据源必需

Vid 数据源的 PlayAuthToken。AppServer 可使用视频点播服务端 SDK 签发。

subtitleAuthToken

String

可选

Vid 数据源的获取字幕 Token。AppServer 使用视频点播服务端 SDK 签发。

dramaEpisodeInfo

VEDramaEpisodeInfoModel

必需

短剧单集信息

payInfo

VEDramaPayInfoModel

付费解锁必需

短剧单集支付解锁信息

VEDramaEpisodeInfoModel
单集信息

dramaInfo

VEDramaInfoModel

必需

短剧信息

episodeNumber

Int

必需

短剧集数

episodeDesc

String

必需

短剧单集描述

VEDramaPayInfoModel
单集付费解锁信息

payStatus

VEDramaPayStatus

必需

短剧付费解锁类型:

  • VEDramaPayStatus_Unpaid:未付费
  • VEDramaPayStatus_Paying:支付中
  • VEDramaPayStatus_Paid:已付费

替换业务接口

  1. 推荐页接口适配:参考 VEDramaDataManager 对象的 requestDramaRecommondList 接口实现在推荐页中获取短剧推荐流。您可将 API 请求修改为您自己的实际业务请求,并将请求到的数据封装为 VEDramaVideoInfoModel 对象返回给页面进行展示。如果当前 VEDramaVideoInfoModel 对象中的字段无法满足业务当前需求,您也可修改源码增加新的字段。

    + (void)requestDramaRecommondList:(NSInteger)offset pageSize:(NSInteger)pageSize result:(RequestDataComplete)complete {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSMutableArray *dramas = [NSMutableArray array];
    
            NSMutableDictionary *param = [NSMutableDictionary dictionary];
            [param setObject:@"mini-drama-video" forKey:@"authorId"];
            [param setObject:@"mini-drama-video" forKey:@"userID"];
            [param setObject:@(VEVideoCodecType_H264) forKey:@"codec"];
            [param setObject:@(VEVideoFormatType_MP4) forKey:@"format"];
            [param setObject:@(offset) forKey:@"offset"];
            [param setObject:@(pageSize) forKey:@"pageSize"];
            [VENetworkHelper requestDataWithUrl:requestDramaRecommondListUrl httpMethod:@"POST" parameters:param success:^(id _Nonnull responseObject) {
                if (responseObject && [ responseObject isKindOfClass:[NSDictionary class]]) {
                    NSArray* results = [responseObject objectForKey:@"result"];
                    for (NSDictionary *dic in results) {
                        // 将请求到的数据封装为 VEDramaVideoInfoModel 返回给页面进行展示
                        VEDramaVideoInfoModel *dramaVideoInfoModel = [[VEDramaVideoInfoModel alloc] initWithDictionary:dic error:nil];
                        [dramas addObject:dramaVideoInfoModel];
                    }
                }
                if (complete) {
                    complete(dramas, nil);
                }
            } failure:^(NSString * _Nonnull errorMessage) {
                if (complete) {
                    complete(nil, errorMessage);
                }
            }];
        });
    }
    
  2. 详情页接口适配:参考 VEDramaDataManager 对象 requestDramaEpisodeList 接口实现在详情页中获取短剧详情流。您可将 API 请求修改为您自己的实际业务请求,并将请求到的数据封装为 VEDramaVideoInfoModel 返回给页面进行展示。如果当前 VEDramaVideoInfoModel 对象中的字段无法满足业务当前需求,您也可修改源码增加新的字段。

    + (void)requestDramaEpisodeList:(NSString *)dramaId episodeNumber:(NSInteger)episodeNumber offset:(NSInteger)offset pageSize:(NSInteger)pageSize result:(RequestDataComplete)complete {
        if (!dramaId) {
            if (complete) {
                complete(nil, @"dramaId is nil !!!");
            }
            return;
        }
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSMutableArray *dramas = [NSMutableArray array];
    
            NSMutableDictionary *param = [NSMutableDictionary dictionary];
            [param setObject:@"mini-drama-video" forKey:@"authorId"];
            [param setObject:@"mini-drama-video" forKey:@"userID"];
            [param setObject:@(VEVideoCodecType_H264) forKey:@"codec"];
            [param setObject:@(VEVideoFormatType_MP4) forKey:@"format"];
            [param setObject:@(offset) forKey:@"offset"];
            [param setObject:@(pageSize) forKey:@"pageSize"];
            [param setObject:dramaId ?: @"" forKey:@"dramaId"];
            @weakify(self);
            [VENetworkHelper requestDataWithUrl:requestDramaEpisodeUrl httpMethod:@"POST" parameters:param success:^(id _Nonnull responseObject) {
                @strongify(self);
                if (responseObject && [ responseObject isKindOfClass:[NSDictionary class]]) {
                    NSArray* results = [responseObject objectForKey:@"result"];
                    for (NSDictionary *dic in results) {
                        // 将请求到的数据封装为 VEDramaVideoInfoModel 返回给页面进行展示
                        VEDramaVideoInfoModel *dramaVideoInfoModel = [[VEDramaVideoInfoModel alloc] initWithDictionary:dic error:nil];
                        [dramas addObject:dramaVideoInfoModel];
                    }
                    // 这部分代码为 Demo 模拟剧集是否付费增加的测试代码,业务实际使用不需要,可删除
                    //if ([ShortDramaCachePayManager shareInstance].openPayTest) {
                    //    [[self class] testDramaPayVideoInfo:dramas];
                    //}
                }
                if (complete) {
                    complete(dramas, nil);
                }
            } failure:^(NSString * _Nonnull errorMessage) {
                if (complete) {
                    complete(nil, errorMessage);
                }
            }];
        });
    }
    
  3. 剧场页接口适配:参考 VEDramaDataManager 对象 requestDramaList 接口实现在剧场页中获取短剧列表。您可将 API 请求修改为您自己的实际业务请求,并将请求到的数据封装为 VEDramaInfoModel 返回给页面进行展示。如果当前 VEDramaInfoModel 对象中的字段无法满足业务当前需求,您也可修改源码增加新的字段。

    + (void)requestDramaList:(NSInteger)offset pageSize:(NSInteger)pageSize result:(RequestDataComplete)complete  {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSMutableArray *dramas = [NSMutableArray array];
    
            NSMutableDictionary *param = [NSMutableDictionary dictionary];
            [param setObject:@"mini-drama-video" forKey:@"authorId"];
            [param setObject:@"mini-drama-video" forKey:@"userID"];
            [param setObject:@(offset) forKey:@"offset"];
            [param setObject:@(pageSize) forKey:@"pageSize"];
    
            [VENetworkHelper requestDataWithUrl:requestDramaListUrl httpMethod:@"POST" parameters:param success:^(id _Nonnull responseObject) {
                if (responseObject && [responseObject isKindOfClass:[NSDictionary class]]) {
                    NSArray* results = [responseObject objectForKey:@"result"];
                    for (NSDictionary *dic in results) {
                        // 将请求到的数据封装为 VEDramaInfoModel 返回给页面进行展示
                        VEDramaInfoModel *dramaModel = [[VEDramaInfoModel alloc] initWithDictionary:dic error:nil];
                        [dramas addObject:dramaModel];
                    }
                }
                if (complete) {
                    complete(dramas, nil);
                }
            } failure:^(NSString * _Nonnull errorMessage) {
                if (complete) {
                    complete(nil, errorMessage);
                }
            }];
        });
    }
    

适配短剧场景特色功能

防录屏

参考 VEVideoPlayerController+DisRecordScreen.h 通过监听系统录屏通知实现防录屏。

- (void)registerScreenCapturedDidChangeNotification {
    if (@available(iOS 11.0, *)) {
        [[NSNotificationCenter defaultCenter] removeObserver:self name:UIScreenCapturedDidChangeNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onScreenCapturedChange:) name:UIScreenCapturedDidChangeNotification object:nil];
    }
}

- (void)onScreenCapturedChange:(NSNotification *)notification {
    if (@available(iOS 11.0, *)) {
        UIScreen *screen = notification.object;
        if (screen) {
            if ([screen isCaptured]) {
                // 正在录屏,停止播放
            } else {
                // 没有录屏,正常播放
            }
        }
    }
}

付费内容解锁播放

短剧 Demo 中付费内容解锁播放的流程具体如下图所示。
Image
短剧 Demo 中实现了完整付费解锁流程,但未接入第三方支付 SDK,仅在 ShortDramaPayViewController 中模拟支付流程逻辑,您需要将这部分替换为真实链路。

自定义 PlayerModule

播放控件架构

Demo 中的播放控件 VEPlayerKit 分为以下三层:

  • PlayerCore:播放器核心封装层,通常不需要修改。
  • PlayerModules:核心开发层。在这一层进行与播放控制相关的开发工作。播放控件层内置了常用的 PlayerModule,支持您将支付、短剧 UI 等业务逻辑自定义为 PlayerModule
  • Scene:场景层。这一层主要负责在不同场景下调用 PlayerCorePlayerModules,实现不同场景下的播放功能。

Image
短剧相关的 PlayerModule 位于 VESceneModule/ShortDrama/PlayerModules 目录。如果 Demo 中已实现的短剧场景无法完全满足实际业务需求,您可以通过自定义 PlayerModule 来满足自定义需求,比如修改播放控件样式、增加新的展示模块等。本节以新增短剧播放按钮 ShortDramaPlayButtonModule 为例介绍自定义 PlayerModule 的步骤。

创建 PlayerModule

  1. 定义播控实例和 UI 控件:

    // 播放器实例
    @property (nonatomic, weak) id<VEVideoPlayback> playerInterface; 
    // 播控手势实例,可以添加单击、双击、拖拽收拾
    @property (nonatomic, weak) id<VEPlayerGestureServiceInterface> gestureService; 
    // 播放视图的实例,您自己的 UI 控件可以添加到播放视图上
    @property (nonatomic, weak) id<VEPlayerActionViewInterface> actionViewInterface;
    
    // 自定义 UI 控件
    @property (nonatomic, strong) UIButton *playButton;
    
  2. 链接播控实例:

    说明

    更多示例代码请见 VEPlayerKit/Interaction

    VEPlayerContextDILink(playerInterface, VEVideoPlayback, self.context);
    VEPlayerContextDILink(gestureService, VEPlayerGestureServiceInterface, self.context);
    VEPlayerContextDILink(actionViewInterface, VEPlayerActionViewInterface, self.context);
    
  3. 绑定播放监听:

    // 监听播放 Action,修改播放按钮显示状态
    // Demo 中内置了常用 Action 事件,您也可以根据需求新增 Action
    @weakify(self);
    [self.context addKey:VEPlayerContextKeyPlayAction withObserver:self handler:^(id  _Nullable object, NSString *key) {
        @strongify(self);
        if (object) {
            [self updatePlayButtonWithPlayState:YES];
        }
    }];
    
  4. 处理手势事件:

    // 播放控件封装手机操作,业务使用非常简单
    // 1. 添加需要的手势操作,例如这里需要一个点击播放页面的手势操作
    [self.gestureService addGestureHandler:self forType:VEGestureType_SingleTap];
    
    
    // 2. 实现手势回调,并在回调中实现自身业务逻辑
    // 注意 PlayerMoudle 需要实现 <VEPlayerGestureHandlerProtocol> 协议
    - (void)handleGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer gestureType:(VEGestureType)gestureType {
        VEVideoPlaybackState playbackState = (VEVideoPlaybackState)[self.context integerForHandlerKey:VEPlayerContextKeyPlaybackState];
        if (playbackState == VEVideoPlaybackStatePlaying) {
            [self.playerInterface pause];
        } else if (playbackState == VEVideoPlaybackStatePaused) {
            [self.playerInterface play];
        }
        [self.context post:@(self.playerInterface.playbackState) forKey:VEPlayerContextKeyPlayButtonSingleTap];
    }
    
  5. 实现 PlayerModule 生命周期函数:

    #pragma mark - Life Cycle
    
    // Module 加载完成
    - (void)moduleDidLoad {
        [super moduleDidLoad];
    }
    
    // 该方法跟 UIViewController viewDidLoad 类似,可以在这里进行添加监听、手势、处理 UI 等操作
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        [self configuratoinCustomView];
        
        [self.gestureService addGestureHandler:self forType:VEGestureType_SingleTap];
        
        @weakify(self);
        [self.context addKey:VEPlayerContextKeyPlayAction withObserver:self handler:^(id  _Nullable object, NSString *key) {
            @strongify(self);
            if (object) {
                [self updatePlayButtonWithPlayState:YES];
            }
        }];
    }
    
    // 处理模板,业务可不关注该方法
    - (void)controlViewTemplateDidUpdate {
        [super controlViewTemplateDidUpdate];
    }
    
    // Module 卸载,这个方法里进行释放监听、手势、移除 UI 等操作
    - (void)moduleDidUnLoad {
        [super moduleDidUnLoad];
        [self.gestureService removeGestureHandler:self];
        [self.context removeHandlersForObserver:self];
        if (self.playButton) {
            [self.playButton removeFromSuperview];
            self.playButton = nil;
        }
    }
    

将 PlayerModule 添加到合适的视图层级

通过 PlayerModule 中链接的 actionViewInterface 实例操作播放器视图。您可以将自定义 PlayerModule 添加到 actionViewInterface 的子视图上。以下示例展示将播放按钮添加到 playbackControlView 视图中,使用常用的布局组件进行布局:

- (void)configuratoinCustomView {
    [self.actionViewInterface.playbackControlView addSubview:self.playButton];
    [self.playButton mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self.actionViewInterface.playbackControlView);
    }];
}

VEPlayerActionViewInterface 对象负责管理播放器视图。Demo 中对视图进行分层,基本可以满足短、中、长场景的业务需求。以下代码详细介绍不同层级的视图适合添加哪些业务 Module:

/** ---------------------------------------  */
/**  播放器视图层级                            **/
/**  -playerContainerView                    **/
/**  --EnginePlayerView                      **/
/**  --ActionView                            **/
/**  ---ControlView                          **/
/** ---------------------------------------  */

@protocol VEPlayerActionViewInterface <NSObject>

/// 播放器交互视图,业务不要直接使用
@property (nonatomic, strong, readonly) VEPlayerActionView *actionView;

/// 播放器的父容器视图。如果当前组件超出播放器视图范围,可以用播放器的父视图进行布局
@property (nonatomic, weak, nullable) UIView *playerContainerView;

/// 在 playbackControl 的下面,比如弹幕
@property (nonatomic, strong, readonly) VEPlayerControlView *underlayControlView;

/// 播放控制层。所有能控制播放器的都叫做 control。Module 中控制功能的控件会加到这个 view 上,控制整体控件的消失和出现
@property (nonatomic, strong, readonly) VEPlayerControlView * playbackControlView;

/// 锁屏时的控制层,例如解锁的按钮等
@property (nonatomic, strong, readonly) VEPlayerControlView * playbackLockControlView;

/// 在 playbackControl 的上面,比如各种的提示、loading 等,不会随着 control 隐藏而隐藏
@property (nonatomic, strong, readonly) VEPlayerControlView * overlayControlView;

@end

加载 PlayerModule

  1. 通过 PlayerModuleLoader 加载自定义 PlayerModule。以下示例代码展示如何在短剧详情页加载 PlayerModule

    @interface ShortDramaDetailPlayerModuleLoader : VEPlayerBaseModuleLoader
    
    @end
    
    @implementation ShortDramaDetailPlayerModuleLoader
    
    - (NSArray<id<VEPlayerBaseModuleProtocol>> *)getCoreModules {
        NSMutableArray *coreModules = [NSMutableArray array];
        [coreModules addObject:[ShortDramaPlayerMaskModule new]];
        [coreModules addObject:[VEPlayerLoadingModule new]];
        [coreModules addObject:[ShortDramaPlayButtonModule new]];
        [coreModules addObject:[VEPlayerSeekModule new]];
        [coreModules addObject:[VEPlayerSeekProgressModule new]];
        [coreModules addObject:[ShortDramaIntroduceModule new]];
        // ... 其它 player module 
        
        return coreModules;
    }
    
  2. 初始化播放器时配置 PlayerModuleLoader

    VEVideoPlayerConfiguration *playerConfig = [VEVideoPlayerConfiguration defaultPlayerConfiguration];
    /// 初始化 PlayerModuleLoader
    ShortDramaDetailPlayerModuleLoader *moduleLoader = [[ShortDramaDetailPlayerModuleLoader alloc] init];
    /// 初始化播放器实例
    VEVideoPlayerController *playerController = [[VEVideoPlayerController alloc] initWithConfiguration:playerConfig moduleLoader:moduleLoader];