多种接入场景demo/实践

6.1、快速接入

6.1.1、公有化

import { Launcher } from "live-cat";
const appKey = "xxxxxxxxxxxxxxxxxxx";
const address = "https://app.3dcat.live";
const bootstrap = async () => {
  try {
    const launch = new Launcher({
      baseOptions: {
        address,
        appKey,
        startType: 1,
      },
    });
    const player = document.querySelector("body");
    //容器必须指定宽高,否则不显示
    document.querySelector("body").style.width = "100vw";
    document.querySelector("body").style.height = "100vh";
    await launch.automata(player);
  } catch (error) {
    console.error(error);
  }
};
window.addEventListener("DOMContentLoaded", () => {
  if (
    navigator.userAgent.includes("miniProgram") ||
    navigator.userAgent.includes("MicroMessenger")
  ) {
    //微信浏览器/微信小程序环境
    document.addEventListener("WeixinJSBridgeReady", bootstrap, false);
  } else {
    bootstrap();
  }
});

6.1.2、私有化

import { LauncherPrivate } from "live-cat";
const appKey = "xxxxxxxxxxxxxxxxxxx";
const address = "私有化服务器地址";
const bootstrap = async () => {
  try {
    const launch = new LauncherPrivate({
      baseOptions: {
        address,
        appKey,
        startType: 1,
      },
    });
    const player = document.querySelector("body");
    //容器必须指定宽高,否则不显示
    document.querySelector("body").style.width = "100vw";
    document.querySelector("body").style.height = "100vh";
    await launch.automata(player);
  } catch (error) {
    console.error(error);
  }
};
window.addEventListener("DOMContentLoaded", () => {
  if (
    navigator.userAgent.includes("miniProgram") ||
    navigator.userAgent.includes("MicroMessenger")
  ) {
    //微信浏览器/微信小程序环境
    document.addEventListener("WeixinJSBridgeReady", bootstrap, false);
  } else {
    bootstrap();
  }
});

6.2、错误代码使用说明

import { Launcher } from "live-cat";
const appKey = "xxxxxxxxxxxxxxxxxxx";
const address = "https://app.3dcat.live";
const bootstrap = async () => {
  try {
    const launch = new Launcher({
      baseOptions: {
        address,
        appKey,
        startType: 1,
      },
    });

    const player = document.querySelector("body");
    document.querySelector("body").style.width = "100vw";
    document.querySelector("body").style.height = "100vh";
    await launch.automata(player).catch((code) => {
      //错误代码
      console.error(code);
    });
  } catch (error) {
    console.error(error);
  }
};
window.addEventListener("DOMContentLoaded", () => {
  if (
    navigator.userAgent.includes("miniProgram") ||
    navigator.userAgent.includes("MicroMessenger")
  ) {
    //微信浏览器/微信小程序环境
    document.addEventListener("WeixinJSBridgeReady", bootstrap, false);
  } else {
    bootstrap();
  }
});

6.3、接入消息双向通信(ue4、unity)

  • 以公有化为例
  • 应用需要集成相应插件:ue4unity
import { Launcher } from 'live-cat'
const appKey = 'xxxxxxxxxxxxxxxxxxx'
const address = 'https://app.3dcat.live'
const bootstrap = async () => {
  try {
    const launch = new Launcher({
      baseOptions: {
        address,
        appKey,
        startType: 1,
      },
      extendOptions: {
        onPlay: () => {
          //web端->应用
          window.connection.emitUIInteraction('text').then(res => {
            console.log(res ? '发送成功' : '发送失败')
          })
        },
      },
    })

    const player = document.querySelector('body')
    document.querySelector('body').style.width = '100vw'
    document.querySelector('body').style.height = '100vh'
    await launch.automata(player)
    const connection = launch.getConnection()
    window.connection = connection
    //应用->web端
    connection.event.interaction.on(msg => {
      console.log('收到应用发来的消息:', msg)
    })
  } catch (error) {
    console.error(error)
  }
}
window.addEventListener('DOMContentLoaded', () => {
  if (
    navigator.userAgent.includes('miniProgram') ||
    navigator.userAgent.includes('MicroMessenger')
  ) {
    //微信浏览器/微信小程序环境
    document.addEventListener('WeixinJSBridgeReady', bootstrap, false)
  } else {
    bootstrap()
  }
})

