游戏直播是Discord产品的核心功能之一,本教程教大家如何1天内开发一款内置游戏直播的国产版Discord应用,用户不仅可以通过IM聊天,也可以进行语聊,看游戏直播,甚至自己进行游戏直播,无任何实时音视频底层技术的Web开发者同样适用,效果如下:
本项目基于环信超级社区的实例项目, 所以我们先从Circle-Demo-Web
这个仓库开启做初始化
运行后, 登录完毕效果如下,
- 个人信息页
- 好友会话页
- 当前加入的频道
- 创建新频道
- 加入服务器
社区(Server)、频道(Channel) 和子区(Thread) 三层结构
社区为一个独立的结构, 不同社区直接相互隔离, 社区包含不同的频道, 代表了不同的话题, 用户在频道中聊天, 而针对一条单独信息产生的回复为子区.
我们本次的项目主要集中在频道部分, 需要加入一个服务器后, 创建一个测试社区, 保证你具有管理员权限.
我们的目标是尽量利用现有api扩展功能, 有几个问题需要解决
- 如何区分普通频道和游戏频道?
- 如何区分当前频道是否有玩家直播, 如果有直播如何获取玩家信息, 第二玩家的状态?
- 多人聊天的状态?
这里直接简单采用频道前缀做特殊区分, 创建频道前缀带video-
的识别为游戏频道, 同时将渲染内容做替换
const isVideoChannel = useMemo(() => {
return currentChannelInfo?.name?.startsWith("");
}, [currentChannelInfo]);
const renderTextChannel = () => {
return (
<>
<MessageList
messageInfo={messageInfo}
channelId={channelId}
handleOperation={handleOperation}
className={s.messageWrap}
/>
<div className={s.iptWrap}>
<Input chatType={CHAT_TYPE.groupChat} fromId={channelId} />
</div>
</>
);
}
const renderStreamChannel = () => {
return (
<>This is a Stream Channel<>
);
}
return (
...
<div className={s.contentWrap}>
{isVideoChannel ? renderStreamChannel() : renderTextChannel()}
</div>
...
);
如果需要区分图标, 可以搜索channelNameWrap
, 分别在channelItem
和Channel/components/Header
中添加一个css类, 通过这个类设置图标图片
如何区分当前频道是否有玩家直播, 如果有直播如何获取玩家信息, 第二玩家的状态?
我们可以复用在频道中发送消息的机制, 直播开始, 结束都可以当做一条特殊的消息发送, 只不过这条消息不承载用户的信息, 而是表达用户上下播的行为
当然这个机制存在一定实时性的问题, 不过大致是可行的.
const sendMessage = useCallback(() => {
if (!text) return;
getTarget().then((target) => {
let msg = createMsg({
chatType,
type: "txt",
to: target,
msg: convertToMessage(ref.current.innerHTML),
isChatThread: props.isThread
});
setText("");
deliverMsg(msg).then(() => {
if (msg.isChatThread) {
setThreadMessage({
message: { ...msg, from: WebIM.conn.user },
fromId: target
});
} else {
insertChatMessage({
chatType,
fromId: target,
messageInfo: {
list: [{ ...msg, from: WebIM.conn.user }]
}
});
scrollBottom();
}
});
});
}, [text, props, getTarget, chatType, setThreadMessage, insertChatMessage]);
去除掉与输入框逻辑耦合的部分, 可以分为两步, createMsg
创建消息, deliverMsg
发送消息, 这两个功能都是环信SDK功能的封装, 经过查阅文档, 它支持发送自定义消息.
在utils中新建一个stream.js
文件来封装直播的逻辑
const sendStreamMessage = (content, channelId) => {
let msg = createMsg({
chatType: CHAT_TYPE.groupChat,
type: "custom",
to: channelId,
ext: {
type: "stream",
...content,
},
});
return deliverMsg(msg)
.then(() => {
console.log("发送成功");
})
.catch(console.error);
};
它接收content表示我们的额外信息, 用户名和上下播状态, channelId区分不同的channel, 对它的调用可以如下
const CMD_START_STREAM = "start";
const CMD_END_STREAM = "end";
sendStreamMessage(
{
user: userInfo?.username,
status: CMD_START_STREAM,
},
channelId
);
sendStreamMessage(
{
user: userInfo?.username,
status: CMD_END_STREAM,
},
channelId
);
第二玩家的状态可以类比第一个玩家用额外的自定义消息实现, 这里不做重复.
关于自定义消息, 原本它的作用是邀请用户加入频道, 你可以在components/CustomMsg
中找到, 我们要额外识别一下直播消息(可以渲染在消息列表里, 也可以直接屏蔽掉).
const isStream = message?.ext?.type === "stream";
const renderStream = () => {
return (<>)
}
if (isStream) {
return renderStream();
} else {
...
}
我们引入声网RTC sdk, 每个进入直播房间的用户都对应维护一个声网客户端,
通过on事件感知远端视频/音频流.
- 注册声网开发者, 并在后台创建一个测试项目
- 项目根目录创建
.env
文件, 存放api token等信息
REACT_APP_AGORA_APPID = your app id
REACT_APP_AGORA_CHANNEL = test
REACT_APP_AGORA_TOKEN = your token
REACT_APP_AGORA_UID = 123xxx
- 添加声网sdk依赖
npm install agora-rtc-sdk-ng