我编写了一个脚本,基本上可以将歌曲同步到多个客户端。问题是,客户端 1 总是比客户端 2 更深入地了解歌曲。
当客户端1正在播放歌曲时,客户端2按下按钮,客户端1将当前时间发送给客户端2,以便客户端2可以拾取时间并播放mp3。
我尝试过预加载mp3,这样客户端2就不会在歌曲开始时浪费时间加载文件。但仍然晚了+/- 0.5秒。
//客户端
$(document).ready(function() {
var currentId = "";
document.getElementById("audio").load();
$.ajax({
url: '/retrieveId',
type: 'post',
success: function(data) {
currentId = data;
console.log(currentId);
}
});
$('#host').click(function () {
$('#audio')[0].play();
var interval2 = setInterval(function() {
var currTime = document.getElementById('audio').currentTime;
$.ajax({
url: '/startedPlaying',
type: 'post',
data: { time: currTime, minute: '10' },
success: function(data) {
}
});
},10);
});
$('#client').click(function() {
document.getElementById("audio").play();
$.ajax({
url: '/retrieveTime',
type: 'post',
success: function(data) {
document.getElementById("audio").currentTime = data;
}
});
})
});
//服务器
app.use(bodyParser.urlencoded({ extended: true }));
app.get("/", function(req, res) {
res.sendFile(path.join(__dirname + "/index.html"));
}).listen(12340);
app.post("/startedPlaying", function(req, res) {
currTime = req.body.time;
res.end();
});
app.post("/retrieveTime", function(req, res) {
res.send(currTime);
res.end();
});
app.post("/retrieveId", function(req, res) {
id++;
res.send(id.toString());
res.end();
});
所以基本上,我需要帮助如何让它们同时运行。
提前谢谢您。
您需要做的第一件事是在将同步的两个客户端之间建立通信通道。考虑到您的延迟要求,我猜测这些客户端在物理上彼此靠近并且可能位于同一网络上。在这种情况下,您将受益于创建点对点 WebRTC 连接以及它们之间的数据通道。一般来说,客户端可以通过他们共享的网络协商连接,而不必将数据发送到服务器并返回,这减少了该数据通道的延迟。
.buffered
来确定已缓冲的时间范围。您通常可以依靠 canplaythrough
事件 来确定播放器何时缓冲到足以播放而无需再次重新缓冲。
然后,决定哪一端是“主”,所有同步数据都来自哪里。开始双面播放。
currentTime
发送给其他客户端。收到后,这些客户应该检查自己的currentTime
,看看距离有多远。从那里开始,他们应该计算如何赶上。例如,如果主服务器位于100.100
,从属客户端位于100.000
,则需要弥补 100 毫秒。如果您将 playbackRate
设置为 1.01
,您将在 10 秒内弥补该时间。或者,1.02
,您将在 5 秒内完成。
您需要想出一些公式来确定不同步的严重程度并轻松修复它。您还需要对该播放速度进行一些合理的限制。 (例如,您可能不希望速度快/慢超过 10% 来同步。)此外,如果两者分离得足够严重,您可能会选择暂停一个,在另一个上设置
currentTime
(这通常会导致一些重新缓冲)并重新开始。
您可以做出的改进是还可以确定主/从设备之间的延迟。如果主设备说“现在是 12.345 秒”,并且您在 100 毫秒后收到该消息,则该时间戳实际上是 12.445。您可以实施某种“ping”,其中连续测量延迟。更好的是,在每条计时消息上都搭载这一点。
这应该让您在时间上非常接近,足以满足大多数用例。
可能有一个 MDN 规范,如 这篇文章 中提到的,但看起来示例很少,而且少数看起来是在付费墙后面
当前的尝试有很大的延迟,但希望提供更多的视角,也许有人可以补充这一点
index.html
<!DOCTYPE html>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<h1>DEMO</h1>
<script src="/socket.io.js"></script>
<audio src="./test.m4a" autoPlay="" playsinline="" controls></audio>
<audio id="player" src="./test.m4a" autoPlay="" playsinline=""></audio>
<div id="button" class="button">PLAY</div>
<script>
async function main(){
// connect
const socket = io()
const button = document.getElementById("button")
// on m4a loaded show play button
player.addEventListener("canplaythrough",()=>{
document.getElementById("button").style.display="block"
})
if (player.readyState > 3) {
document.getElementById("player").style.display="block"
}
// on play
button.addEventListener("click", async ()=>{
socket.emit("message")
// sound.play()
// sound.pause()
})
// on start recieved
socket.on("message", (message) => {
console.log(message)
player.play()
})
socket.on("seek",()=>{
console.log("seek")
player.currentTime = 1
})
}
main()
</script>
<style>
.button{
display : none;
font-size: 15px;
}
</style>
</html>
服务器.js
const express = require('express')
const app = express()
const http = require('http') // Http server that can be extended
const httpServer = http.createServer(app) // Serve API with http server
const { Server } = require("socket.io") // Crete nice websockets server
const io = new Server(httpServer,{cors: // Extend http server with websockets
{origins: ["http://localhost:5173"], methods: ["GET", "POST"]}}) // Allow dev server to access to websockets too
io.events = {"info":"info"} // Define events for ws
const cors = require('cors')
async function main(config) {
const viewPath = config.dist
const port = config.port
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(cors())
app.use(express.static(viewPath))
// Websockets
io.on('connection', (socket) => {
var address = socket?.handshake?.address
socket.on("message",()=>{
console.log("Recieved message")
io.emit("message", "hey")
setTimeout(()=>{io.emit("seek", "hey")}, 4000)
})
socket.conn.on("close", (reason) => {
console.log(`Client ${address} disconnected, Reason: ${reason}`)
})
})
httpServer.listen(port, ()=>{
console.log(`Running the server on port ${port}`)
})
}
// module.exports = main
main({
port : 8080,
dist: "."
})