jmuxer 实例化时,有以下参数
node:指明jmuxer 与那个标签绑定
mode:播放模式,可以选择audio,video,或both
flushingtime:缓存清理间隔,每隔多久去检查缓存
clearBuffer:是否自动清除buffer
fps:帧率,可填可不填,不填的话将根据数据中的duration 来决定。
jmuxer 数据传输格式如下:
2bytes2bytes duration(持续时间)音频长度aac数据视频数据(h264)duration 指的是这一块数据需要播放多少时间,音频长度是大端序,表示随后aac的字节数。这个格式有一定的要求:
(1)若mode 设置为video,是不需要遵从这个格式的,直接将h264传给jmuxer,带格式会导致视频无法播放。
(2)若mode 设置为audio,也不需要遵从这个格式,将aac 数据传给jmuxer
(3)若mode 设置为both,表明这块数据既含有视频,又含有音频,需要按照指定格式填充数据,,然后传给jmuxer,合成mp4。
这里直接贴官方的代码,doc目录下的h264_player.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="description" content="H264 live player using jMuxer"> <meta name="keywords" content="h264 player, mp4 player, mse, mp4 muxing, jmuxer, aac player"> <title>Online h264 player using jMuxer</title> <script async defer src="https://buttons.github.io/buttons.js"></script> </head> <body> <h1>Online h264 player</h1> <div class="github-tools"> <a class="github-button" href="https://github.com/samirkumardas/jmuxer/fork" data-color-scheme="no-preference: dark; light: dark; dark: dark;" data-size="large" data-show-count="true" aria-label="Fork samirkumardas/jmuxer on GitHub">Fork</a> <!-- Place this tag where you want the button to render. --> <a class="github-button" href="https://github.com/samirkumardas/jmuxer" data-color-scheme="no-preference: dark; light: dark; dark: dark;" data-size="large" data-show-count="true" aria-label="Star samirkumardas/jmuxer on GitHub">Star</a> </div> <div id="container" style="width: 600px; margin: 0 auto;"> <div class="meta-info"> <div class="column"> <label for="fps">Frame Rate:</label> <input type="text" id="fps-input" name="fps" value="30" /> </div> <div class="or-column">Or</div> <div class="column"> <label for="fps">Duration (seconds):</label> <input type="text" id="duration-input" name="duration" value="" /> </div> </div> <div class="file-input"> <label for="h264_file">Choose a h264 File (Annex-B)</label> <input type="file" id="h264_file" name="h264_file" /> </div> <button id="play-btn">Play</button> <video width="100%" controls autoplay id="player"></video> <div class="gesture">If it does not play automatically, Click the `video play button` to initiate the video</div> </div> <style type="text/css"> .file-input { padding: 20px 10px; background: #f7f7f7; border: 1px solid #9a9a9a; margin-bottom: 10px; } .meta-info { display: flex; background: #f7f7f7; border: 1px solid #9a9a9a; margin-bottom: 10px; padding: 10px; } .or-column { width: 40px; text-align: center; color: #085a94fa; text-transform: uppercase; font-size: 17px; font-weight: bold; } .meta-info input { padding: 5px; outline: 0; } #play-btn { background: #3498db; color: #fff; padding: 7px; width: 100%; border: 1px solid #0880d0; font-size: 15px; text-transform: uppercase; margin-bottom: 10px; cursor: pointer; } #play-btn:hover { background: #05568c; } .github-tools { position: absolute; top: 15px; right: 15px; } .gesture { font-size: 15px; color: #ad4903; margin-top: 10px; } </style> <script> window.onload = function() { var jmuxer, duration; var reader = new FileReader(); reader.onload = function(e) { jmuxer.feed({ video: new Uint8Array(e.target.result), duration: duration }); }; document.getElementById('play-btn').addEventListener('click', function(e) { if (jmuxer) { jmuxer.destroy(); } var file = document.getElementById('h264_file'); if (!file.files.length) { alert('Please choose a file.'); return; } var fps = parseInt(document.getElementById('fps-input').value) || 30; duration = parseInt(document.getElementById('duration-input').value) * 1000; reader.readAsArrayBuffer(file.files[0]); jmuxer = new JMuxer({ node: 'player', mode: 'video', flushingTime: 1000, fps: fps, clearBuffer: false, debug: true }); }, false); } </script> <script type="text/javascript" src="jmuxer.min.js"></script> </body> </html>只播放aac和只播放h264差不多,mode改为audio,喂给jmuxer的只有audio,duration。
这里有两种做法,第一种是像官方那样,1S的视频,包含1S 内所有的aac + 1S内所有的h264,都喂给jmuxer,这种方法比较适合文件播放。
mode默认为both,官方例子
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="description" content="jMuxer - a simple javascript mp4 muxer for non-standard streaming communications protocol"> <meta name="keywords" content="h264 player, mp4 player, mse, mp4 muxing, jmuxer, aac player"> <title>jMuxer demo</title> </head> <body> <script> function parse(data) { var input = new Uint8Array(data), dv = new DataView(input.buffer), duration, audioLength, audio, video; duration = dv.getUint16(0, true); audioLength = dv.getUint16(2, true); audio = input.subarray(4, (audioLength + 4)); video = input.subarray(audioLength + 4); return { audio: audio, video: video, duration: duration }; } window.onload = function() { var socketURL = 'ws://localhost:8080'; var jmuxer = new JMuxer({ node: 'player', debug: true }); var ws = new WebSocket(socketURL); ws.binaryType = 'arraybuffer'; ws.addEventListener('message',function(event) { var data = parse(event.data); jmuxer.feed(data); }); ws.addEventListener('error', function(e) { console.log('Socket Error'); }); } </script> <script type="text/javascript" src="jmuxer.min.js"></script> </body> </html>这种方式延迟比较大,适合文件播放。但对于延迟比较高的场景,需要特别处理,假设视频fps为25帧,则视频一帧要持续40ms,音频aac 一帧1024个样本,一帧要播放1000*1024/44100 = 23.2ms。可以看出来音频发送是要比视频频繁的,所以一定会出现只有音频没有视频的情况。由于这种方式音视频是交替播放的,如果当前播放的是视频,需要40ms 播放时间,在视频播放期间,又收到了一个aac 音频,aac 数据必须等前面的视频播放完成,才能播放,这就会造成延迟。
另一种就是建立两个标签,每个标签绑定一个jmuxer,音视频播放互不干扰。
<video width="100%" controls autoplay poster="images/loader-thumb.jpg" id="player"></video> <audio width="50%" controls autoplay poster="images/loader-thumb.jpg" id="audioPlayer" ></audio> var jmuxer = new JMuxer({ node: 'player', flushingTime:15 , fps: 30, mode:'video', debug: false }); var audioMuxer = new JMuxer({ node: 'audioPlayer', flushingTime:1, clearBuffer: true, fps: 43,//可以不选 mode:'audio', debug: false }); audioMuxer.feed(audioData); jmuxer.feed(data);这种方案也有以下问题
(1)切换到后台、标签页失去焦点时,video控件会暂停播放
这是chrome 内核的一个机制,当页面失去焦点时,如果video标签内的数据没有音轨,会暂停播放,MSE的数据会堆积,导致延迟。可以考虑采用将页面置顶,失去焦点后定时检查video播放状态 、失去焦点后不播放等策略。
(2)声音丢失问题
当缓存队列的数据不足以播放,播放的时间戳和缓冲队列的时间戳大到一定程度时,会造成声音再也无法播出。