产品文档 直播技术文档 直播JS SDK 集成文档

概述

百家云 直播JS SDK 为客户提供了一套与视图无关的直播底层实现。

简单的说,它是由事件驱动的,这样便实现了内外逻辑的解耦,客户可根据自己的需求,监听或触发相应的事件,从而实现完全自定义的视图。

同时,为了方便客户快速地集成直播中的常见功能,我们也开源了一些常用 UI 组件。

为了保证尽量正确,本文档没有指定具体的版本号,我们会在 github 发布最新的稳定版本,推荐使用。

为了方便客户集成,我们提供了一些可运行的示例

最新同步更新文档请点击查看同步文档

老师先进入教室

老师先进入教室,老师先进入教室,老师先进入教室,重要的事情说三遍。

老师进入教室意味着已经创建了一个教室,后面我们需要用到这个教室的 id。

接下来,老师打开摄像头和麦克风,这样学生才能接收到老师的视频和声音。

因为老师端暂不开放定制,因此老师必须先用客户端进入教室。

学生后进入教室

环境

<!-- 加载 jQuery -->
<script src="path/jquery.js"></script>
<!-- 加载 SDK 主文件 -->
<script src="http://live-cdn.baijiayun.com/js-sdk/{version}/classroom/classroom.js"></script>

参数

BJY.init({
    env: '环境,可选值 test/beta/production,对于客户来说,统一传 production',
    sign: '签名',
    class: {
        id: '教室 id,需要在百家云后台开课,开完课后,把课程 id 传入',
        // 如果需要在后续用到教室相关的信息,可像这样写在下面
        // 如果不用,可省略
        name: '课程名称',
        startTime: '开始时间,精确到毫秒的时间戳',
        endTime: '结束时间,精确到毫秒的时间戳'
    },
    // 当前进入教室的用户的信息
    // 如果是老师进教室,传老师的信息
    // 如果是学生进教室,传学生的信息
    // number 必须确保是唯一的,如果不唯一,后进的用户会踢掉先进的用户
    user: {
        number: '用户 number',
        avatar: '头像地址',
        name: '用户昵称',
        type: '用户类型 0-学生 1-老师 2-助教'
    },
    // 如果知道当前教室的老师信息,可传入,否则可省略
    teacher: {
        number: '老师 number',
        avatar: '头像地址',
        name: '老师昵称',
        type: 1
    }
});

sign 的签名算法请参考文档

需要注意的是,BJY.init 传入的 class.iduser.numberuser.avataruser.nameuser.type 等字段必须和签名算法保持一致,否则无法校验通过。

注意事项

原则上来说,需要确保每个用户进入的是相同的教室

相同的教室需要满足以下条件:

  1. env 相同
  2. class id 相同

登录进度

调用 BJY.init() 之后,会尝试去连接主服务器、信令服务器。

在连接的过程中,教室不可用,为了保证良好的用户体验,通常会设计一个全屏的进度条界面。

// 在这里初始化进度条界面

// 根据下面这些事件更新进度
var eventEmitter = BJY.eventEmitter;
eventEmitter
.one(
    eventEmitter.MASTER_SERVER_CONNECT_SUCCESS,
    function () {
        // 主服务器连接成功
        // 进度条 20%
    }
)
.one(
    eventEmitter.SERVER_INFO_FETCH_SUCCESS,
    function () {
        // 从主服务器获取服务器信息成功
        // 进度条 40%
    }
)
.one(
    eventEmitter.ROOM_SERVER_CONNECT_SUCCESS,
    function () {
        // 信令服务器连接成功
        // 进度条 60%
    }
)
.one(
    eventEmitter.ROOM_SERVER_LOGIN_SUCCESS,
    function () {
        // 信令服务器登录成功
        // 到此表示成功进入教室
        // 进度条 80%
        // 因为已经登录成功,接下来进度条应自动更新到 100%
    }
)
.one(
    eventEmitter.ROOM_SERVER_LOGIN_FAIL,
    function (event, data) {
        // 信令服务器登录失败
    }
)
.one(
    eventEmitter.ROOM_SERVER_LOGIN_OVERFLOW,
    function(event, data) {
        // 信令服务器已满,无法登录
    }
)
.one(
    eventEmitter.ROOM_SERVER_LOGIN_KICKED_OUT,
    function(event, data) {
        // 信令服务器发现当前用户已经被老师踢出,阻止再次进入教室
    }
)
.one(
    eventEmitter.CLASSROOM_CONNECT_FAIL,
    function () {
        // 超时、或其他原因导致的失败
    }
);

