修复了直播界面看不到界面的问题
This commit is contained in:
parent
374c771578
commit
86b21d022a
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
.node_modules/
|
||||
**/node_modules/
|
||||
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# build output
|
||||
build/
|
||||
dist/
|
||||
**/build/
|
||||
**/dist/
|
||||
|
||||
# env
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# OS/IDE
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
.idea/
|
||||
.vscode/
|
||||
2
live-streaming/client/node_modules/.cache/.eslintcache
generated
vendored
2
live-streaming/client/node_modules/.cache/.eslintcache
generated
vendored
|
|
@ -1 +1 @@
|
|||
[{"C:\\Users\\Administrator\\Desktop\\Project\\Test\\live-streaming\\client\\src\\index.js":"1","C:\\Users\\Administrator\\Desktop\\Project\\Test\\live-streaming\\client\\src\\App.jsx":"2","C:\\Users\\Administrator\\Desktop\\Project\\Test\\live-streaming\\client\\src\\pages\\RoomPage.jsx":"3","C:\\Users\\Administrator\\Desktop\\Project\\Test\\live-streaming\\client\\src\\pages\\HomePage.jsx":"4","C:\\Users\\Administrator\\Desktop\\Project\\Test\\live-streaming\\client\\src\\services\\api.js":"5","C:\\Users\\Administrator\\Desktop\\Project\\Test\\live-streaming\\client\\src\\components\\RoomList.jsx":"6","C:\\Users\\Administrator\\Desktop\\Project\\Test\\live-streaming\\client\\src\\components\\CreateRoom.jsx":"7","C:\\Users\\Administrator\\Desktop\\Project\\Test\\live-streaming\\client\\src\\components\\LivePlayer.jsx":"8","C:\\Users\\Administrator\\Desktop\\Project\\Test\\live-streaming\\client\\src\\components\\StreamInfo.jsx":"9","C:\\Users\\Administrator\\Desktop\\Project\\Test\\live-streaming\\client\\src\\components\\RoomCard.jsx":"10"},{"size":310,"mtime":1765701583991,"results":"11","hashOfConfig":"12"},{"size":334,"mtime":1765701610578,"results":"13","hashOfConfig":"12"},{"size":2127,"mtime":1765701673204,"results":"14","hashOfConfig":"12"},{"size":1724,"mtime":1765701625879,"results":"15","hashOfConfig":"12"},{"size":533,"mtime":1765701603938,"results":"16","hashOfConfig":"12"},{"size":476,"mtime":1765701614401,"results":"17","hashOfConfig":"12"},{"size":1607,"mtime":1765701629855,"results":"18","hashOfConfig":"12"},{"size":3669,"mtime":1765701660353,"results":"19","hashOfConfig":"12"},{"size":1389,"mtime":1765701642070,"results":"20","hashOfConfig":"12"},{"size":619,"mtime":1765701612898,"results":"21","hashOfConfig":"12"},{"filePath":"22","messages":"23","suppressedMessages":"24","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"j0zk4s",{"filePath":"25","messages":"26","suppressedMessages":"27","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"28","messages":"29","suppressedMessages":"30","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"31","messages":"32","suppressedMessages":"33","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"34","messages":"35","suppressedMessages":"36","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"37","messages":"38","suppressedMessages":"39","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"40","messages":"41","suppressedMessages":"42","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"43","messages":"44","suppressedMessages":"45","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"46","messages":"47","suppressedMessages":"48","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"49","messages":"50","suppressedMessages":"51","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"C:\\Users\\Administrator\\Desktop\\Project\\Test\\live-streaming\\client\\src\\index.js",[],[],"C:\\Users\\Administrator\\Desktop\\Project\\Test\\live-streaming\\client\\src\\App.jsx",[],[],"C:\\Users\\Administrator\\Desktop\\Project\\Test\\live-streaming\\client\\src\\pages\\RoomPage.jsx",[],[],"C:\\Users\\Administrator\\Desktop\\Project\\Test\\live-streaming\\client\\src\\pages\\HomePage.jsx",[],[],"C:\\Users\\Administrator\\Desktop\\Project\\Test\\live-streaming\\client\\src\\services\\api.js",[],[],"C:\\Users\\Administrator\\Desktop\\Project\\Test\\live-streaming\\client\\src\\components\\RoomList.jsx",[],[],"C:\\Users\\Administrator\\Desktop\\Project\\Test\\live-streaming\\client\\src\\components\\CreateRoom.jsx",[],[],"C:\\Users\\Administrator\\Desktop\\Project\\Test\\live-streaming\\client\\src\\components\\LivePlayer.jsx",[],[],"C:\\Users\\Administrator\\Desktop\\Project\\Test\\live-streaming\\client\\src\\components\\StreamInfo.jsx",[],[],"C:\\Users\\Administrator\\Desktop\\Project\\Test\\live-streaming\\client\\src\\components\\RoomCard.jsx",[],[]]
|
||||
[{"C:\\Users\\Administrator\\Desktop\\zhibo_1\\live-streaming\\client\\src\\index.js":"1","C:\\Users\\Administrator\\Desktop\\zhibo_1\\live-streaming\\client\\src\\App.jsx":"2","C:\\Users\\Administrator\\Desktop\\zhibo_1\\live-streaming\\client\\src\\pages\\HomePage.jsx":"3","C:\\Users\\Administrator\\Desktop\\zhibo_1\\live-streaming\\client\\src\\pages\\RoomPage.jsx":"4","C:\\Users\\Administrator\\Desktop\\zhibo_1\\live-streaming\\client\\src\\services\\api.js":"5","C:\\Users\\Administrator\\Desktop\\zhibo_1\\live-streaming\\client\\src\\components\\CreateRoom.jsx":"6","C:\\Users\\Administrator\\Desktop\\zhibo_1\\live-streaming\\client\\src\\components\\LivePlayer.jsx":"7","C:\\Users\\Administrator\\Desktop\\zhibo_1\\live-streaming\\client\\src\\components\\RoomList.jsx":"8","C:\\Users\\Administrator\\Desktop\\zhibo_1\\live-streaming\\client\\src\\components\\StreamInfo.jsx":"9","C:\\Users\\Administrator\\Desktop\\zhibo_1\\live-streaming\\client\\src\\components\\RoomCard.jsx":"10"},{"size":310,"mtime":1765770657007,"results":"11","hashOfConfig":"12"},{"size":334,"mtime":1765770657002,"results":"13","hashOfConfig":"12"},{"size":1724,"mtime":1765770657008,"results":"14","hashOfConfig":"12"},{"size":2127,"mtime":1765770657008,"results":"15","hashOfConfig":"12"},{"size":533,"mtime":1765770657009,"results":"16","hashOfConfig":"12"},{"size":1607,"mtime":1765770657003,"results":"17","hashOfConfig":"12"},{"size":4682,"mtime":1765786910702,"results":"18","hashOfConfig":"12"},{"size":476,"mtime":1765770657005,"results":"19","hashOfConfig":"12"},{"size":1389,"mtime":1765770657005,"results":"20","hashOfConfig":"12"},{"size":619,"mtime":1765770657004,"results":"21","hashOfConfig":"12"},{"filePath":"22","messages":"23","suppressedMessages":"24","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"1w1jria",{"filePath":"25","messages":"26","suppressedMessages":"27","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"28","messages":"29","suppressedMessages":"30","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"31","messages":"32","suppressedMessages":"33","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"34","messages":"35","suppressedMessages":"36","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"37","messages":"38","suppressedMessages":"39","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"40","messages":"41","suppressedMessages":"42","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"43","messages":"44","suppressedMessages":"45","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"46","messages":"47","suppressedMessages":"48","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"49","messages":"50","suppressedMessages":"51","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"C:\\Users\\Administrator\\Desktop\\zhibo_1\\live-streaming\\client\\src\\index.js",[],[],"C:\\Users\\Administrator\\Desktop\\zhibo_1\\live-streaming\\client\\src\\App.jsx",[],[],"C:\\Users\\Administrator\\Desktop\\zhibo_1\\live-streaming\\client\\src\\pages\\HomePage.jsx",[],[],"C:\\Users\\Administrator\\Desktop\\zhibo_1\\live-streaming\\client\\src\\pages\\RoomPage.jsx",[],[],"C:\\Users\\Administrator\\Desktop\\zhibo_1\\live-streaming\\client\\src\\services\\api.js",[],[],"C:\\Users\\Administrator\\Desktop\\zhibo_1\\live-streaming\\client\\src\\components\\CreateRoom.jsx",[],[],"C:\\Users\\Administrator\\Desktop\\zhibo_1\\live-streaming\\client\\src\\components\\LivePlayer.jsx",[],[],"C:\\Users\\Administrator\\Desktop\\zhibo_1\\live-streaming\\client\\src\\components\\RoomList.jsx",[],[],"C:\\Users\\Administrator\\Desktop\\zhibo_1\\live-streaming\\client\\src\\components\\StreamInfo.jsx",[],[],"C:\\Users\\Administrator\\Desktop\\zhibo_1\\live-streaming\\client\\src\\components\\RoomCard.jsx",[],[]]
|
||||
BIN
live-streaming/client/node_modules/.cache/default-development/0.pack
generated
vendored
BIN
live-streaming/client/node_modules/.cache/default-development/0.pack
generated
vendored
Binary file not shown.
BIN
live-streaming/client/node_modules/.cache/default-development/index.pack
generated
vendored
BIN
live-streaming/client/node_modules/.cache/default-development/index.pack
generated
vendored
Binary file not shown.
|
|
@ -5,26 +5,38 @@ import Hls from 'hls.js';
|
|||
function LivePlayer({ room }) {
|
||||
const videoRef = useRef(null);
|
||||
const playerRef = useRef(null);
|
||||
const triedAltFlvRef = useRef(false);
|
||||
const [error, setError] = useState(null);
|
||||
const [playerType, setPlayerType] = useState(null);
|
||||
|
||||
const flvUrl = room?.streamUrls?.flv;
|
||||
const hlsUrl = room?.streamUrls?.hls;
|
||||
|
||||
useEffect(() => {
|
||||
if (!room?.isLive || !room?.streamUrls) return;
|
||||
if (!room?.isLive || !flvUrl) return;
|
||||
|
||||
triedAltFlvRef.current = false;
|
||||
|
||||
const video = videoRef.current;
|
||||
const { flv: flvUrl, hls: hlsUrl } = room.streamUrls;
|
||||
|
||||
// 优先使用 FLV (低延迟)
|
||||
if (flvjs.isSupported()) {
|
||||
console.log('使用 FLV 播放器');
|
||||
setPlayerType('FLV');
|
||||
const getAltFlvUrl = (url) => {
|
||||
try {
|
||||
const u = new URL(url);
|
||||
if (u.pathname.startsWith('/__defaultVhost__/')) return null;
|
||||
u.pathname = `/__defaultVhost__${u.pathname}`;
|
||||
return u.toString();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const createFlvPlayer = (url) => {
|
||||
const player = flvjs.createPlayer({
|
||||
type: 'flv',
|
||||
url: flvUrl,
|
||||
url,
|
||||
isLive: true
|
||||
}, {
|
||||
enableWorker: true,
|
||||
enableWorker: false,
|
||||
enableStashBuffer: false,
|
||||
stashInitialSize: 128
|
||||
});
|
||||
|
|
@ -32,9 +44,36 @@ function LivePlayer({ room }) {
|
|||
player.attachMediaElement(video);
|
||||
player.load();
|
||||
player.play().catch(console.error);
|
||||
return player;
|
||||
};
|
||||
|
||||
// 优先使用 FLV (低延迟)
|
||||
if (flvjs.isSupported()) {
|
||||
console.log('使用 FLV 播放器');
|
||||
setPlayerType('FLV');
|
||||
|
||||
const altFlvUrl = getAltFlvUrl(flvUrl);
|
||||
let player = createFlvPlayer(flvUrl);
|
||||
|
||||
player.on(flvjs.Events.ERROR, (type, detail) => {
|
||||
console.error('FLV 错误:', type, detail);
|
||||
|
||||
if (!triedAltFlvRef.current && altFlvUrl) {
|
||||
triedAltFlvRef.current = true;
|
||||
setError('播放出错,正在切换线路...');
|
||||
|
||||
try {
|
||||
player.destroy();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
player = createFlvPlayer(altFlvUrl);
|
||||
playerRef.current = player;
|
||||
setError(null);
|
||||
return;
|
||||
}
|
||||
|
||||
setError('播放出错,正在重试...');
|
||||
// 3秒后重试
|
||||
setTimeout(() => {
|
||||
|
|
@ -48,7 +87,7 @@ function LivePlayer({ room }) {
|
|||
playerRef.current = player;
|
||||
}
|
||||
// 降级到 HLS
|
||||
else if (Hls.isSupported()) {
|
||||
else if (hlsUrl && Hls.isSupported()) {
|
||||
console.log('使用 HLS 播放器');
|
||||
setPlayerType('HLS');
|
||||
|
||||
|
|
@ -73,7 +112,7 @@ function LivePlayer({ room }) {
|
|||
playerRef.current = hls;
|
||||
}
|
||||
// 原生 HLS (Safari)
|
||||
else if (video.canPlayType('application/vnd.apple.mpegurl')) {
|
||||
else if (hlsUrl && video.canPlayType('application/vnd.apple.mpegurl')) {
|
||||
console.log('使用原生 HLS');
|
||||
setPlayerType('Native HLS');
|
||||
video.src = hlsUrl;
|
||||
|
|
@ -91,7 +130,7 @@ function LivePlayer({ room }) {
|
|||
playerRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [room]);
|
||||
}, [room?.isLive, flvUrl, hlsUrl]);
|
||||
|
||||
if (!room?.isLive) {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -30,14 +30,14 @@ vhost __defaultVhost__ {
|
|||
# HTTP-FLV 配置 (低延迟)
|
||||
http_remux {
|
||||
enabled on;
|
||||
mount [vhost]/[app]/[stream].flv;
|
||||
mount [app]/[stream].flv;
|
||||
}
|
||||
|
||||
# HTTP 回调配置
|
||||
http_hooks {
|
||||
enabled on;
|
||||
on_publish http://api-server:3001/api/srs/on_publish;
|
||||
on_unpublish http://api-server:3001/api/srs/on_unpublish;
|
||||
on_publish http://localhost:3001/api/srs/on_publish;
|
||||
on_unpublish http://localhost:3001/api/srs/on_unpublish;
|
||||
}
|
||||
|
||||
# GOP 缓存,提高首屏速度
|
||||
|
|
|
|||
125
live-streaming/node_modules/.package-lock.json
generated
vendored
125
live-streaming/node_modules/.package-lock.json
generated
vendored
|
|
@ -35,7 +35,6 @@
|
|||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
|
|
@ -1129,7 +1128,6 @@
|
|||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
|
|
@ -1318,6 +1316,14 @@
|
|||
"baseline-browser-mapping": "dist/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/basic-auth-connect": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/basic-auth-connect/-/basic-auth-connect-1.1.0.tgz",
|
||||
"integrity": "sha512-rKcWjfiRZ3p5WS9e5q6msXa07s6DaFAMXoyowV+mb2xQG+oYdw2QEUyKi0Xp95JvXzShlM+oGy5QuqSK6TfC1Q==",
|
||||
"dependencies": {
|
||||
"tsscmp": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||
|
|
@ -1399,7 +1405,6 @@
|
|||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
|
|
@ -1514,7 +1519,6 @@
|
|||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
|
|
@ -1622,7 +1626,6 @@
|
|||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
|
|
@ -1635,7 +1638,6 @@
|
|||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
|
|
@ -1768,6 +1770,14 @@
|
|||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/dateformat": {
|
||||
"version": "4.6.3",
|
||||
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
|
||||
"integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
|
|
@ -2281,22 +2291,6 @@
|
|||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"ideallyInert": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
|
|
@ -2444,7 +2438,6 @@
|
|||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
|
|
@ -2517,6 +2510,14 @@
|
|||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/http2-express": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/http2-express/-/http2-express-1.1.0.tgz",
|
||||
"integrity": "sha512-rEKh5J8KBh76SZ9ejs4q2SQV5X5DA7pMamuGod2ifPLCRcgm8Ru1awbyPPr56JzzMpUY+MKc2NlUxsGpaaqO4Q==",
|
||||
"engines": {
|
||||
"node": ">= 20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/human-signals": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
||||
|
|
@ -3511,6 +3512,11 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
||||
|
|
@ -3673,6 +3679,28 @@
|
|||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
"version": "2.1.6",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz",
|
||||
"integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==",
|
||||
"bin": {
|
||||
"mkdirp": "dist/cjs/src/bin.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
|
|
@ -3702,6 +3730,28 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-media-server": {
|
||||
"version": "2.7.4",
|
||||
"resolved": "https://registry.npmjs.org/node-media-server/-/node-media-server-2.7.4.tgz",
|
||||
"integrity": "sha512-4nsqfukfnI6tY7d1deqBQSj3KywOiNVlPQkY0tLIjF62rzXo1SDXJXgUU+cVs5e06uO4x/55O4SiAaRH3li/Vg==",
|
||||
"dependencies": {
|
||||
"basic-auth-connect": "^1.1.0",
|
||||
"chalk": "^4.1.2",
|
||||
"dateformat": "^4.6.3",
|
||||
"express": "^4.21.1",
|
||||
"http2-express": "^1.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"minimist": "^1.2.8",
|
||||
"mkdirp": "^2.1.6",
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"bin": {
|
||||
"node-media-server": "bin/app.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.27",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
|
||||
|
|
@ -4791,7 +4841,6 @@
|
|||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
|
|
@ -4867,6 +4916,14 @@
|
|||
"nodetouch": "bin/nodetouch.js"
|
||||
}
|
||||
},
|
||||
"node_modules/tsscmp": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz",
|
||||
"integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==",
|
||||
"engines": {
|
||||
"node": ">=0.6.x"
|
||||
}
|
||||
},
|
||||
"node_modules/type-detect": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
|
||||
|
|
@ -5068,6 +5125,26 @@
|
|||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.18.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
||||
"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/y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
|
|
|
|||
110
live-streaming/package-lock.json
generated
110
live-streaming/package-lock.json
generated
|
|
@ -11,6 +11,7 @@
|
|||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
"node-media-server": "^2.7.0",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -51,7 +52,6 @@
|
|||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
|
|
@ -1145,7 +1145,6 @@
|
|||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
|
|
@ -1334,6 +1333,14 @@
|
|||
"baseline-browser-mapping": "dist/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/basic-auth-connect": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/basic-auth-connect/-/basic-auth-connect-1.1.0.tgz",
|
||||
"integrity": "sha512-rKcWjfiRZ3p5WS9e5q6msXa07s6DaFAMXoyowV+mb2xQG+oYdw2QEUyKi0Xp95JvXzShlM+oGy5QuqSK6TfC1Q==",
|
||||
"dependencies": {
|
||||
"tsscmp": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||
|
|
@ -1415,7 +1422,6 @@
|
|||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
|
|
@ -1530,7 +1536,6 @@
|
|||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
|
|
@ -1638,7 +1643,6 @@
|
|||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
|
|
@ -1651,7 +1655,6 @@
|
|||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
|
|
@ -1784,6 +1787,14 @@
|
|||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/dateformat": {
|
||||
"version": "4.6.3",
|
||||
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
|
||||
"integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
|
|
@ -2459,7 +2470,6 @@
|
|||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
|
|
@ -2532,6 +2542,14 @@
|
|||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/http2-express": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/http2-express/-/http2-express-1.1.0.tgz",
|
||||
"integrity": "sha512-rEKh5J8KBh76SZ9ejs4q2SQV5X5DA7pMamuGod2ifPLCRcgm8Ru1awbyPPr56JzzMpUY+MKc2NlUxsGpaaqO4Q==",
|
||||
"engines": {
|
||||
"node": ">= 20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/human-signals": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
||||
|
|
@ -3526,6 +3544,11 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
||||
|
|
@ -3688,6 +3711,28 @@
|
|||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
"version": "2.1.6",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz",
|
||||
"integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==",
|
||||
"bin": {
|
||||
"mkdirp": "dist/cjs/src/bin.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
|
|
@ -3717,6 +3762,28 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-media-server": {
|
||||
"version": "2.7.4",
|
||||
"resolved": "https://registry.npmjs.org/node-media-server/-/node-media-server-2.7.4.tgz",
|
||||
"integrity": "sha512-4nsqfukfnI6tY7d1deqBQSj3KywOiNVlPQkY0tLIjF62rzXo1SDXJXgUU+cVs5e06uO4x/55O4SiAaRH3li/Vg==",
|
||||
"dependencies": {
|
||||
"basic-auth-connect": "^1.1.0",
|
||||
"chalk": "^4.1.2",
|
||||
"dateformat": "^4.6.3",
|
||||
"express": "^4.21.1",
|
||||
"http2-express": "^1.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"minimist": "^1.2.8",
|
||||
"mkdirp": "^2.1.6",
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"bin": {
|
||||
"node-media-server": "bin/app.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.27",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
|
||||
|
|
@ -4806,7 +4873,6 @@
|
|||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
|
|
@ -4882,6 +4948,14 @@
|
|||
"nodetouch": "bin/nodetouch.js"
|
||||
}
|
||||
},
|
||||
"node_modules/tsscmp": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz",
|
||||
"integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==",
|
||||
"engines": {
|
||||
"node": ">=0.6.x"
|
||||
}
|
||||
},
|
||||
"node_modules/type-detect": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
|
||||
|
|
@ -5083,6 +5157,26 @@
|
|||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.18.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
||||
"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/y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
"node-media-server": "^2.7.0",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,59 @@
|
|||
require('dotenv').config();
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const NodeMediaServer = require('node-media-server');
|
||||
const roomsRouter = require('./routes/rooms');
|
||||
const srsRouter = require('./routes/srs');
|
||||
const errorHandler = require('./middleware/errorHandler');
|
||||
const roomStore = require('./store/roomStore');
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3001;
|
||||
|
||||
const startMediaServer = () => {
|
||||
const host = process.env.SRS_HOST || 'localhost';
|
||||
const rtmpPort = Number(process.env.SRS_RTMP_PORT || 1935);
|
||||
const httpPort = Number(process.env.SRS_HTTP_PORT || 8080);
|
||||
|
||||
try {
|
||||
const nms = new NodeMediaServer({
|
||||
rtmp: {
|
||||
port: rtmpPort,
|
||||
chunk_size: 60000,
|
||||
gop_cache: true,
|
||||
ping: 30,
|
||||
ping_timeout: 60
|
||||
},
|
||||
http: {
|
||||
port: httpPort,
|
||||
allow_origin: '*'
|
||||
}
|
||||
});
|
||||
|
||||
nms.on('prePublish', (id, streamPath) => {
|
||||
const parts = String(streamPath || '').split('/').filter(Boolean);
|
||||
const streamKey = parts[1];
|
||||
if (streamKey) roomStore.setLiveStatus(streamKey, true);
|
||||
});
|
||||
|
||||
nms.on('donePublish', (id, streamPath) => {
|
||||
const parts = String(streamPath || '').split('/').filter(Boolean);
|
||||
const streamKey = parts[1];
|
||||
if (streamKey) roomStore.setLiveStatus(streamKey, false);
|
||||
});
|
||||
|
||||
nms.run();
|
||||
|
||||
console.log(`Media Server RTMP: rtmp://${host}:${rtmpPort}/live (Stream Key = streamKey)`);
|
||||
console.log(`Media Server HTTP-FLV: http://${host}:${httpPort}/live/{streamKey}.flv`);
|
||||
} catch (e) {
|
||||
console.error('[Media Server] Failed to start embedded media server:', e.message);
|
||||
console.error('[Media Server] If ports 1935/8080 are in use, stop the occupying process or change SRS_RTMP_PORT/SRS_HTTP_PORT.');
|
||||
}
|
||||
};
|
||||
|
||||
startMediaServer();
|
||||
|
||||
// 中间件
|
||||
app.use(cors({
|
||||
origin: process.env.CLIENT_URL || 'http://localhost:3000',
|
||||
|
|
|
|||
|
|
@ -3,14 +3,18 @@ const router = express.Router();
|
|||
const roomStore = require('../store/roomStore');
|
||||
const { validateRoom } = require('../middleware/validate');
|
||||
const { generateStreamUrls } = require('../utils/streamUrl');
|
||||
const { getActiveStreamKeys } = require('../utils/srsHttpApi');
|
||||
|
||||
// GET /api/rooms - 获取所有房间
|
||||
router.get('/', (req, res) => {
|
||||
router.get('/', async (req, res) => {
|
||||
const rooms = roomStore.getAll();
|
||||
const activeStreamKeys = await getActiveStreamKeys({ app: 'live' });
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: rooms.map(room => ({
|
||||
...room,
|
||||
isLive: activeStreamKeys.size ? activeStreamKeys.has(room.streamKey) : room.isLive,
|
||||
streamUrls: generateStreamUrls(room.streamKey)
|
||||
}))
|
||||
});
|
||||
|
|
@ -31,7 +35,7 @@ router.post('/', validateRoom, (req, res) => {
|
|||
});
|
||||
|
||||
// GET /api/rooms/:id - 获取单个房间
|
||||
router.get('/:id', (req, res) => {
|
||||
router.get('/:id', async (req, res) => {
|
||||
const room = roomStore.getById(req.params.id);
|
||||
|
||||
if (!room) {
|
||||
|
|
@ -45,6 +49,7 @@ router.get('/:id', (req, res) => {
|
|||
success: true,
|
||||
data: {
|
||||
...room,
|
||||
isLive: (await getActiveStreamKeys({ app: 'live' })).has(room.streamKey) || room.isLive,
|
||||
streamUrls: generateStreamUrls(room.streamKey)
|
||||
}
|
||||
});
|
||||
|
|
|
|||
88
live-streaming/server/utils/srsHttpApi.js
Normal file
88
live-streaming/server/utils/srsHttpApi.js
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
const http = require('http');
|
||||
const https = require('https');
|
||||
|
||||
const requestJson = (url, { timeoutMs = 2000 } = {}) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const u = new URL(url);
|
||||
const lib = u.protocol === 'https:' ? https : http;
|
||||
|
||||
const req = lib.request(
|
||||
{
|
||||
protocol: u.protocol,
|
||||
hostname: u.hostname,
|
||||
port: u.port,
|
||||
path: `${u.pathname}${u.search}`,
|
||||
method: 'GET'
|
||||
},
|
||||
(res) => {
|
||||
let raw = '';
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', (chunk) => (raw += chunk));
|
||||
res.on('end', () => {
|
||||
if (res.statusCode && (res.statusCode < 200 || res.statusCode >= 300)) {
|
||||
return reject(new Error(`SRS HTTP API status ${res.statusCode}`));
|
||||
}
|
||||
|
||||
try {
|
||||
resolve(JSON.parse(raw || '{}'));
|
||||
} catch (e) {
|
||||
reject(new Error('Invalid JSON from SRS HTTP API'));
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
req.on('error', reject);
|
||||
req.setTimeout(timeoutMs, () => {
|
||||
req.destroy(new Error('SRS HTTP API request timeout'));
|
||||
});
|
||||
req.end();
|
||||
});
|
||||
};
|
||||
|
||||
const normalizeStreamName = (s) => {
|
||||
if (!s) return null;
|
||||
if (typeof s === 'string') return s;
|
||||
return s.stream || s.name || s.id || null;
|
||||
};
|
||||
|
||||
const isPublishActive = (streamObj) => {
|
||||
const publish = streamObj && streamObj.publish;
|
||||
if (!publish) return false;
|
||||
if (typeof publish.active === 'boolean') return publish.active;
|
||||
return Boolean(publish.cid);
|
||||
};
|
||||
|
||||
let warnedOnce = false;
|
||||
|
||||
const getActiveStreamKeys = async ({ app = 'live' } = {}) => {
|
||||
const host = process.env.SRS_HOST || 'localhost';
|
||||
const apiPort = process.env.SRS_API_PORT || 1985;
|
||||
|
||||
const url = `http://${host}:${apiPort}/api/v1/streams?count=100`;
|
||||
|
||||
try {
|
||||
const payload = await requestJson(url, { timeoutMs: 1500 });
|
||||
const streams = payload.streams || payload.data?.streams || [];
|
||||
|
||||
const active = new Set();
|
||||
for (const s of streams) {
|
||||
if (app && s.app && s.app !== app) continue;
|
||||
if (!isPublishActive(s)) continue;
|
||||
const name = normalizeStreamName(s);
|
||||
if (name) active.add(name);
|
||||
}
|
||||
|
||||
return active;
|
||||
} catch (e) {
|
||||
if (!warnedOnce) {
|
||||
warnedOnce = true;
|
||||
console.warn(`[SRS] HTTP API unavailable at ${url}. Will fallback to callbacks/in-memory status.`);
|
||||
}
|
||||
return new Set();
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getActiveStreamKeys
|
||||
};
|
||||
|
|
@ -6,7 +6,7 @@ const generateStreamUrls = (streamKey) => {
|
|||
|
||||
return {
|
||||
// 推流地址 (给主播用)
|
||||
rtmp: `rtmp://${host}:${rtmpPort}/live/${streamKey}`,
|
||||
rtmp: `rtmp://${host}:${rtmpPort}/live`,
|
||||
|
||||
// 播放地址 (给观众用)
|
||||
flv: `http://${host}:${httpPort}/live/${streamKey}.flv`,
|
||||
|
|
|
|||
13
package-lock.json
generated
Normal file
13
package-lock.json
generated
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"name": "zhibo_1",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "zhibo_1",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC"
|
||||
}
|
||||
}
|
||||
}
|
||||
12
package.json
Normal file
12
package.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"name": "zhibo_1",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user