使用声网 Web SDK 为直播推流应用创建“举手”功能

举手示意主持人,请求上麦。

作为一名社区驱动型的开发人员,我会通过直播推流应用举行教育研讨会。我通常会在每次会话快结束的时候留一些 Q&A 时间,然后我发现 Q&A 时间总是进展的无比艰难,原因是经常会有好几个参与者同时在会议聊天窗口发消息提问,让我很难记清楚每个人的具体问题,更不用说跟他们进一步沟通了。

所以,我思来想去,觉得解决这个问题的最好办法是与参与者直接对话。

下面这个教程会教大家开发一个网页版应用,在这个应用里,用户可以发送角色升级请求,主持人有权接受或拒绝这个请求。一旦主持人同意用户的请求,用户就会成为直播推流应用里的发言者。下面我们使用(直播 SDK) 声网 Web SDK声网 RTM SDK 来搭建样例 app:

前期准确前图片

前期准备

项目设置

首先,我们克隆这个项目的 GitHub 资源库,得到一个下面这样的项目:

2
声网多人视频直播推流应用的用户界面截图


如果无法理解上面的代码,请查看这个教程。我已经在教程里添加了一个代码,用于在用户界面和 JavaScript 文件中输入用户名。总之,现在我们有了一个全功能视频直播推流应用,而且具有静音和取消静音的功能。


有人举手,我该怎么操作?

无论用户举手或放下手,我们都会使用 声网 RTM SDK 发送频道消息。如果同时有多位主持人,则所有主持人都会收到频道消息,任一主持人都可以选择升级该用户角色或拒绝其请求。

我们根据观众(用户角色)的点击按钮事件来进行相应的操作。如果有观众点击了 举手 按钮,我们就触发该按钮的信息和功能。同时,我们也更新我们创建的全局变量的状态,实时记录用户是否处于举手状态。

// Hand raise state
var handRaiseState = false;
...

// Append a raise hand button for audience (hosts cannot raise their hand)
async function join() { // create Agora client
    client.setClientRole(options.role);
    $("#mic-btn").prop("disabled", false);
    $("#video-btn").prop("disabled", false);
    if (options.role === "audience") {
        $("#mic-btn").prop("disabled", true);
        $("#video-btn").prop("disabled", true);
        $("#raise-hand-div").append(`<button id="raise-hand" type="button" class="btn btn-live btn-sm" disabled>Raise Hand</button>`);
        // Event listeners
        client.on("user-published", handleUserPublished);
        client.on("user-joined", handleUserJoined);
        client.on("user-left", handleUserLeft);
    }
  ...
}
  
...
// RTM Channel Join
        var channelName = $('#channel').val();
        channel = clientRTM.createChannel(channelName);
        channel.join().then(() => {
            console.log('AgoraRTM client channel join success.');
            // Send channel message for raising hand
            $(document).on('click', '#raise-hand', async function () {
                fullDivId = $(this).attr('id');
                if (handRaiseState === false) {
                    $("#raise-hand").text("Lower Hand");
                    handRaiseState = true;
                    console.log("Hand raised.");
                    // Inform channel that rand was raised
                    await channel.sendMessage({ text: "raised" }).then(() => {
                        console.log("Message sent successfully.");
                        console.log("Your message was: raised" + " sent by: " + accountName);
                    }).catch((err) => {
                        console.error("Message sending failed: " + err);
                    })
                }
                else if (handRaiseState === true) {
                    $("#raise-hand").text("Raise Hand");
                    handRaiseState = false;
                    console.log("Hand lowered.");
                    // Inform channel that rand was raised
                    await channel.sendMessage({ text: "lowered" }).then(() => {
                        console.log("Message sent successfully.");
                        console.log("Your message was: lowered" + " sent by: " + accountName);
                    }).catch((err) => {
                        console.error("Message sending failed: " + err);
                    })
                }
            });
            // Get channel message when someone raises hand
            channel.on('ChannelMessage', async function (text, peerId) {
                console.log(peerId + " changed their hand raise state to " + text.text);
                if (options.role === "host") {
                    if (text.text == "raised") {
                        // Ask host if user who raised their hand should be called onto stage or not
                        if (confirm(peerId + " raised their hand. Do you want to make them a host?")) {
                            // Call user onto stage
                            console.log("The host accepted " + peerId + "'s request.");
                            // Send approval message
                        } else {
                            // Inform the user that they were not made a host
                            console.log("The host rejected " + peerId + "'s request.");
                            // Send rejection message
                        }
                    } else if (text.text == "lowered") {
                        console.log("Hand lowered so host can ignore this.");
                    }
                }
            })
             }).catch(error => {
            console.log('AgoraRTM client channel join failed: ', error);
        }).catch(err => {
            console.log('AgoraRTM client login failure: ', err);
        });
    });