6.4、进入有效画面后初始化

import { Launcher } from "live-cat";
const appKey = "xxxxxxxxxxxxxxxxxxx";
const address = "https://app.3dcat.live";
const bootstrap = async () => {
  try {
    const launch = new Launcher({
      baseOptions: {
        address,
        appKey,
        startType: 1,
      },
      extendOptions: {
        onPlay: () => {
          // 初始化操作
        },
      },
    });

    const player = document.querySelector("body");
    document.querySelector("body").style.width = "100vw";
    document.querySelector("body").style.height = "100vh";
    await launch.automata(player);
  } catch (error) {
    console.error(error);
  }
};
window.addEventListener("DOMContentLoaded", () => {
  if (
    navigator.userAgent.includes("miniProgram") ||
    navigator.userAgent.includes("MicroMessenger")
  ) {
    //微信浏览器/微信小程序环境
    document.addEventListener("WeixinJSBridgeReady", bootstrap, false);
  } else {
    bootstrap();
  }
});

6.5、接入微信小程序

  • 步骤
  1. 使用 jssdk 开发,打包成 html 部署到线上(生产环境需要域名解析)
  2. 微信小程序通过 web-view 嵌入 html 地址(域名链接)
  • 开发 html 时候,微信(小程序)环境需要注意监听 WeixinJSBridgeReady 事件,然后开始对接 jssdk。
window.addEventListener("DOMContentLoaded", () => {
  if (
    navigator.userAgent.includes("miniProgram") ||
    navigator.userAgent.includes("MicroMessenger")
  ) {
    //微信浏览器/微信小程序环境
    document.addEventListener("WeixinJSBridgeReady", bootstrap, false);
  } else {
    bootstrap();
  }
});

6.6、投屏交互接入

6.7、替换加载页面各阶段提示

  1. 替换 "可视化服务启动中"
  • 运行 await launch.automata(player)后,调用 launch.getPlayer(),获取 livePlayer,执行如下代码。
livePlayer.showLoadingText("xxxxxx");
  1. 替换 "可视化服务连接中"
  • 运行 await launch.automata(player)后,调用 launch.getPlayer(),获取 livePlayer;调用 launch.getConnection(),获取 connection,执行如下代码。
const TEXT = "xxxxxx";
connection.event.connect.on(() => livePlayer.showLoadingText(TEXT));
connection.event.ready.on(() => livePlayer.showLoadingText(TEXT));
connection.event.receivedTrack.on(() => livePlayer.showLoadingText(TEXT));
connection.event.endCandidate.on(() => livePlayer.showLoadingText(TEXT));
  1. 替换 "连接成功,资源加载中""资源加载成功,请点击进入"
  • 运行 await launch.automata(player)后,调用 launch.getPlayer(),获取 livePlayer;调用 launch.getConnection(),获取 connection,执行如下代码。
connection.event.dataChannelConnected.on(() =>
  livePlayer.showLoadingText("xxxxxx")
);
  1. 替换 所有的异常报错
  • 运行 await launch.automata(player)后,执行如下代码。
launch.destory("xxxx");
//or
livePlayer.showTextOverlay("xxxxxx");

6.8、去除/替换加载流程 UI 页面

  • 挂载容器的时候(await launch.automata(player)),展示业务界面,z-index 属性大于 player 容器,等到 player 出现有效画面(onPlay)在出现有效画面后,在 onPlay 回调中把 业务界面去掉,参考代码如下。
  • 注意自动播放问题:Autoplay policy in Chrome ,可能会出现资源加载中一直进不去,需要引导用户点击 player 元素再展示业务界面。(当用户操作系统为 iOS 时,需在 connectiondataChannelConnected event 被触发时执行 launchgetManualPlay() 方法来获取手动播放方法实例,再将其应用到所需业务场景。)