初始化界面

在未成功登入教室之前,应该根据上面介绍的事件展现进度条的进度。

当 SDK 认为到了可以展现业务界面的时机,会触发下面的事件。

var eventEmitter = BJY.eventEmitter;
eventEmitter
.one(
    eventEmitter.VIEW_RENDER_TRIGGER,
    function (event, data) {
        // 成功进入教室后,会触发此事件告知调用方初始化界面
    }
);

断线重连

因为网络环境的问题,教室可能中途掉线,因此当发生重连时,需提示用户。

var eventEmitter = BJY.eventEmitter;
eventEmitter
.on(
    eventEmitter.CLASSROOM_CONNECT_START,
    function (event, data) {
        if (data.reconnect) {
            // 可提示用户正在重连
        }
    }
)
.on(
    eventEmitter.CLASSROOM_CONNECT_END,
    function (event, data) {
        if (data.reconnect) {
            // 可提示用户重连结束
        }
    }
)
.on(
    eventEmitter.CLASSROOM_CONNECT_FAIL,
    function () {
        // 如果尝试过多次重连依然无法登录之后
        // 触发此事件,调用方可告知用户断网
    }
);

成员信息

人数

进教室总人数(包括退出教室的)

var eventEmitter = BJY.eventEmitter;
eventEmitter
.on(
    eventEmitter.TOTAL_USER_COUNT_CHANGE,
    function (event, data) {
        // data.totalUserCount
    }
);

当前教室人数

var eventEmitter = BJY.eventEmitter;
eventEmitter
.on(
    eventEmitter.USER_COUNT_CHANGE,
    function (event, data) {
        // data.userCount
    }
);

登入登出

在一个教室中,有人进,也会有人出,因此 SDK 提供了以下三个事件:

var eventEmitter = BJY.eventEmitter;
eventEmitter
.on(
    eventEmitter.USER_ADD
    function (event, data) {
        // data.userList 为了批量添加考虑设计成数组
    }
)
.on(
    eventEmitter.USER_UPDATE
    function (event, data) {
        // data.user
        // data.update 更新的部分
    }
)
.on(
    eventEmitter.USER_REMOVE
    function (event, data) {
        // data.user
    }
);

课件白板

环境

css

<link rel="stylesheet" href="http://live-cdn.baijiayun.com/js-sdk/{version}/whiteboard/whiteboard.css">

js

<script src="http://live-cdn.baijiayun.com/js-sdk/{version}/whiteboard/whiteboard.js"></script>

初始化

展现课件白板需要两个元素:

  1. 容器元素:确保 position 不是 static 定位即可,用于限制展示区域的大小
  2. 占位元素:容器元素内部需放置一个占位元素,创建完成后,会替换掉这个占位元素
<!-- 注意应给 container 加样式,而不是 placeholder -->
<div class="container">
    <div class="placeholder"></div>
</div>
var eventEmitter = BJY.eventEmitter;
eventEmitter
.one(
    eventEmitter.VIEW_RENDER_TRIGGER,
    function (event, data) {
        BJY.whiteboard.init({
            element: $('.placeholder')
        });
    }
);

翻页

此组件会自动响应远程的翻页,如果学生需要自己翻页,可触发以下事件。

var eventEmitter = BJY.eventEmitter;

// 上一页
eventEmitter.trigger(
    eventEmitter.PAGE_PREV_TRIGGER
);

// 下一页
eventEmitter.trigger(
    eventEmitter.PAGE_NEXT_TRIGGER
);

// 指定页码
eventEmitter.trigger(
    eventEmitter.PAGE_CHANGE_TRIGGER,
    {
        page: 10
    }
);

如果调用方提供了上一页下一页按钮,则需要根据当前的页码,判断按钮是否需要置灰,为此我们提供了两个方法。

var pageData = BJY.data.page;
// 是否有上一页
pageData.hasPrevPage();
// 是否有下一页
pageData.hasNextPage();

加载状态

