Vue 集成RTRC实时音视频Web端以及实现Web端点击某个视频实现放大功能

RTRC实时音视频Web端

Vue 接入腾讯云【实时音视频】TRTC

vue接入腾讯实时音视频trtc-js-sdk(web版)

comp-rom.vue
<!--
 * @Description: 房间显示
 * @Date: 2022-03-16 17:40:28
 * @LastEditTime: 2022-03-29 16:13:06
-->
<template>
  <div>
    <el-row>
      <div hidden>
        <el-table :data="userLists" v-loading="userLoading" style="width: 100%;" max-height="70%">
          <el-table-column prop="userAudioId" label="用户名"></el-table-column>
          <el-table-column label="操作" align="center">
            <template slot-scope="scope">
              <div v-if="userAudioId != scope.row.userAudioId">
                <el-button
                  v-if="scope.row.isAdmin == '0'"
                  size="small"
                  type="danger"
                  icon="el-icon-delete"
                  @click="goLeave(scope.row)"
                >踢出房间</el-button>
              </div>
            </template>
          </el-table-column>
        </el-table>
        <!--日志展示区-->
        <div class="info-container" style="height: 30%" hidden>
          <div style=" height: 13vh;overflow-x: hidden;">
            <!-- Log 展示区域 -->
            <div v-if="isHostMode" class="log-container" ref="logContainer">
              <p class="log-label">Log:</p>
              <div v-for="(item, index) in logList" :key="index">
                <span class="log-state" v-if="item.type === 'success'">🟩</span>
                <span class="log-state" v-if="item.type === 'failed'">🟥</span>
                <span>{{ item.log }}</span>
              </div>
            </div>
          </div>
        </div>
      </div>

      <el-col :span="12">
        <!-- 远端流区域 -->
        <el-card class="box-card">
          <div class="video-card" style="height:500px;">
            <!-- 放大视频 -->
            <div v-if="switchStream">
              <div id="switchStream" style="width: 650px; height: 488px" @click="reductionVideo()"></div>

              <div>选中用户:{{ switchStream.getUserId() }}</div>
              <el-button
                size="small"
                type="danger"
                icon="el-icon-close"
                v-if="emergencyAudioDTO.isAdmin == '0'"
                @click="goLeave(switchStream)"
              ></el-button>
              <el-button
                :type="obj[switchStream.getUserId()+0] ? 'danger' : ''"
                icon="el-icon-video-camera"
                @click="otherMuteVideos(switchStream.getUserId()+0, switchStream)"
              ></el-button>
              <el-button
                :type="obj[switchStream.getUserId()+1] ? 'danger' : ''"
                icon="el-icon-turn-off-microphone"
                @click="otherMuteAudios(switchStream.getUserId()+1, switchStream)"
              ></el-button>
            </div>
          </div>
        </el-card>
      </el-col>
      <el-col :span="12">
        <el-card class="box-card">
          <div>
            <div id="mapDivContainer" style="width: 100%; height:500px"></div>
          </div>
          <div hidden>
            <mapview ref="mapView"></mapview>
          </div>
        </el-card>
      </el-col>
    </el-row>
    <!-- 进房操作区域 -->
    <el-row>
      <div style="margin-top: 5px;">
        <el-card class="box-card" style="display: flex;">
          <div style="margin:15px 4px;">
            <!-- 本地流区域 自己 -->
            <div v-if="localStream">
              <!-- 本地流播放区域 -->
              <div id="localStream" style="width: 300px; height: 230px" @click="switchVideo(localStream)"></div>
              <div>当前用户:{{ userAudioId }}</div>
              <!-- 本地流操作栏 -->
              <div v-if="isPlayingLocalStream">
                <div>
                  <el-button icon="el-icon-video-camera" v-if="!isMutedVideo" @click="muteVideo"></el-button>
                  <el-button type="danger" icon="el-icon-video-camera" v-if="isMutedVideo" @click="unmuteVideo"></el-button>
                  <el-button icon="el-icon-microphone" v-if="!isMutedAudio" @click="muteAudio"></el-button>
                  <el-button type="danger" icon="el-icon-turn-off-microphone" v-if="isMutedAudio" @click="unmuteAudio"></el-button>
                </div>
              </div>
            </div>
            <div v-else style="width: 300px; height: 230px; border: 1px solid #eee; text-align: center;">
              <img
                class="imgErrors"
                src="@/assets/images/video-erro.png"
                alt="dark"
                title="网络原因,当前可能无法正常视频通话,请检查您的通信协议或摄像头"
              />
              <div>当前用户:【{{userAudioId}}】</div>
            </div>
          </div>

          <!-- 远端流区域 -->
          <div v-if="remoteStreamList.length > 0" style="display: flex;">
            <!-- 远端流区域 -->
            <div style="margin:15px 4px;" v-for="(item, index) in remoteStreamList" :key="index">
              <div
                :id="item.getUserId()"
                :key="item.getUserId()"
                class="userDiv"
                style="width: 300px; height: 230px"
                @click="switchVideo(item)"
              ></div>
              <!--                  v-if="emergencyAudioDTO.isAdmin == '0'"-->
              <b v-for="items in allUserInfo" :key="items.userid">
                <span v-if="items.userid == item.getUserId()">{{items.username}}</span>
              </b>
              <div>用户: {{ item.getUserId() }}</div>
              <el-button
                size="small"
                type="danger"
                icon="el-icon-close"
                v-if="emergencyAudioDTO.isAdmin == '0'"
                @click="goLeave(item)"
              ></el-button>
              <!--                <el-button icon="el-icon-video-camera" v-if="!otherMutedVideo" @click="otherMuteVideos(item,'open')"></el-button>-->
              <el-button
                :type="obj[item.getUserId()+0] ? 'danger' : ''"
                icon="el-icon-video-camera"
                @click="otherMuteVideos(item.getUserId()+0,item)"
              ></el-button>
              <!--                <el-button icon="el-icon-microphone" v-if="!otherMutedAudio" @click="otherMuteAudios(item,'open')"></el-button>-->
              <el-button
                :type="obj[item.getUserId()+1] ? 'danger' : ''"
                icon="el-icon-turn-off-microphone"
                @click="otherMuteAudios(item.getUserId()+1,item)"
              ></el-button>
            </div>
          </div>
        </el-card>
      </div>
    </el-row>
  </div>