//以公有化为例
const launch = new Launcher({
  baseOptions: {
    address,
    appKey,
    startType: 1,
  },
  extendOptions: {
    onPlay: () => {
      //开始播放回调
      //loadingEl 为业务界面容器
      loadingEl.style.display = "none";
    },
  },
});
const player = document.querySelector("body");
document.querySelector("body").style.width = "100vw";
document.querySelector("body").style.height = "100vh";
await launch.automata(player);

let manualPlay;

connection.event.dataChannelConnected.on(() => 
                                         { manualPlay = luanch.getManualPlay()}
                                        );
// 在所需业务场景执行如下方法
manualPlay();

6.9、接入多点触控需求(UE4)

  1. 接入 3DCAT 的UE4 插件
  2. 前台->应用详情->高级设置->开启多点触控。

6.10、浏览器直接接入 sdk

  • 参考链接
  • 需要引入 jssdk 的 umd 版本,以及相关依赖模块,具体见参考链接。

6.11、聚焦应用输入框,发送内容(复制粘贴)

  • 运行 await launch.automata(player)后,调用 launch.getConnection(),获取 connection,执行如下代码。
//开发可以自定义检测到键盘的粘贴事件后执行
connection.send(new TextInput("test text").dumps());

6.12、调节码率

  • 运行 await launch.automata(player)后,执行如下代码。
  • 建议:用户的下行网络最好高于最高码率的 50%,体验最佳。
Connection.changeBandwidth(
  8000, // 初始码率(8M)
  10000, // 最高码率(10M)
  5000 // 最低码率(5M)
);
//or
Connection.changeBandwidth(
  10000 // 码率(均值)
);

6.13、获取连接状态具体说明

  • 通过 launcher 的 toggleStatistics 方法可以切换统计数据开关,参考代码如下:
launcher.toggleStatistics();
window.setInterval(() => {
  const {
    fps,
    bitrate,
    packetLossRate,
    latency,
    averageJitterBufferDelay,
    framesDecoded,
    framesDropped,
    framesReceived,
    keyFramesDecoded,
  } = launcher.report();
  console.log(`
FPS: ${fps}
biterate: ${bitrate}kbps
latency: ${latency}ms
averageJitterBufferDelay: ${averageJitterBufferDelay}ms
packetLossRate: ${(packetLossRate * 100).toFixed(3)}%
`);
}, 1000);

6.14、RTCPeerConnection 、RTCDataChannel 状态说明

  • RTCPeerConnection:通过 connection.pc 获取
//打印ICE的状态
console.log("RTCPeerConnection.connectionState", connection.pc.connectionState);
属性说明类型
new表示至少有一个 ICE 连接(RTCIceTransport (en-US) 或 RTCDtlsTransport (en-US) 对象)处于 new 状态,并且没有连接处于以下状态: connecting、checking、failed、disconnected,或者这些连接都处于 closed 状态string
connecting表示至少有一个 ICE 连接处于正在建立连接string
connected表示每一个 ICE 连接要么正在使用(connected 或 completed 状态),要么已被关闭(closed 状态);并且,至少有一个连接处于 connected 或 completed 状态string
disconnected表示至少有一个 ICE 连接处于 disconnected 状态,并且没有连接处于 failed、connecting 或 checking 状态。string
failed表示至少有一个 ICE 连接处于 failed 的状态string
closed表示 RTCPeerConnection 已关闭string
  • RTCDataChannel:通过 connection.dc 获取
//打印数据通道状态
console.log("RTCDataChannel.readyState", connection.dc.readyState);
属性说明类型
connecting正在初始化连接,正在启动string
open数据通道已经建立string
closing正在关闭数据通道string
closed数据通道已经关闭string

