如何在 NodeJS 中将音乐播放器同步到多个客户端

问题描述 投票:0回答:2

我编写了一个脚本,基本上可以将歌曲同步到多个客户端。问题是,客户端 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();
});

所以基本上,我需要帮助如何让它们同时运行。

提前谢谢您。

javascript node.js synchronization mp3
2个回答
1
投票

您需要做的第一件事是在将同步的两个客户端之间建立通信通道。考虑到您的延迟要求,我猜测这些客户端在物理上彼此靠近并且可能位于同一网络上。在这种情况下,您将受益于创建点对点 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”,其中连续测量延迟。更好的是,在每条计时消息上都搭载这一点。

这应该让您在时间上非常接近,足以满足大多数用例。


0
投票

可能有一个 MDN 规范,如 这篇文章 中提到的,但看起来示例很少,而且少数看起来是在付费墙后面

当前的尝试有很大的延迟,但希望提供更多的视角,也许有人可以补充这一点

socket.io cdn

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: "."
})
© www.soinside.com 2019 - 2024. All rights reserved.