var eventEmitter = BJY.eventEmitter;
eventEmitter
.on(
    eventEmitter.DOC_IMAGE_LOAD_START,
    function (event, data) {
        // 课件图片开始加载
    }
)
.on(
    eventEmitter.DOC_IMAGE_LOAD_SUCCESS,
    function (event, data) {
        // 课件图片加载成功
    }
)
.on(
    eventEmitter.DOC_IMAGE_LOAD_FAIL,
    function (event, data) {
        // 课件图片加载失败
    }
)
.on(
    eventEmitter.DOC_IMAGE_LOAD_TIMEOUT,
    function (event, data) {
        // 课件图片加载超时
    }
)
.on(
    eventEmitter.DOC_IMAGE_LOAD_END,
    function (event, data) {
        // 课件图片加载完成,不论成功、失败、或超时
    }
);

因为加载是异步的,因此除了 eventEmitter.DOC_IMAGE_LOAD_START,其他几个事件在业务层面都会碰到是否是当前页的需求,因此我们扩展了几个事件。

var eventEmitter = BJY.eventEmitter;
eventEmitter
.on(
    eventEmitter.CURRENT_DOC_IMAGE_LOAD_SUCCESS,
    function (event, data) {
        // 课件图片加载成功
    }
)
.on(
    eventEmitter.CURRENT_DOC_IMAGE_LOAD_FAIL,
    function (event, data) {
        // 课件图片加载失败
    }
)
.on(
    eventEmitter.CURRENT_DOC_IMAGE_LOAD_TIMEOUT,
    function (event, data) {
        // 课件图片加载超时
    }
)
.on(
    eventEmitter.CURRENT_DOC_IMAGE_LOAD_END,
    function (event, data) {
        // 课件图片加载完成,不论成功、失败、或超时
    }
);

播放器

为了支持多种播放器,我们抽象出了一个播放器的核心。集成播放器时,首先必须加载这个核心。

<script src="http://live-cdn.baijiayun.com/js-sdk/{version}/player/player.js"></script>
<script src="http://live-cdn.baijiayun.com/js-sdk/{version}/player/core/Player.js"></script>

选择扩展

目前 SDK 提供了两种播放器扩展:

  1. Flash:适用于 PC 网页
  2. H5:适用于微信

加载 Flash 扩展

<script src="http://live-cdn.baijiayun.com/js-sdk/{version}/player/extension/flash.js"></script>
<script>
    BJY.Player.flash.pluginUrl = 'http://live.gsxservice.com/asset/flash/camera.swf';
    BJY.Player.flash.init();
</script>

Flash 扩展支持调节扬声器声音,如下:

var eventEmitter = BJY.eventEmitter;
eventEmitter.trigger(
    eventEmitter.SPEAKER_VOLUME_CHANGE_TRIGGER,
    {
        volume: 100 // 值为 0 - 100
    }
);

加载 H5 扩展

<script src="http://live-cdn.baijiayun.com/js-sdk/{version}/player/extension/html.js"></script>
<script>
    BJY.Player.html.init();
</script>

生命周期管理

当目标用户打开了麦克风或摄像头,我们才能创建播放器,开始观看。

当目标用户离开教室时,需要销毁播放器。

对于观看老师来说,方式如下:

var eventEmitter = BJY.eventEmitter;
var teacherPlayer;

eventEmitter
.on(
    eventEmitter.TEACHER_MEDIA_ON,
    function () {
        if (!teacherPlayer) {
            teacherPlayer = new BJY.Player({
                element: $('.player'),
                user: BJY.store.get('teacher'),
                extension: BJY.Player.flash
            });
        }
    }
)
.on(
    eventEmitter.TEACHER_REMOVE,
    function () {
        if (teacherPlayer) {
            teacherPlayer.dispose();
            teacherPlayer = null;
        }
    }
);

公告

当用户进入教室后,应该先去请求现有的公告。

var eventEmitter = BJY.eventEmitter;
eventEmitter
.one(
    eventEmitter.NOTICE_RES,
    function (event, data) {
        // data.content 公告内容
        // data.link 公告链接,可为空
    }
)
.one(
    eventEmitter.VIEW_RENDER_TRIGGER,
    function (event, data) {
        eventEmitter.trigger(
            eventEmitter.NOTICE_REQ,
        );
    }
);

获取到返回的公告内容后,可根据自己的需求定制界面。

有权限的成员(如老师、助教)可以修改教室内的公告。

var eventEmitter = BJY.eventEmitter;
eventEmitter.on(
    eventEmitter.NOTICE_CHANGE_TRIGGER,
    {
        content: '公告内容',
        link: '公告链接,可选'
    }
);

如果公告被修改,会立即广播给教室内的所有成员

var eventEmitter = BJY.eventEmitter;
eventEmitter.on(
    eventEmitter.NOTICE_CHANGE,
    function (event, data) {
        // 公告被修改了
        // data.content 公告内容
        // data.link 公告链接,可为空
    }
);

