(Web)vue+声网-实现视频通话及调试

1,687 阅读9分钟
原文链接: dongxin03.github.io

都是官方文档,仅供我自己巩固

1、准出

  • 有个项目 vue(别的项目也行 react啥的)
  • 注册声网,在控制台创建个应用获取 appID(必须)

2、开始创建

  • 这段代码是视频的时候,会将视频插入的dom,可以把这段dom放到你要视频的页面中;
  • videoIdList 这个变量是我自己定义的,主要是存放远端视频(别人的视频)的id
  • local_stream 这个元素id是我要插入本地视频的地方,也可以叫别的名字;(下面会讲)
  • remote_video_ + 远端视频id ,这个元素id是我要插入远端视频的地方,也可以叫别的名字;(下面会讲)
  • 注意 这两个最好包在一个id为video的元素下 不然总会插入到别的地方 (我也不会调 哈哈哈哈)*
    <div id='video' :style="{height:'100%',width:'100%'}">
        <div class="video-view review-right-video">
            <div id="local_stream" :style="{height:'100%',width:'100%'}"></div> // 本地的视频会插入到这个ID下
            <div id="local_video_info" class="video-profile hide"></div>
            <div id="video_autoplay_local" class="autoplay-fallback hide"></div>
        </div>
        <div 
            v-for="item in videoIdList" :key="item" 
            :id="`remote_video_panel_`+item"  // 远端的视频会插入到这个id 下  
            :class="['video-view review-center-video',imageUrl ? 'active' : '']"
        >
            <div 
                :style="{height:'100%',width:'100%'}" 
                :id="`remote_video_`+item" 
            ></div>
            <div :id="`remote_video_info_`+item" class="video-profile hide"></div>
            <div :id="`video_autoplay_`+item" class="autoplay-fallback hide"></div>
        </div>
    </div>
    

3、对接声网

  • 安装

  • npm install agora-rtc-sdk ;

  • 也可以使用CDN

  • CDN <script src="https://cdn.agora.io/sdk/release/AgoraRTCSDK-3.0.0.js"></script>;

  • 引入

  • import AgoraRTC from 'agora-rtc-sdk';

4、整个声网的文件

*
*
*

  • 完整的代码在最下面,直接看代码就行
  • 我建了一个文件 叫 agoraRTCfunct(都是网易翻译来的)

1.引入 sdk
import AgoraRTC from 'agora-rtc-sdk';
2.设置两个参数变量
主要的就是appID
频道是自己定义的,就像设置一个房间,别人会根据你的频道号加入到你的视频当中

const rtc = {
    client: null,
    joined: false, //是否已经加入频道
    published: false, // 是否已经发布
    localStream: null,
    remoteStreams: [],
    params: {}
};

const  option = {
    appID: "f3e69727bee94580be65eb6193a72b89",
    channel: "123", // 频道
    uid: 1123, // 用户id
    token: "" // 手机app上用的  没用
};

3.开始声网

  • 开始监听的代码在下面 , 监听视频过程中的很多事件
  • 创建本地流的代码也在下面
    • 主要是创建了一个频道,
    • 初始化一个频道
    • 加入到这个频道
  • 在页面中引入 import agora from ‘./agoraRTCfunct.js’; 创建的这个文件*
  • thet是vue页面的this,我在页面初始化(mounted)的时候调用了 agora.agoraFunction(this); 传入了this*
  • 因为有用户加入的时候会用到
    const agoraFunction = function(thet){
        if(rtc.joined){
            Notification.info("您已经加入频道");
            return;
        };
        // 创建客户端
        rtc.client = AgoraRTC.createClient({mode: "live", codec: "vp8"});
        rtc.params = {mode:'live', codec: 'vp8'}; // 赋值输入框的值
        Listening(thet);                  // 开始监听
        // 初始化
        rtc.client.init(option.appID, function () {
            console.log("初始化成功");
            //加入频道
            rtc.client.join(option.token ? option.token : null, option.channel, option.uid ? +option.uid : null, function (uid) {
                console.log("成功----频道号码: " + option.channel + "你的id: " + uid);
                rtc.joined = true;
                rtc.params.uid = uid;   // 获取到自己的id
                createStream();           // 创建本地流
            }, function(err) {
                console.error("加入频道失败", err)
            })
        }, (err) => {
        console.error(err);
        }); 
    };
    

