智能生产制作提供专业在线的直播剪辑能力,针对时效性内容,您可以同时进行直播和剪辑。通过阅读本文,您可以了解如何接入直播剪辑Web SDK。

使用说明

本文中引入的直播剪辑Web SDK的版本号1.1.2仅供参考。获取最新的版本,请参见直播剪辑工程——帮助信息

操作步骤

  1. 引入直播剪辑Web SDK。

    在项目前端页面文件中的<head>标签处引入直播剪辑Web SDK的CSS文件,如下所示:

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

    <body>标签处添加一个用以挂载剪辑界面的<div>节点,并在<body>标签末尾添加引入Web SDK的JS文件,同时添加一个用以调用Web SDK的<script>节点。

    <body>
      <div id="aliyun-live-editor" style="height:700px"></div> // 您可以根据需要改变 container 高度
      <script src="https://g.alicdn.com/thor-server/live-editing-websdk/1.1.2/index.js"></script>
      <script>
        // 调用 SDK 的代码放在这里
      </script>
    </body>
  2. 初始化直播剪辑Web SDK。
    window.AliyunLiveEditor.init(config);

config属性说明

参数 类型 必填 描述 引入版本
locale string 界面语言,取值:
  • zh-CN(默认值):中文。
  • en-US:英文。
1.0.0
container Element Web SDK生成界面挂载的DOM节点。 1.0.0
projectId string 直播剪辑工程ID。 1.0.0
onBackButtonClick () => void; 左上角返回按钮响应函数,如果不传,则不显示此按钮。 1.0.0
updateEditingProject (req: { ProjectId: string; Title: string }) => Promise<void>; 修改剪辑工程标题,如果不传,则不允许修改,参数及返回值详情请参见UpdateEditingProject 1.0.0
getEditingProject (req: { ProjectId: string }) => Promise<Response<GetEditingProjectRsp>>; 获取剪辑工程的元信息,例如:工程标题、保存路径等信息,参数及返回值详情请参见GetEditingProject 1.0.0
getEditingProjectMaterials (req: { ProjectId: string }) => Promise<Response<GetEditingProjectMaterialsRsp>>; 获取直播工程关联的直播流和片段,参数及返回值详情请参见GetEditingProjectMaterials 1.0.0
getLiveEditingIndexFile (req: { ProjectId: string; DomainName: string; AppName: string; StreamName: string }) => Promise<Response<{ IndexFile: string }>>; 获取直播录制流的播放地址,参数及返回值详情请参见GetLiveEditingIndexFile 1.0.0
describeLiveDomainConfigs (req: { DomainName: string; FunctionNames: string }) => Promise<Response<DomainConfigs>>; 查询直播域名配置,参数及返回值详情请参见DescribeLiveDomainConfigs 1.0.0
submitLiveEditingJob (req: SubmitLiveEditingJobReq) => Promise<Response<{ MediaId: string }>>; 提交合成片段任务,参数及返回值详情请参见SubmitLiveEditingJob 1.0.0
getMediaInfo (req: { MediaId: string }) => Promise<Response<{ MediaInfo: MediaInfo }>>; 查询合成片段的状态,参数及返回值详情请参见GetMediaInfo 1.0.0
onExport (segments: Segment[]) => void; 右上角导出到视频剪辑功能响应函数,segments为您选中的片段数组。 1.0.0
getDescribeLiveSnapshotConfig (req: { DomainName: string; AppName: string }) => Promise<Response<GetDescribeLiveSnapshotConfigList>>; 查询直播截图配置,参数及返回值详情请参见DescribeLiveSnapshotConfig 1.1.0
getDescribeLiveStreamSnapshotInfo (req: { DomainName: string; AppName: string; StreamName: string; StartTime: string; EndTime: string; Limit: number; Order: string }) => Promise<Response<LiveStreamSnapshotInfoList>>; 查询截图内容,参数及返回值详情请参见DescribeLiveStreamSnapshotInfo 1.1.0
batchGenOSSUrlWithSign (req: { signList: SnapshotOssInfo[] }) => Promise<Response<SignedUrl[]>>; 批量生成带鉴权串的OSS地址,即SignedUrl。
说明 如果截图配置的存储位置为私有Bucket,您需要在自己的服务端实现接口(用于批量生成带临时访问凭证的OSS资源地址),同时需要注意过期时间。具体操作,请参见在URL中包含签名
1.1.2

数据结构说明:

  • Segment
    interface Segment {
      title: string; // 片段的标题
      mediaId: string; // 片段的媒资ID
      coverUrl: string | null; // 片段的封面图地址,可能为 null
      duration: number; // 片段的时长,单位为毫秒
    }
  • SnapshotOssInfo
    interface SnapshotOssInfo {
      region: string; // 接入区域
      bucketName: string; // OSS存储 Bucket 名称
      objectName: string; // OSS存储的文件名,path/to/object.*
    }

init()示例代码

重要 Web SDK只负责界面交互,不会发起请求,您需要通过Web SDK调用请求逻辑。请求本身应该先发送给您自己的服务端,您自己的服务端再根据AccessKey信息(AccessKey ID和AccessKey Secret)转发给相关的阿里云OpenAPI。
// 注意,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);
  },
  getDescribeLiveSnapshotConfig: req => {
    return request('DescribeLiveSnapshotConfig', req);
  },
  getDescribeLiveStreamSnapshotInfo: req => {
    return request('DescribeLiveStreamSnapshotInfo', req);
  },
  batchGenOSSUrlWithSign: req => {
    return request('multiGenerateOSSURLWithSign', req); // https://help.aliyun.com/document_detail/31952.htm
  },
  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);
};

相关接口说明: