// components/agora/agora.ts // import * as AgoraMiniappSDK from '../../utils/Agora_Miniapp_SDK_for_WeChat.js' import * as AgoraMiniappSDK from "agora-miniapp-sdk"; import { ComponentWithComputed, behavior } from 'miniprogram-computed' interface ConfigPropType { sdkAppId: string token: string chanelName: string userId: string rtcSDKInit: boolean } type ConfigProp = { type: ObjectConstructor, value: ConfigPropType } ComponentWithComputed({ /** * 组件的属性列表 */ behaviors: [behavior], properties: { config: { type: Object, value: {} }, init: { type: Boolean, value: false }, muted: { type: Boolean, value: false } }, watch: { 'config.**': function (obj) { console.error('watch-config', obj) if (obj.sdkAppId.length > 0) { this.setData({ config: obj }, () => { if (obj.rtcSDKInit) { this.initRTC(); } }) } }, 'muted': function (val) { console.error('watch-muted', val) this.onMute(val); } }, /** * 组件的初始数据 */ data: { hostRtmpAdress: "", client: {} as AgoraMiniappSDK.Client, config: {} as ConfigPropType, isInit: false, muted: false, debug: false, uid: "", //当前Uid media: [] as any[], }, lifetimes: { attached(): void { console.log('attached agrora'); }, detached(): void { console.log('detached agrora'); if (this.data.client) { this.data.uid = '' if ('leave' in this.data.client) { this.data.client.leave(); } } } }, /** * 组件的方法列表 */ methods: { setDataAsync(data: any): Promise { return new Promise(resolve => void this.setData(data, resolve)) }, async initRTC() { try { if (!this.data.isInit) { this.data.isInit = true //@ts-ignore let client: AgoraMiniappSDK.Client = new AgoraMiniappSDK.Client(); // this.data.client = client //@ts-ignore AgoraMiniappSDK.LOG.setLogLevel(-1); await this.setDataAsync({ client: client }); this.subscribeEvents(); await client.init(this.data.config.sdkAppId); console.warn('join--config', this.data.config); await client.setRole('broadcaster') let uid if (!this.data.isReconnect) { uid = await client.join(this.data.config.token, this.data.config.chanelName, this.data.config.userId, true, 1); } else { uid = await client.rejoin(this.data.config.token, this.data.config.chanelName, this.data.config.userId, [this.data.config.userId], true, 1); } let url = await client.publish(); console.log('url', url) this.setData({ uid: uid, isReconnect: false }); let ts = new Date().getTime(); this.data.uid = uid; this.addMedia(0, this.data.uid, url, { key: ts }); } } catch (error) { console.error('initRTC-error', error) let code = error.code || 0; if (code == 903 || code == 904 || code === 501 || code === 431) { this.reconnect(); } } }, async reconnect() { await this.setDataAsync({ isInit: false }) console.warn('reconnect'); if (this.data.client) { this.data.client.destroy(); } await this.setDataAsync({ isReconnect: true }); this.triggerEvent('reconnect'); // await this.initRTC(); }, async onMute(muted: boolean) { if (muted) { this.data.isInit && await this.data.client.muteLocal('audio') } else { this.data.isInit && await this.data.client?.unmuteLocal('audio') } this.setData({ muted: muted }) }, /** * return player component via uid */ getPlayerComponent(uid: string | number) { const agoraPlayer = this.selectComponent(`#rtc-player-${uid}`); return agoraPlayer; }, onPusherNetstatus: function (e: any) { this.data.client.updatePusherNetStatus(e.detail); }, onPusherStatechange: function (e: any) { this.data.client.updatePusherStateChange(e.detail); }, addMedia(mediaType: number, uid: string | number, url: string, options: any) { console.log(`add media ${mediaType} ${uid} ${url}`); let media = this.data.media || []; if (mediaType === 0) { //pusher media.splice(0, 0, { key: options.key, type: mediaType, uid: `${uid}`, holding: false, url: url, left: 0, top: 0, width: 0, height: 0 }); } else { //player media.push({ key: options.key, rotation: options.rotation, type: mediaType, uid: `${uid}`, holding: false, url: url, left: 0, top: 0, width: 0, height: 0 }); } this.refreshMedia(media); }, /** * update media object * the media component will be fully refreshed if you try to update key * property. */ refreshMedia(media: any[]): Promise { return new Promise((resolve) => { console.log(`updating media: ${JSON.stringify(media)}`); this.setData({ media: media }, () => { resolve(); }); }); }, updateMedia(uid: string | number, options: any) { console.log(`update media ${uid} ${JSON.stringify(options)}`); let media = this.data.media || []; let changed = false; for (let i = 0; i < media.length; i++) { let item = media[i]; if (`${item.uid}` === `${uid}`) { media[i] = Object.assign(item, options); changed = true; console.log(`after update media ${uid} ${JSON.stringify(item)}`) break; } } }, onPlayerNetstatus: function (e: any) { // 遍历所有远端流进行数据上报 let allPlayerStream = this.data.media.filter(m => m.uid !== this.data.uid); allPlayerStream.forEach((item: any) => { this.data.client.updatePlayerNetStatus(item.uid, e.detail); }); }, onPlayerStatechange: function (e) { let allPlayerStream = this.data.media.filter(m => m.uid !== this.data.uid); // 这里 需要去获取所有远端流的 uid allPlayerStream.forEach((item: any) => { this.data.client.updatePlayerStateChange(item.uid, e.detail); }); }, /** * remove media from view */ removeMedia(uid: string | number) { console.log(`remove media ${uid}`); let media = this.data.media || []; media = media.filter((item: any) => { return `${item.uid}` !== `${uid}` }); if (media.length !== this.data.media.length) { this.refreshMedia(media); } else { console.log(`media not changed: ${JSON.stringify(media)}`) // return Promise.resolve(); } }, /** * 注册stream事件 */ subscribeEvents() { /** * sometimes the video could be rotated * this event will be fired with ratotion * angle so that we can rotate the video * NOTE video only supportes vertical or horizontal * in case of 270 degrees, the video could be * up side down */ // this.data.client.on("video-rotation", (e) => { // console.log(`video rotated: ${e.rotation} ${e.uid}`) // setTimeout(() => { // const player = this.getPlayerComponent(e.uid); // player && player.rotate(e.rotation); // }, 1000); // }); /** * fired when new stream join the channel */ this.data.client.on("stream-added", async (e) => { let uid = e.uid; const ts = new Date().getTime(); console.warn(`stream ${uid} added `); /** * subscribe to get corresponding url */ const { url, rotation } = await this.data.client.subscribe(uid); let media = this.data.media || []; let matchItem = null; for (let i = 0; i < media.length; i++) { let item = this.data.media[i]; if (`${item.uid}` === `${uid}`) { //if existing, record this as matchItem and break matchItem = item; break; } } if (!matchItem) { //if not existing, add new media this.addMedia(1, uid, url, { key: ts, rotation: rotation }) } else { // if existing, update property // change key property to refresh live-player this.updateMedia(matchItem.uid, { url: url, key: ts, }); } }); /** * remove stream when it leaves the channel */ this.data.client.on("stream-removed", e => { let uid = e.uid; console.warn(`stream ${uid} removed`); this.removeMedia(uid); }); /** * when bad thing happens - we recommend you to do a * full reconnect when meeting such error * it's also recommended to wait for few seconds before * reconnect attempt */ this.data.client.on("error", err => { let errObj = err || {}; let code = errObj.code || 0; let reason = errObj.reason || ""; console.warn(`error: ${code}, reason: ${reason}`); // wx.showToast({ // title: `argora有错误,code:${code}`, // icon: 'none', // duration: 5000 // }); if (code === 501 || code === 904) { this.reconnect(); } }); /** * there are cases when server require you to update * player url, when receiving such event, update url into * corresponding live-player, REMEMBER to update key property * so that live-player is properly refreshed * NOTE you can ignore such event if it's for pusher or happens before * stream-added */ this.data.client.on('update-url', e => { console.log(`update-url: ${JSON.stringify(e)}`); let uid = e.uid; let url = e.url; let ts = new Date().getTime(); if (`${uid}` === `${this.data.uid}`) { // if it's not pusher url, update console.log(`ignore update-url`); } else { this.updateMedia(uid, { url: url, key: ts, }); } }); this.data.client.on("token-privilege-will-expire", () => { console.warn("当前 token 即将过期,请更新 token"); // this.data.client.renewToken(); this.reconnect(); }); this.data.client.on("token-privilege-did-expire", () => { console.warn("当前 token 已过期,请更新 token 并重新加入频道"); this.reconnect(); }); } } })