Преглед на файлове

1.安卓原生插件js消息通知

lovetiya преди 4 години
родител
ревизия
257f795344
променени са 2 файла, в които са добавени 440 реда и са изтрити 255 реда
  1. 277 0
      jhlive/jhlive.nvue
  2. 163 255
      jhlive/live-wechat.vue

+ 277 - 0
jhlive/jhlive.nvue

@@ -0,0 +1,277 @@
+<template>
+	<view class="container-jhlive" data-type="jhlive">
+    <JhliveApp 
+      class="liveView"
+      ref="liveView"
+      :sdkAppID="sdkAppID"
+      :userSig="userSig"
+      :userid="userId" 
+      :roomid="roomId" 
+      :isAuthor="isAuthor"
+      :linkMic="linkMic" 
+      @onError="onError"
+	  @onRemoteUserEnterRoom="onRemoteUserEnterRoom"
+	  @onRemoteUserLeaveRoom="onRemoteUserLeaveRoom" />
+    <view class="top_left_box">
+      <image class="top_left_box_img" :src="avatar"></image>
+      <view class="top_left_box_text">
+        <text class="top_left_box_text1">{{name}}</text>
+        <text class="top_left_box_text2" >{{num}}</text>
+      </view>
+    </view>
+
+    <view class="top-right-box">
+			<view class="top-right-box-btn" v-if="isAuthor" @click="btnBeautify">
+        <text class="top-right-box-btn-text">美颜</text>
+      </view>
+      <view class="top-right-box-btn" v-if="isAuthor" @click="btnChangeCamera">
+        <text class="top-right-box-btn-text">切换</text>
+      </view>
+    </view>
+
+    <view class="bottom-right-box">
+      <view class="bottom-right-box-btn" @click="btnLike">
+        <text class="bottom-right-box-btn-text">点赞</text>
+      </view>
+    </view>
+	</view>
+</template>
+
+<script>
+
+import LibGenerateTestUserSig from "./lib-generate-test-usersig-es.min";
+
+// #ifdef MP-WEIXIN
+import JhliveApp from './live-wechat';
+//#endif
+
+const LOGTAG = '--JHLIVE--:';
+const EXPIRETIME = 604800;
+
+export default {
+  name: 'jhlive',
+  // #ifdef MP-WEIXIN
+  components: {JhliveApp},
+  //#endif
+
+	data() {
+		return {
+      userSig: '',
+      beautifyLevel: false,
+		};
+	},
+
+	props: {
+    sdkAppID: Number,
+    secretKey: String,
+    userId: String,
+    roomId: String,
+    isAuthor: Boolean,
+    linkMic: Boolean,
+    name: String,
+    avatar: String,
+    num: String,
+	},
+	onLoad() {
+    this.init();
+	},
+	onUnload() {
+		this.destroy();
+	},
+	methods: {
+    init() {
+      this.remoteUsers = [];
+      uni.setKeepScreenOn({
+        keepScreenOn: true
+      });
+    },
+    destroy() {
+      uni.setKeepScreenOn({
+        keepScreenOn: false
+      });
+      this.exitRoom()
+    },
+		enterRoom() {
+      if(!this.$refs.liveView) {
+        console.log(LOGTAG+'no jhlive');
+        uni.showToast({
+          title: '直播组件初始化失败',
+          duration: 2000
+        });
+        return;
+      }
+      console.log(LOGTAG+'enterRoom', this.isAuthor, this.linkMic, this.sdkAppID, this.secretKey, this.userId, this.roomId);
+      const generator = new LibGenerateTestUserSig(this.sdkAppID, this.secretKey, EXPIRETIME);
+      this.userSig = generator.genTestUserSig(String(this.userId));
+      this.$nextTick(() => this.$refs.liveView.enterRoom());
+		},
+		exitRoom() {
+      console.log(LOGTAG+'exitRoom');
+			this.$refs.liveView.exitRoom();
+    },
+    btnChangeCamera() {
+      console.log(LOGTAG+'btnChangeCamera');
+      this.$refs.liveView.switchCarema();
+    },
+    btnBeautify() {
+      console.log(LOGTAG+'btnBeautify', this.beautifyLevel);
+      // blv - 美颜级别取值范围0 - 9; 0表示关闭,1 - 9值越大
+      // wlv - 亮度级别取值范围0 - 9; 0表示关闭,1 - 9值越大
+      // beautyStyle  美颜风格.三种美颜风格:0 :光滑 1:自然 2:朦胧
+      this.beautifyLevel = !this.beautifyLevel;
+      this.$refs.liveView.setBeauty(
+        this.beautifyLevel?9:0,
+        this.beautifyLevel?9:0,
+        this.beautifyLevel?0:0,
+      );
+    },
+    handleLiveEvent(event) {
+      if(event.eventName=='onRemoteUserEnterRoom'&&!this.remoteUsers.find(v=>v==event.eventData)) {
+        this.remoteUsers.push(event.eventData);
+        this.$emit('onRemoteUser', this.remoteUsers);
+      }else if(event.eventName=='onRemoteUserLeaveRoom') {
+        this.remoteUsers = this.remoteUsers.filter(v=>v!=event.eventData);
+        this.$emit('onRemoteUser', this.remoteUsers);
+      }else if(event.eventName=='onError') {
+        this.$emit('onError', event.eventData.msg);
+        uni.showToast({
+          title: event.eventData.msg,
+          duration: 2000
+        });
+      }
+    },
+	onError: (e)=> {
+		uni.showToast({
+		  title: e.detail,
+		  duration: 2000
+		});
+        this.$emit('onError', e.detail);
+    },
+	onRemoteUserEnterRoom: (e)=> {
+		if(this.remoteUsers.find(v=>v==e.detail)) {
+			this.remoteUsers.push(e.detail);
+			this.$emit('onRemoteUser', this.remoteUsers);
+		}
+	},
+	onRemoteUserLeaveRoom: (e)=> {
+		this.remoteUsers = this.remoteUsers.filter(v=>v!=e.detail);
+		this.$emit('onRemoteUser', this.remoteUsers);
+	}
+	
+	}
+};
+</script>
+
+<style scoped>
+
+.container-jhlive {
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  align-items: stretch;
+  background-color: black;
+  width: 100%;
+  height: 100%;
+}
+
+.liveView {
+  flex: 1;
+}
+
+.top_left_box {
+  left: 0;
+  top: 0;
+  margin-left: 10px;
+  margin-top: 10px;
+  position: absolute;
+  z-index: 100;
+  background-color:rgba(0, 0, 0, 0.4);
+  border-radius: 1000px;
+  display: flex;
+  flex-direction: row;
+  padding: 10px;
+}
+.top_left_box_img {
+  width: 80rpx;
+  height: 80rpx;
+  border-radius: 1000px;
+  background-color: white;
+}
+.top_left_box_text {
+  margin-left: 10px;
+  flex: 1;
+}
+.top_left_box_text1 {
+  font-size: 28rpx;
+  color: white;
+}
+.top_left_box_text2 {
+  font-size: 20rpx;
+  color: white;
+}
+
+
+.top-right-box {
+  right: 0;
+  top: 0;
+  margin-top: 10px;
+  margin-right: 10px;
+  position: absolute;
+  z-index: 100;
+  display: flex;
+  flex-direction: row;
+}
+.top-right-box-btn {
+	color: white;
+  margin-left: 5px;
+}
+.top-right-box-btn-text {
+  color: white;
+}
+
+
+.bottom-left-box {
+  left: 0;
+  bottom: 0;
+  margin-left: 10px;
+  margin-bottom: 10px;
+  position: absolute;
+  z-index: 100;
+}
+.bottom-left-box-login {
+  color: white;
+}
+.bottom-left-box-messages {
+
+}
+.bottom-left-box-message {
+
+}
+.bottom-left-box-input-box {
+
+}
+.bottom-left-box-input {
+  color: #fff;
+  font-size: 30rpx;
+}
+
+
+.bottom-right-box {
+  right: 0;
+  bottom: 0;
+  margin-bottom: 10px;
+  margin-right: 10px;
+  position: absolute;
+  z-index: 100;
+  display: flex;
+  flex-direction: row;
+}
+.bottom-right-box-btn {
+	color: white;
+  margin-left: 5px;
+}
+.bottom-right-box-btn-text {
+	color: white;
+}
+
+</style>

