index.html 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="description" content="jMuxer - a simple javascript mp4 muxer for non-standard streaming communications protocol">
  6. <meta name="keywords" content="h264 player, mp4 player, mse, mp4 muxing, jmuxer, aac player">
  7. <title>JMuxer demo</title>
  8. <script async defer src="https://buttons.github.io/buttons.js"></script>
  9. <style type="text/css">
  10. .github-tools {
  11. position: absolute;
  12. top: 15px;
  13. right: 15px;
  14. }
  15. a.h264-player {
  16. font-size: 20px;
  17. text-decoration: none;
  18. color: #07568e;
  19. margin-top: 10px;
  20. display: block;
  21. }
  22. .gesture {
  23. font-size: 15px;
  24. color: #ad4903;
  25. margin-top: 10px;
  26. }
  27. </style>
  28. </head>
  29. <body>
  30. <h2>jMuxer Demo</h2>
  31. <p>Sample demo node server is running on heroku free hosting</p>
  32. <br /><br />
  33. <div class="github-tools">
  34. <!-- Place this tag where you want the button to render. -->
  35. </div>
  36. <div id="container" style="width: 600px; margin: 0 auto;">
  37. <video width="70%" disablePictureInPicture="true" autoplay poster="images/loader-thumb.jpg" id="player"></video>
  38. <audio width="50%" preload="auto" autoplay controls poster="images/loader-thumb.jpg" id="audioPlayer"></audio>
  39. <div class="gesture">If it does not play automatically, Click the `video play button` to initiate the video</div>
  40. </div>
  41. <body oncontextmenu="Back()">
  42. </body>
  43. <script src="helper.js">
  44. </script>
  45. <script>
  46. //隐藏控件 controls
  47. var fpsCount = 0;
  48. var requestCount = 0;
  49. var timeCount = 0;
  50. var isVisuable = true;
  51. var isFeed = true;
  52. var isDrag = false;
  53. var shoudDrop = false;
  54. var isEnough = true;
  55. var ifCanPlay = false;
  56. var isFinish = false;
  57. var delayTime = new Date().getTime();
  58. var feedTime = new Date().getTime();
  59. var readyTime = new Date().getTime();
  60. var requestTime = new Date().getTime();
  61. var curTime = new Date().getTime();
  62. var requestTime = new Date().getTime(); //记录离开时间
  63. var myVideo = document.getElementById("player");
  64. var myAudio = document.getElementById("audioPlayer");
  65. var audioBuffer = [];
  66. var audioBack = [];
  67. Module = {};
  68. Module.onRuntimeInitialized = function() {
  69. console.log("Wasm 加载成功!")
  70. isFinish = true;
  71. }
  72. document.addEventListener("visibilitychange", () => {
  73. if (document.visibilityState == "visible") {
  74. console.log("页面可见,继续喂视频");
  75. //requestTime = new Date().getTime();
  76. isVisuable = true;
  77. } else {
  78. isVisuable = false;
  79. isFeed = false;
  80. myVideo.pause();
  81. }
  82. });
  83. myVideo.play();
  84. myVideo.addEventListener('pause', function() {
  85. //console.log("视频播放暂停");
  86. isFeed = false;
  87. });
  88. myAudio.addEventListener('canplay', function() {
  89. console.log("缓冲区大小 %f", myAudio.buffered.end(0) - myAudio.buffered.start(0));
  90. });
  91. /*function decodeAAC(data)
  92. {
  93. var retPtr = Module._malloc(4 * 5 * 1024);//接收的数据
  94. var inputPtr = Module._malloc(4 * data.length);//输入数据
  95. for( i =0;i < data.length;i++)
  96. {
  97. Module.HEAPU8[(inputPtr)+i] = data[i];//转换为堆数据
  98. }
  99. var pcmLen = Module._feedData(retPtr, inputPtr, data.length);
  100. if(pcmLen > 0)
  101. {
  102. //console.log("%d帧 aac 解码成功, %d", decodeCount, pcmLen);
  103. var pcmData = new Uint8Array(pcmLen);
  104. for(i = 0;i < pcmLen;i++)
  105. {
  106. pcmData[i] = Module.HEAPU8[(retPtr)+i]
  107. }
  108. player.feed(pcmData);
  109. }
  110. else
  111. {
  112. console.log("%d帧 aac 解码失败, %d", decodeCount, pcmLen);
  113. }
  114. decodeCount++;
  115. Module._free(inputPtr);
  116. Module._free(retPtr);
  117. } */
  118. //解协议
  119. function ParseProto(data) {
  120. var temp = "";
  121. var input = new Uint8Array(data),
  122. duration,
  123. video,
  124. audio;
  125. if (input[0] == 0 && input[1] == 0 && input[2] == 0 && input[3] == 1) {
  126. // debugger
  127. video = input;
  128. duration = 24;
  129. var nalType = input[4] & 0x1f; //nalType == 0x07|| nalType == 0x08 || nalType == 0x05
  130. if (!isFeed) {
  131. if (nalType == 0x05) {
  132. console.log("发现I帧");
  133. }
  134. if (nalType == 0x05 && isVisuable) {
  135. console.log("检测到I帧 %d,重新渲染, 耗时 %d ms", nalType, new Date().getTime() - requestTime);
  136. isFeed = true;
  137. }
  138. }
  139. } else if (input[0] == 0xff) {
  140. if (!isEnough) {
  141. requestCount++;
  142. //audioBuffer.push(input);
  143. }
  144. audio = input;
  145. if (new Date().getTime() - curTime > 100) {
  146. delayTime = new Date().getTime();
  147. console.log("接收时间 %d ms", new Date().getTime() - curTime);
  148. }
  149. curTime = new Date().getTime();
  150. duration = 24;
  151. //console.log("duration %d", duration);
  152. } else if (input[0] == 0x68) {
  153. if (input[23] == 0x05) //横竖屏标识
  154. {
  155. var state = CheckScreenDirection(input.slice(24, 24 + 8));
  156. if (state == 1) {
  157. console.log("安卓卡此时竖屏");
  158. //竖屏处理
  159. } else {
  160. console.log("安卓卡此时横屏");
  161. //横屏处理
  162. }
  163. }
  164. if (input[23] == 0x0b) {
  165. console.log("多端登陆");
  166. }
  167. //console.log("屏幕旋转 %s", PrintArry(input));
  168. }
  169. return {
  170. audio: audio,
  171. video: video,
  172. duration: duration
  173. };
  174. }
  175. window.onload = function() {
  176. // var socketURL = 'wss://jmuxer-demo-server.herokuapp.com';
  177. //socketURL = "ws://127.0.0.1:8080"
  178. // socketURL = "ws://192.168.11.233:8080"
  179. //socketURL = "ws://14.215.128.98:14112";
  180. var socketURL = "ws://192.168.11.66:9101";
  181. // socketURL = "ws://14.215.128.98:14077";
  182. //socketURL = "wss://192.168.11.242:9104";
  183. var jmuxer = new JMuxer({
  184. node: 'player',
  185. flushingTime: 15,
  186. fps: 30,
  187. mode: 'video',
  188. debug: false
  189. });
  190. var audioMuxer = new JMuxer({
  191. node: 'audioPlayer',
  192. flushingTime: 15,
  193. clearBuffer: true,
  194. fps: 60, //可以不选,原先43
  195. mode: 'audio',
  196. debug: false
  197. });
  198. curTime = new Date().getTime();
  199. var ws = new WebSocket(socketURL);
  200. ws.binaryType = 'arraybuffer';
  201. //断开检测
  202. ws.onclose = function(e) {
  203. alert("websocket连接断开");
  204. console.log('websocket 断开: ' + e.code + ' ' + e.reason + ' ' + e.wasClean);
  205. console.log(e);
  206. }
  207. ws.addEventListener('open', function(event) {
  208. console.log("发送配置帧");
  209. ws.send(ConfigChannel("RK3923C1201900139"));
  210. });
  211. ws.addEventListener('error', function(event) {
  212. console.log("连接失败");
  213. });
  214. ws.addEventListener('message', function(event) {
  215. var data = ParseProto(event.data); //JAVA服务器转发
  216. //console.log("收到数据");
  217. var audioData = {
  218. audio: data.audio,
  219. video: null,
  220. duration: data.duration
  221. };
  222. var videoData = {
  223. audio: null,
  224. video: data.video,
  225. duration: data.duration
  226. };
  227. if (myAudio.readyState == 2) {
  228. requestTime = new Date().getTime();
  229. isEnough = false;
  230. console.log("数据存储不够,出现声音停止,时间差 %f", myAudio.buffered.end(0));
  231. myAudio.pause();
  232. //myAudio.playbackRate = 2;
  233. } else if (myAudio.readyState == 4 && isEnough == false) {
  234. myAudio.play();
  235. var time = new Date().getTime();
  236. isEnough = true;
  237. console.log("填满耗时 %d ms, 填充帧数 %d, 填充延迟 %d ms", time - requestTime, requestCount, requestCount * 23);
  238. console.log("----接收到启动 %d ms, 缓冲区 %f---", time - delayTime, myAudio.buffered.end(0) - myAudio.played.end(0));
  239. }
  240. if (data.audio != null) //喂音频
  241. {
  242. if (myAudio.buffered.length > 0 && myAudio.played.length > 0) {
  243. var bufferTime = myAudio.buffered.end(0) - myAudio.played.end(0);
  244. //console.log(" bufferTime %d", bufferTime);
  245. if (bufferTime > 1) {
  246. //console.log("丢掉一些包");
  247. //return;
  248. }
  249. }
  250. audioMuxer.feed(audioData);
  251. }
  252. if (data.video != null) //喂视频
  253. {
  254. if (isFeed) {
  255. jmuxer.feed(data);
  256. }
  257. //jmuxer.feed(videoData);
  258. }
  259. });
  260. myVideo.onmousedown = function(event) {
  261. //放在此处只是为了方便演示,实际使用中查找横竖屏只要刚连接上时调用一次就好。
  262. //var checkBuffer = GetScreenState();
  263. //ws.send(checkBuffer);
  264. if (!isFeed) {
  265. console.log("重新申请I帧");
  266. requestTime = new Date().getTime();
  267. var buffer = RequestIFrame();
  268. //var buffer = new Uint8Array([0x01]);
  269. ws.send(buffer);
  270. }
  271. //console.log("报文 %s", PrintArry(buffer));
  272. if (event.button == 0) {
  273. var posX = event.offsetX * 1080 * 1.0 / myVideo.clientWidth;
  274. var posY = event.offsetY * 1920 * 1.0 / myVideo.clientHeight;
  275. var buffer = ExexuteMouseDown(posX.toString(), posY.toString());
  276. ws.send(buffer);
  277. isDrag = true;
  278. }
  279. }
  280. myVideo.onmousemove = function(event) {
  281. if (isDrag && event.button == 0) {
  282. var posX = event.offsetX * 1080 * 1.0 / myVideo.clientWidth;
  283. var posY = event.offsetY * 1920 * 1.0 / myVideo.clientHeight;
  284. var buffer = ExexuteMouseMove(posX.toString(), posY.toString());
  285. ws.send(buffer);
  286. //console.log("移动位置 %d, %d", posX, posY);
  287. }
  288. }
  289. myVideo.onmouseup = function(event) {
  290. isDrag = false;
  291. var posX = event.offsetX * 1080 * 1.0 / myVideo.clientWidth;
  292. var posY = event.offsetY * 1920 * 1.0 / myVideo.clientHeight;
  293. var buffer = ExexuteMouseUp(posX.toString(), posY.toString());
  294. ws.send(buffer);
  295. }
  296. myVideo.onkeydown = function(event) {
  297. ExexuteKeyDown(e.keyCode);
  298. }
  299. }
  300. function Back() {
  301. if (event.button == 2) {
  302. //ExexuteKeyDown(4);
  303. }
  304. ExexuteKeyDown(4);
  305. window.event.returnValue = false;
  306. return false;
  307. }
  308. </script>
  309. <script type="text/javascript" src="jmuxer.js"></script>
  310. <!--<script type="text/javascript" src="aac.js"></script>-->
  311. </body>
  312. </html>