禁言和解禁

进入教室之后,应查询当前用户是否被禁言。

如果用户被禁言,建议置灰聊天输入框,或者干脆把聊天输入框干掉。

var eventEmitter = BJY.eventEmitter;
eventEmitter
.on(
    eventEmitter.VIEW_RENDER_TRIGGER,
    function () {
        // 查询当前用户的状态
        eventEmitter.trigger(
            eventEmitter.USER_STATE_REQ,
            {
                number: BJY.store.get('user.number')
            }
        );
    }
)
.one(
    eventEmitter.USER_STATE_RES,
    function (event, data) {
        var forbidSendMessage = data.userState.forbidSendMessage;
        if (forbidSendMessage) {
            // 如果 forbidSendMessage.duration > 0 表示被禁言 
            // duration 单位为秒
        }
    }
);

单个禁言/解禁

var eventEmitter = BJY.eventEmitter;
eventEmitter
.on(
    eventEmitter.MESSAGE_SEND_FORBID,
    function (event, data) {
        if (data.forbidSelf) {
            var duration = data.duration;
            // 根据 duration 判断是禁言还是解禁
            // duration 单位是秒
        }
    }
);

全体禁言/解禁

var eventEmitter = BJY.eventEmitter;
eventEmitter
.on(
    eventEmitter.MESSAGE_SEND_FORBID_ALL_CHANGE,
    function (event, data) {
        if (data.forbidAll) {
            // 是否全体禁言
        }
    }
);

消息收发

消息抽象为收发两个动作,通过频道参数,可自由实现为不同的功能,如聊天、问答。

为了控制频道数量(降低宕机风险),新开频道需报备,否则无法使用。

发消息

var eventEmitter = BJY.eventEmitter;
eventEmitter.trigger(
    eventEmitter.MESSAGE_SEND,
    {
        channel: '频道',
        content: '文本消息内容',
    }
);

如果是表情或图片等富文本消息,需用 data 字段,如下:

var eventEmitter = BJY.eventEmitter;
eventEmitter.trigger(
    eventEmitter.MESSAGE_SEND,
    {
        channel: '频道',
        data: {
            type: 'emoji',
            url: 'https://xxx.com/a.png',
            key: 'smile'
        }
    }
);
var eventEmitter = BJY.eventEmitter;
eventEmitter.trigger(
    eventEmitter.MESSAGE_SEND,
    {
        channel: '频道',
        data: {
            type: 'image',
            url: 'https://xxx.com/a.png',
            width: 100,
            height: 100
        }
    }
);

收消息

var eventEmitter = BJY.eventEmitter;
eventEmitter.on(
    eventEmitter.MESSAGE_RECEIVE,
    function (event, data) {
        // data.id   消息 id
        // data.time  服务器为发出的消息加上的服务器时间
        // data.channel  频道
        // data.from  发送方信息
        // data.content 文本消息内容
        // data.data 富文本消息
    }
);

历史消息

当用户进入教室时,教室里没有任何消息,此时不论是为了展示效果(空空的总不太好看吧),或者获取是否有历史消息,都应该请求一次历史消息。

当历史消息返回时,调用方应记录返回的 next,用于下次请求更早的历史消息。

请求历史消息的时机需要注意,因为消息走的是聊天服务器,因此必须成功登录聊天服务器之后,才能请求。

请求历史消息

var eventEmitter = BJY.eventEmitter;
eventEmitter.on(
    eventEmitter.CHAT_SERVER_LOGIN_SUCCESS,
    function () {
        eventEmitter.trigger(
            eventEmitter.MESSAGE_PULL_REQ,
            {
                channel: '频道',
                next: 'MESSAGE_PULL_RES 返回的 next,第一次拉取,可不传 next',
                count: '拉取数量,默认 10 条'
            }
        );
    }
);

返回历史消息

var eventEmitter = BJY.eventEmitter;
eventEmitter.on(
    eventEmitter.MESSAGE_PULL_RES,
    function (event, data) {
        // data.channel 
        // data.next 用作下次请求历史消息的 next 参数
        // data.hasMore  是否还有更多历史消息
        // data.messageList
    }
);

发言

对于有发言权限的用户来说,在教室中可以自由的开始和结束发言,这完全由业务需求决定,SDK 不便封装。

对于没有发言权限的用户来说(比如学生),发言必须经过申请,当老师或助教同意后,用户才可获得发言权限。

