推拉流视频成功

This commit is contained in:
mmc
2026-05-12 11:21:31 +08:00
parent 307ee479db
commit 899a31984f
3 changed files with 334 additions and 127 deletions

86
package-lock.json generated
View File

@@ -34,6 +34,7 @@
"react-redux": "^9.2.0",
"react-router-dom": "^7.14.1",
"recharts": "^3.8.1",
"socket.io-client": "^4.8.3",
"tailwind-merge": "^3.5.0",
"vite": "^6.2.0",
"vite-plugin-cesium": "^1.2.23",
@@ -2015,6 +2016,12 @@
"win32"
]
},
"node_modules/@socket.io/component-emitter": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
"license": "MIT"
},
"node_modules/@spz-loader/core": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@spz-loader/core/-/core-0.3.1.tgz",
@@ -3838,6 +3845,28 @@
"node": ">= 0.8"
}
},
"node_modules/engine.io-client": {
"version": "6.6.4",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.4.tgz",
"integrity": "sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw==",
"license": "MIT",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.4.1",
"engine.io-parser": "~5.2.1",
"ws": "~8.18.3",
"xmlhttprequest-ssl": "~2.1.1"
}
},
"node_modules/engine.io-parser": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/enhanced-resolve": {
"version": "5.20.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz",
@@ -6677,6 +6706,34 @@
"is-arrayish": "^0.3.1"
}
},
"node_modules/socket.io-client": {
"version": "4.8.3",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz",
"integrity": "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==",
"license": "MIT",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.4.1",
"engine.io-client": "~6.6.1",
"socket.io-parser": "~4.2.4"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-parser": {
"version": "4.2.6",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.6.tgz",
"integrity": "sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==",
"license": "MIT",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.4.1"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -7745,6 +7802,35 @@
"node": ">=8"
}
},
"node_modules/ws": {
"version": "8.18.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/xmlhttprequest-ssl": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
"integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/y18n": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",

View File

@@ -37,6 +37,7 @@
"react-redux": "^9.2.0",
"react-router-dom": "^7.14.1",
"recharts": "^3.8.1",
"socket.io-client": "^4.8.3",
"tailwind-merge": "^3.5.0",
"vite": "^6.2.0",
"vite-plugin-cesium": "^1.2.23",

View File

@@ -1,33 +1,47 @@
import React, { useEffect, useState } from "react";
import AgoraRTC, {
IMicrophoneAudioTrack,
} from "agora-rtc-sdk-ng";
import React, {
useEffect,
useRef,
useState,
} from "react";
import AgoraRTC from "agora-rtc-sdk-ng";
import {
Button,
Input,
Card,
Badge,
message,
Space,
} from "antd";
import { io } from "socket.io-client";
const APP_ID =
"62a87b833b7e48888aca06b625f88c14";
const CHANNEL =
"test_rtc_channel";
const TOKEN =
"007eJxTYGhZyLP1Ekt5av/jV1mPpi98fG75FqcXvDl7/wuUJjNeEVukwGBmlGhhnmRhbJxknmpiAQSJyYkGZklmRqZpFhbJhiYfZjJlNQQyMrDxcrIwMkAgiC/AUJJaXBJfVJIcn5yRmJeXmsPAAABcCyR4";
const TOKEN = "007eJxTYGhZyLP1Ekt5av/jV1mPpi98fG75FqcXvDl7/wuUJjNeEVukwGBmlGhhnmRhbJxknmpiAQSJyYkGZklmRqZpFhbJhiYfZjJlNQQyMrDxcrIwMkAgiC/AUJJaXBJfVJIcn5yRmJeXmsPAAABcCyR4";
const client = AgoraRTC.createClient({
mode: "rtc",
codec: "vp8",
});
export default function AgoraRtcTest() {
// websocket
const socket =
io("http://localhost:3001");
export default function AgoraRtcRoom() {
const [uid] = useState(() =>
String(Math.floor(Math.random() * 100000))
String(
Math.floor(
Math.random() * 100000
)
)
);
const [connected, setConnected] =
@@ -36,8 +50,17 @@ export default function AgoraRtcTest() {
const [published, setPublished] =
useState(false);
const [remoteUsers, setRemoteUsers] =
useState<any[]>([]);
const [msg, setMsg] =
useState("");
const [msgList, setMsgList] =
useState<string[]>([]);
const localVideoRef =
useRef<HTMLDivElement>(null);
const remoteVideoRef =
useRef<HTMLDivElement>(null);
useEffect(() => {
@@ -45,24 +68,40 @@ export default function AgoraRtcTest() {
try {
// 连接状态
// websocket 加入房间
socket.emit(
"join-room",
CHANNEL
);
// 收消息
socket.on(
"receive-message",
(data) => {
setMsgList(prev => [
...prev,
`${data.uid}: ${data.message}`
]);
}
);
// rtc 状态
client.on(
"connection-state-change",
(state) => {
console.log(
"连接状态:",
state
);
if (
state === "CONNECTED"
state ===
"CONNECTED"
) {
setConnected(true);
message.success(
"RTC 已连接"
setConnected(
true
);
}
}
@@ -71,10 +110,13 @@ export default function AgoraRtcTest() {
// 远端用户发布
client.on(
"user-published",
async (user, mediaType) => {
async (
user,
mediaType
) => {
console.log(
"收到远端流:",
"远端发布",
user.uid,
mediaType
);
@@ -84,40 +126,29 @@ export default function AgoraRtcTest() {
mediaType
);
// 视频
if (
mediaType === "audio"
mediaType ===
"video"
) {
user.audioTrack?.play();
message.success(
`收到用户 ${user.uid} 音频流`
user.videoTrack?.play(
remoteVideoRef.current!
);
}
setRemoteUsers([
...client.remoteUsers
]);
// 音频
if (
mediaType ===
"audio"
) {
user.audioTrack?.play();
}
}
);
// 用户离开
client.on(
"user-left",
(user) => {
console.log(
"用户离开:",
user.uid
);
setRemoteUsers([
...client.remoteUsers
]);
}
);
// 加入频道
// 加入 rtc
await client.join(
APP_ID,
CHANNEL,
@@ -125,17 +156,15 @@ export default function AgoraRtcTest() {
uid
);
console.log(
"加入频道成功"
setConnected(true);
message.success(
"RTC 已连接"
);
} catch (err) {
console.error(err);
message.error(
"初始化失败"
);
}
};
@@ -144,6 +173,8 @@ export default function AgoraRtcTest() {
return () => {
client.leave();
socket.disconnect();
};
}, []);
@@ -154,32 +185,34 @@ export default function AgoraRtcTest() {
try {
// 创建麦克风轨道
const micTrack:
IMicrophoneAudioTrack =
// 创建音频轨道
const audioTrack =
await AgoraRTC.createMicrophoneAudioTrack();
// 推流
// 创建视频轨道
const videoTrack =
await AgoraRTC.createCameraVideoTrack();
// 本地播放
videoTrack.play(
localVideoRef.current!
);
// 发布
await client.publish([
micTrack
audioTrack,
videoTrack,
]);
console.log(
"推流成功"
);
message.success(
"开始推流"
);
setPublished(true);
message.success(
"推流成功"
);
} catch (err) {
console.error(
"推流失败:",
err
);
console.error(err);
message.error(
"推流失败"
@@ -187,29 +220,47 @@ export default function AgoraRtcTest() {
}
};
// 发消息
const sendMessage =
() => {
if (!msg) return;
socket.emit(
"send-message",
{
roomId:
CHANNEL,
uid,
message:
msg,
}
);
setMsg("");
};
return (
<Card
title="Agora RTC 推拉流测试"
title="Agora RTC 音视频 + 聊天"
style={{
width: 700,
margin: "30px auto",
width: 1000,
margin:
"20px auto",
}}
>
<div
<Space
direction="vertical"
style={{
marginBottom: 12,
width: "100%",
}}
>
当前 UID:{uid}
</div>
<div
style={{
marginBottom: 12,
}}
>
<div>
UID:{uid}
</div>
<Badge
color={
@@ -224,63 +275,132 @@ export default function AgoraRtcTest() {
}
/>
</div>
<div
style={{
marginBottom: 20,
}}
>
<Badge
color={
published
? "green"
: "orange"
<Button
type="primary"
onClick={
startPublish
}
text={
disabled={
published
? "已推流"
: "未推流"
}
/>
>
开始音视频推流
</Button>
</div>
<div
style={{
display: "flex",
gap: 20,
}}
>
<Button
type="primary"
onClick={startPublish}
disabled={
!connected ||
published
}
>
开始推流
</Button>
<div>
<div
style={{
marginTop: 30,
}}
>
<h3>
远端用户:
</h3>
{remoteUsers.map(
(user) => (
<h3>
本地视频
</h3>
<div
key={user.uid}
>
用户:
{user.uid}
</div>
)
)}
ref={
localVideoRef
}
style={{
width: 320,
height: 240,
background:
"#000",
}}
/>
</div>
</div>
<div>
<h3>
远端视频
</h3>
<div
ref={
remoteVideoRef
}
style={{
width: 320,
height: 240,
background:
"#000",
}}
/>
</div>
</div>
<div>
<h3>
聊天
</h3>
<Space>
<Input
value={msg}
onChange={e =>
setMsg(
e.target.value
)
}
style={{
width: 300,
}}
/>
<Button
onClick={
sendMessage
}
>
发送
</Button>
</Space>
<div
style={{
marginTop: 20,
height: 200,
border:
"1px solid #ddd",
padding: 10,
overflowY:
"auto",
}}
>
{msgList.map(
(
item,
index
) => (
<div
key={
index
}
>
{
item
}
</div>
)
)}
</div>
</div>
</Space>
</Card>
);