多种接入场景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,
        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,执行如下代码。
connection.event.connect.on(() => livePlayer.showLoadingText("xxxxxx"));
  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 元素再展示业务界面。
//以公有化为例
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);

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 调试参考

-在任一浏览器标签地址打开 "chrome://webrtc-internals",即可看到 WebRTC 的完整调试数据

6.16、键盘事件自定义

  1. 启动器初始化的时候取消挂载默认键盘事件,OnPlay 执行自定义的键盘事件。参考如下:
let connection, player, ele;
const launch = new Launcher({
  baseOptions: { address, appKey, startType: 1 },
  extendOptions: {
    eventOption: {
      enableKeyBoard: false,
    },
    onPlay: () => {
      const video = launch.getPlayer().videoElement
      createKeyboardStream(video);
    },
  },
});
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、重置超时时间

  • 可以自行新增工具栏,以上报功能为例
  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)
    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()
  }
})
业务咨询:400-8037-298