</template>

<script>
import Mapview from '@/components/comp-map'
// export default {
//   components: { Mapview }
// }
</script>
<script>

var map, mapTc, mapTc1;
import rtc from './mixins/rtc.js';
import shareRtc from './mixins/share-rtc.js';
import LibGenerateTestUserSig from '@/utils/lib-generate-test-usersig.min.js';
import screenfull from 'screenfull'
import { listAudioRecive, joinRoom, kickOutRoom, deleteAudioRecive, getUserInfo, upOnlineUser } from "@/api/web/bigScreen/emergencyCommandScreen/emergencyCommandScreen";

import Mapview from '../components/comp-map';
import TRTC from 'trtc-js-sdk';

var markList = [];
export default {
  name: 'compRoom',
  mixins: [rtc, shareRtc],
  props: {
    type: String,
    sdkAppId: Number,
    secretKey: String,
    userId: String,
    userAudioId: String,
    roomId: Number,
    cameraId: String,
    microphoneId: String,
    inviteUserSig: String,
  },
  components: { Mapview },
  data() {
    return {
      fullscreen: false,
      list: [
        { key: "第一个盒子" },
        { key: "第一个盒子" },
        { key: "第一个盒子" },
        { key: "第一个盒子" },
        { key: "第一个盒子" },
        { key: "第一个盒子" }
      ],
      otherMutedVideo: false,
      streamList: [],
      otherMutedAudio: false,
      logList: [],
      userIds: [],
      inviteLink: '',
      showCopiedTip: false,
      userLists: [],
      userLoading: false,
      lng: 0,
      lat: 0,
      map: null,
      divWidth: 90,
      divHeight: 90,
      changeSizeStatus: true,
      timers1: null,
      timers2: null,
      timers3: null,
      longitude: 0,
      latitude: 0,
      allUserInfo: [],
      emergencyAudioDTO: {},
      switchStream: null,
      currentID: '',
      isLocalStream: true,
    };
  },
  // 销毁组件前清除定时器
  beforeDestroy() {
    clearInterval(this.timers1);
    this.timers1 = null;
    clearInterval(this.timers2);
    this.timers2 = null;
  },
  computed: {
    isHostMode() {
      return this.type !== 'invite';
    },
    isEnLang() {
      // return this.$i18n.locale === 'en';
    },
    showInviteLink() {
      return this.isHostMode && (this.isJoined || this.isShareJoined) && this.inviteLink;
    },
  },
  mounted() {
    window.addEventListener('beforeunload', e => this.beforeunloadHandler(e))
    this.handleJoinRoom()
    // this.$nextTick(()=>{
    //   this.initAMap()
    // })
  },
  updated() {
    // this.handleJoinRoom()
  },
  watch: {
    cameraId(val) {
      this.switchDevice('video', val);
    },
    microphoneId(val) {
      this.switchDevice('audio', val);
    },

    roomId(newVal) {
      // debugger
      this.roomId = newVal
      // console.log("new监听"+newVal)
      // this.handleJoinRoom()
    },
    userAudioId(newVal) {
      // debugger
      this.userAudioId = newVal
      console.log("new监听" + newVal)
      this.handleJoinRoom()
      // this.$nextTick(()=>{
      //   this.initAMap()
      // })
    },
    latitude(newVal) {
      // alert(newVal)
      this.joinRooms()
    },
    remoteStreamList(val) {
      // console.log(val, '远端用户');
      this.upOnlineUser(val)
    },
    leaveUserId(val) {
      if (this.currentID = val) {
        if (this.switchStream) {
          document.getElementById('switchStream').innerHTML = ""
          this.switchStream = null;
          this.currentID = null;
          // debugger
        }
      }
    }
  },
  destroyed() {
    window.removeEventListener('beforeunload', e => this.beforeunloadHandler(e))
  },
  methods: {
    // 页面监听浏览器刷新和非正常关闭,当非正常退出用户时,删除房间信息
    beforeunloadHandler(e) {
      var query = {}
      query.userId = this.userId
      query.roomId = this.roomId
      query.userAudioId = this.userAudioId
      deleteAudioRecive(query)

      // debugger
      // window.confirm('11111刷新後当前页面的数据會丟失,确定要刷新?')
      // e = e || window.event
      // if (e) {
      //   e.returnValue = '关闭提示2'
      // }
      // return '关闭提示23'
    },
    // 销毁组件前清除定时器
    clearTimes() {
      clearInterval(this.timers1);
      this.timers1 = null;
      clearInterval(this.timers2);
      this.timers2 = null;
      clearInterval(this.timers3);
      this.timers3 = null;
    },
    initAMap() {
      var marker = {};
      var zoom = 12;
      var lon = 112.544772;
      var lat = 30.678429;
      if (this.map == null) {
        this.map = new T.Map('mapDivContainer');
        this.map.centerAndZoom(new T.LngLat(lon, lat), zoom);
      }
      var lays = this.map.getOverlays();//获取地图上所有的覆盖物
      // var s = 0;
      for (var i = lays.length - 1; i >= 0; i--) { //倒序删除避免长度发生变化
        if (lays[i].Type = 1000) {   //根据地图上覆盖物的类型删除
          this.map.removeOverLay(lays[i]); //从地图上移除。
          //        s++;
        }
      }
      //创建标注对象
      for (let i = 0; i < this.userLists.length; i++) {
        const mapElement = this.userLists[i];
        marker = new T.Marker(new T.LngLat(mapElement.longitude, mapElement.latitude));
        // 只定位到自己
        if (mapElement.userAudioId === this.userAudioId) {
          if (mapElement.longitude !== 0 && mapElement.latitude !== 0) {
            this.map.centerAndZoom(new T.LngLat(mapElement.longitude, mapElement.latitude));
            //向地图上添加标注
            this.map.addOverLay(marker);
          }
        } else {
          this.map.addOverLay(marker);
        }
      }
      this.timers1 = setTimeout(() => {
        console.log("1111111111111")
        this.initAMap()
      }, 1500);

    },
    changeSize() {
      this.changeSizeStatus = !this.changeSizeStatus
      var changeSizeStatus = !this.changeSizeStatus
      if (changeSizeStatus) {
        this.divWidth = this.divWidth + 20
        this.divHeight = this.divHeight + 20
      } else {
        this.divWidth = this.divWidth - 20
        this.divHeight = this.divHeight - 20
      }
    },
    // initMap(){
    //   this.map = new AMap.Map("map1", {
    //     WebGLParams: {
    //       preserveDrawingBuffer: true
    //     },
    //     center: [105.602725, 37.076636],
    //     zoomEnable: true, //是否禁止缩放
    //     zoom: 18, //缩放比例
    //     dragEnable: true,//禁止拖动
    //     cursor: 'hand' // 鼠标在地图上的表现形式,小手
    //   })
    // },
    generateInviteLink() {
      if (!this.isHostMode) {
        return;
      }
      const { sdkAppId, secretKey, roomId } = this;
      const inviteUserId = `user_${parseInt(Math.random() * 100000000, 10)}`;
      const userSigGenerator = new LibGenerateTestUserSig(sdkAppId, secretKey, 604800);
      const inviteUserSig = userSigGenerator.genTestUserSig(inviteUserId);
      this.inviteLink = encodeURI(`${location.origin}${location.pathname}#/invite?sdkAppId=${sdkAppId}&userSig=${inviteUserSig}&roomId=${roomId}&userId=${inviteUserId}`);
    },
    handleCopyInviteLink() {
      navigator.clipboard.writeText(this.inviteLink);
      this.showCopiedTip = true;
      setTimeout(() => {
        this.showCopiedTip = false;
      }, 1500);
      this.generateInviteLink();
    },
    // 点击【Join Room】按钮
    async handleJoinRoom() {
      // var that = this
      // that.userId = userId
      // that.roomId = roomId
      // debugger
      console.log("子组件传值1" + this.userId)
      console.log("子组件传值2" + this.roomId)
      if (this.isHostMode) {
        if (!this.sdkAppId || !this.secretKey) {
          alert('Please enter sdkAppId and secretKey');
          return;
        }
        if (!this.userId || !this.roomId) {
          alert('Please enter userId and roomId');
          return;
        }
        const userSigGenerator = new LibGenerateTestUserSig(this.sdkAppId, this.secretKey, 604800);
        this.userSig = userSigGenerator.genTestUserSig(this.userId);
      } else {
        if (!this.sdkAppId || !this.inviteUserSig || !this.userId || !this.roomId) {
          alert('Please reacquire the invitation link');
          return;
        }
        this.userSig = this.inviteUserSig;
      }
      // 将加入的用户写入到表里面
      await this.joinRoom();
      await this.initClient();
      await this.join();
      await this.initLocalStream();
      await this.playLocalStream();
      await this.publish();
      this.generateInviteLink();

      // 监听踢出房间事件
      this.client.on('client-banned', async (event) => {
        alert("很抱歉,您当前被主持人踢出房间了")

        this.isPublished = false;
        this.localStream = null;

        // 调用自定义事件,传入参数
        this.$emit('joinRoomVisible', false)
        await this.leave();
      });

    },
    // 获取当时定位
    getLocation() {
      var that = this
      this.timers2 = setTimeout(() => {
        console.log("2222222222222222222222222222222222222")
        that.getLocation()
      }, 5000)
      // debugger
      AMap.plugin('AMap.Geolocation', function () {
        var geolocation = new AMap.Geolocation({
          enableHighAccuracy: true,//是否使用高精度定位,默认:true
          timeout: 10000,          //超过10秒后停止定位,默认:5s getLat()
          buttonPosition: 'RB',    //定位按钮的停靠位置
          buttonOffset: new AMap.Pixel(10, 20),//定位按钮与设置的停靠位置的偏移量,默认:Pixel(10, 20)
          zoomToAccuracy: true,   //定位成功后是否自动调整地图视野到定位点

        });
        geolocation.getCurrentPosition(function (status, result) {
          // debugger
          if (status === 'complete') {
            this.lng = result.position.getLng();
            this.lat = result.position.getLat();
            var query = {}
            query.userId = that.userId
            query.roomId = that.roomId
            query.longitude = this.lng
            that.longitude = this.lng
            that.latitude = this.lat
            query.latitude = this.lat
            query.userAudioId = that.userAudioId
            // // 加入登入用户进入房间信息
            // if(that.timers2 != null){
            //   joinRoom(query).then(response => {
            //     // 查询用户信息
            //     // that.initUserInfo();
            //     // that.timers2 = null
            //   });
            // }

          } else {
            // 查询用户信息
            // that.initUserInfo();
          }
        });
      });
    },
    async joinRooms() {
      var that = this
      // debugger
      var query = {}
      query.userId = that.userId
      query.roomId = that.roomId
      query.latitude = that.latitude
      query.longitude = that.longitude
      query.userAudioId = that.userAudioId
      console.log(query)
      // 加入登入用户进入房间信息
      joinRoom(query).then(response => {
      });
    },
    async joinRoom() {

      var query = {}
      query.userId = this.userId
      query.roomId = this.roomId
      query.latitude = this.lng
      query.longitude = this.lat
      query.userAudioId = this.userAudioId
      // 加入登入用户进入房间信息
      var that = this
      joinRoom(query).then(response => {
        // 查询用户信息
        this.initUserInfo();
        this.getUserInfo();
        // this.upOnlineUser();
        this.$nextTick(() => {
          this.getLocation()
          // debugger
          // this.initAMap()
        })
        this.initAMap()
        // this.$nextTick(()=>{
        //   this.getLocation()
        // })
      });
    },

    // 用户信息
    initUserInfo() {

      var that = this
      var query = {}
      query.roomId = this.roomId
      that.userLoading = true;

      //需要定时执行的代码
      listAudioRecive(query).then(res => {
        that.userLists = res.data.emergencyAudioReciveList
        that.userLoading = false;
        this.timers3 = setTimeout(() => {
          this.initUserInfo()
        }, 5500);
      });
    },
    // 关闭其他人视频
    otherMuteVideos(index, item) {
      this.obj[index] = !this.obj[index]
      this.obj = Object.assign({}, { ...this.obj })
      if (this.obj[index]) {
        item.muteVideo();
      } else {
        item.unmuteVideo();
      }
    },
    // 关闭其他人语音
    otherMuteAudios(index, item) {
      this.obj[index] = !this.obj[index]
      this.obj = Object.assign({}, { ...this.obj })
      if (this.obj[index]) {
        item.muteAudio();
      } else {
        item.unmuteAudio();
      }
    },
    // 浏览器刷新和关闭未正常退出事件
    /** 在刷新和关闭之前询问 **/
    beforeRefreshClose() {
      let self = this;
      window.onbeforeunload = function (e) {
        if (self.$route.name == "路由名称") {
          e = e || window.event;
          // 兼容IE8和Firefox 4之前的版本
          if (e) e.returnValue = "关闭提示1";
          // Chrome, Safari, Firefox 4+, Opera 12+ , IE 9+
          return "关闭提示2";
        } else window.onbeforeunload = null;
        this.handleLeave()
      };
    },
    // 点击【Publish】按钮
    async handlePublish() {
      await this.publish();
    },

    // 点击【Unpublish】按钮
    async handleUnpublish() {
      await this.unPublish();
    },
    // 查询全部用户
    getUserInfo() {
      var query = {}
      query.roomId = this.roomId
      query.userId = this.$store.getters.userId + ""
      getUserInfo(query).then(res => {
        this.allUserInfo = res.data.userInfo
        this.emergencyAudioDTO = res.data.emergencyAudioDTO
      });
    },
    // 剔除不活跃的离线用户
    upOnlineUser(remoteStreamList) {
      var query = {}
      query.roomId = this.roomId
      query.userIds = []
      remoteStreamList.forEach((item, index) => {
        this.userIds.push(item.userId_)
      });
      debugger
      upOnlineUser(query).then(res => {

      });
    },
    goLeave(row) {
      // alert(row)
      var query = {}
      query.userId = row.userId_
      query.roomId = this.roomId
      // query.userAudioId = row.userAudioId
      kickOutRoom(query)
    },
    // 点击【Leave Room】按钮
    async handleLeave() {
      // debugger
      // 将当前用户进入房间信息删除
      var query = {}
      query.userId = this.userId
      query.roomId = this.roomId
      query.userAudioId = this.userAudioId
      deleteAudioRecive(query)
      await this.leave();
    },

    // 点击【开始屏幕分享】按钮
    async handleStartScreenShare() {
      if (!this.sdkAppId || !this.secretKey) {
        alert('Please enter sdkAppId and secretKey');
        return;
      }
      await this.initShareClient();
      await this.initShareLocalStream();
      await this.handleShareJoin();
      await this.handleSharePublish();
      this.generateInviteLink();
    },

    // 点击【停止屏幕分享按钮】
    async handleStopScreenShare() {
      await this.handleShareUnpublish();
      await this.handleShareLeave();
    },

    // 显示成功的 Log
    addSuccessLog(log) {
      if (!this.isHostMode) {
        return;
      }
      this.logList.push({
        type: 'success',
        log,
      });
      const { scrollHeight } = this.$refs.logContainer;
      this.$refs.logContainer.scrollTop = scrollHeight;
    },

    // 显示失败的 Log
    addFailedLog(log) {
      if (!this.isHostMode) {
        return;
      }
      this.logList.push({
        type: 'failed',
        log,
      });
      const { scrollHeight } = this.$refs.logContainer;
      this.$refs.logContainer.scrollTop = scrollHeight;
    },
    reportSuccessEvent(name) {
      // this.$aegis.reportEvent({
      //   name,
      //   ext1: 'joinRoom-success',
      //   ext2: 'webrtcQuickDemoVue2',
      //   ext3: 1400763604,
      // });
    },
    reportFailedEvent(name, error, type = 'rtc') {
      // debugger
      // this.$aegis.reportEvent({
      //   name,
      //   ext1: `${name}-failed#${this.roomId}*${type === 'share' ? this.shareUserId : this.userId}*${error.message}`,
      //   ext2: 'webrtcQuickDemoVue2',
      //   ext3: this.sdkAppId,
      // });
    },
    // 放大视频(切换视频)
    async switchVideo(item) {
      console.log(item, '当前点击');
      // this.switchStream = item;
      this.currentID = item.getUserId();
      // this.isLocalStream = false;

      // 检测您当前的浏览器是否支持从 video 元素采集 stream
      const isVideoCapturingSupported = () => 'captureStream' in HTMLVideoElement.prototype;
      // 检测您当前的浏览器是否支持从 video 元素采集 stream
      if (!isVideoCapturingSupported()) {
        console.log('your browser does not support capturing stream from video element');
        return
      }

      if (this.switchStream) {
        document.getElementById('switchStream').innerHTML = "";
        this.switchStream = null;
      }

      // 获取您页面在播放视频的 video 标签 
      // const video = document.getElementById(item.getUserId()).querySelector('div').querySelector('video');
      const video = document.getElementById('video_' + item.id_);
      console.log(video, 'video');
      // 从播放的视频采集视频流
      const stream = video.captureStream();
      const audioTrack = stream.getAudioTracks()[0];
      const videoTrack = stream.getVideoTracks()[0];
      this.switchStream = TRTC.createStream({ userId: item.getUserId(), audioSource: audioTrack, videoSource: videoTrack });
      // 请确保视频属性跟外部传进来的视频源一致,否则会影响视频通话体验
      this.switchStream.setVideoProfile('480p');
      await this.switchStream.initialize();
      this.switchStream.play('switchStream', { objectFit: 'contain' }).catch();

    },
    // 还原视频
    reductionVideo() {
      if (this.switchStream) {
        document.getElementById('switchStream').innerHTML = "";
        this.switchStream = null;
        this.currentID = null;
      }
    }
  },
};
</script>

