本文提供视频点播 Android 端集成短剧场景的方案概览,并为您详细介绍如何快速集成短剧 Demo。
火山引擎视频点播针对短剧场景提供四种客户端集成方案。下表列出这四种解决方案的详细区别。
集成方案 | 集成短剧 Demo | 集成短视频场景控件 | 集成播放控件 PlayerKit | 集成播放器 SDK |
---|---|---|---|---|
方案说明 | 基于短视频场景控件,提供支持防录屏、剧集切换、 切换页面无缝续播、付费内容解锁播放等短剧场景特色功能。 | 基于播放控件,提供短视频播放场景框架,但不包含业务逻辑。 | 基于播放器 SDK,提供 View 层播放能力,屏蔽播放器使用细节。 | 直接集成播放器 SDK。 |
适用客户 | 适用于开发新 App、可切换业务接口层逻辑的客户。 | 适用于开发新 App 或者已有 App 但可切换业务播放架构的客户。 | 适用于开发新 App 或者已有 App 但可切换业务播放架构的客户。 |
|
开发工作 | 仅需切换业务接口层逻辑和转换数据结构。 | 客户需自行实现网络数据交互、短剧播放控件 UI 浮层、短剧播放页面切换、短剧支付等功能。 | 客户需自行实现网络数据交互、短剧播放控件 UI 浮层、短剧播放页面切换、短剧支付等功能。 | 客户自行实现播放器功能以及短剧场景特色功能。 |
上线时间 | 最快一周上线 | 最快两周上线 | 两周至一个月 | 一个月左右 |
相关链接 | 本文 |
在集成短剧 Demo 前,建议您先参考文档跑通 Demo,体验短剧场景的功能。
短剧 Demo 源码位于在 VEVodDemo-Android 仓库 vod-demo
文件夹内,其目录结构说明如下:
|--VEVodDemo-android |--|--app // 主 app (壳工程) |--|--vod-demo-api // vod-demo 模块与壳工程交互接口(组件化) |--|--vod-demo // 业务 demo 层 |--|--vod-scenekit // 场景控件层 |--|--vod-playerkit // 播放控件层 |--|--vod-settingskit // 播放设置模块
运行以下命令将源码下载至本地:
git clone https://github.com/volcengine/VEVodDemo-android cd VEVodDemo-android
将以下文件夹拷贝至您的项目根目录下,层级结构与 VEVodDemo-Android
中保持一致:
gradle-config vod-demo vod-demo-api vod-playerkit vod-scenekit vod-settingskit
说明
复制完成后,建议运行一次 git commit
,并在 commit message 中记录 VEVodDemo-Android
当前最新的 Commit ID。因为根据业务需要,源码可能会有变动,这次 commit 可以帮您追溯。
在项目根目录下的 build.gradle
文件中的 repositories
部分配置 google
、mavenCentral()
和火山引擎 maven 服务。
allprojects { repositories { google() mavenCentral() maven { url "https://artifact.bytedance.com/repository/Volcengine/" // 火山引擎 maven 服务 } } }
在 settings.gradle
文件中引入 Demo 模块:
include ':app' include ':vod-demo' include ':vod-demo-api' apply from: file("gradle-config/vod_playerkit_library_settings.gradle") apply from: file("gradle-config/vod_scenekit_library_settings.gradle")
在 App module 的 build.gradle
文件中添加对 Demo 模块的依赖:
// 在 app 的 build.gradle 文件添加 Java 8 支持 android { // ... compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { api project(":vod-demo") }
同步 gradle。如果 Android Studio 中没有报错,则表示成功引入了 Demo 相关模块。
在项目中添加点播 SDK 所需的权限和混淆规则,具体请见集成 SDK。短剧 Demo 无新增权限,混淆规则已在 consumer-rules.pro
中配置,您无需额外工作。
调用 VodSDK 的 init
方法进行初始化。你可以修改 init
方法中的 L.ENABLE_LOG
来开启或关闭日志。
public class App extends Application { @Override public void onCreate() { super.onCreate(); VodSDK.init(this, "your app id", "your app name", "your app channel", "your app version", "assets:///your_license_name.lic", ); } }
vod-demo
模块的 Manifest
中已配置了 DramaMainActivity
,您可参考以下代码直接使用:
DramaMainActivity.intentInto(activity);
您可参考以下步骤快速将短剧 Demo 与您的业务逻辑进行适配:
minidrama/data/remote/api
包中定义的 API。├ VodSDK.java // Demo 层播放器 SDK 初始化类 ├ data │ └ remote │ ├ api │ │ └ DramaApi.java // 短剧 HTTP API 接口类 │ └ model │ └ drama │ ├ DramaInfo.java // 短剧信息 │ ├ EpisodeInfo.java // 单集信息 │ ├ EpisodePayInfo.java // 单集付费信息 │ └ EpisodeVideo.java // 单集视频 └ ui └ minidrama ├ data │ └ remote │ └ api │ ├ GetDramaDetailApi.java // 获取短剧详情流 API │ ├ GetDramasApi.java // 获取短剧封面流 API │ ├ GetEpisodeRecommendApi.java // 获取短剧推荐流 API │ └ GetEpisodesApi.java // 指定 ID 获取付费后短剧视频 API └ scene ├ detail │ ├ DramaDetailVideoActivity.java // 短剧详情页壳 Activity │ ├ DramaDetailVideoFragment.java // 短剧详情页 Fragment │ ├ DramaEpisodePayDialogFragment.java // 短剧付费弹窗 │ └ DramaEpisodeSelectDialogFragment.java // 短剧选集弹窗 ├ main │ └ DramaMainActivity.java // 短剧首页 ├ recommend │ └ DramaRecommendVideoFragment.java // 短剧推荐页 ├ theater │ └ DramaTheaterFragment.java // 短剧剧场页 └ widgets ├ DramaVideoViewFactory.java // 短剧 VideoView 工厂 ├ bottom │ ├ EpisodeSelectorViewHolder.java // 短剧选集控件 │ └ SpeedIndicatorViewHolder.java // 短剧倍速控件 └ layer ├ DramaBottomProgressBarLayer.java // 短剧进度条浮层 ├ DramaGestureLayer.java // 短剧手势浮层 ├ DramaTimeProgressDialogLayer.java // 短剧进度展示浮层 └ DramaVideoLayer.java // 短剧视频浮层
推荐页接口适配:参考以下示例代码在推荐页中调用获取短剧推荐流接口。
说明
也可参考 GetEpisodeRecommend.java 中的实现。
public class DramaRecommendVideoFragment extends BaseFragment { private GetEpisodeRecommendApi mRemoteApi; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // mRemoteApi = new GetEpisodeRecommend(); mRemoteApi = new AppGetEpisodeRecommendApi(); } } // 可参考 GetEpisodeRecommend.java 中的实现。 public class AppGetEpisodeRecommendApi implements GetEpisodeRecommendApi { @Override public void getRecommendEpisodeVideoItems(String account, int pageIndex, int pageSize, RemoteApi.Callback<List<EpisodeVideo>> callback) { } @Override public void cancel() { } }
详情页接口适配:参考以下示例代码在详情页中调用获取短剧详情流接口。
说明
也可参考 GetDramaDetail.java 中的实现。
public class DramaDetailVideoFragment extends BaseFragment { private GetDramaDetailApi mRemoteApi; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // mRemoteApi = new MockGetDramaDetail(); mRemoteApi = new AppGetDramaDetail(); } } public class AppGetDramaDetailApi implements GetDramaDetailApi { @Override public void getDramaDetail(int startIndex, int pageSize, String dramaId, Integer orderType, RemoteApi.Callback<List<EpisodeVideo>> callback) { } @Override public void cancel() { } }
剧场页接口适配:参考以下示例代码在剧场页中调用获取短剧列表接口。
说明
也可参考 GetDramas.java 中的实现。
public class DramaTheaterFragment extends BaseFragment { private GetDramasApi mRemoteApi; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // mRemoteApi = new GetDramas(); mRemoteApi = new AppGetGetDramasApi(); } } public class AppGetGetDramasApi implements GetDramaDetailApi { @Override public void getDramas(int pageIndex, int pageSize, RemoteApi.Callback<List<DramaInfo>> callback) { } @Override public void cancel() { } }
为构建短剧业务逻辑,短剧 Demo 层定义了 DramaInfo
(短剧信息)、EpisodeVideo
(单集视频)、EpisodeInfo
(单集信息)、EpisodePayInfo
(单集付费解锁信息)等数据结构。您需要将你自己的应用服务端返回的数据结构转换为上述数据结构。数据结构具体说明如下表所示。
类 | 字段 | 类型 | 是否必需 | 描述 |
---|---|---|---|---|
DramaInfo | dramaId | String | 必需 | 短剧 ID |
dramaTitle | String | 必需 | 短剧名 | |
coverUrl | String | 必需 | 短剧封面 | |
totalEpisodeNumber | Int | 必需 | 短剧总集数 | |
latestEpisodeNumber | Int | 可选 | 短剧最新集数 | |
EpisodeVideo | vid | Int | 必需 | 视频 ID |
caption | String | 可选 | 视频标题 | |
duration | Double | 必需 | 视频时长(单位:秒) | |
coverUrl | String | 必需 | 封面地址 | |
videoUrl | String | 使用 DirectUrl 数据源必需 | 视频播放地址 | |
videoModel | String | 用 VideoModel 数据源必需 | VideoModel 数据源 JSON 结构。AppServer 可通过 GetPlayInfo 接口获取。 | |
playAuthToken | String | 用 Vid 数据源必需 | Vid 数据源的 PlayAuthToken。AppServer 可使用视频点播服务端 SDK 签发。 | |
subtitleAuthToken | String | 可选 | Vid 数据源的获取字幕 Token。AppServer 使用视频点播服务端 SDK 签发。 | |
episodeInfo | EpisodeInfo | 必需 | 短剧单集信息 | |
episodePayInfo | EpisodePayInfo | 付费解锁必需 | 短剧单集支付解锁信息 | |
EpisodeInfo | dramaInfo | DramaInfo | 必需 | 短剧信息 |
episodeNumber | Int | 必需 | 短剧集数 | |
episodeDesc | String | 必需 | 短剧单集描述 | |
EpisodePayInfo | payType | Int | 必需 | 短剧支付解锁类型:
|
短剧 Demo 中使用了短视频场景控件,而 VideoItem
是该控件的数据结构。因此在短剧 Demo 中需要通过以下示例代码将 EpisodeVideo
转换为 VideoItem
才能与短视频场景控件配合使用。
// 将 episodeVideo 转为 videoItem VideoItem videoItem = EpisodeVideo.toVideoItem(episodeVideo);
基于短视频场景控件构建的组件,都只能获取到 VideoItem
,无法感知 EpisodeVideo
。因此如果您需要基于短视频场景控件构建短剧业务,需要提供通过 VideoItem
获取 EpisodeVideo
的能力。获取到 EpisodeVideo
后,您就获得了短剧相关信息,继而可以编写短剧相关的业务逻辑。
// 从 videoItem 中获取对应的 episodeVideo EpisodeVideo episodeVideo = EpisodeVideo.get(videoItem);
可参考以下示例代码在 Activity 的 onCreate
方法中调用系统 API 开启防录屏功能。短剧 Demo 的 DramaMainActivity
、DramaDetailVideoActivity
中已默认开启防录屏。
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); }
短剧 Demo 中付费内容解锁播放的流程具体如下图所示。
短剧 Demo 未接入第三方支付 SDK。目前使用 mock 的方式串通了流程。您可参考以下步骤替换为真实链路:
pay
方法中的 mock 支付逻辑。GetEpisodesApi
接口,替换 onCreate
中实例化的 MockGetEpisodes
逻辑。示例代码如下:
public class DramaEpisodePayDialogFragment extends BaseDialogFragment { private GetEpisodesApi mGetEpisodeApi; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // ... // TODO replace mock code mGetEpisodeApi = new MockGetEpisodes(); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); view.findViewById(R.id.pay).setOnClickListener(v -> pay()); } private void pay() { // TODO replace mock code MockThirdPartPayService.requestPay(mEpisode, () -> { if (getActivity() == null) return; // mock 第三方支付返回成功 fetchUnlockedEpisode(mEpisode); }); } }