伯索开放平台
首页API文档SDK文档
伯索官网
首页API文档SDK文档
伯索官网
  1. 直播SDK
  • 历史课堂数据回调接入
  • 签名示例
  • 历史课堂转视频接入方式
  • 历史课堂/微课下载接口
  • query参数说明
  • 文档解析接口
  • SDK预解析服务接入说明
  • 「伯索云学堂」跳转接入指南
  • SDK版本发布记录
  • Web SDK
    • PlasoStyleUpime API说明
    • 云盘接入
    • 接入预解析能力 (Web端)
    • 直播SDK
      • 快速接入
    • 微课SDK
      • 快速接入
    • 批注SDK
      • 快速接入
  • Electron SDK
    • 快速接入
  • Android SDK
    • 直播SDK
      • 快速接入
    • 微课SDK
      • 快速接入
    • 批注SDK
      • 快速接入
  • Harmony SDK
    • 直播SDK
      • 快速接入
  • iOS SDK
    • 直播SDK
      • 快速接入
    • 微课SDK
      • 快速接入
    • 批注SDK
      • 快速接入
  • 播放器SDK
    • Web播放器
  1. 直播SDK

快速接入

鸿蒙对外 SDK#

2026.09.29 对外版本:
版本号:1.68.100(OHPM 包 styleupime)
注意事项
当前 SDK 只支持学生端接入,暂不支持老师端、助教端接入。 进课时 userType 请使用 listener。

1. 实时课堂集成方式#

1. 环境要求#

组件版本要求
HarmonyOS5.0+(API 12+)
DevEco Studio5.0+
语言ArkTS
宿主工程建议:
工程根目录 build-profile.json5 中 compatibleSdkVersion ≥ 5.0.0(12)(Demo 为 5.0.0(12))
module.json5 中 deviceTypes 包含 phone / tablet(或 default)

2. 配置伯索的 ohpm 仓库#

在工程根目录 .ohpmrc 中添加伯索的 ohpm 仓库(styleupime 发布在 /repos/upime,需放在第一位;SDK 依赖的 @ohos/flutter_* 等包同样优先从 /repos/upime 拉取):

3. 添加 SDK 依赖#

在项目的 entry/oh-package.json5 中添加 SDK 依赖:
{
  "dependencies": {
    "styleupime": "1.68.100"
  }
}
然后在工程根目录执行:

4. 配置权限#

在 entry/src/main/module.json5 中声明课堂所需权限:
{
  "module": {
    "requestPermissions": [
      { "name": "ohos.permission.INTERNET" },
      {
        "name": "ohos.permission.CAMERA",
        "reason": "$string:permission_camera_reason",
        "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
      },
      {
        "name": "ohos.permission.MICROPHONE",
        "reason": "$string:permission_microphone_reason",
        "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
      }
    ]
  }
}
对应 string.json 中补充权限用途说明。

5. 初始化 SDK#

在 EntryAbility 中接入 SDK 生命周期,并注册 IResourceProvider(见下一节)。IResourceProvider 不能为 null,后续启动实时课堂需要使用 StyleUpime 对象。
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { StyleUpime, StyleUpimeAbilityHelper } from 'styleupime';

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    StyleUpimeAbilityHelper.onAbilityCreate(this);
    StyleUpime.getInstance().setResourceProvider(this.resProvider);
  }

  onDestroy(): void {
    StyleUpimeAbilityHelper.onAbilityDestroy(this);
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    StyleUpimeAbilityHelper.onWindowStageCreate(this, windowStage);
    windowStage.loadContent('pages/Index', (err) => {});
  }

  onWindowStageDestroy(): void {
    StyleUpimeAbilityHelper.onWindowStageDestroy(this);
  }
}

6. 添加实时课堂页面#

启动实时课堂需要宿主提供课堂页。请新增 pages/PlasoLiveClassPage,并在 main_pages.json 中登记。
entry/src/main/resources/base/profile/main_pages.json:
{
  "src": [
    "pages/Index",
    "pages/PlasoLiveClassPage"
  ]
}
entry/src/main/ets/pages/PlasoLiveClassPage.ets:
import { PlasoLiveClassView, LiveClassBackPressBridge } from 'styleupime';

