UNPKG

yimultiscreenserver-sdk-web

Version:

YiMultiScreenServer SDK for Web

695 lines (634 loc) 32 kB
import { HTTPConfig } from "../config/HTTPConfig"; import { GlobalCache } from "../core/GlobalCache"; import { HttpRequestExecutor } from "../core/http/HttpRequestExecutor"; import { IHttpRequester } from "../core/http/IHttpRequester"; import { AccountBindDeviceResponse } from "../core/http/response/AccountBindDeviceResponse"; import { CastSessionResponse } from "../core/http/response/CastSessionResponse"; import { Code } from "../core/http/response/Code"; import { ConnectDisplayResponse } from "../core/http/response/ConnectDisplayResponse"; import { EmptyResponse } from "../core/http/response/EmptyResponse"; import { LoginUserResponse } from "../core/http/response/LoginUserResponse"; import { Result } from "../core/http/response/Result"; import { VerifyCodeResponse } from "../core/http/response/VerifyCodeResponse"; import { JsonParser } from "../core/json/JsonParser"; import { MQTTLogic } from "../core/mqtt/MQTTLogic"; import { ControllerMQTTImpl } from "../core/mqtt/impl/ControllerMQTTImpl"; import { IControllerMQTTCallback } from "../core/mqtt/impl/IControllerMQTTCallback"; import { BindDeviceAction } from "../core/mqtt/message/BindDeviceAction"; import { CastSessionMessage } from "../core/mqtt/message/CastSessionMessage"; import { DataMessage } from "../core/mqtt/message/DataMessage"; import { MsgClientType } from "../core/mqtt/message/MsgClientType"; import { SessionAction } from "../core/mqtt/message/SessionAction"; import { CastError } from "../util/CastError"; import { SDKLog } from "../util/SDKLog"; import { AccountBindDeviceVO } from "../vo/account/AccountBindDeviceVO"; import { LoginUserVO } from "../vo/account/LoginUserVO"; import { CastProtocol } from "../vo/cast/CastProtocol"; import { CastSessionVO } from "../vo/cast/CastSessionVO"; import { CastType } from "../vo/cast/CastType"; import { ControllerDeviceType } from "../vo/device/ControllerDeviceType"; import { ControllerDeviceVO } from "../vo/device/ControllerDeviceVO"; import { DevicePair } from "../vo/device/DevicePair"; import { DisplayDeviceVO } from "../vo/device/DisplayDeviceVO"; import { P2PKeyVO } from "../vo/p2p/P2PKeyVO"; import { DisplayReportVO } from "../vo/report/DisplayReportVO"; import { IControllerCallback } from "./IControllerCallback"; import { SessionDestroyReason } from '../core/mqtt/message/SessionDestroyReason'; export class ControllerManager implements IControllerMQTTCallback { private readonly TAG: string = "ControllerManager"; private readonly jsonParser: JsonParser; private readonly p2pKeyFromPeer: P2PKeyVO; private readonly httpRequestExecutor: HttpRequestExecutor; private readonly cache: GlobalCache; private guid: string; private mqttLogic: MQTTLogic | undefined; private callback: IControllerCallback | undefined; constructor() { this.p2pKeyFromPeer = new P2PKeyVO(); this.cache = new GlobalCache(); this.httpRequestExecutor = new HttpRequestExecutor(); this.jsonParser = new JsonParser(); this.guid = "fake-guid"; } setGuid(guid: string): this { this.guid = guid; return this; } setCallback(callback: IControllerCallback): this { this.callback = callback; return this; } setHttpRequester(requester: IHttpRequester): this { this.httpRequestExecutor.setHttpRequester(requester); return this; } setHttpConfig(config: HTTPConfig): this { this.httpRequestExecutor.setHTTPConfig(config); return this; } setMQTTLib(mqtt: MQTTLogic): this { this.mqttLogic = mqtt; return this; } createControllerDevice(controllerDevice: ControllerDeviceVO): this { this.cache.addControllerDevice(controllerDevice.guid, controllerDevice); return this; } updateControllerType(type: ControllerDeviceType): this { let cd = this.cache.getControllerDevice(this.guid); cd!.controllerDeviceType = type; this.cache.addControllerDevice(this.guid, cd!); return this; } updateNetwork(ip: string, gateway: string, mask: string): this { let cd = this.cache.getControllerDevice(this.guid); cd!.ip = ip; cd!.gateway = gateway; cd!.mask = mask; this.cache.addControllerDevice(this.guid, cd!); return this; } /** * 阻塞方法,控制终端给指定手机号发送注册/登录验证码 * 请使用Task调用,不需要在Task中进行await,因为函数结果是通过回调触发 * @param phoneNumber 手机号 */ async requestVerifyCode(phoneNumber: string): Promise<boolean> { let result: Result<VerifyCodeResponse> = await this.httpRequestExecutor.requireVerifyCode(phoneNumber); if (Code.OK !== result.code) { switch (result.code) { case Code.ERR_SMS_REACH_TODAY_COUNT: this.callback?.onError(CastError.create(CastError.MAIN_ACCOUNT_FAIL, CastError.SUB_SEND_SMS_REACH_TODAY_COUNT_FAIL)); break; case Code.ERR_SMS_INVALID_PHONE_NUMBER | Code.ERR_SMS_OTHER_API_EXCEPTION: this.callback?.onError(CastError.create(CastError.MAIN_ACCOUNT_FAIL, CastError.SUB_SEND_SMS_FAIL)); break; default: break; } return false; } SDKLog.e(this.TAG, "requestVerifyCode: " + result); return result.data!.isSMSSuccessSend; } private subscribeAccountTopic(user: LoginUserVO): void { let mqtt: ControllerMQTTImpl = this.mqttLogic as ControllerMQTTImpl; mqtt.subscribeAccountTopic(user.account.accountId); } private unsubscribeAccountTopic(user: LoginUserVO): void { let mqtt: ControllerMQTTImpl = this.mqttLogic as ControllerMQTTImpl; mqtt.unsubscribeAccountTopic(user.account.accountId); } /** * 阻塞方法,控制终端通过手机号和短信验证码登录账号,如果账号不存在则自动注册 * 返回值为空意味着登录失败,请检查错误信息 * @param phoneNumber 手机号 * @param verifyCode 短信验证码 * @returns LoginUserVO | null 登录成功后返回用户信息,失败则返回null */ async login(phoneNumber: string, verifyCode: string): Promise<LoginUserVO | null> { let temp = this.cache.getControllerDevice(this.guid); let result: Result<LoginUserResponse> = await this.httpRequestExecutor.login(phoneNumber, verifyCode, temp!.controllerDeviceType, this.guid, "verifyCode"/*目前先写死走手机验证码*/); if (Code.OK !== result.code) { switch (result.code) { case Code.ERR_AUTH_INVALID_VERIFY_CODE : this.callback?.onError(CastError.create(CastError.MAIN_ACCOUNT_FAIL, CastError.SUB_VERIFY_CODE_INVALID_FAIL)); break; default: break; } return null; } SDKLog.e(this.TAG, "login: " + result); let user: LoginUserVO = result.data!.user; this.subscribeAccountTopic(user); return user; } /** * 阻塞方法,控制终端登录账号后,将access token存储下来,每次启动如果本地有access token,则调用此方法 * 此方法如果未正常返回,则说明需要通过UI引导用户重新登录,刷新access token * 返回值为空意味着刷新失败,请检查错误信息 * @param accessToken: 控制端登录账号成功后拿到的唯一令牌 * @returns LoginUserVO | null 登录成功后返回用户信息,失败则返回null */ async refresh(accessToken: string): Promise<LoginUserVO | null> { let result: Result<LoginUserResponse> = await this.httpRequestExecutor.refresh(this.guid, accessToken); if (Code.OK !== result.code) { switch (result.code) { case Code.ERR_AUTH_INVALID_TOKEN : this.callback?.onError(CastError.create(CastError.MAIN_ACCOUNT_FAIL, CastError.SUB_USER_TOKEN_INVALID_FAIL)); break; case Code.ERR_USER_LOGIN_ACCOUNT_NOT_EXIST : this.callback?.onError(CastError.create(CastError.MAIN_ACCOUNT_FAIL, CastError.SUB_ACCOUNT_NOT_EXIST_FAIL)); break; default: break; } return null; } SDKLog.e(this.TAG, "refresh: " + result); let user: LoginUserVO = result.data!.user; this.subscribeAccountTopic(user); return user; } /** * 阻塞方法,控制终端登录账号后,退出登录此账号 * @param user: 已登录账号的用户信息 * @returns boolean 退出成功返回true,否则返回false */ async logout(user: LoginUserVO): Promise<boolean> { let result: Result<EmptyResponse> = await this.httpRequestExecutor.logout(user.accessToken); if (Code.OK !== result.code) { switch (result.code) { case Code.ERR_AUTH_INVALID_TOKEN : this.callback?.onError(CastError.create(CastError.MAIN_ACCOUNT_FAIL, CastError.SUB_USER_TOKEN_INVALID_FAIL)); break; case Code.ERR_USER_LOGIN_ACCOUNT_NOT_EXIST : this.callback?.onError(CastError.create(CastError.MAIN_ACCOUNT_FAIL, CastError.SUB_ACCOUNT_NOT_EXIST_FAIL)); break; default: break; } return false; } SDKLog.e(this.TAG, "logout: " + result); this.unsubscribeAccountTopic(user); return true; } /** * 阻塞方法,控制终端注销指定账号(已登录的账号) * @param user: 已登录账号的用户信息 * @returns boolean 注销成功返回true,否则返回false */ async unRegister(user: LoginUserVO): Promise<boolean> { let result: Result<EmptyResponse> = await this.httpRequestExecutor.unRegister(user.accessToken); if (Code.OK !== result.code) { switch (result.code) { case Code.ERR_AUTH_INVALID_TOKEN : this.callback?.onError(CastError.create(CastError.MAIN_ACCOUNT_FAIL, CastError.SUB_USER_TOKEN_INVALID_FAIL)); break; case Code.ERR_USER_LOGIN_ACCOUNT_NOT_EXIST : this.callback?.onError(CastError.create(CastError.MAIN_ACCOUNT_FAIL, CastError.SUB_ACCOUNT_NOT_EXIST_FAIL)); break; default: break; } return false; } SDKLog.e(this.TAG, "unRegister: " + result); this.unsubscribeAccountTopic(user); return true; } /** * 阻塞方法,控制终端登录账号后,获取此账号下所有已绑定的显示设备 * @param accessToken: 控制端登录账号成功后拿到的唯一令牌 * @returns AccountBindDeviceVO[] | null 已绑定的显示设备列表,失败则返回null */ async getAllBoundDevices(accessToken: string): Promise<AccountBindDeviceVO[] | null> { let result: Result<AccountBindDeviceResponse> = await this.httpRequestExecutor.getAllBoundDevices(accessToken); if (Code.OK !== result.code) { switch (result.code) { case Code.ERR_AUTH_INVALID_TOKEN : this.callback?.onError(CastError.create(CastError.MAIN_ACCOUNT_FAIL, CastError.SUB_USER_TOKEN_INVALID_FAIL)); break; default: break; } return null; } SDKLog.e(this.TAG, "getAllBoundDevices: " + result); return result.data!.boundDisplayDevices; } /** * 阻塞方法,控制终端登录账号后,对指定显示终端与本账号建立绑定关系 * @param accessToken: 控制端登录账号成功后拿到的唯一令牌 * @param displayDeviceId 显示终端设备ID * @param alias 显示终端设备别名 * @returns AccountBindDeviceVO | null 绑定的显示设备,失败则返回null */ async bindDisplay(displayDeviceId: string, alias: string, accessToken: string): Promise<AccountBindDeviceVO | null> { let result: Result<AccountBindDeviceResponse> = await this.httpRequestExecutor.bindDisplay(displayDeviceId, alias, accessToken); if (Code.OK !== result.code) { switch (result.code) { case Code.ERR_AUTH_INVALID_TOKEN : this.callback?.onError(CastError.create(CastError.MAIN_ACCOUNT_FAIL, CastError.SUB_USER_TOKEN_INVALID_FAIL)); break; case Code.ERR_DEVICE_NOT_EXIST : this.callback?.onError(CastError.create(CastError.MAIN_ACCOUNT_FAIL, CastError.SUB_BIND_DISPLAY_NOT_EXIST_FAIL)); break; case Code.ERR_DEVICE_ALREADY_BOUND : this.callback?.onError(CastError.create(CastError.MAIN_ACCOUNT_FAIL, CastError.SUB_BIND_DISPLAY_ALREADY_BOUND_FAIL)); break; default: break; } return null; } SDKLog.e(this.TAG, "bindDisplay: " + result); for (let device of result.data!.boundDisplayDevices) { if (device.displayDevice.displayDeviceId === displayDeviceId) { SDKLog.e(this.TAG, "bindDisplay, deivce: " + device); return device } } return null; } /** * 阻塞方法,控制终端登录账号后,对指定的已绑定的显示设备进行更新,更新其别名(UI展示名称) * @param accessToken: 控制端登录账号成功后拿到的唯一令牌 * @param alias 显示终端设备别名 * @param displayDeviceId 显示终端设备ID * @returns AccountBindDeviceVO | null 绑定的显示设备,失败则返回null */ async updateBindDisplayAlias(displayDeviceId: string, alias: string, accessToken: string): Promise<AccountBindDeviceVO | null> { let result: Result<AccountBindDeviceResponse> = await this.httpRequestExecutor.updateBindDisplayAlias(displayDeviceId, alias, accessToken); if (Code.OK !== result.code) { switch (result.code) { case Code.ERR_AUTH_INVALID_TOKEN : this.callback?.onError(CastError.create(CastError.MAIN_ACCOUNT_FAIL, CastError.SUB_USER_TOKEN_INVALID_FAIL)); break; case Code.ERR_DEVICE_NOT_EXIST : this.callback?.onError(CastError.create(CastError.MAIN_ACCOUNT_FAIL, CastError.SUB_BIND_DISPLAY_NOT_EXIST_FAIL)); break; case Code.ERR_DEVICE_NOT_BOUND : this.callback?.onError(CastError.create(CastError.MAIN_ACCOUNT_FAIL, CastError.SUB_BIND_DISPLAY_NOT_BOUND_FAIL)); break; default: break; } return null; } SDKLog.e(this.TAG, "updateBindDisplayAlias: " + result); for (let device of result.data!.boundDisplayDevices) { if (device.displayDevice.displayDeviceId === displayDeviceId) { SDKLog.e(this.TAG, "updateBindDisplayAlias, deivce: " + device); return device; } } return null; } /** * 阻塞方法,控制终端登录账号后,解除指定显示终端与此账号之间的绑定关系 * @param accessToken: 控制端登录账号成功后拿到的唯一令牌 * @param displayDeviceId 显示终端设备ID * @returns 解除绑定成功返回true,失败则返回false */ async unBindDisplay(displayDeviceId: string, accessToken: string): Promise<boolean> { let result: Result<EmptyResponse> = await this.httpRequestExecutor.unBindDisplay(displayDeviceId, accessToken); if (Code.OK !== result.code) { switch (result.code) { case Code.ERR_AUTH_INVALID_TOKEN : this.callback?.onError(CastError.create(CastError.MAIN_ACCOUNT_FAIL, CastError.SUB_USER_TOKEN_INVALID_FAIL)); break; case Code.ERR_DEVICE_NOT_BOUND : this.callback?.onError(CastError.create(CastError.MAIN_ACCOUNT_FAIL, CastError.SUB_BIND_DISPLAY_NOT_EXIST_FAIL)); break; default: break; } return false; } SDKLog.e(this.TAG, "unBindDisplay: " + result); return true; } async connectDisplay(displayCode: number): Promise<DevicePair | null> { let temp = this.cache.getControllerDevice(this.guid); let result: Result<ConnectDisplayResponse> = await this.httpRequestExecutor.connectDisplay(displayCode, temp!); if (Code.OK !== result.code) { this.callback?.onError(result.msg); this.callback?.onError(CastError.create(CastError.CONNECT_FAIL, CastError.WAN_CONNECT_FAIL)); return null; } let report: DisplayReportVO = DisplayReportVO.copy(result.data!.displayReport); this.cache.addConnectedDisplay(this.guid, report.displayDevice.displayCode, report); temp!.outIp = result.data!.outIp; this.cache.addControllerDevice(this.guid, temp!); this.mqttLogic?.connect(report.displayDevice.displayCode); this.callback?.onDisplayConnected(report.displayDevice, this.cache.getControllerDevice(this.guid)!); //正式服不支持Retained消息,因此SDK在connect后单独触发一下 this.callback?.onDisplayReport(report); return new DevicePair(report, this.cache.getControllerDevice(this.guid)!); } async disconnectDisplay(displayCode: number): Promise<DisplayDeviceVO | null> { let result: Result<EmptyResponse> = await this.httpRequestExecutor.disconnectDisplay(displayCode, this.cache.getControllerDevice(this.guid)!); if (Code.OK !== result.code) { this.callback?.onError(result.msg); this.callback?.onError(CastError.create(CastError.DISCONNECT_FAIL, CastError.DISCONNECT_FAIL)); return null; } await this.mqttLogic?.disconnect(displayCode); let r = this.cache.getConnectedDisplayByID(this.guid); if (null === r) { SDKLog.e(this.TAG, "disconnectDisplay error: " + displayCode + " not in cache"); } else { this.cache.removeConnectedDisplay(this.guid, displayCode); } this.callback?.onDisplayDisconnected(r!.displayDevice); return r!.displayDevice; } async createCastSession(castType: CastType, protocol: CastProtocol, accessToken?: string): Promise<CastSessionVO | null> { let r = this.cache.getConnectedDisplayByID(this.guid); if (null === r) { SDKLog.e(this.TAG, "createCastSession error: controller not connect to any display"); return null; } let result: Result<CastSessionResponse> = await this.httpRequestExecutor.createCastSession( r.displayDevice.displayCode, this.guid, castType, protocol, accessToken ); if (Code.OK !== result.code) { this.callback?.onError(result.msg); this.callback?.onError(CastError.create(CastError.TRANSMIT_FAIL, CastError.WAN_TRANSMIT_SESSION_CREATE_FAIL)); return null; } let s: CastSessionVO = new CastSessionVO(); s.castSessionId = result.data!.castSessionId;; s.castType = castType; s.castProtocol = protocol; s.guid = this.guid; s.displayCode = r!.displayDevice.displayCode; this.cache.addCastSession(s.castSessionId, s); return s; } /** * 投屏会话接收端通知发送端自己已经做好准备,发送端可以准备开始往指定的投屏会话推流 * 会触发发送端的 onReadyToPush 函数 * @param sessionId 要推流的投屏会话id */ readyToPush(sessionId: number): void { let session: CastSessionVO | null = this.cache.getCastSession(sessionId); if (null === session) { SDKLog.e(this.TAG, "readyToPush: session not found"); return; } this.sendCastSessionMessage(session, SessionAction.CONFIG, this.generateMediaPayload(session, "")); } startPush(sessionId: number, mediaPayload: string): void { let session: CastSessionVO | null = this.cache.getCastSession(sessionId); if (null === session) { SDKLog.e(this.TAG, "startPush: session not found"); return; } this.sendCastSessionMessage(session, SessionAction.START, this.generateMediaPayload(session, mediaPayload)); } stopPush(sessionId: number): void { let session: CastSessionVO | null = this.cache.getCastSession(sessionId); if (null === session) { SDKLog.e(this.TAG, "stopPush: session not found"); return; } this.sendCastSessionMessage(session, SessionAction.STOP, ""); } private generateMediaPayload(session: CastSessionVO, mediaPayload: string): string { let s: string = ""; if (CastProtocol.P2P === session.castProtocol) { s = this.jsonParser.toJson(this.p2pKeyFromPeer); } else if (CastProtocol.NRTC === session.castProtocol) { s = session.castSessionId + ""; } else if (CastProtocol.HTTP === session.castProtocol) { s = mediaPayload; } return s; } private sendCastSessionMessage(session: CastSessionVO, action: SessionAction, mediaPayload: string): void { let r = this.cache.getConnectedDisplayByID(this.guid); if (null === r) { SDKLog.e(this.TAG, "sendCastSessionMessage error: we have not connect display in cache"); return; } let message: CastSessionMessage = new CastSessionMessage(MsgClientType.CONTROLLER, MsgClientType.DISPLAY); session.controllerDevice = this.cache.getControllerDevice(this.guid!)!; message.castSession = session; message.action = action; message.mediaPayload = mediaPayload; this.mqttLogic?.publish(r!.displayDevice.displayCode, message); } /** * 主动销毁投屏会话,携带销毁原因 * @param sessionId 试图销毁的投屏会话id * @param reason 销毁原因,可选 * @returns 如果会话成功被销毁则返回会话数据类,否则返回null */ async destroyCastSession(sessionId: number, reason?: SessionDestroyReason): Promise<CastSessionVO | null> { let result: Result<EmptyResponse> = await this.httpRequestExecutor.destroyCastSession(sessionId, reason?? SessionDestroyReason.CONTROLLER_NORMAL_FINISH); if (Code.OK !== result.code) { this.callback?.onError(result.msg); this.callback?.onError(CastError.create(CastError.TRANSMIT_FAIL, CastError.WAN_TRANSMIT_SESSION_DESTROY_FAIL)); return null; } let s: CastSessionVO | null = this.cache.getCastSession(sessionId); this.cache.removeCastSession(sessionId); return s; } async destroyAllCastSessions(): Promise<void> { let all: IterableIterator<CastSessionVO> = this.cache.castSessionValues(); for (let session of all) { await this.destroyCastSession(session.castSessionId); }; } /** * 阻塞方法,控制终端主动告知显示端,当前投屏会话出现异常需要处理(停推、停收、销毁) * @param sessionId 异常投屏会话id * @param error 异常原因 */ castSessionError(sessionId: number, error: string | CastError): void { let session: CastSessionVO | null = this.cache.getCastSession(sessionId); if (null == session) { SDKLog.e(this.TAG, "castSessionError: session not found"); return; } if (typeof error === 'string') { this.sendCastSessionMessage(session, SessionAction.ERROR, error); } else { this.sendCastSessionMessage(session, SessionAction.ERROR, this.jsonParser.toJson(error)); } } sendPrivateData(data: string): void { let r = this.cache.getConnectedDisplayByID(this.guid); if (null === r) { SDKLog.e(this.TAG, "sendPrivateData error: we have not connect display in cache"); return; } let message: DataMessage = new DataMessage(); message.data = data; this.mqttLogic?.publish(r!.displayDevice.displayCode, message); } getP2PKey(): P2PKeyVO | null { // 目前的网页端无法使用供应商的p2p sdk,所以网页发起的所有投屏方式都不能走p2p return null; } onServerDied(): void { let result: Result<EmptyResponse> = new Result<EmptyResponse>(); result.code = Code.ERR; result.msg = "server died!!"; this.callback?.onError(result.msg); this.callback?.onError(CastError.create(CastError.CONNECT_PEER_OFFLINE, CastError.WAN_CONNECT_SERVER_OFFLINE)); } onDisplayDied(): void { SDKLog.i(this.TAG, "onDisplayDied"); let r = this.cache.getConnectedDisplayByID(this.guid); if (null !== r) { this.cache.removeOnlineDisplay(r!.displayDevice.displayDeviceId, r!.displayDevice.displayCode); this.cache.removeConnectedDisplay(this.guid, r!.displayDevice.displayCode); this.callback?.onDisplayOffline(r!.displayDevice); } else { SDKLog.e(this.TAG, "onDisplayDied error: we have not connected to this display"); } } onMySelfDisconnected(cause: string): void { this.callback?.onMySelfDisconnected(cause); } onDisplayReport(report: DisplayReportVO): void { SDKLog.i(this.TAG, "onDisplayReport: " + report); let r = this.cache.getOnlineDisplayByID(report.displayDevice.displayDeviceId); if (null !== r) { let prevIsDisplayOnline = r.isDisplayDeviceOnline; r = DisplayReportVO.copy(report); this.cache.addOnlineDisplay(r.displayDevice.displayDeviceId, r.displayDevice.displayCode, r); this.callback?.onDisplayReport(report); if (prevIsDisplayOnline && !r.isDisplayDeviceOnline) { // 从在线到离线,一定是display主动调用了displayOffline,因此主动触发onDisplayOffline SDKLog.d(this.TAG, "onDisplayReport, display device call displayOffline: " + report); this.callback?.onDisplayOffline(r.displayDevice); } } else { SDKLog.e(this.TAG, "onDisplayDied error: we have not connected to this display"); } } onCastSession(session: CastSessionVO, action: SessionAction, mediaPayload: string, reason?: SessionDestroyReason): void { if (this.guid != session.guid) { SDKLog.e(this.TAG, "onCastSession error: guid not equal, except: " + this.guid + ", actual: " + session.guid); return; } if (null === this.cache.getConnectedDisplayByID(this.guid)) { SDKLog.e(this.TAG, "onCastSession error: we have not connected to any display"); return; } if (this.cache.getConnectedDisplayByID(this.guid)!.displayDevice.displayCode != session.displayCode) { SDKLog.e(this.TAG, "onCastSession error: session display not equal connected display"); return; } switch (action) { case SessionAction.CREATE: this.cache.addCastSession(session.castSessionId, session); this.callback?.onCastSessionCreate(session); break; case SessionAction.CONFIG: // 目前的网页端无法使用供应商的p2p sdk,所以网页发起的所有投屏方式都不能走p2p // 也就不需要做任何P2PKEY解析处理 switch (session.castType) { case CastType.MIX_CAST: case CastType.DOCUMENT: this.callback?.onReadyToPush(session, mediaPayload); break; } break; case SessionAction.START: // 目前的网页端无法使用供应商的p2p sdk,所以网页发起的所有投屏方式都不能走p2p // 也就不需要做任何P2PKEY解析处理 this.callback?.onCastSessionPushStart(session, mediaPayload); break; case SessionAction.STOP: this.callback?.onCastSessionPushEnd(session); break; case SessionAction.DESTROY: this.cache.removeCastSession(session.castSessionId); this.callback?.onCastSessionDestroy(session, reason?? SessionDestroyReason.NONE); break; case SessionAction.ERROR: try { let tmp: any = this.jsonParser.fromJson(mediaPayload); if (this.isValidMyDataType(tmp)) { this.callback?.onError(CastError.create(tmp.main, tmp.sub)); } else { this.callback?.onError(mediaPayload); } } catch (error) { SDKLog.e(this.TAG, `Failed to parse JSON: ${error}`); this.callback?.onError(mediaPayload); } break; } } private readonly isValidMyDataType = (data: any): data is CastError => { return ( typeof data === "object" && data !== null && typeof data.main === "number" && typeof data.sub === "number" ); }; onUserHasBeenKicked(user: LoginUserVO): void { SDKLog.i(this.TAG, "onUserHasBeenKicked: " + user); this.callback?.onUserHasBeenKicked(user); } onAccountBindDevice(accountBindDevice: AccountBindDeviceVO, action: BindDeviceAction): void { SDKLog.i(this.TAG, "onAccountBindDevice: " + accountBindDevice + ", action: " + action); if (BindDeviceAction.DEVICE_ONLINE == action) { this.callback?.onAccountBindDeviceOnline(accountBindDevice); } else if (BindDeviceAction.DEVICE_OFFLINE == action) { this.callback?.onAccountBindDeviceOffline(accountBindDevice); } } }