MediaSource API问题:视频无法正常播放
你已经搞定了服务器到客户端的数据传输,这已经迈过了一大步!不过视频播放器没画面的问题,大概率和MediaSource的流媒体处理逻辑、视频格式或者数据分片有关,我给你几个可以排查的方向:
1. 先确认视频格式是否支持流式传输
MediaSource API只认碎片化MP4(fMP4),普通MP4把元数据(moov box)放在文件开头,这种格式没办法边传边播。你可以用ffmpeg把视频转成fMP4试试:
ffmpeg -i video.mp4 -movflags frag_keyframe+empty_moov video_fmp4.mp4
替换原视频后再测试,这是流式播放的基础,很多人都会忽略这一步。
2. 检查数据分片的正确性
你现在直接把fs.createReadStream的chunk推给客户端,但ReadStream的默认chunk是随机切割的,完全不考虑MP4的媒体片段结构。SourceBuffer需要的是完整、连续的媒体片段,不是随便切的二进制块。
可以试试:
- 改用按MP4的moof/mdat单元来分片读取,或者用专门的流媒体处理逻辑。
- 在客户端打印每个收到的chunk长度,对比原文件的分片是否合理,比如如果某个chunk太小,可能就是无效的片段。
3. 完善SourceBuffer的错误监听和状态判断
你的客户端代码里没有监听SourceBuffer的错误,很可能已经报错了你没看到。加上错误监听看看:
self.sourceBuffer.addEventListener('error', (err) => { console.error('SourceBuffer解析出错:', err); });
另外,在appendBuffer之前,一定要确保SourceBuffer不在updating状态,你的代码里已经有判断,但可以再加上日志,看看是不是一直处于updating状态导致数据堆在队列里:
case "Video_File": const data = new Uint8Array(payload.data); console.log('收到数据块,长度:', data.length, '队列长度:', self.queue.length, '是否在更新:', self.sourceBuffer.updating); if (self.sourceBuffer.updating || self.queue.length > 0) { self.queue.push(data); } else { try { self.sourceBuffer.appendBuffer(data); } catch(e) { console.error('appendBuffer失败:', e); } } break;
4. 调整MediaSource的初始化时机
你用了2秒的setTimeout来初始化,这有点靠运气。Magic Mirror的模块有domLoaded生命周期钩子,你可以把初始化逻辑移到那里,确保video元素已经完全挂载到DOM上:
domLoaded: function() { const self = this; self.mediaSource = new MediaSource(); self.queue = []; const videoElement = document.getElementById(self.identifier+"_player"); videoElement.src = window.URL.createObjectURL(self.mediaSource); self.mediaSource.addEventListener('sourceopen', function(e){ // 保留原有的sourceopen逻辑 self.sendSocketNotification("INITIATEDEVICES"); }, false); }
这样比固定延迟更可靠。
5. 验证数据传输的完整性
可以在服务器端先获取文件总大小,然后客户端统计接收的字节数,看看是不是丢包了:
服务器端:
const fs = require('fs'); const filePath = "./modules/MMM-MP4Player/video.mp4"; fs.stat(filePath, (err, stats) => { if (!err) { self.sendSocketNotification('Video_Total_Size', stats.size); } }); // 再发送数据流 const readStream = fs.createReadStream(filePath); readStream.addListener('data', function(data){ self.sendSocketNotification('Video_File',data); });
客户端:
socketNotificationReceived: function(notification, payload){ var self = this; switch(notification){ // ... 其他case case "Video_Total_Size": self.totalSize = payload; self.receivedSize = 0; break; case "Video_File": const data = new Uint8Array(payload.data); self.receivedSize += data.length; console.log('已接收:', self.receivedSize, '/', self.totalSize); // 保留原有的数据处理逻辑 break; } }
如果最后接收的大小和总大小不一致,那就是传输过程中丢包了,需要调整socket的传输配置。
内容的提问来源于stack exchange,提问作者Asim Siddiqui