@Entry
@Component
struct PlasoLiveClassPage {
  build() {
    Stack() {
      PlasoLiveClassView()
    }
    .width('100%')
    .height('100%')
  }

  onBackPress(): boolean {
    return LiveClassBackPressBridge.handleBackPress();
  }
}
页面路径必须为 pages/PlasoLiveClassPage。

7. 实现 IResourceProvider#

IResourceProvider 是定制化资料中心接口,APP 提供该接口的实现,并传入 SDK。实现后通过 StyleUpime.getInstance().setResourceProvider 注册。
import {
  IResourceProvider,
  IResourceCallback,
  SignCallback,
  IUrlCallback,
  StyleUpime
} from 'styleupime';

class AppResourceProvider implements IResourceProvider {
  supportResourceCenter(): boolean {
    // 是否支持定制化资料中心,返回 true 代表支持,false 代表不支持
    return false;
  }

  showResourceCenter(_checkAi: boolean): void {
    // 显示 APP 定制化资料中心,在此处实现跳转到定制化资料中心页面
  }

  dismissResourceCenter(): void {
    // 关闭 APP 定制化资料中心
  }

  getExtFileName(info: Object | null, callback: IResourceCallback): void {
    // APP 根据传入的文件标识内容返回其可以访问的完整 URL
    callback.onFilePath(null);
  }

  getPreParseFileName(info: Object | null, suffix: string | null, callback: IResourceCallback): void {
    // APP 根据传入的预解析文件标识内容返回其可以访问的完整 URL
    callback.onFilePath(null);
  }

  signQuery(queryMap: Map<string, Object> | null, cb: SignCallback): boolean {
    // 请求 SDK Client 进行参数签名;窗口模式下插入文件,需要从服务器获取签名
    if (queryMap) {
      queryMap.set('appId', Config.appId); // Config.appId 填写从伯索获取的 appId,这个参数是必需的
      queryMap.set('validTime', 120);
      queryMap.set('validBegin', Math.floor(Date.now() / 1000));
      const obj: Record<string, string | number> = {};
      queryMap.forEach((v: Object, k: string) => {
        obj[k] = (typeof v === 'string' || typeof v === 'number') ? v : `${v}`;
      });
      // 根据 queryMap、appKey 生成 query
      SignHelper.sign(obj, Config.appKey).then((signed: string) => {
        cb.onSignCompleted(signed);
      }).catch(() => {
        cb.onSignCompleted(null);
      });
    } else {
      cb.onSignCompleted(null);
    }
    return true;
  }

  getWebviewUrl(_type: number, callback: IUrlCallback): void {
    // 请求 SDK Client 是否有教材包的链接需要在课堂内打开
    callback.onGetUrl('https://www.plaso.cn');
  }
}

StyleUpime.getInstance().setResourceProvider(new AppResourceProvider());
在 signQuery 方法中需要生成 query,生成方法参考如下形式:
import { cryptoFramework } from '@kit.CryptoArchitectureKit';
import { util } from '@kit.ArkTS';

class SignHelper {
  static async sign(params: Record<string, string | number>, signKey: string): Promise<string> {
    const keys = Object.keys(params).sort();
    const sorted = keys.map((key: string) => `${key}=${params[key]}`).join('&');
    params['signature'] = await SignHelper.encrypt(sorted, signKey);
    return SignHelper.buildQuery(params);
  }

  private static buildQuery(params: Record<string, string | number>): string {
    return Object.keys(params)
      .map((key: string) => `${key}=${encodeURIComponent(params[key].toString())}`)
      .join('&');
  }

  private static async encrypt(encryptText: string, signKey: string): Promise<string> {
    const macAlg = cryptoFramework.createMac('SHA1');
    const keyBlob: cryptoFramework.DataBlob = {
      data: new util.TextEncoder().encodeInto(signKey)
    };
    const symKey = await cryptoFramework.createSymKeyGenerator('HMAC').convertKey(keyBlob);
    await macAlg.init(symKey);
    await macAlg.update({ data: new util.TextEncoder().encodeInto(encryptText) });
    const macResult = await macAlg.doFinal();
    let hex = '';
    for (let i = 0; i < macResult.data.length; i++) {
      const part = macResult.data[i].toString(16);
      hex += part.length === 1 ? `0${part}` : part;
    }
    return hex.toUpperCase().replace(/^0+/, '');
  }
}

