gemercheung 2 år sedan
förälder
incheckning
9175258e81

+ 25 - 8
miniprogram/api/sign.ts

@@ -1,21 +1,38 @@
 
 
-import { GET_SIG } from '../utils/apiList'
+import { GET_SIG, GET_ARGOA_SIG, GET_COMMON_SIG } from '../utils/apiList'
 import { request, Response } from '../utils/http'
 
 interface SingResType {
-  expire: number,
-  sdkAppId: number,
+  expire: number | string,
+  sdkAppId: number | string,
   sign: string,
+  operatorType: number
 }
 type RoomDetailRes = Response & {
   data: SingResType
 }
-export const getRTCSig = async (userId: string): Promise<SingResType> => {
+// export const getRTCSig = async (userId: string): Promise<SingResType> => {
 
-  const res = await request.get<RoomDetailRes>(GET_SIG, {
-    userId
+//   const res = await request.get<RoomDetailRes>(GET_SIG, {
+//     userId
+//   })
+//   return res.data
+
+// }
+export const getRTCSig = async (userId: string | number, roleId: string | number, channelName: string): Promise<SingResType> => {
+
+  const res = await request.get<RoomDetailRes>(GET_COMMON_SIG, {
+    userId,
+    roleId,
+    channelName
   })
-  return res.data
+  return res.code === 0 ? res.data : {
+    sdkAppId: '',
+    expire: '',
+    sign: '',
+    operatorType: -1
+  }
+
+}
 
-}

+ 3 - 3
miniprogram/api/user.ts

@@ -35,9 +35,9 @@ export const getUserInfo = async (): Promise<UserResType> => {
 
 }
 
-export const updateUserInfo = async (params: Partial<GlobalUserInfo>): Promise<UserResType> => {
-  const res = await request.post<UserRes>(WX_UPDATE_USER, params)
-  return res.data
+export const updateUserInfo = async (params: Partial<GlobalUserInfo>): Promise<Response> => {
+  const res = await request.post<Response>(WX_UPDATE_USER, params)
+  return res
 
 }
 

+ 7 - 0
miniprogram/components/agora/agora.json

@@ -0,0 +1,7 @@
+{
+  "component": true,
+  "usingComponents": {
+    "agora-pusher": "./comp/agora-pusher/agora-pusher",
+    "agora-player": "./comp/agora-player/agora-player"
+  }
+}

+ 5 - 0
miniprogram/components/agora/agora.scss

@@ -0,0 +1,5 @@
+/* components/agora/agora.wxss */
+.rtc-veiwer{
+  visibility: hidden;
+  opacity: 0;
+}

+ 376 - 0
miniprogram/components/agora/agora.ts

@@ -0,0 +1,376 @@
+// 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
+}
+type ConfigProp = {
+  type: ObjectConstructor,
+  value: ConfigPropType
+}
+
+ComponentWithComputed({
+  /**
+   * 组件的属性列表
+   */
+  behaviors: [behavior],
+  properties: {
+    config: <ConfigProp>{
+      type: Object,
+      value: {}
+    },
+    muted: {
+      type: Boolean,
+      value: false
+    }
+  },
+
+  watch: {
+    'config': function (obj) {
+      console.error('watch', obj)
+      if (obj.sdkAppId.length > 0) {
+        this.setData({
+          config: obj
+        }, () => {
+          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.client.leave();
+      }
+    }
+  },
+  /**
+   * 组件的方法列表
+   */
+  methods: {
+    setDataAsync(data: any): Promise<void> {
+      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')
+          const uid = await client.join(this.data.config.token, this.data.config.chanelName, this.data.config.userId, true, 1);
+          let url = await client.publish();
+          console.log('url', url)
+          this.setData({
+            uid: uid
+          });
+          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();
+        // this.data.client.
+      }
+      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<void> {
+      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();
+      });
+    }
+  }
+})

+ 9 - 0
miniprogram/components/agora/agora.wxml

@@ -0,0 +1,9 @@
+<!--components/agora/agora.wxml-->
+<view class="rtc-veiwer">
+  <block wx:for="{{media}}" wx:key="key">
+    <agora-pusher wx:if="{{item.type === 0 }}" id="rtc-pusher" x="{{item.left}}" y="{{item.top}}" width="{{item.width}}" height="{{item.height}}" url="{{item.url}}" muted="{{muted}}" debug="{{debug}}" bindpushfailed="onPusherFailed" bindnetstatus="onPusherNetstatus" bindstatechange="onPusherStatechange">
+    </agora-pusher>
+    <agora-player wx:if="{{item.type === 1 }}" id="rtc-player-{{item.uid}}" uid="{{item.uid}}" x="{{item.left}}" y="{{item.top}}" width="{{item.width}}" height="{{item.height}}" debug="{{debug}}" url="{{item.url}}" bindnetstatus="onPlayerNetstatus" bindstatechange="onPlayerStatechange">
+    </agora-player>
+  </block>
+</view>

+ 157 - 0
miniprogram/components/agora/comp/agora-player/agora-player.js

@@ -0,0 +1,157 @@
+// components/agora-player/agora-player.js
+Component({
+  /**
+   * 组件的属性列表
+   */
+  properties: {
+    width: {
+      type: Number,
+      value: 0
+    },
+    height: {
+      type: Number,
+      value: 0
+    },
+    x: {
+      type: Number,
+      value: 0
+    },
+    y: {
+      type: Number,
+      value: 0
+    },
+    debug: {
+      type: Boolean,
+      value: !1
+    },
+    /**
+     * 0 - loading, 1 - ok, 2 - error
+     */
+    status: {
+      type: String,
+      value: "loading",
+      observer: function (newVal, oldVal, changedPath) {
+        console.log(`player status changed from ${oldVal} to ${newVal}`);
+      }
+    },
+    orientation: {
+      type: String,
+      value: "vertical"
+    },
+    name: {
+      type: String,
+      value: ""
+    },
+    uid: {
+      type: String,
+      value: ""
+    },
+    url: {
+      type: String,
+      value: "",
+      observer: function (newVal, oldVal, changedPath) {
+        // 属性被改变时执行的函数(可选),也可以写成在methods段中定义的方法名字符串, 如:'_propertyChange'
+        // 通常 newVal 就是新设置的数据, oldVal 是旧数据
+        console.log(`player url changed from ${oldVal} to ${newVal}, path: ${changedPath}`);
+      }
+    }
+  },
+
+  /**
+   * 组件的初始数据
+   */
+  data: {
+    playContext: null,
+    detached: false
+  },
+
+  /**
+   * 组件的方法列表
+   */
+  methods: {
+    /**
+     * start live player via context
+     * in most cases you should not call this manually in your page
+     * as this will be automatically called in component ready method
+     */
+    start: function () {
+      const uid = this.data.uid;
+      console.log(`starting player ${uid}`);
+      if (this.data.status === "ok") {
+        console.log(`player ${uid} already started`);
+        return;
+      }
+      if (this.data.detached) {
+        console.log(`try to start pusher while component already detached`);
+        return;
+      }
+      this.data.playContext.play();
+    },
+
+    /**
+     * stop live pusher context
+     */
+    stop: function () {
+      const uid = this.data.uid;
+      console.log(`stopping player ${uid}`);
+      this.data.playContext.stop();
+    },
+
+    /**
+     * rotate video by rotation
+     */
+    rotate: function (rotation) {
+      let orientation = rotation === 90 || rotation === 270 ? "horizontal" : "vertical";
+      console.log(`rotation: ${rotation}, orientation: ${orientation}, uid: ${this.data.uid}`);
+      this.setData({
+        orientation: orientation
+      });
+    },
+
+    /**
+     * 播放器状态更新回调
+     */
+    playerStateChange: function (e) {
+      this.triggerEvent('statechange', e);
+      console.log(`live-player id: ${e.target.id}, code: ${e.detail.code}`)
+      let uid = parseInt(e.target.id.split("-")[1]);
+      if (e.detail.code === 2004) {
+        console.log(`live-player ${uid} started playing`);
+        if(this.data.status === "loading") {
+          this.setData({
+            status: "ok"
+          });
+        }
+      } else if (e.detail.code === -2301) {
+        console.log(`live-player ${uid} stopped`, "error");
+        this.setData({
+          status: "error"
+        })
+      }
+    },
+
+    playerNetStatus: function(e) {
+      this.triggerEvent('netstatus', e);
+    },
+  },
+  /**
+   * 组件生命周期
+   */
+  ready: function () {
+    console.log(`player ${this.data.uid} ready`);
+    this.data.playContext || (this.data.playContext = wx.createLivePlayerContext(`player-${this.data.uid}`, this));
+    // if we already have url when component mounted, start directly
+    if(this.data.url) {
+      this.start();
+    }
+  },
+  moved: function () {
+    console.log(`player ${this.data.uid} moved`);
+  },
+  detached: function () {
+    console.log(`player ${this.data.uid} detached`);
+    // auto stop player when detached
+    this.data.playContext && this.data.playContext.stop();
+    this.data.detached = true;
+  }
+})

+ 4 - 0
miniprogram/components/agora/comp/agora-player/agora-player.json

@@ -0,0 +1,4 @@
+{
+  "component": true,
+  "usingComponents": {}
+}

+ 16 - 0
miniprogram/components/agora/comp/agora-player/agora-player.wxml

@@ -0,0 +1,16 @@
+<!--components/agora-player/agora-player.wxml-->
+<view class="play-container" style="left:{{x}}px; top:{{y}}px; width: {{width}}px; height: {{height}}px; ">
+  <live-player wx:if="{{url!==''}}" id="player-{{uid}}" src="{{url}}" mode="RTC" 
+  class="player" orientation="{{orientation}}"
+   bindstatechange="playerStateChange" 
+   bindnetstatus="playerNetStatus" 
+   object-fit="fillCrop" 
+   style="height:{{height}}px; position: absolute; width: 100%; top: 0; left: 0;"
+    debug="{{debug}}" autoplay="true"/>
+  <cover-view wx-if="{{status !== 'ok'}}" class="sud flex-center-column" style="position: absolute; width: 100%; height:{{height}}px;justify-content:center">
+    <cover-image style="width: 128px;height:103px" src="../../images/{{status}}.png"></cover-image>
+  </cover-view>
+  <cover-view class="" style="position: absolute;top:10px;left:10px;font-size: 28rpx; right: 10px">
+    {{name}}({{uid}})
+  </cover-view>
+</view>

+ 12 - 0
miniprogram/components/agora/comp/agora-player/agora-player.wxss

@@ -0,0 +1,12 @@
+/* components/agora-player/agora-player.wxss */
+
+.play-container{
+  background: black;
+  display: block;
+  position: absolute;
+}
+
+.sud{
+  background-color: #1B2A38;
+  opacity:0.65; 
+}

+ 158 - 0
miniprogram/components/agora/comp/agora-pusher/agora-pusher.js

@@ -0,0 +1,158 @@
+// components/agora-pusher.js
+
+Component({
+  /**
+   * 组件的属性列表
+   */
+  properties: {
+    minBitrate: {
+      type: Number,
+      value: 200
+    },
+    maxBitrate: {
+      type: Number,
+      value: 500
+    },
+    width: {
+      type: Number,
+      value: 0
+    },
+    height: {
+      type: Number,
+      value: 0
+    },
+    x: {
+      type: Number,
+      value: 0
+    },
+    y: {
+      type: Number,
+      value: 0
+    },
+    muted: {
+      type: Boolean,
+      value: !1
+    },
+    debug: {
+      type: Boolean,
+      value: !1
+    },
+    beauty: {
+      type: String,
+      value: 0
+    },
+    aspect: {
+      type: String,
+      value: "3:4"
+    },
+    /**
+     * 0 - loading, 1 - ok, 2 - error
+     */
+    status: {
+      type: String,
+      value: "loading",
+      observer: function (newVal, oldVal, changedPath) {
+        console.log(`player status changed from ${oldVal} to ${newVal}`);
+      }
+    },
+    url: {
+      type: String,
+      value: "",
+      observer: function (newVal, oldVal, changedPath) {
+        // 属性被改变时执行的函数(可选),也可以写成在methods段中定义的方法名字符串, 如:'_propertyChange'
+        // 通常 newVal 就是新设置的数据, oldVal 是旧数据
+        console.log(`pusher url changed from ${oldVal} to ${newVal}, path: ${changedPath}`);
+      }
+    }
+  },
+
+  /**
+   * 组件的初始数据
+   */
+  data: {
+    pusherContext: null,
+    detached: false
+  },
+
+  /**
+   * 组件的方法列表
+   */
+  methods: {
+    /**
+     * start live pusher via context
+     * in most cases you should not call this manually in your page
+     * as this will be automatically called in component ready method
+     */
+    start() {
+      console.log(`starting pusher`);
+      this.data.pusherContext.stop();
+      if (this.data.detached) {
+        console.log(`try to start pusher while component already detached`);
+        return;
+      }
+      this.data.pusherContext.start();
+    },
+
+    /**
+     * stop live pusher context
+     */
+    stop() {
+      console.log(`stopping pusher`);
+      this.data.pusherContext.stop();
+    },
+
+    /**
+     * switch camera direction
+     */
+    switchCamera() {
+      this.data.pusherContext.switchCamera();
+    },
+
+    /**
+     * 推流状态更新回调
+     */
+    recorderStateChange: function (e) {
+      this.triggerEvent('statechange', e);
+      console.log(`live-pusher code: ${e.detail.code} - ${e.detail.message}`)
+      if (e.detail.code === -1307) {
+        //re-push
+        console.log('live-pusher stopped', "error");
+        this.setData({
+          status: "error"
+        })
+        //emit event
+        this.triggerEvent('pushfailed');
+      }
+
+      if (e.detail.code === 1008) {
+        //started
+        console.log(`live-pusher started`);
+        if(this.data.status === "loading") {
+          this.setData({
+            status: "ok"
+          })
+        }
+      }
+    },
+    recorderNetChange: function(e) {
+      this.triggerEvent('netstatus', e);
+    }
+  },
+
+  /**
+   * 组件生命周期
+   */
+  ready: function () {
+    console.log("pusher ready");
+    this.data.pusherContext || (this.data.pusherContext = wx.createLivePusherContext(this));
+  },
+  moved: function () {
+    console.log("pusher moved");
+   },
+  detached: function () {
+    console.log("pusher detached");
+    // auto stop pusher when detached
+    this.data.pusherContext && this.data.pusherContext.stop();
+    this.data.detached = true;
+  }
+})

+ 4 - 0
miniprogram/components/agora/comp/agora-pusher/agora-pusher.json

@@ -0,0 +1,4 @@
+{
+  "component": true,
+  "usingComponents": {}
+}

+ 12 - 0
miniprogram/components/agora/comp/agora-pusher/agora-pusher.wxml

@@ -0,0 +1,12 @@
+<!--components/agora-pusher.wxml-->
+<view class="pusher-container" id="rtcpusher"
+  style="top: {{y}}px; left: {{x}}px; width: {{width}}px; height: {{height}}px; position: absolute;">
+  <live-pusher wx:if="{{url!==''}}" style="height:{{height}}px; position: absolute; width: 100%; " url="{{url}}"
+    mode="RTC" aspect="{{aspect}}" class="camera" bindstatechange="recorderStateChange"
+    bindnetstatus="recorderNetChange" background-mute="true" muted="{{muted}}" beauty="{{beauty}}" max-bitrate="500"
+    min-bitrate="200" waiting-image="https://webdemo.agora.io/away.png" debug="{{debug}}" autopush="true" />
+  <cover-view wx-if="{{status !== 'ok'}}" class="sud flex-center-column"
+    style="position: absolute; width: 100%; height: 100%;justify-content:center">
+    <cover-image style="width: 128px;height:103px" src="../../images/{{status}}.png"></cover-image>
+  </cover-view>
+</view>

+ 12 - 0
miniprogram/components/agora/comp/agora-pusher/agora-pusher.wxss

@@ -0,0 +1,12 @@
+/* components/agora-pusher.wxss */
+
+.pusher-container{
+  background: black;
+  display: block;
+  position: absolute;
+}
+
+.sud{
+  background-color: #1B2A38;
+  opacity:0.65; 
+}

+ 15 - 7
miniprogram/components/profilePatch/profilePatch.ts

@@ -69,18 +69,26 @@ Component({
 
     async updateUserInfo() {
       const wxUserId = wx.getStorageSync('wxUserId');
-      
-      await updateUserInfo({
+
+      const res = await updateUserInfo({
         wxUserId,
         nickName: this.data.nickname,
         avatarUrl: this.data.avatar,
         phoneNumber: this.data.phoneNumber,
       })
-      await getUserInfo();
-      this.setData({
-        ifShow: false
-      });
-      this.triggerEvent('update')
+      if (res.code === 0) {
+        await getUserInfo();
+        this.setData({
+          ifShow: false
+        });
+        this.triggerEvent('update')
+      } else {
+        wx.showToast({
+          title: res.message
+        });
+      }
+      console.log('updateUserInfo-结果', res);
+
 
     },
     async checkSession(): Promise<boolean> {

+ 11 - 5
miniprogram/pages/personal/personal.ts

@@ -93,12 +93,12 @@ Page({
     app.resetUserInfo();
     wx.showToast({
       title: '登出成功!',
-  
+
     });
     wx.switchTab({
-      url:"/pages/my/my"
+      url: "/pages/my/my"
     })
- 
+
   },
   async checkSession(): Promise<boolean> {
     let isExist = false
@@ -130,11 +130,17 @@ Page({
         console.log('res', res.phoneNumber)
         if (res.phoneNumber) {
           const wxUserId = wx.getStorageSync('wxUserId');
-          await updateUserInfo({
+          const result = await updateUserInfo({
             wxUserId,
             phoneNumber: res.phoneNumber
           })
-          await getUserInfo();
+          if (result.code === 0) {
+            await getUserInfo();
+          } else {
+            wx.showToast({
+              title: result.message
+            });
+          }
         }
       }
     }

+ 2 - 1
miniprogram/pages/room/room.json

@@ -1,6 +1,7 @@
 {
   "usingComponents": {
     "voice": "../../components/voice-v4/voice",
-    "profile-patch": "../../components/profilePatch/profilePatch"
+    "profile-patch": "../../components/profilePatch/profilePatch",
+    "agora": "../../components/agora/agora"
   }
 }

+ 109 - 21
miniprogram/pages/room/room.ts

@@ -2,7 +2,7 @@
 
 import { fetchRoom, RoomDetailType } from '../../api/fetchRoom'
 import { shareRoom } from '../../api/shareRoom'
-import { getRTCSig } from '../../api/sign'
+import { getRTCSig, getArgoraRTCSig } from '../../api/sign'
 import { server } from '../../config'
 import { authorizeRecord } from '../../utils/util'
 import { audioManger, AudioManger } from './libs/audioManager'
@@ -26,8 +26,11 @@ Page({
     webviewParams: {} as SocketParams,
     isTour: 0,
     m: '',
-    showShare: false
-
+    showShare: false,
+    // roleId: ''
+    argoraConfig: { sdkAppId: '', token: '', chanelName: "", userId: "" },
+    muted: true,
+    audioOperatorType: NaN,
   },
 
 
@@ -62,19 +65,37 @@ Page({
   async setRole(roomId: string) {
     let roomInfo: RoomDetailType
     roomInfo = await fetchRoom(roomId);
-    let num = roomInfo.sceneData && roomInfo.sceneData?.length ? roomInfo?.sceneData[0].num :'';
+    if (!roomInfo) {
+      wx.showModal({
+        title: '提示',
+        content: '链接已失效!',
+        showCancel: false,
+        success(res) {
+          if (res.confirm) {
+            console.log('用户点击确定')
+            wx.switchTab({
+              url: "/pages/index/index"
+            })
+          }
+        }
+      });
+      return
+    }
+
+    let num = roomInfo.sceneData && roomInfo.sceneData?.length ? roomInfo?.sceneData[0].num : '';
+
+
     if (!num) {
-      console.error('场景码不能为空!');
       wx.showModal({
         title: '提示',
-        content: '服务器初始化异常,请稍后再试!',
+        content: '链接已失效!',
         showCancel: false,
         success(res) {
           if (res.confirm) {
             console.log('用户点击确定')
-        
-            wx.redirectTo({
-              url:"/pages/myScene/myScene"
+
+            wx.switchTab({
+              url: "/pages/index/index"
             })
           }
         }
@@ -206,7 +227,6 @@ Page({
       this.handleJoinSocket();
       this.handleJoinRTC();
     }
-
   },
   updateUserInfo(data?: any) {
     console.log('webview-updateUserInfo')
@@ -238,15 +258,61 @@ Page({
 
   },
   async handleJoinRTC() {
-    const userId = wx.getStorageSync('wxUserId')
-    const sign = await getRTCSig(userId);
-    const isTour = this.data.isTour
+    const userId = wx.getStorageSync('wxUserId');
+    const roleId = this.data.role === 'leader' ? 1 : 2;
+    const roomId = this.data.roomId;
+    // const roomId = "test001"
+    const sign = await getRTCSig(userId, 1, roomId);
+    console.log('sign', sign);
+    if (typeof sign?.operatorType == 'number') {
+      console.log('语音类型', sign.operatorType);
+
+      this.setData({
+        audioOperatorType: sign.operatorType,
+      });
+      switch (sign.operatorType) {
+        case 0:
+          wx.showToast({
+            title: '当前语音:腾讯'
+          })
+          this.handleJoinTRTCRTC(userId, roomId, String(sign.sdkAppId), sign.sign);
+          break;
+        case 1:
+          wx.showToast({
+            title: '当前语音:声网'
+          })
+          this.handleJoinArgoraRTC(userId, roomId, String(sign.sdkAppId), sign.sign);
+          break;
+        default:
+          wx.showToast({
+            title: '语音初始化失败!'
+          })
+          break;
+      }
+    }
+
+  },
+  // argora语音
+  async handleJoinArgoraRTC(userId: string, roomId: string, sdkAppId: string, token: string) {
+    this.setData({
+      argoraConfig: {
+        sdkAppId: sdkAppId,
+        token: token,
+        chanelName: roomId,
+        //@ts-ignore
+        userId: userId as any,
+        // roleId: roleId as any
+      }
+    })
+  },
+  // trtc语音
+  async handleJoinTRTCRTC(userId: string, roomId: string, sdkAppId: string, token: string) {
 
     this.audioManger = audioManger({
-      roomId: this.data.webviewParams.roomId,
+      roomId: roomId,
       userId: userId,
-      sdkAppID: sign.sdkAppId,
-      sig: sign.sign,
+      sdkAppID: Number(sdkAppId),
+      sig: token,
       role: this.data.webviewParams.role,
       noMute: false,
     })
@@ -281,9 +347,11 @@ Page({
    * 生命周期函数--监听页面卸载
    */
   onUnload() {
-    this.audioManger && this.audioManger.stop()
+    if (this.audioManger && 'stop' in this.audioManger) {
+      this.audioManger.stop()
+    }
 
-    if (this.socket) {
+    if (this.socket && 'off' in this.socket) {
       this.socket.off('action', this.handleSocketAction)
       this.socket.off('signal', this.handleSocketSignal)
       this.socket && this.socket.disconnect()
@@ -344,12 +412,28 @@ Page({
         this.handleInviteMember(action.userId, action.data)
         break;
       case 'changeScene':
-        this.audioManger.changeMute(true);
+        this.hanldeAudioDefaultMuted(true);
         break;
       default:
         break;
     }
   },
+
+  hanldeAudioDefaultMuted(value: boolean) {
+    switch (this.data.audioOperatorType) {
+      case 0:
+        this.audioManger.changeMute(value);
+        break;
+      case 1:
+        this.setData({ muted: value });
+        break;
+      default:
+        break;
+    }
+
+  },
+
+
   handleSocketSignal(data) {
     debugger
   },
@@ -359,7 +443,8 @@ Page({
       const f_userId = userId.replace('user_', '')
       const app = getApp<IAppOption>();
       if (app.globalData.userInfo?.wxUserId == f_userId) {
-        this.audioManger.changeMute(muted)
+        // this.audioManger.changeMute(muted)
+        this.hanldeAudioDefaultMuted(muted);
       }
     }
   },
@@ -378,6 +463,9 @@ Page({
     this.setData({
       showShare: false
     })
+  },
+  handleArgoraReconnect() {
+    console.log('handleArgoraReconnect');
+    this.handleJoinRTC();
   }
-
 })

+ 5 - 2
miniprogram/pages/room/room.wxml

@@ -1,5 +1,5 @@
 <!--pages/room/room.wxml-->
-<voice />
+
 <profile-patch show="{{patchProfile}}" showPhone="{{patchProfilePhone}}" bind:update="handleProfilePatch" />
 <!-- <cover-view class="test_view">
   <t-dialog id="t-dialog" />
@@ -17,4 +17,7 @@
     <button class="cancel btn" bindtap="cancelShare">取消</button>
   </cover-view>
 
-</cover-view>
+</cover-view>
+<!-- 语音 -->
+<voice wx:if="{{audioOperatorType === 0}}" />
+<agora wx:if="{{audioOperatorType === 1 && isTour === 0 }}" config="{{argoraConfig}}" muted="{{muted}}" bind:reconnect="handleArgoraReconnect" />

+ 2 - 0
miniprogram/utils/apiList.ts

@@ -4,6 +4,8 @@ export const GET_MY_ROOM_LIST = '/takelook/roomList'
 export const GET_HOME_ROOM_LIST = '/takelook/wxApi/roomList'
 export const GET_ROOM = '/takelook/roomInfo'
 export const GET_SIG = '/takelook/tencentYun/getSign'
+export const GET_COMMON_SIG = '/takelook/rtcMedia/getToken'
+export const GET_ARGOA_SIG = '/takelook/agoraIO/getAgoraToken'
 export const LEAVE_ROOM = '/takelook/inOrOutRoom'
 export const WX_LOGIN = '/takelook/wxApi/wxLogin'
 export const WX_GET_PHONE = '/takelook/wxApi/getPhone'

+ 59 - 7
package-lock.json

@@ -9,19 +9,32 @@
       "version": "1.0.0",
       "license": "ISC",
       "dependencies": {
+        "agora-miniapp-sdk": "latest",
+        "miniprogram-computed": "^4.4.0",
         "miniprogram-network": "^5.3.0-alpha.0",
         "miniprogram-request": "^5.3.0-alpha.0",
-        "tdesign-miniprogram": "^0.30.0"
+        "tdesign-miniprogram": "^0.31.0"
       },
       "devDependencies": {
         "miniprogram-api-typings": "^3.8.1"
       }
     },
+    "node_modules/agora-miniapp-sdk": {
+      "version": "2.6.1",
+      "resolved": "http://192.168.0.47:4873/agora-miniapp-sdk/-/agora-miniapp-sdk-2.6.1.tgz",
+      "integrity": "sha512-BzK4AvJx4br559D6VI16MSBWw8F8ClY5qsUJqiY1PJFivdTvdIC0dnvXC3k7EOc+nRSBHja1OXgp09KuUA3whA==",
+      "license": "ISC"
+    },
     "node_modules/dayjs": {
       "version": "1.11.7",
       "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.7.tgz",
       "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ=="
     },
+    "node_modules/fast-deep-equal": {
+      "version": "2.0.1",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+      "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
+    },
     "node_modules/miniprogram-api-typings": {
       "version": "3.8.1",
       "resolved": "https://registry.npmmirror.com/miniprogram-api-typings/-/miniprogram-api-typings-3.8.1.tgz",
@@ -33,6 +46,15 @@
       "resolved": "https://registry.npmmirror.com/miniprogram-cancel-token/-/miniprogram-cancel-token-5.1.2.tgz",
       "integrity": "sha512-FuEqPbJNUKQQRcpNxis9uecVpHrjzGp0N9UfTHkQZjJuxP8OOD2fXWRcqFBh6WmitHwfSj4IzVJ+VFSJEglFyg=="
     },
+    "node_modules/miniprogram-computed": {
+      "version": "4.4.0",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/miniprogram-computed/-/miniprogram-computed-4.4.0.tgz",
+      "integrity": "sha512-th9y1Ua7H6rC47Vs20/QFC6zlq/A/92zbKrCxkJNlXf7xwNdg86BRRFvccR8yIwVXRXjMYUrcsVDq6bwzbE0Cg==",
+      "dependencies": {
+        "fast-deep-equal": "^2.0.1",
+        "rfdc": "^1.1.4"
+      }
+    },
     "node_modules/miniprogram-downloader": {
       "version": "5.3.0-alpha.0",
       "resolved": "https://registry.npmmirror.com/miniprogram-downloader/-/miniprogram-downloader-5.3.0-alpha.0.tgz",
@@ -91,21 +113,37 @@
         "miniprogram-network-utils": "^5.3.0-alpha.0"
       }
     },
+    "node_modules/rfdc": {
+      "version": "1.3.0",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/rfdc/-/rfdc-1.3.0.tgz",
+      "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA=="
+    },
     "node_modules/tdesign-miniprogram": {
-      "version": "0.30.0",
-      "resolved": "https://registry.npmmirror.com/tdesign-miniprogram/-/tdesign-miniprogram-0.30.0.tgz",
-      "integrity": "sha512-ayR74I76niTNtwiLHzYU2+2mVIB94+9nbPiOw/CSoiVm4PafxOyZMvP4AJC4UXPS+UWTRIo2ANC6oK9xTDLIIw==",
+      "version": "0.31.0",
+      "resolved": "http://192.168.0.47:4873/tdesign-miniprogram/-/tdesign-miniprogram-0.31.0.tgz",
+      "integrity": "sha512-DblLMdByCoYQCyMPTNokyxK4VR5Mq+WWKXCAYSuM4mQrPqNIFuuKAGlD/BmYbuyF+FulkZUWasYw6dGYMZiLmg==",
+      "license": "MIT",
       "dependencies": {
         "dayjs": "^1.10.7"
       }
     }
   },
   "dependencies": {
+    "agora-miniapp-sdk": {
+      "version": "2.6.1",
+      "resolved": "http://192.168.0.47:4873/agora-miniapp-sdk/-/agora-miniapp-sdk-2.6.1.tgz",
+      "integrity": "sha512-BzK4AvJx4br559D6VI16MSBWw8F8ClY5qsUJqiY1PJFivdTvdIC0dnvXC3k7EOc+nRSBHja1OXgp09KuUA3whA=="
+    },
     "dayjs": {
       "version": "1.11.7",
       "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.7.tgz",
       "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ=="
     },
+    "fast-deep-equal": {
+      "version": "2.0.1",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+      "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
+    },
     "miniprogram-api-typings": {
       "version": "3.8.1",
       "resolved": "https://registry.npmmirror.com/miniprogram-api-typings/-/miniprogram-api-typings-3.8.1.tgz",
@@ -117,6 +155,15 @@
       "resolved": "https://registry.npmmirror.com/miniprogram-cancel-token/-/miniprogram-cancel-token-5.1.2.tgz",
       "integrity": "sha512-FuEqPbJNUKQQRcpNxis9uecVpHrjzGp0N9UfTHkQZjJuxP8OOD2fXWRcqFBh6WmitHwfSj4IzVJ+VFSJEglFyg=="
     },
+    "miniprogram-computed": {
+      "version": "4.4.0",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/miniprogram-computed/-/miniprogram-computed-4.4.0.tgz",
+      "integrity": "sha512-th9y1Ua7H6rC47Vs20/QFC6zlq/A/92zbKrCxkJNlXf7xwNdg86BRRFvccR8yIwVXRXjMYUrcsVDq6bwzbE0Cg==",
+      "requires": {
+        "fast-deep-equal": "^2.0.1",
+        "rfdc": "^1.1.4"
+      }
+    },
     "miniprogram-downloader": {
       "version": "5.3.0-alpha.0",
       "resolved": "https://registry.npmmirror.com/miniprogram-downloader/-/miniprogram-downloader-5.3.0-alpha.0.tgz",
@@ -175,10 +222,15 @@
         "miniprogram-network-utils": "^5.3.0-alpha.0"
       }
     },
+    "rfdc": {
+      "version": "1.3.0",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/rfdc/-/rfdc-1.3.0.tgz",
+      "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA=="
+    },
     "tdesign-miniprogram": {
-      "version": "0.30.0",
-      "resolved": "https://registry.npmmirror.com/tdesign-miniprogram/-/tdesign-miniprogram-0.30.0.tgz",
-      "integrity": "sha512-ayR74I76niTNtwiLHzYU2+2mVIB94+9nbPiOw/CSoiVm4PafxOyZMvP4AJC4UXPS+UWTRIo2ANC6oK9xTDLIIw==",
+      "version": "0.31.0",
+      "resolved": "http://192.168.0.47:4873/tdesign-miniprogram/-/tdesign-miniprogram-0.31.0.tgz",
+      "integrity": "sha512-DblLMdByCoYQCyMPTNokyxK4VR5Mq+WWKXCAYSuM4mQrPqNIFuuKAGlD/BmYbuyF+FulkZUWasYw6dGYMZiLmg==",
       "requires": {
         "dayjs": "^1.10.7"
       }

+ 4 - 2
package.json

@@ -6,13 +6,15 @@
   "author": "",
   "license": "ISC",
   "dependencies": {
+    "miniprogram-computed": "^4.4.0",
     "miniprogram-network": "^5.3.0-alpha.0",
     "miniprogram-request": "^5.3.0-alpha.0",
-    "tdesign-miniprogram": "^0.30.0"
+    "tdesign-miniprogram": "^0.31.0",
+    "agora-miniapp-sdk": "latest"
   },
   "devDependencies": {
     "miniprogram-api-typings": "^3.8.1"
   },
   "main": ".eslintrc.js",
   "description": ""
-}
+}

+ 43 - 43
project.config.json

@@ -1,44 +1,44 @@
-{
-  "description": "项目配置文件",
-  "packOptions": {
-    "ignore": [],
-    "include": []
-  },
-  "miniprogramRoot": "miniprogram/",
-  "compileType": "miniprogram",
-  "projectname": "ts-sass-demo",
-  "setting": {
-    "ignoreDevUnusedFiles": false,
-    "packNpmManually": true,
-    "packNpmRelationList": [
-      {
-        "packageJsonPath": "./package.json",
-        "miniprogramNpmDistDir": "miniprogram/"
-      }
-    ],
-    "useCompilerPlugins": [
-      "typescript",
-      "sass"
-    ],
-    "babelSetting": {
-      "ignore": [],
-      "disablePlugins": [],
-      "outputPath": ""
-    },
-    "es6": true,
-    "enhance": true,
-    "ignoreUploadUnusedFiles": true,
-    "minified": true,
-    "condition": false
-  },
-  "simulatorType": "wechat",
-  "simulatorPluginLibVersion": {},
-  "condition": {},
-  "srcMiniprogramRoot": "miniprogram/",
-  "appid": "wxd55ee54235e90359",
-  "libVersion": "2.28.1",
-  "editorSetting": {
-    "tabIndent": "insertSpaces",
-    "tabSize": 2
-  }
+{
+  "description": "项目配置文件",
+  "packOptions": {
+    "ignore": [],
+    "include": []
+  },
+  "miniprogramRoot": "miniprogram/",
+  "compileType": "miniprogram",
+  "projectname": "ts-sass-demo",
+  "setting": {
+    "ignoreDevUnusedFiles": false,
+    "packNpmManually": true,
+    "packNpmRelationList": [
+      {
+        "packageJsonPath": "./package.json",
+        "miniprogramNpmDistDir": "miniprogram/"
+      }
+    ],
+    "useCompilerPlugins": [
+      "typescript",
+      "sass"
+    ],
+    "babelSetting": {
+      "ignore": [],
+      "disablePlugins": [],
+      "outputPath": ""
+    },
+    "es6": true,
+    "enhance": true,
+    "ignoreUploadUnusedFiles": true,
+    "minified": true,
+    "condition": false
+  },
+  "simulatorType": "wechat",
+  "simulatorPluginLibVersion": {},
+  "condition": {},
+  "srcMiniprogramRoot": "miniprogram/",
+  "appid": "wxd55ee54235e90359",
+  "libVersion": "2.28.1",
+  "editorSetting": {
+    "tabIndent": "insertSpaces",
+    "tabSize": 2
+  }
 }

+ 8 - 8
project.private.config.json

@@ -1,9 +1,9 @@
-{
-  "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
-  "projectname": "livestream-miniapp",
-  "setting": {
-    "compileHotReLoad": false,
-    "bigPackageSizeSupport": true
-  },
-  "libVersion": "2.25.3"
+{
+  "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
+  "projectname": "livestream-miniapp",
+  "setting": {
+    "compileHotReLoad": false,
+    "bigPackageSizeSupport": true
+  },
+  "libVersion": "2.32.0"
 }

+ 1 - 1
tsconfig.json

@@ -24,7 +24,7 @@
   "include": [
     "./**/*.ts",
     "./typings/**/*.d.ts"
-, "miniprogram/utils/socket.io-v4-no-msgpack.js"  ],
+, "miniprogram/utils/socket.io-v4-no-msgpack.js", "miniprogram/components/agora/agora.js"  ],
   "exclude": [
     "node_modules"
   ],