老师或助教也可以主动邀请用户发言,邀请发言分为两种:强制邀请、非强制邀请。

强制邀请时,被邀请的用户没有选择的机会,只能开始发言。

非强制邀请时,被邀请的用户可以选择接受或者拒绝

申请发言邀请发言涉及大量的状态保持和切换,SDK 为此专门提供了一个模块,方便开发者快速实现自己的需求。

环境

<script src="http://live-cdn.baijiayun.com/js-sdk/{version}/user/userSpeak.js"></script>

申请发言

申请

申请发言的时候,会自动判断当前用户是否正在被邀请发言,如果是,则响应邀请发言,而无需申请发言。

// 需传入超时时间,成功发出申请返回 true
if (BJY.userSpeak.startApply(5000)) {
    // 更新视图,比如把“申请”按钮改为“取消申请”
}

取消申请

当用户正在申请发言,可以取消申请。

// 成功发出取消申请返回 true
if (BJY.userSpeak.cancelApply()) {
    // 更新视图,比如把“取消申请”按钮改为“申请”
}

处理申请

对于有权限处理申请的用户来说(比如老师或助教),可点击界面中的“同意”或“拒绝”按钮处理申请。

// 同意,处理成功返回 true
if (BJY.userSpeak.processApply('userId', true)) {
    // 更新视图
}

// 拒绝,处理成功返回 true
if (BJY.userSpeak.processApply('userId', false)) {
    // 更新视图
}

响应申请结果

申请结果有三种:

  1. 申请被同意
  2. 申请被拒绝
  3. 申请超时(老师或助教长时间未处理)
  4. 申请被取消(自己取消的)

SDK 会发出对应的四种事件:

var eventEmitter = BJY.eventEmitter;
eventEmitter
.on(
    eventEmitter.SPEAK_APPLY_RESULT_ACCEPT,
    function (event, data) {
        // data.operator 操作人
    }
)
.on(
    eventEmitter.SPEAK_APPLY_RESULT_REJECT,
    function (event, data) {
        // data.operator 操作人
    }
)
.on(
    eventEmitter.SPEAK_APPLY_RESULT_TIMEOUT,
    function (event, data) {
        // 超时
    }
)
.on(
    eventEmitter.SPEAK_APPLY_RESULT_CANCEL,
    function (event, data) {
        // 自己取消的
    }
);

邀请发言

邀请

邀请发言的时候,会自动判断该用户是否正在申请发言,如果是,则同意申请,而无需邀请发言。

// userId 邀请的用户 ID
// force 是否强制邀请
// timeout 超时时间
// 成功发出邀请返回 true
if (BJY.userSpeak.startInvite(userId, force, timeout)) {
    // 更新视图,比如把“邀请”按钮改为“取消邀请”
}

取消邀请

当正在邀请某个用户发言时,可以取消这次的邀请。

// 成功取消邀请返回 true
if (BJY.userSpeak.cancelInvite(userId)) {
    // 更新视图,比如把“取消申请”按钮改为“申请”
}

处理邀请

对于被邀请的用户来说,如果是非强制邀请,可选择同意拒绝

// 同意,处理成功返回 true
if (BJY.userSpeak.processInvite(true)) {
    // 更新视图
}

// 拒绝,处理成功返回 true
if (BJY.userSpeak.processInvite(false)) {
    // 更新视图
}

响应邀请结果

邀请结果有三种:

  1. 强制邀请,发起人可立即响应结果
  2. 非强制邀请,发起人会收到同意、拒绝、超时、取消四种结果

SDK 会发出对应的四种事件:

var eventEmitter = BJY.eventEmitter;
eventEmitter
.on(
    eventEmitter.SPEAK_INVITE_RESULT_ACCEPT,
    function (event, data) {
        // data.user 邀请对象
    }
)
.on(
    eventEmitter.SPEAK_INVITE_RESULT_REJECT,
    function (event, data) {
        // data.user 邀请对象
    }
)
.on(
    eventEmitter.SPEAK_INVITE_RESULT_TIMEOUT,
    function (event, data) {
        // 超时
    }
)
.on(
    eventEmitter.SPEAK_INVITE_RESULT_CANCEL,
    function (event, data) {
        // 发起人取消邀请
    }
);

终止发言

在发言过程中,当前用户、老师、助教都有权限终止该用户的发言。

// 终止成功返回 true
if (BJY.userSpeak.stopSpeak('userId')) {
    // 更新视图
}