8. 启动实时课堂#

8.1 水印和日志配置(可选)#

实时课堂支持配置日志和水印,在启动实时课堂之前可根据需要进行设置,如不需要可以忽略。
import { ClassConfig, UpimeLogConfig, UpimeWaterMarkConfig } from 'styleupime';

const config = new ClassConfig();

const logConfig = new UpimeLogConfig();
logConfig.logDir = `${context.filesDir}/logs`;
logConfig.logLevel = UpimeLogConfig.DEBUG; // DEBUG: 0  INFO: 1  WARN: 2  ERROR: 3
config.logConfig = logConfig;

const waterMarkConfig = new UpimeWaterMarkConfig();
waterMarkConfig.text = 'waterMark';
waterMarkConfig.size = 14;
waterMarkConfig.opacity = 0.5;
config.waterMarkConfig = waterMarkConfig;
UpimeLogConfig 参数说明:
参数名参数类型是否必须默认值参数说明
logDirstring否{filesDir}/logs日志目录
logLevelnumber否INFO(1)日志级别 DEBUG: 0 INFO: 1 WARN: 2 ERROR: 3
UpimeWaterMarkConfig 参数说明:
参数名参数类型是否必须默认值参数说明
textstring否-水印
sizenumber否-字号
opacitynumber否-透明度 0~1

8.2 设置违禁词(可选)#

用户可以在进课堂前传入自定义违禁词列表。设置后,课堂内的消息输入将应用此词库进行过滤。
StyleUpime.getInstance().updateProhibitedWords(['违禁词1', '违禁词2']);

8.3 启动实时课堂流程#