4.创建本地流(就是打开摄像头能看到自己)
重要的是

  • rtc.localStream.play(‘local_stream’); // 插入到这个id中
  • local_stream 这个是我自己定义的ID 本地的视频会插入到这个元素中
  • 发布本地流是把,本地的视频推到远端 、要不别人看不见
    const createStream = function(){ //创建本地流
         // 创建本地流
         rtc.localStream = AgoraRTC.createStream({
            streamID: rtc.params.uid, // 在上一步获取到的自己的视频id
            audio: true,
            video: true,
            screen: false, // 是不是需要共享屏幕   这个只在谷歌浏览器有用   不咋好使
        });
        // 初始化本地流
        rtc.localStream.setVideoProfile("360p_1"); //  640X360  15  400 视频清晰度
        rtc.localStream.init(function () {
            console.log("本地流-初始化-成功",rtc.localStream);
            // 发布本地流
            rtc.localStream.play('local_stream'); // 插入到这个id中
            publish(); // 发布本地流
        }, function (err) {
            console.error("本地流-初始化-失败", err);
        });
    }
    
  • 发布根底流*
    const publish =  function () { // 发布本地流
        if (!rtc.client) {
          Notification.info("您还没加入频道");
          return;
        }
        if (rtc.published) {
            Notification.info("您已经发布频道");
          return;
        }
        const oldState = rtc.published;
    
        // publish localStream
        rtc.client.publish(rtc.localStream, function (err) {
          rtc.published = oldState;
          console.log("publish failed");
          Toast.error("publish failed")
          console.error(err);
        })
        Notification.success("发布频道成功");
        rtc.published = true
    }
    