<style lang="scss" scoped>
.video-card {
  display: flex;
  flex-wrap: nowrap;
  justify-content: center;
  align-content: center;
  align-items: center;
}
.rtc-container {
  .label {
    margin: 14px 0 6px;
    text-align: left;
    font-weight: bold;
  }

  .control-container {
    text-align: left;
    margin-bottom: 10px;
    div:not(:nth-last-child(1)) {
      margin-bottom: 10px;
    }
    .button:not(:first-child) {
      margin-left: 2px;
    }
  }

  .invite-link-container {
    width: 100%;
    color: #084298;
    background-color: #cfe2ff;
    position: relative;
    padding: 10px 16px;
    margin-bottom: 16px;
    border: 1px solid #b6d4fe;
    border-radius: 0.25rem;
    .invite-input {
      margin-top: 10px;
    }
    .invite-btn {
      display: flex;
      cursor: pointer;
    }
  }

  .info-container {
    width: 100%;
    display: flex;
    justify-content: space-between;
    .log-container {
      flex-grow: 1;
      border: 1px solid #dddddd;
      height: 360px;
      padding: 10px;
      margin-right: 16px;
      overflow-y: scroll;
      .log-label {
        margin: 0 0 6px;
        font-weight: bold;
      }
      .log-state {
        display: inline-block;
        margin-right: 6px;
      }
      > div {
        font-size: 12px;
      }
    }
    .local-stream-container {
      width: 480px;
      height: 360px;
      position: relative;
      flex-shrink: 0;
      .local-stream-content {
        width: 100%;
        height: 100%;
      }
      .local-stream-control {
        width: 100%;
        height: 30px;
        position: absolute;
        bottom: 0;
        background-color: rgba(0, 0, 0, 0.3);
        display: flex;
        justify-content: flex-end;
        align-items: center;
        padding: 0 10px;
        .control {
          margin-left: 10px;
        }
        .icon-class {
          color: #fff;
          cursor: pointer;
          width: 20px;
          height: 20px;
        }
      }
    }
  }

  .info-container-mobile {
    display: block;
    .log-container {
      margin-right: 0;
    }
    .local-stream-container {
      width: 320px;
      height: 240px;
      margin-top: 10px;
    }
  }

  .remote-container {
    width: 100%;
    margin-top: 10px;
    display: flex;
    flex-wrap: wrap;
    .remote-stream-container {
      width: 320px;
      height: 240px;
      margin: 0 10px 10px 0;
    }
  }
  .line {
    border-right: 1px solid #ccc;
  }
  .imgErrors {
    max-width: 100%;
    max-height: 100%;
    position: relative;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
  }
}