async function getQuery(): Promise<string> {
  const params: Record<string, string | number> = {
    appId: Config.appId, // Config.appId 填写从伯索获取的 appId
    appType: 'liveclassSDK',
    beginTime: Math.floor(Date.now() / 1000),
    endTime: endTime,
    mediaType: meetingType,
    meetingId: meetingId,
    meetingType: useMeetingMode ? 'meeting' : 'public',
    loginName: userName,
    userName: userName,
    userType: userType,
    validTime: 1080000,
    onlineMode: onlineMode,
    d_sharpness: sharpness,
    d_dimension: resolution,
    vendorType: rtcType,
    enableNewClassExam: enableNewClassExam,
    d_enableObjectEraser: editElementMode
  };
  if (videoStream !== -1) {
    params['videoStream'] = videoStream;
  }
  // SignHelper.sign 方法参考上一节,Config.appKey 填写从伯索获取的 appKey
  return SignHelper.sign(params, Config.appKey);
}
query 参数说明:参数说明表格
query 生成完成后需要在启动实时课堂时传给 SDK,需要构建一个 ClassConfig 对象。ClassConfig 创建完毕后,通过 StyleUpime 调用 launchLiveClass 即可启动实时课堂。
async function launchLiveClass(context: common.UIAbilityContext): Promise<void> {
  const query = await getQuery();
  const config = new ClassConfig();
  config.classURL = query;
  config.query = query;
  config.host = 'https://www.plaso.cn';
  config.userName = userName;
  config.taskId = meetingId;
  config.openFileMode = UpimeConfig.OPEN_FILE_MODE_WINDOW;
  config.toolboxItems = UpimeConfig.ToolBoxItem.ALL;
  config.enableInteractPpt = enablePptInteract;
  config.teachToolTypes = UpimeConfig.TeacherToolType.All;
  config.supportUndo = undoSupport;
  config.useNewSmallBoard = useNewSmallBoard;
  config.supportSelect = supportSelect;
  config.endRemindTime = remindTime;
  config.redPacketLimit = redPacketLimit;
  config.residentCamera = isResidentCamera;
  config.auxiliaryCamera = isAuxiliaryCamera;
  config.supportHighlighter = supportHighlighter;
  config.forbiddenScreenShot = forbiddenScreenShot;
  config.enableVote = enableVote;
  config.enableSaveBoard = enableSaveBoard;
  config.enableAns = true;
  config.enableStarShow = enableStarShow;
  config.hideRecordingIndicator = hideRecordingIndicator;
  config.showStudentClassElapsedTime = showStudentClassElapsedTime;

  const upime = StyleUpime.getInstance();
  upime.setLiveClassExitHandler((event: NativeLiveClassExitEvent) => {
    // 退出实时课堂后回调
  });
  upime.launchLiveClass(config, context);
}
ClassConfig 参数说明:
参数名参数类型是否必要默认值参数说明
classURLstring是-实时课堂配置参数 query。如果 query 里面有中文字符串,此中文字符串需要编码后放到 query 里传入
querystring是-实时课堂配置参数 query,与 classURL 使用同一份签名结果
hoststring是-服务地址,使用线上环境即可
userNamestring否-当前用户名称
taskIdstring否-会议 / 课堂 ID
classMemberUser[]否-课堂成员列表
allowLocalPPTboolean否true是否允许在实时课堂中插入本地 PPT
enableSendMessageboolean否true消息界面是否显示消息输入框
hideOtherClientboolean否false是否显示学生不在班级的所有学生和助教
endRemindTimenumber否0实时课堂添加双减的提示时间,到达这个时间之后,会提示用户(秒)
redPacketLimitnumber否0红包雨个数限制
defaultPermissionnumber否0会议模式默认权限
supportSelectboolean否true是否支持工具栏选择箭头
openFileModenumber否0文件打开模式
toolboxItemsnumber否UpimeConfig.ToolBoxItem.ALL百宝箱工具条目配置(工具请参考 UpimeConfig.ToolBoxItem)
enableInteractPptboolean否false是否开启 PPT 交互
teachToolTypesnumber否0教具类型支持的类型(类型请参考 UpimeConfig.TeacherToolType)
supportUndoboolean否false是否支持撤销操作
useNewSmallBoardboolean否false是否使用新版小黑板
supportHighlighterboolean否false是否使用荧光笔
forbiddenScreenShotboolean否false是否防止课堂截屏
enableVoteboolean否false是否支持投票工具
enableSaveBoardboolean否false是否支持保存板书功能
chatMsgPopupnumber否0学生进入课堂时消息弹框提醒设置。0: 本地记录; 1: 远端控制开启; 2: 远端控制关闭。默认 0,仅对学生生效,老师不受此配置控制
enableAnsboolean否true是否开启降噪功能
enableStarShowboolean否false是否支持显示积分排行榜
recordingPreparationTipstring否-服务端录制课堂延时提示语,只有使用服务端录制课堂才需要添加这个提示语
hideRecordingIndicatorboolean否false是否隐藏「录制中」状态字样
showStudentClassElapsedTimeboolean否false学生端是否显示已开课时间文案
logConfigUpimeLogConfig否-日志配置
waterMarkConfigUpimeWaterMarkConfig否-水印配置

9. 资料中心接入事项#

本章节说明资料中心两类文件(非预解析、预解析)的接入要点与回调契约:
非预解析文件:通过 insertObject 直接插入,视 info 类型决定是否触发回调签名(getExtFileName)
预解析文件:通过 insertObject 插入后,由 SDK 回调 getPreParseFileName,按页 / 资源后缀返回可访问直链
通用回调接口(签名与取址):
getExtFileName(非预解析使用)
方法签名:getExtFileName(info: Object | null, callback: IResourceCallback): void
语义:基于插入时的 info 计算资源直链(含签名),成功回调 callback.onFilePath(realUrl),失败回调 callback.onFilePath(null)
getPreParseFileName(预解析使用)
方法签名:getPreParseFileName(info: Object | null, suffix: string | null, callback: IResourceCallback): void
语义:基于预解析文件标识(info)与页面后缀(suffix)计算资源直链(含签名),成功回调 callback.onFilePath(realUrl),失败回调 callback.onFilePath(null)
插入文件时调用:
import { PlasoUpimeBoard, UpimeObject } from 'styleupime';

PlasoUpimeBoard.getInstance().insertObject(
  UpimeObject.TYPE_PDF,
  info,
  title
);