5.监听视频过程中的事件

  • 重要的是有别人加入你的视频中的事件(和离开)
  • 主要是有用户加入频道的时候,要及时创建一个指定的对方id的元素*
  • remoteStream.play(“remote_video_” + id); // 插入到 id为 remote_video_ + id 元素中
  • “remote_video_” + id 这个就是我定义的元素, 对方的视频会插入到这里
    const Listening = function(thet){ // 订阅事件
        // ---- 订阅开始
        rtc.client.on("error", (err) => {
            console.log("===>",err)
        });
        rtc.client.on("peer-leave", function(evt) { // 有用户离开时
            var uid = evt.uid;
            var reason = evt.reason; // 离线原因 Quit -- 主动离开  ServerTimeOut -- 超时掉线(也有可能是主动离开)
            if (uid != rtc.params.uid) {
                thet.deleteView(uid);
            };
            Notification.info("用户离开");
            console.log("用户离线" , uid, "reason: ", reason);
        });
        rtc.client.on("stream-published", function(evt){ // 发布视频流本地触发
            var remoteStream = evt.stream;
            var id = remoteStream.getId();
            thet.myId = id;
        });
        rtc.client.on("stream-added", function (evt) {   //有远程流加入时
            var remoteStream = evt.stream;
            var id = remoteStream.getId();
            if (id !== rtc.params.uid) {
                rtc.client.subscribe(remoteStream, function (err) { // 订阅加入的远程端视频
                    console.log("订阅远程端视频失败", err);
                })
            };
            console.log('加入的远程端视频流: ', id);
        });
        rtc.client.on("stream-subscribed", function (evt) { //订阅远程流
            var remoteStream = evt.stream;
            var id = remoteStream.getId();
            rtc.remoteStreams.push(remoteStream); // 不知道干啥的
            Notification.success("用户加入"+id);
            thet.addView(id);
            setTimeout(()=>{ // vue创建元素 是异步的   (一会处理)
                remoteStream.play("remote_video_" + id); // 插入到 id为 remote_video_ + id 元素中
            },300);
            console.log('接收到的远程端视频: ', id);
        });
        rtc.client.on("stream-removed", function (evt) { // 对方取消发布
            var remoteStream = evt.stream;
            var id = remoteStream.getId();
            remoteStream.stop("remote_video_" + id);
            rtc.remoteStreams = rtc.remoteStreams.filter(function (stream) {
              return stream.getId() !== id;
            })
            thet.deleteView(id);
            console.log('stream-removed remote-uid: ', id);
        });
        rtc.client.on("onTokenPrivilegeWillExpire", function(){ // token过期前30秒调用
            // client.renewToken(token);
            console.log("token即将过期")
        });
        rtc.client.on("onTokenPrivilegeDidExpire", function(){ // token已经失效
            // client.renewToken(token);
            console.log("token已经失效");
        });
        rtc.client.on("network-quality", function(stats) { // 本地用户的网络质量
            console.log("下行", callQuality[stats.downlinkNetworkQuality]);
            console.log("上行", callQuality[stats.uplinkNetworkQuality]);
        }); 
        rtc.client.on("mute-audio", function(evt) { // 对方关闭了语音
            var uid = evt.uid;
            Notification.info("用户"+uid+"已静音");
            console.log("mute audio:" + uid);
            //alert("mute audio:" + uid);
        });
        rtc.client.on("unmute-audio", function (evt) { // 对方打开了语音
            var uid = evt.uid;
            Notification.info("用户"+uid+"打来了语音");
            console.log("unmute audio:" + uid);
        });
        rtc.client.on("mute-video", function (evt) { // 对方关闭摄像头
            var uid = evt.uid;
            Notification.info("用户"+uid+"关闭了摄像头");
            console.log("mute video" + uid);
            //alert("mute video:" + uid);
        });
        rtc.client.on("unmute-video", function (evt) { // 对方打开了摄像头
            var uid = evt.uid;
            Notification.info("用户"+uid+"打开了了摄像头");
            console.log("unmute video:" + uid);
        });
        // ---- 订阅结束
    };
    
  • 我在监听到有用户加入的时候,把用户的id插入到一个数组里,循环渲染出了有特定id的元素(thet.addView(id);)
  • 我在监听到有用户离开的时候,把用户的id从这个数组中去除掉,在循环渲染出了有特定id的元素(thet.deleteView(id);)
    addView(id){
        if(id == this.myId || this.videoIdList.indexOf(id) != -1) return;
        const list = this.videoIdList;
        list.push(id);
        this.videoIdList = list;
    },
    deleteView(id){
        const index = this.videoIdList.indexOf(id);
        const list = this.videoIdList;
        list.splice(index,1);
        this.videoIdList = list;
    },
    