::v-deep .el-card__body {
  display: flex;
}
</style>

<i18n>
{
  "en": {
    "Operation": "Operation",
    "Join Room": "Join Room",
    "Publish": "Publish",
    "Unpublish": "Unpublish",
    "Leave Room": "Leave Room",
    "Start Screen Share": "Start Screen Share",
    "Stop Screen Share": "Stop Screen Share",
    "Please enter sdkAppId and secretKey": "Please enter sdkAppId and secretKey",
    "Please enter userId and roomId": "Please enter userId and roomId",
    "Please reacquire the invitation link": "Please reacquire the invitation link!"
  },
  "zh": {
    "Operation": "操作",
    "Join Room": "进入房间",
    "Publish": "发布流",
    "Unpublish": "取消发布流",
    "Leave Room": "离开房间",
    "Start Screen Share": "开始共享屏幕",
    "Stop Screen Share": "停止共享屏幕",
    "Please enter sdkAppId and secretKey": "请输入 sdkAppId 和 secretKey",
    "Please enter userId and roomId": "请输入 userId 和 roomId",
    "Please reacquire the invitation link": "请重新获取邀请链接!"
  }
}
</i18n>

rtc.js
/*
 * @Description: 音视频通话集成
 * @Date: 2022-03-14 17:15:23
 * @LastEditTime: 2022-03-23 17:47:14
 */
