我一直在构建一个音乐应用程序,今天我终于开始尝试在其中播放音乐。
作为环境设置的概要,我将音乐文件存储为MP3,并使用GridFS将其上传到MongoDB数据库中。然后,我使用socket.io服务器从MongoDB数据库下载块,并将它们作为单独的发射发送到前端,在那里由WebAudio API处理并计划播放。
当他们玩的时候,他们都按照正确的顺序,但每次(大概是在大块之间(都有一个非常小的故障或在同一个位置跳跃,我似乎无法摆脱。据我所知,它们都排在一起,所以我找不到它们之间有任何差距或重叠的原因。如有任何帮助,我们将不胜感激。这是代码:
套接字路由
socket.on('stream-audio', () => {
db.client.db("dev").collection('music.files').findOne({"metadata.songId": "3"}).then((result) =>{
const bucket = new GridFSBucket(db.client.db("dev"), {
bucketName: "music"
});
bucket.openDownloadStream(result._id).on('data',(chunk) => {
socket.emit('audio-chunk',chunk)
});
});
});
前端
//These variable are declared as object variables, hence all of the "this" keywords
context: new (window.AudioContext || window.webkitAudioContext)(),
freeTime: null,
numChunks: 0,
chunkTracker: [],
...
this.socket.on('audio-chunk', (chunk) => {
//Keeping track of chunk decoding status so that they don't get scheduled out of order
const chunkId = this.numChunks
this.chunkTracker.push({
id: chunkId,
complete: false,
});
this.numChunks += 1;
//Callback to the decodeAudioData function
const decodeCallback = (buffer) => {
var shouldExecute = false;
const trackIndex = this.chunkTracker.map((e) => e.id).indexOf(chunkId);
//Checking if either it's the first chunk or the previous chunk has completed
if(trackIndex !== 0){
const prevChunk = this.chunkTracker.filter((e) => e.id === (chunkId-1))
if (prevChunk[0].complete) {
shouldExecute = true;
}
} else {
shouldExecute = true;
}
//THIS IS THE ACTUAL WEB AUDIO API STUFF
if (shouldExecute) {
if (this.freeTime === null) {
this.freeTime = this.context.currentTime
}
const source = this.context.createBufferSource();
source.buffer = buffer
source.connect(this.context.destination)
if (this.context.currentTime >= this.freeTime){
source.start()
this.freeTime = this.context.currentTime + buffer.duration
} else {
source.start(this.freeTime)
this.freeTime += buffer.duration
}
//Update the tracker of the chunks that this one is complete
this.chunkTracker[trackIndex] = {id: chunkId, complete: true}
} else {
//If the previous chunk hasn't processed yet, check again in 50ms
setTimeout((passBuffer) => {
decodeCallback(passBuffer)
},50,buffer);
}
}
decodeCallback.bind(this);
this.context.decodeAudioData(chunk,decodeCallback);
});
任何帮助都将不胜感激,谢谢!
作为环境设置的概述,我将音乐文件存储为MP3,并使用GridFS上传到MongoDB数据库中。
如果你愿意,你可以这样做,但现在我们有像Minio这样的工具,使用更常见的API可以更容易地做到这一点。
然后我使用socket.io服务器从MongoDB数据库下载块,并将它们作为单独的发射发送到前端
不要走这条路。web套接字或Socket.IO的开销是没有原因的。正常的HTTP请求就可以了。
其中的由Web Audio API处理并计划播放。
您不能以这种方式进行流式传输。Web Audio API不支持有用的流,除非你碰巧有原始PCM块,而你没有。
据我所知,它们都排在一起,所以我找不到它们之间有任何间隙或重叠的原因。
有损编解码器不会为您提供准确的样本输出。尤其是MP3,如果你给它一些任意数量的样本,你最终会得到至少一个完整的MP3帧(~576个样本(输出。事实上,你需要在第一个音频帧之前的数据才能正常工作。如果你想解码一个流,你需要一个流开始。你不能用这种方式独立解码MP3。
幸运的是,该解决方案还简化了您的操作。只需从服务器返回HTTP流,并使用HTML音频元素<audio>
或new Audio(url)
。浏览器将处理所有缓冲。只要确保你的服务器能够处理范围请求,你就可以开始了。