其他的就没有比较重要的了

  • 导出了这几个方法
    export default {
        rtc, // 参数
        option, // 参数
        agoraFunction, // 声网开始的方法  创建  初始化 加入频道 
        publish, // 发布本地流
        unpublish, // 停止发布本地流
        leave, // 离开频道
        getDevices, // 获取可用的 摄像头 麦克风
    };
    
  • 完整的代码*
    import AgoraRTC from 'agora-rtc-sdk';
    import {Notification} from 'element-ui';
    
    const callQuality = { // 通话质量
        0:"质量未知",
        1:"质量极好",
        2:"用户主观感觉和极好差不多,但码率可能略低于极好",
        3:"用户主观感受有瑕疵但不影响沟通",
        4:"勉强能沟通但不顺畅",
        5:"网络质量非常差,基本不能沟通",
        6:"网络连接断开,完全无法沟通",
    }
    
    const rtc = {
        client: null,
        joined: false, //是否已经加入频道
        published: false, // 是否已经发布
        localStream: null,
        remoteStreams: [],
        params: {}
    };
    
    const  option = {
        appID: "f3e69727bee94580be65eb6193a72b89",
        channel: "123", // 频道
        uid: 1123, // 用户id
        token: "" // 手机app上用的  没用
    };
    
    const leave = function () { // 离开频道 
        if (!rtc.client) {
            Notification.info("您还没加入频道");
            return;
        }
        if (!rtc.joined) {
            Notification.info("您还没加入频道(2)");
            return;
        }
        rtc.client.leave(function () {
            rtc.localStream.stop();
            rtc.localStream.close();
            while (rtc.remoteStreams.length > 0) {
                var stream = rtc.remoteStreams.shift();
                var id = stream.getId();
                stream.stop();
                removeView(id);
            }
            rtc.localStream = null;
            rtc.remoteStreams = [];
            rtc.client = null;
            rtc.published = false;
            rtc.joined = false;
            Notification.info("离开频道成功");
        }, function (err) {
            Notification.error("离开频道失败");
            console.error(err);
        })
    }
    
    function unpublish (rtc) { // 取消发布本地流
        if (!rtc.client) {
            Notification.info("您还没加入频道");
            return;
        }
        if (!rtc.published) {
            Toast.error("您已经发布频道");
            return;
        }
        var oldState = rtc.published;
        rtc.client.unpublish(rtc.localStream, function (err) {
          rtc.published = oldState;
          console.error(err);
        })
        Notification.info("取消发布成功");
        rtc.published = false;
    }
    
    const createStream = function(){ //创建本地流
         // 创建本地流
         rtc.localStream = AgoraRTC.createStream({
            streamID: rtc.params.uid,
            audio: true,
            video: true,
            screen: false,
        });
        // 初始化本地流
        rtc.localStream.setVideoProfile("360p_1"); //  640X360  15  400
        rtc.localStream.init(function () {
            console.log("本地流-初始化-成功",rtc.localStream);
            // 发布本地流
            rtc.localStream.play('local_stream'); // 插入到这个id中
            publish(); // 发布本地流
        }, function (err) {
            console.error("本地流-初始化-失败", err);
        });
    }
    
    const publish =  function () { // 发布本地流
        if (!rtc.client) {
          Notification.info("您还没加入频道");
          return;
        }
        if (rtc.published) {
            Notification.info("您已经发布频道");
          return;
        }
        const oldState = rtc.published;
    
        // publish localStream
        rtc.client.publish(rtc.localStream, function (err) {
          rtc.published = oldState;
          console.log("publish failed");
          Toast.error("publish failed")
          console.error(err);
        })
        Notification.success("发布频道成功");
        rtc.published = true
    }
    
    const Listening = function(thet){ // 订阅事件
        // ---- 订阅开始
        rtc.client.on("error", (err) => {
            console.log("===>",err)
        });
        rtc.client.on("peer-leave", function(evt) { // 有用户离开时
            var uid = evt.uid;
            var reason = evt.reason; // 离线原因 Quit -- 主动离开  ServerTimeOut -- 超时掉线(也有可能是主动离开)
            if (uid != rtc.params.uid) {
                thet.deleteView(uid);
            };
            Notification.info("用户离开");
            console.log("用户离线" , uid, "reason: ", reason);
        });
        rtc.client.on("stream-published", function(evt){ // 发布视频流本地触发
            var remoteStream = evt.stream;
            var id = remoteStream.getId();
            thet.myId = id;
        });
        rtc.client.on("stream-added", function (evt) {   //有远程流加入时
            var remoteStream = evt.stream;
            var id = remoteStream.getId();
            if (id !== rtc.params.uid) {
                rtc.client.subscribe(remoteStream, function (err) { // 订阅加入的远程端视频
                    console.log("订阅远程端视频失败", err);
                })
            };
            console.log('加入的远程端视频流: ', id);
        });
        rtc.client.on("stream-subscribed", function (evt) { //订阅远程流
            var remoteStream = evt.stream;
            var id = remoteStream.getId();
            rtc.remoteStreams.push(remoteStream); // 不知道干啥的
            Notification.success("用户加入"+id);
            thet.addView(id);
            setTimeout(()=>{ // vue创建元素 是异步的   (一会处理)
                remoteStream.play("remote_video_" + id); // 插入到 id为 remote_video_ + id 元素中
            },300);
            console.log('接收到的远程端视频: ', id);
        });
        rtc.client.on("stream-removed", function (evt) { // 对方取消发布
            var remoteStream = evt.stream;
            var id = remoteStream.getId();
            remoteStream.stop("remote_video_" + id);
            rtc.remoteStreams = rtc.remoteStreams.filter(function (stream) {
              return stream.getId() !== id;
            })
            thet.deleteView(id);
            console.log('stream-removed remote-uid: ', id);
        });
        rtc.client.on("onTokenPrivilegeWillExpire", function(){ // token过期前30秒调用
            // client.renewToken(token);
            console.log("token即将过期")
        });
        rtc.client.on("onTokenPrivilegeDidExpire", function(){ // token已经失效
            // client.renewToken(token);
            console.log("token已经失效");
        });
        rtc.client.on("network-quality", function(stats) { // 本地用户的网络质量
            console.log("下行", callQuality[stats.downlinkNetworkQuality]);
            console.log("上行", callQuality[stats.uplinkNetworkQuality]);
        }); 
        rtc.client.on("mute-audio", function(evt) { // 对方关闭了语音
            var uid = evt.uid;
            Notification.info("用户"+uid+"已静音");
            console.log("mute audio:" + uid);
            //alert("mute audio:" + uid);
        });
        rtc.client.on("unmute-audio", function (evt) { // 对方打开了语音
            var uid = evt.uid;
            Notification.info("用户"+uid+"打来了语音");
            console.log("unmute audio:" + uid);
        });
        rtc.client.on("mute-video", function (evt) { // 对方关闭摄像头
            var uid = evt.uid;
            Notification.info("用户"+uid+"关闭了摄像头");
            console.log("mute video" + uid);
            //alert("mute video:" + uid);
        });
        rtc.client.on("unmute-video", function (evt) { // 对方打开了摄像头
            var uid = evt.uid;
            Notification.info("用户"+uid+"打开了了摄像头");
            console.log("unmute video:" + uid);
        });
        // ---- 订阅结束
    };
    
    // 开始声网
    const agoraFunction = function(thet){
        // 创建客户端
        if(rtc.joined){
            Notification.info("您已经加入频道");
            return;
        };
        rtc.client = AgoraRTC.createClient({mode: "live", codec: "vp8"});
        rtc.params = {mode:'live', codec: 'vp8'}; // 赋值输入框的值
        Listening(thet); // 开始监听
        // 初始化
        rtc.client.init(option.appID, function () {
            console.log("初始化成功");
            //加入频道
            rtc.client.join(option.token ? option.token : null, option.channel, option.uid ? +option.uid : null, function (uid) {
                console.log("成功----频道号码: " + option.channel + "你的id: " + uid);
                rtc.joined = true;
                rtc.params.uid = uid;
                createStream(); // 创建本地流
            }, function(err) {
                console.error("加入频道失败", err)
            })
        }, (err) => {
        console.error(err);
        }); 
    };
    
    const getDevices =  function (next) { // 获取音视频设备信息
        AgoraRTC.getDevices(function (items) {
            items.filter(function (item) {
                return ['audioinput', 'videoinput'].indexOf(item.kind) !== -1
            }).map(function (item) {
                return {
                    name: item.label,
                    value: item.deviceId,
                    kind: item.kind,
                }
            });
            var videos = [];
            var audios = [];
            for (var i = 0; i < items.length; i++) {
                var item = items[i];
                if ('videoinput' == item.kind) {
                    var name = item.label;
                    var value = item.deviceId;
                    if (!name) {
                        name = "camera-" + videos.length;
                    }
                    videos.push({
                        name: name,
                        value: value,
                        kind: item.kind
                    });
                }
                if ('audioinput' == item.kind) {
                    var name = item.label;
                    var value = item.deviceId;
                    if (!name) {
                        name = "microphone-" + audios.length;
                    }
                    audios.push({
                        name: name,
                        value: value,
                        kind: item.kind
                    });
                }
            }
            console.log({videos: videos, audios: audios});
        //   next({videos: videos, audios: audios});
        });
    }
    export default {
        rtc, // 参数
        option, // 参数
        agoraFunction, // 声网开始的方法   
        publish, // 发布本地流
        unpublish, // 停止发布本地流
        leave, // 离开频道
        getDevices, // 获取可用的 摄像头 麦克风
    };
    

到此结束