代码拉取完成,页面将自动刷新
同步操作将从 jmfe/jsmpeg 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
const http = require('http');
const ws = require('ws');
const { URL } = require('url');
const Splitter = require('./size-split');
const prefix = `[YUV-Server] `;
class Server {
attachSource(source) {
const id = source.id;
if (id in this.sessions) {
this.sessions[id].add(source);
} else {
const set = (this.sessions[id] = new Set());
set.add(source);
}
console.log(`${prefix}attach new Session: ${id}`);
}
dettachSource(source) {
const id = source.id;
if (id in this.sessions && this.sessions[id].has(source)) {
this.sessions[id].delete(source);
console.log(`${prefix}detach new Session: ${id}`);
}
}
broadcast(id, chunk) {
if (id in this.sessions) {
const sess = this.sessions[id].values();
for (const s of sess) {
s.onMessage(chunk);
}
}
}
constructor(port) {
this.sessions = {};
this.publisher = {};
this.server = http
.createServer((req, res) => {
// 不超时
const url = req.url || '/';
if (!url.startsWith('/push')) {
res.statusCode = 400;
res.write(
'hello from yuv server, push stream to /push?id={id}&width={width}&height={height}',
);
res.end();
return;
}
const parsed = new URL('http://host'+ url);
let id = parsed.searchParams.get('id'),
width = parsed.searchParams.get('width'),
height = parsed.searchParams.get('height');
if (id == null || width == null || height == null) {
res.statusCode = 400;
res.write('id、width、height are required');
res.end();
return;
}
console.log(`${prefix}Stream connected: ${id}`);
res.connection.setTimeout(0);
const nwidth = parseInt(width);
const nheight = parseInt(height);
// var codedWidth = ((nwidth + 15) >> 4) << 4;
// Reading Frames of uncompressed yuv video file?
// https://raspberrypi.stackexchange.com/questions/28033/reading-frames-of-uncompressed-yuv-video-file
// YUV format is YUV420p which uses 1.5 bytes per pixel
const frameSize = (nwidth * nheight * 3) >> 1;
const stream = req.pipe(new Splitter(frameSize));
this.publisher[id] = {
width: nwidth,
height: nheight,
};
stream.on('data', c => {
this.broadcast(id, c);
});
req.on('end', () => {
console.log(`${prefix}Stream closed: ${id}`);
delete this.publisher[id];
});
})
.listen(port, () => {
console.log(
`${prefix}Listening for incomming YUV Stream on http://127.0.0.1:${port}`,
);
});
/**
* 使用 webSocket 拉取流
*/
this.wss = new ws.Server({
server: this.server,
verifyClient: (info, cb) => {
if (info.req.url && info.req.url.startsWith('/ws')) {
cb(true);
} else {
cb(false, undefined, 'use /ws/*');
}
},
});
// 新连接
this.wss.on('connection', (client, req) => {
const url = req.url;
const id = url.slice(4);
console.log(`${prefix}new player attached from ${url} - ${id}`);
if (!!this.publisher[id]) {
client.send(
JSON.stringify({
type: 'initial',
data: this.publisher[id],
}),
);
}
let buzy = false;
const source = {
id,
onMessage: data => {
if (client.readyState === client.OPEN) {
// 推送
if (buzy) {
return;
}
buzy = true;
client.send(data, { binary: true }, function ack() {
buzy = false;
});
}
},
};
this.attachSource(source);
client.on('message', data => {
console.log(`${prefix}incomming message from ${id}`, data);
switch (data.toString().trim()) {
}
});
client.on('close', () => {
console.log(`${prefix} player dettached: ${id}`);
this.dettachSource(source);
});
});
}
}
new Server(9999);
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。