6.15、WebRTC 调试参考

  • 方法1:在任一浏览器标签地址打开 "chrome://webrtc-internals",即可看到 WebRTC 的完整调试数据
  • 方法2:调用Launcher的showDashboard方法,显示仪表盘推流性能数据(注:空数据情况下记得调用launcher的toggleStatistics方法切换状态)
  • 方法3:调用Launcher的exportLog方法,导出webrtc-internals统计数据(注:空数据情况下记得调用launcher的toggleStatistics方法切换状态)

6.16、键盘事件自定义

  • 需求背景:应用需支持组合键输入
  1. 启动器初始化的时候取消挂载默认键盘事件,OnPlay 执行自定义的键盘事件。参考如下:
let connection, player, ele;
const launch = new Launcher({
  baseOptions: { address, appKey, startType: 1 },
  extendOptions: {
    eventOption: {
      enableKeyBoard: false,
    },
    onPlay: () => {
      createKeyboardStream(ele);
    },
  },
});
await launch.automata(document.body);
connection = launch.getConnection();
player = launch.getPlayer();
ele = player.videoElement;
  1. 然后自定义键盘事件,比如实现组合键,参考代码如下:
function createKeyboardStream(target) {
  //防止按到tab键切换dom
  target.tabIndex = -1;
  const onInteract = () => target.focus();
  target.addEventListener("touchstart", onInteract, { passive: true });
  target.addEventListener("mouseenter", onInteract);
  target.addEventListener("click", onInteract);

  target.addEventListener("keydown", (e) => {
    e.preventDefault();
    let capsLock = e.getModifierState("CapsLock");
    let shift = e.getModifierState("Shift");
    //includes " ~ ( ) _ { } : " < > ? "
    let signalKeyCode = [186, 187, 188, 189, 190, 191, 192, 219, 220, 221, 222];
    if (
      (capsLock || shift) &&
      (signalKeyCode.includes(e.keyCode) ||
       (e.keyCode >= 65 && e.keyCode <= 90) ||
       (e.keyCode >= 48 && e.keyCode <= 57))
    ) {
      connection.send(new TextInput(e.key, true).dumps(), true);
      return;
    }
    connection.send(Keyboard.fromKeyboardEvent(e, true).dumps(), true);
  });

  target.addEventListener("keyup", (e) => {
    e.preventDefault();
    connection.send(Keyboard.fromKeyboardEvent(e, false).dumps(), true);
  });
}

6.17、自定义工具栏(实现上报功能)

  • 可以自行新增工具栏,以上报功能为例
  1. 启动器初始化的时候指定上报栏目。参考如下:
const launch = new Launcher({
  baseOptions: { address, appKey, startType: 1 },
  extendOptions: {
    toolOption: {
      extendTools: [
        {
          icon: "data: image/png; base64xxxxxxxxx",
          text: "上报错误",
          platform: 3,
          order: 0,
          onClick: () => {
            API.post("/api/feed/xxxxxx", { error: "xxxxxx" });
          },
        },
      ],
    },
  },
});

6.18、重置超时时间

  • 需求背景:在不需要与应用交互的前提下,防止长时间未交互导致超时断连。
import { Launcher } from 'live-cat'
const appKey = 'xxxxxxxxxxxxxxxxxxx'
const address = 'https://app.3dcat.live'
const bootstrap = async () => {
  try {
    const launch = new Launcher({
      baseOptions: {
        address,
        appKey,
        startType: 1,
      },
    })
    const player = document.querySelector('body')
    document.querySelector('body').style.width = '100vw'
    document.querySelector('body').style.height = '100vh'
    await launch.automata(player)
    const connection = launch.getConnection()
    //重置超时时间
    connection.send('', true)
  } catch (error) {
    console.error(error)
  }
}
window.addEventListener('DOMContentLoaded', () => {
  if (
    navigator.userAgent.includes('miniProgram') ||
    navigator.userAgent.includes('MicroMessenger')
  ) {
    //微信浏览器/微信小程序环境
    document.addEventListener('WeixinJSBridgeReady', bootstrap, false)
  } else {
    bootstrap()
  }
})