import TRTC from 'trtc-js-sdk';
import { isUndefined } from '@/utils/utils.js';

export default {
  data() {
    return {
      client: null,
      localStream: null,
      remoteStreamList: [],
      obj:{},
      isJoining: false,
      isJoined: false,
      isPublishing: false,
      isPublished: false,
      isMutedVideo: false,
      isMutedAudio: false,
      isPlayingLocalStream: false,
      leaveUserId: null,
    };
  },

  methods: {
    // 初始化客户端
    async initClient() {
      this.client = TRTC.createClient({
        mode: 'rtc',
        sdkAppId: this.sdkAppId,
        userId: this.userId,
        userSig: this.userSig,
      });
      this.addSuccessLog(`Client [${this.userId}] created.`);
      this.handleClientEvents();
    },

    async initLocalStream() {
      this.localStream = TRTC.createStream({
        audio: true,
        video: true,
        userId: this.userId,
        cameraId: this.cameraId,
        microphoneId: this.microphoneId,
      });
      try {
        await this.localStream.initialize();
        this.addSuccessLog(`LocalStream [${this.userId}] initialized.`);
      } catch (error) {
        this.localStream = null;
        this.addFailedLog(`LocalStream failed to initialize. Error: ${error.message}.`);
        throw error;
      }
    },

    playLocalStream() {
      this.localStream.play('localStream')
        .then(() => {
          this.isPlayingLocalStream = true;
          this.addSuccessLog(`LocalStream [${this.userId}] playing.`);
        })
        .catch((error) => {
          this.addFailedLog(`LocalStream [${this.userId}] failed to play. Error: ${error.message}`);
        });
    },

    destroyLocalStream() {
      this.localStream && this.localStream.stop();
      this.localStream && this.localStream.close();
      this.localStream = null;
      this.isPlayingLocalStream = false;
    },

    playRemoteStream(remoteStream, element) {
      if (remoteStream.getType() === 'main' && remoteStream.getUserId().indexOf('share') >= 0) {
        remoteStream.play(element, { objectFit: 'contain' }).catch();
      } else {
        remoteStream.play(element).catch();
      }
    },

    resumeStream(stream) {
      stream.resume();
    },

    async join() {
      if (this.isJoining || this.isJoined) {
        return;
      }
      this.isJoining = true;
      !this.client && await this.initClient();
      try {
        await this.client.join({ roomId: this.roomId });
        this.isJoining = false;
        this.isJoined = true;

        this.addSuccessLog(`Join room [${this.roomId}] success.`);
        this.reportSuccessEvent('joinRoom');

        this.startGetAudioLevel();
      } catch (error) {
        this.isJoining = false;
        console.error('join room failed', error);
        this.addFailedLog(`Join room ${this.roomId} failed, please check your params. Error: ${error.message}`);
        this.reportFailedEvent('joinRoom', error);
        throw error;
      }
    },

    async publish() {
      if (!this.isJoined || this.isPublishing || this.isPublished) {
        return;
      }
      this.isPublishing = true;
      try {
        await this.client.publish(this.localStream);
        this.isPublishing = false;
        this.isPublished = true;

        this.addSuccessLog('LocalStream is published successfully.');
        this.reportSuccessEvent('publish');
        if(this.localStream) {
          this.switchVideo(this.localStream);
        }    
      } catch (error) {
        this.isPublishing = false;
        console.error('publish localStream failed', error);
        this.addFailedLog(`LocalStream is failed to publish. Error: ${error.message}`);
        this.reportFailedEvent('publish');
        throw error;
      }
    },

    async unPublish() {
      if (!this.isPublished || this.isUnPublishing) {
        return;
      }
      this.isUnPublishing = true;
      try {
        await this.client.unpublish(this.localStream);
        this.isUnPublishing = false;
        this.isPublished = false;

        this.addSuccessLog('localStream unpublish successfully.');
        this.reportSuccessEvent('unpublish');
      } catch (error) {
        this.isUnPublishing = false;
        console.error('unpublish localStream failed', error);
        this.addFailedLog(`LocalStream is failed to unpublish. Error: ${error.message}`);
        this.reportFailedEvent('unpublish', error);
        throw error;
      }
    },

    async subscribe(remoteStream, config = { audio: true, video: true }) {
      try {
        await this.client.subscribe(remoteStream, {
          audio: isUndefined(config.audio) ? true : config.audio,
          video: isUndefined(config.video) ? true : config.video,
        });
        this.addSuccessLog(`Subscribe [${remoteStream.getUserId()}] success.`);
        this.reportSuccessEvent('subscribe');
      } catch (error) {
        console.error(`subscribe ${remoteStream.getUserId()} with audio: ${config.audio} video: ${config.video} error`, error);
        this.addFailedLog(`Subscribe ${remoteStream.getUserId()} failed!`);
        this.reportFailedEvent('subscribe', error);
      }
    },

    async unSubscribe(remoteStream) {
      try {
        await this.client.unsubscribe(remoteStream);
        this.addSuccessLog(`unsubscribe [${remoteStream.getUserId()}] success.`);
        this.reportSuccessEvent('unsubscribe');
      } catch (error) {
        console.error(`unsubscribe ${remoteStream.getUserId()} error`, error);
        this.addFailedLog(`unsubscribe ${remoteStream.getUserId()} failed!`);
        this.reportFailedEvent('unsubscribe', error);
      }
    },

    async leave() {
      if (!this.isJoined || this.isLeaving) {
        return;
      }
      this.isLeaving = true;
      this.stopGetAudioLevel();
      this.isPublished && await this.unPublish();
      this.localStream && this.destroyLocalStream();

      try {
        await this.client.leave();
        this.isLeaving = false;
        this.isJoined = false;

        this.addSuccessLog('Leave room success.');
        this.reportSuccessEvent('leaveRoom');
      } catch (error) {
        this.isLeaving = false;
        console.error('leave room error', error);
        this.addFailedLog(`Leave room failed. Error: ${error.message}`);
        this.reportFailedEvent('leaveRoom', error);
        throw error;
      }
    },

    muteVideo() {
      if (this.localStream) {
        this.localStream.muteVideo();
        this.isMutedVideo = true;
        this.addSuccessLog('LocalStream muted video.');
      }
    },

    muteAudio() {
      if (this.localStream) {
        this.localStream.muteAudio();
        this.isMutedAudio = true;
        this.addSuccessLog('LocalStream muted audio.');
      }
    },

    unmuteVideo() {
      if (this.localStream) {
        this.localStream.unmuteVideo();
        this.isMutedVideo = false;
        this.addSuccessLog('LocalStream unmuted video.');
      }
    },

    unmuteAudio() {
      if (this.localStream) {
        this.localStream.unmuteAudio();
        this.isMutedAudio = false;
        this.addSuccessLog('LocalStream unmuted audio.');
      }
    },

    switchDevice(type, deviceId) {
      try {
        if (this.localStream) {
          this.localStream.switchDevice(type, deviceId);
          this.addSuccessLog(`Switch ${type} device success.`);
        }
      } catch (error) {
        console.error('switchDevice failed', error);
        this.addFailedLog(`Switch ${type} device failed.`);
      }
    },

    startGetAudioLevel() {
      // 文档:https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/module-ClientEvent.html#.AUDIO_VOLUME
      this.client.on('audio-volume', (event) => {
        event.result.forEach(({ userId, audioVolume }) => {
          if (audioVolume > 2) {
            console.log(`user: ${userId} is speaking, audioVolume: ${audioVolume}`);
          }
        });
      });
      this.client.enableAudioVolumeEvaluation(200);
    },

    stopGetAudioLevel() {
      this.client && this.client.enableAudioVolumeEvaluation(-1);
    },

    handleClientEvents() {
      this.client.on('error', (error) => {
        console.error(error);
        alert(error);
      });
      this.client.on('client-banned', async (event) => {
        console.warn(`client has been banned for ${event.reason}`);

        this.isPublished = false;
        this.localStream = null;
        await this.leave();
      });
      // fired when a remote peer is joining the room
      this.client.on('peer-join', (event) => {
        const { userId } = event;
        console.log(`peer-join ${userId}`, event);
      });
      // fired when a remote peer is leaving the room
      this.client.on('peer-leave', (event) => {
        const { userId } = event;
        console.log(`peer-leave ${userId}`, event);
        this.leaveUserId = userId;
      });

      // fired when a remote stream is added
      this.client.on('stream-added', (event) => {
        const { stream: remoteStream } = event;
        const remoteUserId = remoteStream.getUserId();
        if (remoteUserId === `share_${this.userId}`) {
          // don't need screen shared by us
          this.unSubscribe(remoteStream);
        } else {
          console.log(`remote stream added: [${remoteUserId}] type: ${remoteStream.getType()}`);
          // subscribe to this remote stream
          this.subscribe(remoteStream);
          this.addSuccessLog(`RemoteStream added: [${remoteUserId}].`);
        }
      });
      // fired when a remote stream has been subscribed
      this.client.on('stream-subscribed', (event) => {
        const { stream: remoteStream } = event;
        const remoteUserId = remoteStream.getUserId();
        console.log('stream-subscribed userId: ', remoteUserId);
        this.addSuccessLog(`RemoteStream subscribed: [${remoteUserId}].`);
        this.remoteStreamList.push(remoteStream);

        this.remoteStreamList.forEach((item) => {
          for (let i = 0; i < 2; i++) {
            this.$set(this.obj,item.id+i,false);
          }
        })
        this.$nextTick(() => {
          this.playRemoteStream(remoteStream, remoteUserId);
        });
      });
      // fired when the remote stream is removed, e.g. the remote user called Client.unpublish()
      this.client.on('stream-removed', (event) => {
        const { stream: remoteStream } = event;
        remoteStream.stop();
        const index = this.remoteStreamList.indexOf(remoteStream);
        if (index >= 0) {
          this.remoteStreamList.splice(index, 1);
        }
        console.log(`stream-removed userId: ${remoteStream.getUserId()} type: ${remoteStream.getType()}`);
      });

      this.client.on('stream-updated', (event) => {
        const { stream: remoteStream } = event;
        console.log(`type: ${remoteStream.getType()} stream-updated hasAudio: ${remoteStream.hasAudio()} hasVideo: ${remoteStream.hasVideo()}`);
        this.addSuccessLog(`RemoteStream updated: [${remoteStream.getUserId()}] audio:${remoteStream.hasAudio()}, video:${remoteStream.hasVideo()}.`);
      });

      this.client.on('mute-audio', (event) => {
        const { userId } = event;
        console.log(`${userId} mute audio`);
        this.addSuccessLog(`[${event.userId}] mute audio.`);
      });
      this.client.on('unmute-audio', (event) => {
        const { userId } = event;
        console.log(`${userId} unmute audio`);
        this.addSuccessLog(`[${event.userId}] unmute audio.`);
      });
      this.client.on('mute-video', (event) => {
        const { userId } = event;
        console.log(`${userId} mute video`);
        this.addSuccessLog(`[${event.userId}] mute video.`);
      });
      this.client.on('unmute-video', (event) => {
        const { userId } = event;
        console.log(`${userId} unmute video`);
        this.addSuccessLog(`[${event.userId}] unmute video.`);
      });

      this.client.on('connection-state-changed', (event) => {
        console.log(`RtcClient state changed to ${event.state} from ${event.prevState}`);
      });

      this.client.on('network-quality', (event) => {
        const { uplinkNetworkQuality, downlinkNetworkQuality } = event;
        console.log(`network-quality uplinkNetworkQuality: ${uplinkNetworkQuality}, downlinkNetworkQuality: ${downlinkNetworkQuality}`);
      });
    },
  },
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值