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

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

1、准出

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

2、开始创建

  • 这段代码是视频的时候,会将视频插入的dom,可以把这段dom放到你要视频的页面中;
  • videoIdList 这个变量是我自己定义的,主要是存放远端视频(别人的视频)的id
  • local_stream 这个元素id是我要插入本地视频的地方,也可以叫别的名字;(下面会讲)
  • remote_video_ + 远端视频id ,这个元素id是我要插入远端视频的地方,也可以叫别的名字;(下面会讲)
  • 注意 这两个最好包在一个id为video的元素下 不然总会插入到别的地方 (我也不会调 哈哈哈哈)*
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <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
频道是自己定义的,就像设置一个房间,别人会根据你的频道号加入到你的视频当中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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*
  • 因为有用户加入的时候会用到
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    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 本地的视频会插入到这个元素中
  • 发布本地流是把,本地的视频推到远端 、要不别人看不见
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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);
    });
    }
  • 发布根底流*
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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 这个就是我定义的元素, 对方的视频会插入到这里
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    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);)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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;
    },

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

  • 导出了这几个方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    export default {
    rtc, // 参数
    option, // 参数
    agoraFunction, // 声网开始的方法 创建 初始化 加入频道
    publish, // 发布本地流
    unpublish, // 停止发布本地流
    leave, // 离开频道
    getDevices, // 获取可用的 摄像头 麦克风
    };
  • 完整的代码*
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    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, // 获取可用的 摄像头 麦克风
    };

到此结束