...

收到用户请求之后,我怎么同意或拒绝呢?

因为我们只打算对发送请求的用户进行升级或拒绝操作,所以我们向用户发送点对点消息来更新同意和拒绝状态。

如果用户的角色升级请求被同意,用户会收到成功把角色更改为主持人的点对点消息,随后该用户会以主持人角色重新加入频道。

...
            // Get channel message when someone raises hand
            channel.on('ChannelMessage', async function (text, peerId) {
                console.log(peerId + " changed their hand raise state to " + text.text);
                if (options.role === "host") {
                    if (text.text == "raised") {
                        // Ask host if user who raised their hand should be called onto stage or not
                        $('#confirm').modal('show');
                        $('#modal-body').text(peerId + " raised their hand. Do you want to make them a host?");
                        $('#promoteAccept').click(async function () {
                            // Call user onto stage
                            console.log("The host accepted " + peerId + "'s request.");
                            await clientRTM.sendMessageToPeer({
                                text: "host"
                            },
                                peerId,
                            ).then(sendResult => {
                                if (sendResult.hasPeerReceived) {
                                    console.log("Message has been received by: " + peerId + " Message: host");
                                } else {
                                    console.log("Message sent to: " + peerId + " Message: host");
                                }
                            }).catch(error => {
                                console.log("Error sending peer message: " + error);
                            });
                            $('#confirm').modal('hide');
                        });
                        $("#cancel").click(async function () {
                            // Inform the user that they were not made a host
                            console.log("The host rejected " + peerId + "'s request.");
                            await clientRTM.sendMessageToPeer({
                                text: "audience"
                            },
                                peerId,
                            ).then(sendResult => {
                                if (sendResult.hasPeerReceived) {
                                    console.log("Message has been received by: " + peerId + " Message: audience");
                                } else {
                                    console.log("Message sent to: " + peerId + " Message: audience");
                                }
                            }).catch((error) => {
                                console.log("Error sending peer message: " + error);
                            });
                            $('#confirm').modal('hide');
                        });
                    } else if (text.text == "lowered") {
                        $('#confirm').modal('hide');
                        console.log("Hand lowered so host can ignore this.");
                    }
                }
            })
            // Display messages from host when they approve the request
            clientRTM.on('MessageFromPeer', async function ({
                text
            }, peerId) {
                console.log(peerId + " changed your role to " + text);
                if (text === "host") {
                    await leave();
                    options.role = "host";
                    console.log("Role changed to host.");
                    await client.setClientRole("host");
                    await join();
                    $("#host-join").attr("disabled", true);
                    $("#audience-join").attr("disabled", true);
                    $("#raise-hand").attr("disabled", false);
                    $("#leave").attr("disabled", false);
                } else if (text === "audience" && options.role !== "audience") {
                    alert("The host rejected your proposal to be called onto stage.");
                    $("#raise-hand").attr("disabled", false);
                }
            })
        }).catch(error => {
            console.log('AgoraRTM client channel join failed: ', error);
        }).catch(err => {
            console.log('AgoraRTM client login failure: ', err);
        });
    });
...


我们已经把应用程序的架构好了,现在可以进行测试~

注意: 测试的时候,可以使用2个或以上浏览器选项卡来模拟通话中的多个用户。


总结

大功告成啦!

根据上面的教程,我们成功在网页直播推流应用中添加了基于请求的角色升级服务。有些读者想看到配置好的应用,但没法跟我一起编码,所以我已经把所有的(直播SDK)代码都上传到了 GitHub。

感谢阅读这篇教程的童鞋们~如果大家有任何问题,可以在下方评论。如果觉得有可以改进的地方,欢迎随时复刻这个仓库并创建拉取请求!


其他资源

  • 想了解更多关于声网 Web SDK (直播SDK)和其他用例的信息,请点击 这里 查看开发者指南。


原文作者:Akshat Gupta Akshat Gupta 是声网Agora 的实习开发人员,主攻前端 web 技术,致力于帮助开发人员把声网Agora Web SDK 集成进各种应用中,为此写了不少文章,也开发了多个项目。
原文链接:Building a Raise-Your-Hand Feature for Live Streams Using the Agora Web SDK
注册登录 后评论
    // 作者
    声网技术社区 发布于 声网开发者社区
    • 0
    // 本帖子
    // 相关帖子
    Coming soon...
    • 0
    使用声网 Web SDK 为直播推流应用创建“举手”功能声网技术社区 发布于 声网开发者社区