被终止的用户需要响应终止操作。

var eventEmitter = BJY.eventEmitter;
eventEmitter
.on(
    eventEmitter.MEDIA_SWITCH_TRIGGER,
    function (event, data) {
        // 音视频全关
        if (!data.videoOn && !data.audioOn) {
            var operator = data.operator;
            if (operator) {
                console.log('你被' + operator.name + '终止发言');
            }
        }
        // 如果你用到了下面即将涉及的 userPublish 模块,可直接调用修改音视频状态
        BJY.userPublish.setDevice(myPlayer, data.videoOn, data.audioOn);
    }
);

操作麦克风、摄像头

如果你希望和老师互动,通常要用到麦克风和摄像头设备。

一般来说,我们会在进教室之后,创建一个自己的播放器(比如前面提到的 Flash 播放器)。

var flash = BJY.Player.flash;
flash.pluginUrl = 'http://live.gsxservice.com/asset/flash/camera.swf';
flash.init();

var myPlayer = new BJY.Player({
    element: $('.player'),
    user: BJY.store.get('user'),
    extension: flash
});

环境

<script src="http://live-cdn.baijiayun.com/js-sdk/{version}/user/userPublish.js"></script>

打开麦克风

BJY.userPublish.setDevice(myPlayer, null, true);

关闭麦克风

BJY.userPublish.setDevice(myPlayer, null, false);

打开摄像头

BJY.userPublish.setDevice(myPlayer, true);

关闭摄像头

BJY.userPublish.setDevice(myPlayer, false);

获取操作结果

setDevice(player, videoOn, audioOn) 会返回一个操作结果,共有以下几种:

  • true:操作成功
  • false:状态没发生变化,比如当前摄像头是打开的,你再次设置为打开
  • BJY.userPublish.ERROR_PERMISSION_DENIED:没有权限开启音视频设备,需举手被同意之后才有权限
  • BJY.userPublish.ERROR_MIC_NOT_FOUND: 没有可用的麦克风
  • BJY.userPublish.ERROR_CAMERA_NOT_FOUND:没有可用的摄像头

链路和服务器节点切换

百家云客户端支持 TCP 和 UDP 推流,推流方(老师)可以从中选择一种。

  • 当推流方是 UDP 时,拉流方可以是 TCP 或 UDP
  • 当推流方是 TCP 时,拉流方必须是 TCP,且不能切换节点

如果用网页上课,推流方仅支持 TCP 推流。

切换链路

推流或拉流方切换链路时,需要触发以下事件。

var eventEmitter = BJY.eventEmitter;

// 推流方
eventEmitter.trigger(
    eventEmitter.UPLINK_LINK_TYPE_CHANGE_TRIGGER,
    {
        linkType: '0 - TCP, 1 - UDP'
    }
);

// 拉流方
eventEmitter.trigger(
    eventEmitter.DOWNLINK_LINK_TYPE_CHANGE_TRIGGER,
    {
        linkType: '0 - TCP, 1 - UDP'
    }
);

切换链路成功之后,会触发相应的 XXXXX_LINK_TYPE_CHANGE 事件。

var eventEmitter = BJY.eventEmitter;

eventEmitter
.on(
    eventEmitter.UPLINK_LINK_TYPE_CHANGE,
    function (event, data) {
        // data.linkType
        // data.server 当前使用的服务器节点
        // data.serverList 可选的服务器节点
    }
)

.on(
    eventEmitter.DOWNLINK_LINK_TYPE_CHANGE,
    function (event, data) {
        // data.linkType
        // data.server 当前使用的服务器节点
        // data.serverList 可选的服务器节点
    }
);

当接收到以上事件,需刷新界面中的服务器列表(如果你的需求包含支持切换服务器的话...)

切换节点

每种链路都会有对应的备选服务器节点列表,当网络不佳时,可选择其他节点。

当用户点击某个节点时,可触发以下事件。

var eventEmitter = BJY.eventEmitter;

// 推流方
eventEmitter.trigger(
    eventEmitter.UPLINK_SERVER_NODE_CHANGE_TRIGGER,
    {
        ip: '选中的节点 ip',
        port: '选中的节点 port',
    }
);

// 拉流方
eventEmitter.trigger(
    eventEmitter.DOWNLINK_SERVER_NODE_CHANGE_TRIGGER,
    {
        ip: '选中的节点 ip',
        port: '选中的节点 port'
    }
);