|
- <!DOCTYPE html>
-
- <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta charset="utf-8" />
- <title>WebSocket MSE Fmp4 demo</title>
- </head>
- <body>
- <h1>MSE FMp4 Demo</h1>
- <video id="stream_live" width="640" height="480" controls="false" autoplay="true"
- muted="muted"
- preload="auto">
- 浏览器不支持
- </video>
- <ul id="messagesList"></ul>
- <script>
- //var mimeCodec = 'video/mp4;codecs="avc1.4D0014, mp4a.40.2"';
- // *** USER PARAMETERS ***
- var verbose = true;
- // var verbose = true; // enable for saturating the console ..
- var buffering_sec = 1; // use some reasonable value
- var buffering_sec_seek = buffering_sec * 0.9;
- // ..seek the stream if it's this much away or
- // from the last available timestamp
- var buffering_sec_seek_distance = buffering_sec * 0.5;
- // .. jump to this distance from the last avail. timestamp
- // *** INTERNAL PARAMETERS ***
- // set mimetype and codec
- var mimeType = "video/mp4";
- var codecs = "avc1.4D0014"; // https://wiki.whatwg.org/wiki/Video_type_parameters
- // if your stream has audio, remember to include it in these definitions.. otherwise your mse goes sour
- var codecPars = mimeType + ';codecs="' + codecs + '"';
- var stream_started = false; // is the source_buffer updateend callback active nor not
- // create media source instance
- var ms = new MediaSource();
- // queue for incoming media packets
- var queue = [];
- var stream_live; // the HTMLMediaElement (i.e. <video> element)
- var ws; // websocket
- var seeked = false; // have have seeked manually once ..
- var cc = 0;
- var source_buffer; // source_buffer instance
- var pass = 0;
- // *** MP4 Box manipulation functions ***
- // taken from here: https://stackoverflow.com/questions/54186634/sending-periodic-metadata-in-fragmented-live-mp4-stream/
- function toInt(arr, index) { // From bytes to big-endian 32-bit integer. Input: Uint8Array, index
- var dv = new DataView(arr.buffer, 0);
- return dv.getInt32(index, false); // big endian
- }
- function toString(arr, fr, to) { // From bytes to string. Input: Uint8Array, start index, stop index.
- // https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
- return String.fromCharCode.apply(null, arr.slice(fr, to));
- }
- function getBox(arr, i) { // input Uint8Array, start index
- return [toInt(arr, i), toString(arr, i + 4, i + 8)]
- }
- function getSubBox(arr, box_name) { // input Uint8Array, box name
- var i = 0;
- res = getBox(arr, i);
- main_length = res[0]; name = res[1]; // this boxes length and name
- i = i + 8;
- var sub_box = null;
- while (i < main_length) {
- res = getBox(arr, i);
- l = res[0]; name = res[1];
-
- if (box_name == name) {
- sub_box = arr.slice(i, i + l)
- }
- i = i + l;
- }
- return sub_box;
- }
- function hasFirstSampleFlag(arr) { // input Uint8Array
- // [moof [mfhd] [traf [tfhd] [tfdt] [trun]]]
- var traf = getSubBox(arr, "traf");
- if (traf == null) { return false; }
- var trun = getSubBox(traf, "trun");
- if (trun == null) { return false; }
- // ISO/IEC 14496-12:2012(E) .. pages 5 and 57
- // bytes: (size 4), (name 4), (version 1 + tr_flags 3)
- var flags = trun.slice(10, 13); // console.log(flags);
- f = flags[1] & 4; // console.log(f);
- return f == 4;
- }
- // consider these callbacks:
- // - putPacket : called when websocket receives data
- // - loadPacket : called when source_buffer is ready for more data
- // Both operate on a common fifo
- function putPacket(arr) {
- // receives ArrayBuffer. Called when websocket gets more data
- // first packet ever to arrive: write directly to source_buffer
- // source_buffer ready to accept: write directly to source_buffer
- // otherwise insert it to queue
- var memview = new Uint8Array(arr);
- if (verbose) { console.log("got", arr.byteLength, "bytes. Values=", memview[0], memview[1], memview[2], memview[3], memview[4]); }
- res = getBox(memview, 0);
- main_length = res[0]; name = res[1]; // this boxes length and name
- // if ((name == "ftyp") && (pass == 0)) {
- // pass = pass + 1;
- // console.log("got ftyp");
- // }
- // else if ((name == "moov") && (pass == 1)) {
- // pass = pass + 1;
- // console.log("got moov");
- // }
- // else if ((name == "moof") && (pass == 2)) {
- // if (hasFirstSampleFlag(memview)) {
- // pass = pass + 1;
- // console.log("got that special moof");
- // }
- // else {
- // return;
- // }
- // }
- // else if (pass < 3) {
- // return;
- // }
- // keep the latency to minimum
- let latest = stream_live.duration;
- if ((stream_live.duration >= buffering_sec) &&
- ((latest - stream_live.currentTime) > buffering_sec_seek)) {
- console.log("seek from ", stream_live.currentTime, " to ", latest);
- df = (stream_live.duration - stream_live.currentTime); // this much away from the last available frame
- if ((df > buffering_sec_seek)) {
- seek_to = stream_live.duration - buffering_sec_seek_distance;
- stream_live.currentTime = seek_to;
- }
- }
- data = arr;
- if (!stream_started) {
- if (verbose) { console.log("Streaming started: ", memview[0], memview[1], memview[2], memview[3], memview[4]); }
- stream_started = true;
- source_buffer.appendBuffer(data);
-
- cc = cc + 1;
- return;
- }
- queue.push(data); // add to the end
- if (verbose) { console.log("queue push:", queue.length); }
- }
-
- function loadPacket() { // called when source_buffer is ready for more
- if (!source_buffer.updating) { // really, really ready
- if (queue.length > 0) {
- inp = queue.shift(); // pop from the beginning
- if (verbose) { console.log("queue pop:", queue.length); }
- var memview = new Uint8Array(inp);
- if (verbose) { console.log(" ==> writing buffer with", memview[0], memview[1], memview[2], memview[3]); }
- source_buffer.appendBuffer(inp);
- cc = cc + 1;
- }
- else { // the queue runs empty, so the next packet is fed directly
- stream_started = false;
- }
- }
- else { // so it was not?
- }
- }
-
- function opened() { // MediaSource object is ready to go
- // https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/duration
- ms.duration = buffering_sec;
- source_buffer = ms.addSourceBuffer(codecPars);
- // https://developer.mozilla.org/en-US/docs/Web/API/source_buffer/mode
- var myMode = source_buffer.mode;
- source_buffer.mode = 'sequence';
- // source_buffer.mode = 'segments';
- source_buffer.addEventListener("updateend", loadPacket);
-
- ws = new WebSocket("ws://127.0.0.1:15555/live.mp4?sim=12345678901&channel=3&token=123456"); //创建WebSocket连接
- ws.onmessage = function (e) {
- //当客户端收到服务端发来的消息时,触发onmessage事件,参数e.data包含server传递过来的数据
- console.log(e.data);
- putPacket(e.data);
- }
- }
-
- function startup() {
- ms.addEventListener('sourceopen', opened, false);
- // get reference to video
- stream_live = document.getElementById('stream_live');
- // set mediasource as source of video
- stream_live.src = window.URL.createObjectURL(ms);
- }
- function base64ToArrayBuffer(base64) {
- var binary_string = window.atob(base64);
- var len = binary_string.length;
- var bytes = new Uint8Array(len);
- for (var i = 0; i < len; i++) {
- bytes[i] = binary_string.charCodeAt(i);
- }
- return bytes.buffer;
- }
- window.onload = function () {
- startup();
- }
-
- </script>
- </body>
- </html>
|