You need to enable JavaScript to run this app.
导航
Android 端集成
最近更新时间:2024.11.07 17:09:25首次发布时间:2024.10.10 11:51:38

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

集成方案概览

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

集成方案

集成短剧 Demo

集成短视频场景控件

集成播放控件 PlayerKit

集成播放器 SDK

方案说明

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

基于播放控件,提供短视频播放场景框架,但不包含业务逻辑。

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

直接集成播放器 SDK。

适用客户

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

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

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

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

开发工作

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

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

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

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

上线时间

最快一周上线

最快两周上线

两周至一个月

一个月左右

相关链接

本文

集成场景控件 (Android)

集成播放控件 (Android)

集成 iOS 点播 SDK

跑通 Demo

在集成短剧 Demo 前,建议您先参考文档跑通 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   // 播放设置模块

步骤 1:下载源码

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

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

步骤 2:拷贝代码

将以下文件夹拷贝至您的项目根目录下,层级结构与 VEVodDemo-Android 中保持一致:

gradle-config
vod-demo
vod-demo-api
vod-playerkit
vod-scenekit
vod-settingskit

说明

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

步骤 3:配置 Maven 仓库

在项目根目录下的 build.gradle 文件中的 repositories 部分配置 google
mavenCentral() 和火山引擎 maven 服务。

allprojects {
    repositories {
        google()
        mavenCentral()
        maven {
            url "https://artifact.bytedance.com/repository/Volcengine/" // 火山引擎 maven 服务
        }
    }
}

步骤 4:引入短剧模块

  1. 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")
    
  2. 在 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")
    }
    
  3. 同步 gradle。如果 Android Studio 中没有报错,则表示成功引入了 Demo 相关模块。

步骤 5:配置权限及混淆规则

在项目中添加点播 SDK 所需的权限和混淆规则,具体请见集成 SDK。短剧 Demo 无新增权限,混淆规则已在 consumer-rules.pro 中配置,您无需额外工作。

步骤 6:初始化短剧 Demo

调用 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",
        );
    }
}

步骤 7:集成短剧页面

vod-demo 模块的 Manifest 中已配置了 DramaMainActivity,您可参考以下代码直接使用:

DramaMainActivity.intentInto(activity);

适配业务逻辑

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

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

短剧 Demo 源码结构说明

├ 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                  // 短剧视频浮层

数据层适配

替换业务接口

  1. 推荐页接口适配:参考以下示例代码在推荐页中调用获取短剧推荐流接口。

    说明

    也可参考 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() {
        }
    }
    
  2. 详情页接口适配:参考以下示例代码在详情页中调用获取短剧详情流接口。

    说明

    也可参考 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() {
        }
    }
    
  3. 剧场页接口适配:参考以下示例代码在剧场页中调用获取短剧列表接口。

    说明

    也可参考 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

必需

短剧支付解锁类型:

  • 免费:EpisodePayInfo.EPISODE_PAY_TYPE_FREE = 0
  • 收费未解锁:EpisodePayInfo.EPISODE_PAY_TYPE_LOCKED = 1
  • 收费已解锁:EpisodePayInfo.EPISODE_PAY_TYPE_UNLOCKED = 2

短剧 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 的 DramaMainActivityDramaDetailVideoActivity 中已默认开启防录屏。

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
}

付费内容解锁播放

短剧 Demo 中付费内容解锁播放的流程具体如下图所示。
Image
短剧 Demo 未接入第三方支付 SDK。目前使用 mock 的方式串通了流程。您可参考以下步骤替换为真实链路:

  1. 接入第三方支付 SDK,替换 pay 方法中的 mock 支付逻辑。
  2. 实现 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);
        });
    }
}