+ 163 - 255
jhlive/live-wechat.vue

@@ -8,7 +8,7 @@
           :data-streamid="item.streamID"
           :data-streamtype="item.streamType"
           :src="item.src"
-          mode="RTC"
+          :mode="item.mode"
           :autoplay="item.autoplay"
           :mute-audio="item.muteAudio"
           :mute-video="item.muteVideo"
@@ -29,7 +29,7 @@
           :idAttr="item.streamID" />
       </template>
     </view>
-    <view class="pusher-container" :style="'display:'+pusherStyle">
+    <view v-if="pusher.url" class="pusher-container" :style="'display:'+pusherStyle">
       <live-pusher
         class="pusher"
         :url="pusher.url"
@@ -70,11 +70,87 @@
 </template>
 
 <script>
+const LOGTAG = '--JHLIVE--:';
+const pusherStateCodeDic = {
+  1001: '已经连接推流服务器',
+  1002: '已经与服务器握手完毕,开始推流',
+  1003: '打开摄像头成功',
+  1004: '录屏启动成功',
+  1005: '推流动态调整分辨率',
+  1006: '推流动态调整码率',
+  1007: '首帧画面采集完成',
+  1008: '编码器启动',
+  1018: '进房成功',
+  1019: '退出房间',
+  2003: '渲染首帧视频',
+  1020: '远端用户全量列表更新',
+  1031: '远端用户进房通知',
+  1032: '远端用户退房通知',
+  1033: '用户视频状态变化,新增stream或者更新stream',
+  1034: '远端用户音频状态位变化通知',
+  '-1301': '打开摄像头失败',
+  '-1302': '打开麦克风失败',
+  '-1303': '视频编码失败',
+  '-1304': '音频编码失败',
+  '-1307': '推流连接断开',
+  '-100018': '进房失败',
+  '5000': '小程序被挂起',
+  '1021': '网络类型发生变化,需要重新进房',
+  '2007': '本地视频播放loading',
+  '2004': '本地视频播放开始',
+}
+
+const defaultStreamParams = {
+  streamType: '',
+  src: '',
+  mode: 'RTC',
+  autoplay: true, // 7.0.9 必须设置为true,否则 Android 有概率调用play()失败
+  muteAudio: false, // 默认不拉取音频,需要手动订阅
+  muteVideo: false, // 默认不拉取视频,需要手动订阅
+  orientation: 'vertical', // 画面方向 vertical horizontal
+  objectFit: 'fillCrop', // 填充模式,可选值有 contain,fillCrop
+  enableBackgroundMute: false, // 进入后台时是否静音(已废弃,默认退台静音)
+  minCache: 1, // 最小缓冲区,单位s(RTC 模式推荐 0.2s)
+  maxCache: 2, // 最大缓冲区,单位s(RTC 模式推荐 0.8s)
+  soundMode: 'speaker', // 声音输出方式 ear speaker
+  enableRecvMessage: 'false', // 是否接收SEI消息
+  autoPauseIfNavigate: true, // 当跳转到其它小程序页面时,是否自动暂停本页面的实时音视频播放
+  autoPauseIfOpenNative: true // 当跳转到其它微信原生页面时,是否自动暂停本页面的实时音视频播放
+}
+
 export default {
   name: 'jhlive-wechat',
 	data() {
 		return {
-      pusher: null,
+      pusher: {
+        url: '',
+        mode: 'RTC', // RTC:实时通话(trtc sdk) live:直播模式(liteav sdk)
+        autopush: false, // 自动推送
+        enableCamera: false, // 是否开启摄像头
+        enableMic: false, // 是否开启麦克风
+        enableAgc: false, // 是否开启音频自动增益
+        enableAns: false, // 是否开启音频噪声抑制
+        enableEarMonitor: false, // 是否开启耳返(目前只在iOS平台有效)
+        enableAutoFocus: true, // 是否自动对焦
+        enableZoom: false, // 是否支持调整焦距
+        minBitrate: 200, // 最小码率
+        maxBitrate: 1000, // 最大码率
+        videoWidth: 360, // 视频宽(若设置了视频宽高就会忽略aspect)
+        videoHeight: 640, // 视频高(若设置了视频宽高就会忽略aspect)
+        beautyLevel: 0, // 美颜,取值范围 0-9 ,0 表示关闭
+        whitenessLevel: 0, // 美白,取值范围 0-9 ,0 表示关闭
+        videoOrientation: 'vertical', // vertical horizontal
+        videoAspect: '9:16', // 宽高比,可选值有 3:4,9:16
+        frontCamera: 'front', // 前置或后置摄像头,可选值:front,back
+        enableRemoteMirror: false, // 设置推流画面是否镜像,产生的效果会表现在 live-player
+        localMirror: 'auto', // auto:前置摄像头镜像,后置摄像头不镜像(系统相机的表现)enable:前置摄像头和后置摄像头都镜像 disable: 前置摄像头和后置摄像头都不镜像
+        enableBackgroundMute: false, // 进入后台时是否静音
+        audioQuality: 'high', // 高音质(48KHz)或低音质(16KHz),可选值:high,low
+        audioVolumeType: 'voicecall', // 声音类型 可选值: media: 媒体音量,voicecall: 通话音量
+        audioReverbType: 0, // 音频混响类型 0: 关闭 1: KTV 2: 小房间 3:大会堂 4:低沉 5:洪亮 6:金属声 7:磁性
+        waitingImage: '', // 当微信切到后台时的垫片图片 trtc暂不支持
+        waitingImageHash: ''
+      },
       users: [],
 		};
 	},
@@ -89,13 +165,13 @@ export default {
   },
   computed: {
     streams() {
-      console.log('--users', this.users);
+      console.log(LOGTAG+'users', this.users);
       let streams = [];
       this.users.forEach(v=>{
         let userStreams = Object.entries(v.streams);
         streams = streams.concat(userStreams.map(([kk,vv])=>vv).filter(vv=>vv.hasVideo));
       });
-      console.log('--streams', streams);
+      console.log(LOGTAG+'streams', streams);
       return streams;
     },
     playerStyle() {
@@ -107,55 +183,15 @@ export default {
   },
 	methods: {
     enterRoom() {
-      this.pusher = {
-        url: 'room://cloud.tencent.com/rtc?sdkappid=' +
-        this.sdkAppID +
-        '&roomid=' +
-        this.roomid +
-        '&userid=' +
-        this.userid +
-        '&usersig=' +
-        this.userSig +
-        '&appscene=' +
-        'live' +
-        '&encsmall=' +
-        '1' +
-        '&cloudenv=' +
-        'PRO',
-        mode: 'RTC', // RTC:实时通话(trtc sdk) live:直播模式(liteav sdk)
-        autopush: this.isAuthor||this.linkMic, // 自动推送
-        enableCamera: this.isAuthor, // 是否开启摄像头
-        enableMic: this.isAuthor||this.linkMic, // 是否开启麦克风
-        enableAgc: false, // 是否开启音频自动增益
-        enableAns: false, // 是否开启音频噪声抑制
-        enableEarMonitor: false, // 是否开启耳返(目前只在iOS平台有效)
-        enableAutoFocus: true, // 是否自动对焦
-        enableZoom: false, // 是否支持调整焦距
-        minBitrate: 200, // 最小码率
-        maxBitrate: 1000, // 最大码率
-        videoWidth: 360, // 视频宽(若设置了视频宽高就会忽略aspect)
-        videoHeight: 640, // 视频高(若设置了视频宽高就会忽略aspect)
-        beautyLevel: 0, // 美颜,取值范围 0-9 ,0 表示关闭
-        whitenessLevel: 0, // 美白,取值范围 0-9 ,0 表示关闭
-        videoOrientation: 'vertical', // vertical horizontal
-        videoAspect: '9:16', // 宽高比,可选值有 3:4,9:16
-        frontCamera: 'front', // 前置或后置摄像头,可选值:front,back
-        enableRemoteMirror: false, // 设置推流画面是否镜像,产生的效果会表现在 live-player
-        localMirror: 'auto', // auto:前置摄像头镜像,后置摄像头不镜像(系统相机的表现)enable:前置摄像头和后置摄像头都镜像 disable: 前置摄像头和后置摄像头都不镜像
-        enableBackgroundMute: false, // 进入后台时是否静音
-        audioQuality: 'high', // 高音质(48KHz)或低音质(16KHz),可选值:high,low
-        audioVolumeType: 'voicecall', // 声音类型 可选值: media: 媒体音量,voicecall: 通话音量
-        audioReverbType: 0, // 音频混响类型 0: 关闭 1: KTV 2: 小房间 3:大会堂 4:低沉 5:洪亮 6:金属声 7:磁性
-        waitingImage: '', // 当微信切到后台时的垫片图片 trtc暂不支持
-        waitingImageHash: ''
-      };
-
-      console.log('JHLIVE-PUSHER CONFIG', this.pusher);
-      setTimeout(()=>{
+      this.pusher.url = `room://cloud.tencent.com/rtc?sdkappid=${this.sdkAppID}&roomid=${this.roomid}&userid=${this.userid}&usersig=${this.userSig}&appscene=live&encsmall=1&cloudenv=PRO`;
+      this.pusher.autopush = this.isAuthor||this.linkMic;
+      this.pusher.enableCamera = this.isAuthor;
+      this.pusher.enableMic = this.isAuthor||this.linkMic;
+      this.$nextTick(()=>{
         this.pusherContext = wx.createLivePusherContext();
         this.pusherContext.start();
-        console.log('JHLIVE-PUSHER pusherContext', this.pusherContext);
-      },1000);
+      })
+      console.log(LOGTAG+'pusher',this.pusher);
 		},
 		exitRoom: function() {
 			if (this.pusherContext) {
@@ -165,224 +201,96 @@ export default {
     },
     switchCarema() {
       this.pusher.frontCamera = this.pusher.frontCamera=='front'?'back':'front';
-      this.$forceUpdate();
+      console.log(LOGTAG+'pusher',this.pusher);
     },
     setBeauty(blv, wlv) {
       this.pusher.beautyLevel = blv;
       this.pusher.whitenessLevel = wlv;
-      this.$forceUpdate();
+      console.log(LOGTAG+'pusher',this.pusher);
     },
+
     /** --- pusher event handler --- **/
 		pusherStateChangeHandlerFun: function(event) {
 			const code = event.detail.code;
       const message = event.detail.message;
+      console.log(LOGTAG+'pusherState',code, pusherStateCodeDic[code]);
       
-			switch (code) {
-				case 0:
-					console.log(message, code);
-					break;
-
-				case 1001:
-					console.log('已经连接推流服务器', code);
-					break;
-
-				case 1002:
-					console.log('已经与服务器握手完毕,开始推流', code);
-					break;
-
-				case 1003:
-					console.log('打开摄像头成功', code);
-					break;
-
-				case 1004:
-					console.log('录屏启动成功', code);
-					break;
-
-				case 1005:
-					console.log('推流动态调整分辨率', code);
-					break;
-
-				case 1006:
-					console.log('推流动态调整码率', code);
-					break;
-
-				case 1007:
-					console.log('首帧画面采集完成', code);
-					break;
-
-				case 1008:
-					console.log('编码器启动', code);
-					break;
-
-				case 1018:
-					console.log('进房成功', code);
-					break;
-
-				case 1019:
-					console.log('退出房间', code);
-					break;
-
-				case 2003:
-					console.log('渲染首帧视频', code);
-					break;
-
-				case 1020: // 远端用户全量列表更新
-          break;
-        case 1031: {// 远端用户进房通知
-          const data = JSON.parse(event.detail.message)||{};
-          console.log('远端用户进房通知', data);
-          (data.userlist||[]).forEach( v=> {
-            if(!this.users.find(vv => vv.userid == v.userid)) {
-              this.users.push({
-                userid: v.userid,
-                streams: {},
-              });
-            }
-          })
-        }break;
-        case 1032: {// 远端用户退房通知
-          console.log('远端用户退房通知', data);
-          const data = JSON.parse(event.detail.message)||{};
-          (data.userlist||[]).forEach( v=> {
-            let user = this.users.find(v => v.userid == data.userid);
-            if(!user) return;
-            user.streams.forEach(vv=>{
-              if(vv.playerContext) {
-                vv.playerContext.stop;
-                vv.playerContext = null;
-              }
+			// 远端用户进房通知
+      if(code == 1031) {
+        const data = JSON.parse(event.detail.message)||{};
+        (data.userlist||[]).forEach( v=> {
+          if(!this.users.find(vv => vv.userid == v.userid)) {
+            this.users.push({
+              userid: v.userid,
+              streams: {},
             });
-            user.streams = [];
-          })
-          this.users = this.users.filter(v => (data.userlist||[]).find(vv=>vv.userid!=v.userid));
-        }break;
-        case 1033: {// 用户视频状态变化,新增stream或者更新stream 状态
-          console.log('用户视频状态变化,新增stream或者更新stream', data);
-          const data = JSON.parse(event.detail.message)||{};
-          (data.userlist||[]).forEach( v=> {
-            let user = this.users.find(vv => v.userid == v.userid);
-            if(!user) return;
-              let stream = user.streams[v.streamtype] = user.streams[v.streamtype] || {
-              streamType: v.streamtype,
-              src: '',
-              mode: 'RTC',
-              autoplay: true, // 7.0.9 必须设置为true,否则 Android 有概率调用play()失败
-              muteAudio: false, // 默认不拉取音频,需要手动订阅
-              muteVideo: false, // 默认不拉取视频,需要手动订阅
-              orientation: 'vertical', // 画面方向 vertical horizontal
-              objectFit: 'fillCrop', // 填充模式,可选值有 contain,fillCrop
-              enableBackgroundMute: false, // 进入后台时是否静音(已废弃,默认退台静音)
-              minCache: 1, // 最小缓冲区,单位s(RTC 模式推荐 0.2s)
-              maxCache: 2, // 最大缓冲区,单位s(RTC 模式推荐 0.8s)
-              soundMode: 'speaker', // 声音输出方式 ear speaker
-              enableRecvMessage: 'false', // 是否接收SEI消息
-              autoPauseIfNavigate: true, // 当跳转到其它小程序页面时,是否自动暂停本页面的实时音视频播放
-              autoPauseIfOpenNative: true // 当跳转到其它微信原生页面时,是否自动暂停本页面的实时音视频播放
-            }
-
-            stream.userID = v.userid;
-            stream.streamID = v.userid + '_' + stream.streamType;
-            stream.hasVideo = v.hasvideo;
-            stream[`has${stream.streamType}Video`] = stream.hasVideo;
-            stream.src = v.playurl;
-            // stream.objectFit = stream.hasVideo && stream.streamType === 'aux' && 'contain';
+            this.$emit('liveEvent', {detail:{eventName: 'onRemoteUserEnterRoom', eventData: {userId: v.userid}}});
+          }
+        })
+      }
 
-            if(stream.hasVideo) stream.playerContext = wx.createLivePlayerContext(stream.streamID, this);
-          })
-          this.users = [...this.users];
-        }break;
-        case 1034: {// 远端用户音频状态位变化通知
-          console.log('远端用户音频状态位变化通知', data);
-          const data = JSON.parse(event.detail.message)||{};
-          (data.userlist||[]).forEach( v=> {
-            let user = this.users.find(vv => v.userid == vv.userid);
-            if(!user) return;
-            
-            v.streamtype = 'main';
-            let stream = user.streams[v.streamtype] = user.streams[v.streamtype] || {
-              streamType: v.streamtype,
-              src: '',
-              mode: 'RTC',
-              autoplay: true, // 7.0.9 必须设置为true,否则 Android 有概率调用play()失败
-              muteAudio: false, // 默认不拉取音频,需要手动订阅
-              muteVideo: false, // 默认不拉取视频,需要手动订阅
-              orientation: 'vertical', // 画面方向 vertical horizontal
-              objectFit: 'fillCrop', // 填充模式,可选值有 contain,fillCrop
-              enableBackgroundMute: false, // 进入后台时是否静音(已废弃,默认退台静音)
-              minCache: 1, // 最小缓冲区,单位s(RTC 模式推荐 0.2s)
-              maxCache: 2, // 最大缓冲区,单位s(RTC 模式推荐 0.8s)
-              soundMode: 'speaker', // 声音输出方式 ear speaker
-              enableRecvMessage: 'false', // 是否接收SEI消息
-              autoPauseIfNavigate: true, // 当跳转到其它小程序页面时,是否自动暂停本页面的实时音视频播放
-              autoPauseIfOpenNative: true // 当跳转到其它微信原生页面时,是否自动暂停本页面的实时音视频播放
+      // 远端用户退房通知
+      if(code == 1032) {
+        const data = JSON.parse(event.detail.message)||{};
+        (data.userlist||[]).forEach( v=> {
+          let user = this.users.find(v => v.userid == data.userid);
+          if(!user) return;
+          user.streams.forEach(vv=>{
+            if(vv.playerContext) {
+              vv.playerContext.stop;
+              vv.playerContext = null;
             }
+          });
+          user.streams = [];
+          this.$emit('liveEvent', {detail:{eventName: 'onRemoteUserLeaveRoom', eventData: {userId: v.userid}}});
+        })
+        this.users = this.users.filter(v => (data.userlist||[]).find(vv=>vv.userid!=v.userid));
+      }
 
-            stream.userID = v.userid;
-            stream.streamID = v.userid + '_' + stream.streamType;
-            stream.hasAudio = v.hasaudio;
-            stream[`has${stream.streamType}Audio`] = stream.hasAudio;
-            stream.src = v.playurl;
-
-            if(stream.hasAudio) stream.playerContext = wx.createLivePlayerContext(stream.streamID, this);
-          })
-          this.users = [...this.users];
-        }break;
-				case -1301:
-					console.error('打开摄像头失败: ', code);
-					break;
-
-				case -1302:
-					console.error('打开麦克风失败: ', code);
-					break;
-
-				case -1303:
-					console.error('视频编码失败: ', code);
-					break;
-
-				case -1304:
-					console.error('音频编码失败: ', code);
-					break;
-
-				case -1307:
-					console.error('推流连接断开: ', code);
-					break;
-
-				case -100018:
-					console.error('进房失败: ', code, message);
-					break;
-
-				case 5000:
-					console.log('小程序被挂起: ', code); 
-          this.exitRoom()
-          this.enterRoom()
-					break;
-
-				case 1021:
-					console.log('网络类型发生变化,需要重新进房', code);
-					break;
-
-				case 2007:
-					console.log('本地视频播放loading: ', code);
-					break;
-
-				case 2004:
-					console.log('本地视频播放开始: ', code);
-					break;
-
-				default:
-					console.log(message, code);
-			}
+      // 用户视频状态变化,新增stream或者更新stream 状态
+      if(code == 1033) {
+        const data = JSON.parse(event.detail.message)||{};
+        (data.userlist||[]).forEach( v=> {
+          let user = this.users.find(vv => v.userid == v.userid);
+          if(!user) return;
+          let stream = user.streams[v.streamtype] = user.streams[v.streamtype] || {...defaultStreamParams};
+          stream.streamtype = v.streamtype;
+          stream.userID = v.userid;
+          stream.streamID = v.userid + '_' + stream.streamType;
+          stream.hasVideo = v.hasvideo;
+          stream[`has${stream.streamType}Video`] = stream.hasVideo;
+          stream.src = v.playurl;
+          if(stream.hasVideo) stream.playerContext = wx.createLivePlayerContext(stream.streamID, this);
+        })
+        this.users = [...this.users];
+      }
+        
+      // 远端用户音频状态位变化通知
+      if(code == 1034) {
+        console.log('远端用户音频状态位变化通知', data);
+        const data = JSON.parse(event.detail.message)||{};
+        (data.userlist||[]).forEach( v=> {
+          let user = this.users.find(vv => v.userid == vv.userid);
+          if(!user) return;
+          v.streamtype = 'main';
+          let stream = user.streams[v.streamtype] = user.streams[v.streamtype] || {...defaultStreamParams}
+          stream.streamtype = v.streamtype;
+          stream.userID = v.userid;
+          stream.streamID = v.userid + '_' + stream.streamType;
+          stream.hasAudio = v.hasaudio;
+          stream[`has${stream.streamType}Audio`] = stream.hasAudio;
+          stream.src = v.playurl;
+          if(stream.hasAudio) stream.playerContext = wx.createLivePlayerContext(stream.streamID, this);
+        })
+        this.users = [...this.users];
+      }
 		},
 		pusherNetStatusHandlerFun: function(event) {
 			
 		},
 		pusherErrorHandlerFun: function(event) {
-			try {
-        console.error('pusher error: ');
-				const code = event.detail.errCode;
-				const message = event.detail.errMsg;
-        console.error(code, message);
-			} catch (exception) {}
+      this.$emit('liveEvent', {detail:{eventName: 'onError', eventData: {msg: '推送错误'}}});
+      console.error(LOGTAG+'pusher error: ',event.detail);
 		},
 		pusherBGMStartHandlerFun: function(event) {
 			
@@ -396,13 +304,13 @@ export default {
     
     /** --- player event handler --- **/
 		playerStateChangeFun: function(event) {
-      console.log('playerStateChangeFun');
+      console.log(LOGTAG+'playerStateChangeFun');
 		},
 		playerFullscreenChangeFun: function(event) {
-      console.log('playerFullscreenChangeFun');
+      console.log(LOGTAG+'playerFullscreenChangeFun');
 		},
 		playerNetStatusFun: function(event) {
-      console.log('playerNetStatusFun');
+      console.log(LOGTAG+'playerNetStatusFun');
       let dataset = event.currentTarget.dataset;
       let info = event.detail.info;
       let user = this.users.find(v => v.userid == dataset.userid);
@@ -413,7 +321,7 @@ export default {
       stream.videoHeight = info.videoHeight;
 		},
 		playerAudioVolumeNotifyFun: function(event) {
-      console.log('playerAudioVolumeNotifyFun');
+      console.log(LOGTAG+'playerAudioVolumeNotifyFun');
 		},
 	}
 };