直播剪辑 WebSDK 接入指南

aliyun-live-editing-websdk 是一个快速搭建直播剪辑 web 端界面的 SDK,本文将介绍如何接入这个 SDK

一、准备工作

在 html 的 head 标签中引入 SDK 的 CSS 文件,如下所示:

<head>
  <link rel="stylesheet" href="https://g.alicdn.com/thor-server/live-editing-websdk/x.x.x/index.css">
</head>

其中 x.x.x 为版本号,当前最新版本为 1.0.0

然后在 html 的 body 标签中创建一个用以挂载剪辑界面的节点,在 body 标签的尾部引入 SDK 的 JS 文件,再引用一个 script 用以调用 WebSDK,如下所示:

<body>
  <div id="aliyun-live-editor" style="height:700px"></div> // 您可以根据需要改变 container 高度
  <script src="https://g.alicdn.com/thor-server/live-editing-websdk/x.x.x/index.js"></script>
  <script>
    // 调用 SDK 的代码放在这里
  </script>
</body>

二、初始化

WebSDK 目前只提供单独 JS 引入的方式,后续会考虑提供 npm 包。该单独 JS 会在 window 上挂载 AliyunLiveEditor 对象。

WebSDK 调用方式如下:

window.AliyunLiveEditor.init(config);

config 是一个对象,对象的 key-value 说明见下一节

三、config 参数说明

注意:这里列出了参考链接的 config 参数,都是希望调用 OpenAPI 对应的接口,在大部分情况下,前端不需要关心参数及返回值,只需要透传函数参数给 OpenAPI,再将 OpenAPI 的响应返回给 WebSDK 即可,具体可参考第五节的示例代码

参数名

参数说明

是否必选

参数类型

参数类型说明

引入版本

locale

界面语言

'zh-CN' | 'en-US'

语言代码,默认展示中文

1.0.0

container

WebSDK 生成界面挂载的 DOM 节点

Element

DOM 节点

1.0.0

projectId

直播剪辑工程的 id

string

1.0.0

onBackButtonClick

一个函数,用户点击左上角返回按钮后被调用,如不传,则不展示返回按钮

() => void;

1.0.0

updateEditingProject

一个函数,用户修改工程标题后被调用,如不传,则不允许修改工程标题

(req: { ProjectId: string; Title: string }) => Promise<void>;

参数及返回值请参考:修改云剪辑工程

1.0.0

getEditingProject

获取直播工程的元信息,如工程标题、保存路径

(req: { ProjectId: string }) => Promise<Response<GetEditingProjectRsp>>;

参数及返回值请参考:获取单个云剪辑工程

1.0.0

getEditingProjectMaterials

获取直播工程关联的直播流和片段

(req: { ProjectId: string }) => Promise<Response<GetEditingProjectMaterialsRsp>>;

参数及返回值请参考:获取剪辑工程关联素材

1.0.0

getLiveEditingIndexFile

获取直播录制流的播放地址

(req: { ProjectId: string; DomainName: string; AppName: string; StreamName: string }) => Promise<Response<{ IndexFile: string }>>;

参数及返回值请参考:获取直播剪辑索引文件

1.0.0

describeLiveDomainConfigs

查询直播域名配置

(req: { DomainName: string; FunctionNames: string }) => Promise<Response<DomainConfigs>>;

参数及返回值请参考:查询直播域名配置

1.0.0

submitLiveEditingJob

提交合成片段任务

(req: SubmitLiveEditingJobReq) => Promise<Response<{ MediaId: string }>>;

参数及返回值请参考:提交直播剪辑任务

1.0.0

getMediaInfo

查询合成片段的状态

(req: { MediaId: string }) => Promise<Response<{ MediaInfo: MediaInfo }>>;

参数及返回值请参考:获取媒资内容信息

1.0.0

onExport

一个函数,当用户勾选片段并点击“导出到视频剪辑”后被调用

(segments: Segment[]) => void;

参数 segments 是用户所勾选的片段数组,参考下一节的 Segment

1.0.0

四、数据结构补充说明

Segment

interface Segment {
  title: string; // 片段的标题
  mediaId: string; // 片段的媒资 id
  coverUrl: string | null; // 片段的封面图地址,可能为 null
  duration: number; // 片段的时长,单位毫秒
}

五、init 方法示例代码

WebSDK 本身只负责界面和交互,不会发起请求,您需要将请求的逻辑提供给 WebSDK 调用。请求本身应该先发送给您自己的服务端,您自己的服务端再根据自己的 AccessKeyId, AccessKeySecret 转发给相关的阿里云 OpenAPI

CreateEditingProject 请参考:创建云剪辑工程

AddEditingProjectMaterials 请参考:新增剪辑工程关联素材

// 注意,WebSDK 本身并不提供 request 这个方法,这里仅作为示例,您可以使用您喜欢的网络请求库,如 axios 等

const projectId = 'exampleId';

window.AliyunLiveEditor.init({
  locale: 'zh-CN',
  container: document.getElementById('aliyun-live-editor'),
  projectId,
  onBackButtonClick: () => {
    // 跳转回列表页
    window.location.href = '/mediaEdit/list/live';
  },
  updateEditingProject: req => {
    return request('UpdateEditingProject', req);
  },
  getEditingProject: req => {
    return request('GetEditingProject', req);
  },
  getEditingProjectMaterials: req => {
    return request('GetEditingProjectMaterials', req);
  },
  getLiveEditingIndexFile: req => {
    return request('GetLiveEditingIndexFile', req);
  },
  describeLiveDomainConfigs: req => {
    return request('DescribeLiveDomainConfigs', req);
  },
  submitLiveEditingJob: req => {
    return request('SubmitLiveEditingJob', req);
  },
  getMediaInfo: req => {
    return request('GetMediaInfo', req);
  },
  onExport: async segments => {
    const { ProjectMaterials = [] } = await request('GetEditingProjectMaterials', {
      ProjectId: projectId
    }).then(res => res.data);
    let videoEditingProjectId;
    if (ProjectMaterials.length) {
      // 已经有绑定的视频剪辑工程
      videoEditingProjectId = ProjectMaterials[0];
    } else {
      // 创建新的普通剪辑工程
      const { Project } = await request('CreateEditingProject', {
        Title: `直播剪辑视频_${projectId}`,
      }).then(res => res.data);
      // 绑定直播剪辑工程和普通剪辑工程
      await request('AddEditingProjectMaterials', {
        ProjectId: projectId,
        MaterialMaps: JSON.stringify({ editingProject: Project.ProjectId })
      });
      videoEditingProjectId = Project.ProjectId;
    }

    const mediaIds = segments.map(s => s.mediaId);

    await handleBindingMaterials(mediaIds, videoEditingProjectId);

    // 打开普通剪辑工程的页面
    window.open(`/mediaEdit/detail/${videoEditingProjectId}`);
  },
});

// 因为新增素材绑定接口有一次请求最多绑定10条的限制,故分批操作
async function handleBindingMaterials(MediaIds, ProjectId) => {
  const promiseGroup = [];

  const addTimes = Math.ceil(MediaIds.length / 10);
  for (let i = 0; i < addTimes; i++) {
    const newMap = {};
    const videoList = MediaIds.slice(i * 10, (i + 1) * 10);

    if (videoList.length) {
      newMap.video = videoList.join(',');
    }

    promiseGroup.push(
      request('AddEditingProjectMaterials', {
        ProjectId,
        MaterialMaps: JSON.stringify(newMap)
      })
    );
  }

  await Promise.all(promiseGroup);
};