多音轨电影同步播放缓冲卡顿及跨浏览器兼容问题咨询
多轨媒体播放同步与缓冲问题解决求助
我有一部1.5小时的电影,译制成了多种语言,每种译制版都是5.1杜比音效,音乐、音效和人声基本在独立声道,已分离为前置、中置、后置的单独文件。目标是实现特定人群用对应语言发声,观众可切换语言的观影体验。
因为熟悉且跨平台,我用JavaScript开发了音轨切换逻辑,但清楚Web环境并不适合处理同步大量长时长媒体轨道,这是目前唯一能完成开发的方案。
当前实现细节
媒体转码配置
- 视频用ffmpeg转码为MP4,参数
-crf 25,还添加了流媒体适配配置 - 音频从OGG改为MP3,因为MP3跳转友好性更强
音频轨道加载方式
创建多个audio元素加载分离的音频轨道,每个元素设置preload="auto"并调用load():
audset[0] = [document.createElement("audio"), document.createElement("audio"), document.createElement("audio"), document.createElement("audio")]; audset[0][0].src = "Movie." + audchoices[0] + ".1.mp3"; audset[0][1].src = "Movie." + audchoices[1] + ".1.mp3"; audset[0][2].src = "Movie." + audchoices[2] + ".1.mp3"; audset[1] = [document.createElement("audio"), document.createElement("audio"), document.createElement("audio"),document.createElement("audio")]; audset[1][0].src = "Movie." + audchoices[0] + ".2.mp3"; audset[1][1].src = "Movie." + audchoices[1] + ".2.mp3"; audset[1][2].src = "Movie." + audchoices[2] + ".2.mp3"; audset[2] = [document.createElement("audio"), document.createElement("audio"), document.createElement("audio"),document.createElement("audio")]; audset[2][0].src = "Movie." + audchoices[0] + ".4.mp3"; audset[2][1].src = "Movie." + audchoices[1] + ".4.mp3"; audset[2][2].src = "Movie." + audchoices[2] + ".4.mp3";
同步逻辑主循环
以包含5.1低音声道的主音频元素audM为同步基准,所有轨道与它对齐,视频绘制到canvas用于后续字幕绘制:
function update(raftime) { var bfound = 0; var bufsum = 0; hasloaded = 0; for(var a = 0; a < 3; a++) { for(var b = 0; b < 4; b++) { if(audM.paused) { audset[a][b].pause(); audset[a][b].currentTime = audM.currentTime; } else if(audset[a][b].paused) { audset[a][b].play(); } if(audset[a][b].currentTime < audM.currentTime - 0.1 || audset[a][b].currentTime > audM.currentTime + 0.1) audset[a][b].currentTime = audM.currentTime; for(var bf = 0; bf < audset[a][b].buffered.length; bf++) { bufsum += audset[a][b].buffered.end(bf); if(audM.currentTime > audset[a][b].buffered.start(bf) && audM.currentTime < audset[a][b].buffered.end(bf)) { // bf = audset[a][b].buffered.length; bfound += 1; } } bufsum /= audset[a][b].buffered.length; hasloaded += audset[a][b].readyState; } } if(vid.currentTime < audM.currentTime - 0.1 || vid.currentTime > audM.currentTime + 0.1) vid.currentTime = audM.currentTime; if(audM.paused) { vid.pause(); vid.currentTime = audM.currentTime; } else if(vid.paused) { vid.play(); } for(var t = 0; t < bookmarks.length - 1; t++) { if(audM.currentTime >= bookmarks[t][0] && audM.currentTime <= bookmarks[t + 1][0]) { for(var a = 0; a < 3; a++) { for(var b = 0; b < 4; b++) { if(bookmarks[t][a + 1] == b) audset[a][b].volume = audM.volume; else audset[a][b].volume = 0; } } t = bookmarks.length; } } var sf = -1; for(var s = 0; s < subset.length; s++) { if(audM.currentTime > subset[s].start && audM.currentTime < subset[s].end) { sf = s; s = subset.length; } } if(lastsub != sf) { if(sf > -1) subdisp.innerHTML = subset[sf].text; else subdisp.innerHTML = ""; subdisp.parentNode.parentNode.parentNode.style.top = (audM.getBoundingClientRect().top - subdisp.getBoundingClientRect().height - 4) + "px"; lastsub = sf; } for(var bf = 0; bf < vid.buffered.length; bf++) { bufsum += vid.buffered.end(bf); if(audM.currentTime > vid.buffered.start(bf) && audM.currentTime < vid.buffered.end(bf)) { // bf = vid.buffered.length; bfound += 1; } } hasloaded += vid.readyState; // hasloaded += audM.readyState; bufsum /= vid.buffered.length; bufsum /= 12; if(bfound < 13) { ctx.drawImage(vid,0,0); ctx.fillStyle = "white"; ctx.globalCompositeOperation = "xor"; for(var a = 0; a < 3; a++) { for(var b = 0; b < 4; b++) { for(var bf = 0; bf < audset[a][b].buffered.length; bf++) { var bs = (audset[a][b].buffered.start(bf) / audset[a][b].duration) * 1920; var be = (audset[a][b].buffered.end(bf) / audset[a][b].duration) * 1920; ctx.fillRect(bs,800-b,be-bs,1); } } } for(var bf = 0; bf < vid.buffered.length; bf++) { var bs = (vid.buffered.start(bf) / vid.duration) * 1920; var be = (vid.buffered.end(bf) / vid.duration) * 1920; ctx.fillRect(bs,802+b,be-bs,1); } ctx.fillRect((audM.currentTime / audM.duration) * 1920,790,1,10); ctx.strokeStyle = "white"; ctx.lineWidth = 5; ctx.beginPath(); ctx.arc(960, 540, 50, Math.PI * ((raftime / 1000) % 2), Math.PI * ((raftime / 1000) % 2) + ((2 * Math.PI) * (bfound / 13))); ctx.stroke(); ctx.font = "bold 24px Ariel"; ctx.fillText(Math.floor((bfound / 13) * 100) + "%", 940, 550); ctx.globalCompositeOperation = "source-over"; audM.currentTime = vid.currentTime; for(var a = 0; a < 3; a++) { for(var b = 0; b < 4; b++) { if(audset[a][b].currentTime < audM.currentTime) audM.currentTime = audset[a][b].currentTime; } } } else { if(vid.readyState == 1 && lasttime != vid.currentTime) { vid.play(); ctx.drawImage(vid,0,0); lasttime = vid.currentTime; } else if(vid.readyState == 4 && lasttime != vid.currentTime) { ctx.drawImage(vid,0,0); lasttime = vid.currentTime; } } audM.blur(); canvas.focus(); requestAnimationFrame(update); }
遇到的核心问题
- 缓冲停滞:播放到影片1/4或1/5时需缓冲所有轨道,勉强可用;但跳转到中部及以后位置时,彻底陷入缓冲停滞,部分缓冲范围始终无法加载完成。
- 浏览器兼容性:MP4视频仅能在Chrome中加载,Firefox无法运行,对比可用MP4文件仅编码库版本不同。
- 内存疑问:猜测可能是浏览器内存不足,但不确定如何手动清理缓冲或会话缓存。
求助方向
- 如何优化Web环境下的多轨媒体缓冲与同步逻辑,解决跳转后的缓冲停滞问题?
- 如何解决Firefox无法加载MP4的兼容性问题?
- 若Web API并非最佳方案,基于Java开发14轨媒体播放器有哪些关键思路或技术点可以参考?
内容的提问来源于stack exchange,提问作者Edward
相关产品推荐
相关产品推荐