6.19、移动端以及PC端中文输入方案

  • 背景:无法直接输入中文以及移动端无法直接输入
  • 前置条件:应用接入通信插件:UE应用通信/U3D应用通信
  • 主要思路:当用户点击应用输入框,应用发送指令给web端,web端唤起输入框(自定义),输入完毕通过TextInput接口传输到云端。
  1. 当用户点击应用输入框,应用发送指令(比如"input_focus")给web端
  2. web端接收到"input_focus"指令,唤起自定义的输入框,示例图如下: 步骤2示例图

3.输入完毕通过TextInput接口传输到云端

import { Launcher } from 'live-cat'
import { TextInput } from "ray-streaming" // "live-cat"生产依赖
const appKey = 'xxxxxxxxxxxxxxxxxxx'
const address = 'https://app.3dcat.live'
const bootstrap = async () => {
  try {
    const launch = new Launcher({
      baseOptions: {
        address,
        appKey,
        startType: 1,
      },
    })
    const player = document.querySelector('body')
    document.querySelector('body').style.width = '100vw'
    document.querySelector('body').style.height = '100vh'
    await launch.automata(player)
    const connection = launch.getConnection()
    //应用->web端
    connection.event.interaction.on(msg => {
      console.log('收到应用发来的消息:', msg)
      if(msg === 'input_focus'){
        // show input modal
      } 
    })
  } catch (error) {
    console.error(error)
  }
}

//文本传输事件
const sendText = () =>{
  connection.send(new TextInput("input框输入的内容").dumps());
}
window.addEventListener('DOMContentLoaded', () => {
  if (
    navigator.userAgent.includes('miniProgram') ||
    navigator.userAgent.includes('MicroMessenger')
  ) {
    //微信浏览器/微信小程序环境
    document.addEventListener('WeixinJSBridgeReady', bootstrap, false)
  } else {
    bootstrap()
  }
})

6.20、上传资产文件并且获取文件同步状态(云端应用加载上传文件)

  • 背景:需要通过web端上传资产文件并且保证能够实时加载文件
  • 前置条件:
  1. 接入3dcat后端接口文档
  2. 接入raysync-sdk(web端镭速上传服务)
  3. 接入live-cat(web端3dcat推流服务),具体文档:live-cat接入文档
  • 步骤:
  1. 在3dcat管理平台开启Access Key选项,获取AccessKey ID以及AccessKey Secret。

  2. 用户通过Java/NodeJS等后端服务接入3cat后端接口,首先需要鉴权签名,需要对接的接口有:

  3. 搭建上传页面(demo里面的pulic/index.html),接入raysync-sdk。

    • 通过A接口获取上传地址相关
    • 通过B接口获取用户名
    • 通过C接口获取应用云端根目录以及appKey
    • 通过D接口轮询上传文件的同步状态
    • 通过E接口获取AccessKey ID
  4. 搭建推流页面(demo里面的pulic/player.html),接入live-cat。
  • 完整demo(NodeJS/express注释版本)下载

6.21、自定义loading、loadingbar、toolbar图标

const loadingImage = document.createElement('img')
loadingImage.style.height = '200px'
loadingImage.style.marginBottom = '40px'
loadingImage.src = 'string'

const toolBarLogoImage = document.createElement('img')
toolBarLogoImage.style.height = '60px'
toolBarLogoImage.style.width = '60px'
toolBarLogoImage.style.borderRadius = '50%'
toolBarLogoImage.src = 'string'

const loadingBarImage = document.createElement('img')
toolBarLogoImage.style.width = '24px'
toolBarLogoImage.src = 'string'

const launch = new Launcher({
  baseOptions: { address, appKey, startType: 1 },
  extendOptions: {
    loadingImage,
    loadingBarImage,
    toolBarLogoImage,
  },
});

// or

const launch = new Launcher({
  baseOptions: { address, appKey, startType: 1 },
  extendOptions: {
    loadingImage:"string",// url or base64
    loadingBarImage:"string",// url or base64
    toolBarLogoImage:"string",// url or base64
  },
});
业务咨询:400-8037-298