9.1 非预解析文件#

资料中心打开非预解析文件时,需要创建如下 UpimeObject 并调用 insertObject 将文件插入课堂:
UpimeObject {
  info;   // 只可传入 string 或 List 两种类型
  type;   // 可传入 TYPE_PPT,TYPE_DOC,TYPE_XLS,TYPE_PDF,TYPE_AUDIO,TYPE_VIDEO
  title;  // 文件显示标题
}
(1) info 传入 string 类型
type 可为 TYPE_PPT、TYPE_DOC、TYPE_XLS、TYPE_PDF、TYPE_AUDIO、TYPE_VIDEO
info 必须是「可直接访问的 HTTP 地址」(无需额外签名)
行为:调用 insertObject 后,直接将文件插入白板,不会触发额外回调
(2) info 传入 List 类型
type 建议用于 TYPE_AUDIO、TYPE_VIDEO
info 为 List 类型(实现方自定义结构)
行为:调用 insertObject 插入后会回调 getExtFileName。实现方需在 getExtFileName 中完成签名并返回完整可访问的 HTTP 地址

9.2 预解析文件#

资料中心打开预解析文件时,需要创建如下 UpimeObject 并调用 insertObject 将文件插入课堂:
UpimeObject {
  info;   // 只可传入 List 类型
  type;   // 可传入 TYPE_I_SPRING_PPT,TYPE_DOC,TYPE_XLS,TYPE_PDF
  title;  // 文件显示标题
}
行为:调用 insertObject 后,会回调 getPreParseFileName;实现方需在该函数中完成签名并返回完整可访问的 HTTP 地址
参数说明:
info:预解析文件的唯一标识。常见形态为 List,包含键 "id",例如 ["parsed", {"id":"abcdef123456"}](推荐)。实现方应优先从 List 中读取 id,据此计算文件地址
suffix:资源相对路径 / 页面后缀,例如 "/1.jpg"、"/index.html"、"/ispring.html"、"/info.json"
callback:成功 callback.onFilePath(realUrl),失败 callback.onFilePath(null)
提示:suffix 在参与拼接与请求时通常需要进行 URL 编码;若包含图片处理等查询参数,请整体进行编码后透传到服务端签名。

10. 对接注意事项#

(1) 传入 appId 和 appKey 值。签名请通过在服务器中完成,Demo 中只是样例。
signQuery(queryMap: Map<string, Object> | null, cb: SignCallback): boolean {
  if (queryMap) {
    queryMap.set('appId', xxx); // 自己机构的 appId
    queryMap.set('validTime', 120);
    queryMap.set('validBegin', Math.floor(Date.now() / 1000));
    // TODO get it from server
    // this is only for test
    SignHelper.sign(obj, xxx).then((signed: string) => { // 自己机构的 appKey
      cb.onSignCompleted(signed);
    });
  } else {
    cb.onSignCompleted(null);
  }
  return true;
}
(2) 必须添加 pages/PlasoLiveClassPage,并写入 main_pages.json,否则无法进入实时课堂。
(3) EntryAbility 中必须调用 StyleUpimeAbilityHelper 的生命周期方法。

11. 参考代码 Demo#

以上就是实时课堂 SDK 的集成步骤,更加详细步骤请参考代码 Demo。
Demo 仓库地址:https://github.com/PlasoOpenPlatform/PlasoSdkHosSample
使用步骤:
1.
从 Demo 仓库 PlasoOpenPlatform/PlasoSdkHosSample 下载代码(git clone https://github.com/PlasoOpenPlatform/PlasoSdkHosSample.git,或下载 ZIP 并解压到本地)
2.
确认 entry/oh-package.json5 中 SDK 版本为 1.68.100
{
  "dependencies": {
    "styleupime": "1.68.100"
  }
}
3.
修改配置文件 entry/src/main/ets/config/Config.ets,填入伯索分配的 appId 和 key 后即可体验
export class Config {
  static appId: string = '<your-appId>';
  static appKey: string = '<your-app-key>';
}
修改于 2026-10-08 02:19:45
上一页
快速接入
下一页
快速接入
Built with