index.html 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  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. <div id="container" style="width: 600px; margin: 0 auto;">
  31. <video width="100%" autoplay poster="images/loader-thumb.jpg" id="player"></video>
  32. <audio width="50%" controls autoplay poster="images/loader-thumb.jpg" id="audioPlayer"></audio>
  33. </div>
  34. <body oncontextmenu="Back()">
  35. </body>
  36. <script src="helper.js">
  37. </script>
  38. <script>
  39. //隐藏控件 controls
  40. var fpsCount = 0;
  41. var timeCount = 0;
  42. var isFeed = true;
  43. var isDrag = false;
  44. var curTime = new Date().getTime();
  45. var requestTime = new Date().getTime(); //记录离开时间
  46. var myVideo = document.getElementById("player");
  47. var myAudio = document.getElementById("audioPlayer");
  48. document.addEventListener("visibilitychange", () => {
  49. if (document.visibilityState == "visible") {
  50. console.log("页面可见,继续喂视频");
  51. requestTime = new Date().getTime();
  52. myVideo.play();
  53. } else {
  54. isFeed = false;
  55. //myVideo.pause();
  56. }
  57. });
  58. window.MediaSource = window.MediaSource || window.WebKitMediaSource;
  59. this.isMSESupported = !!window.MediaSource;
  60. if (!this.isMSESupported) {
  61. alert("不支持MSE");
  62. } else {
  63. alert("支持MSE");
  64. }
  65. myVideo.addEventListener('pause', function() {
  66. console.log("视频播放暂停");
  67. isFeed = false;
  68. });
  69. myAudio.addEventListener('onerror', function() {
  70. console.log("音频播放暂停");
  71. //myAudio.play();
  72. });
  73. function parse(data) {
  74. var input = new Uint8Array(data),
  75. dv = new DataView(input.buffer),
  76. duration,
  77. audioLength,
  78. audio,
  79. video;
  80. duration = dv.getUint16(0, true); //获取duration
  81. audioLength = dv.getUint16(2, true);
  82. if (audioLength == 0) {
  83. video = input.subarray(4);
  84. console.log("只有视频");
  85. } else {
  86. audio = input.subarray(4, (audioLength + 4));
  87. video = input.subarray(audioLength + 4);
  88. console.log("视频 + 音频");
  89. }
  90. return {
  91. audio: audio,
  92. video: video,
  93. duration: duration
  94. };
  95. }
  96. //解协议
  97. function ParseProto(data) {
  98. var temp = "";
  99. var input = new Uint8Array(data),
  100. duration,
  101. video,
  102. audio;
  103. if (input[0] == 0 && input[1] == 0 && input[2] == 0 && input[3] == 1) {
  104. // debugger
  105. video = input;
  106. duration = 24;
  107. var nalType = input[4] & 0x1f; //
  108. if (nalType == 0x07 || nalType == 0x08 || nalType == 0x05) //策略, 找到sps、sps、或I帧,才继续渲染
  109. {
  110. if (!isFeed) {
  111. console.log("%d, 检测到I帧,重新渲染, 耗时 %d ms", nalType, new Date().getTime() - requestTime);
  112. }
  113. isFeed = true;
  114. } else {
  115. }
  116. } else if (input[0] == 0xff) {
  117. audio = input;
  118. duration = 22;
  119. } else if (input[0] == 0x68) {
  120. if (input[23] == 0x05) //横竖屏标识
  121. {
  122. var state = CheckScreenDirection(input.slice(24, 24 + 8));
  123. if (state == 1) {
  124. console.log("安卓卡此时竖屏");
  125. //竖屏处理
  126. } else {
  127. console.log("安卓卡此时横屏");
  128. //横屏处理
  129. }
  130. }
  131. //console.log("屏幕旋转 %s", PrintArry(input));
  132. }
  133. return {
  134. audio: audio,
  135. video: video,
  136. duration: duration
  137. };
  138. }
  139. window.onload = function() {
  140. var socketURL = 'wss://jmuxer-demo-server.herokuapp.com';
  141. // socketURL = "ws://localhost:8080"
  142. // socketURL = "ws://192.168.11.238:8080";//C# websocket服务器中转
  143. // socketURL="wss://test.androidscloud.com/videoWebSocket?clientType=1&cardIp=14.215.128.96&port=2005&sn=RK3930C2301900005"
  144. //socketURL = "ws://127.0.0.1:8022/videoWebSocket?clientType=0&sn=RK3923C1201900139&cardIp=192.168.11.66&port=9100";
  145. /*socketURL = "ws://192.168.31.242:8022/videoWebSocket?clientType=0&sn=RK3923C1201900139&cardIp=192.168.11.66&port=9100";*/
  146. //flushingTime, 缓存刷新时间
  147. // socketURL = "ws://14.215.128.98:14112";
  148. socketURL = "ws://192.168.11.66:9101";
  149. var jmuxer = new JMuxer({
  150. node: 'player',
  151. flushingTime: 15,
  152. fps: 30,
  153. mode: 'video',
  154. debug: false
  155. });
  156. var audioMuxer = new JMuxer({
  157. node: 'audioPlayer',
  158. flushingTime: 1,
  159. clearBuffer: true,
  160. fps: 43, //可以不选
  161. mode: 'audio',
  162. debug: false
  163. });
  164. /*var costTime = new Date().getTime() - curTime;
  165. if(costTime > 5)
  166. {
  167. console.log("websocket接收延迟 %d ms", costTime);
  168. }*/
  169. curTime = new Date().getTime();
  170. var ws = new WebSocket(socketURL);
  171. ws.binaryType = 'arraybuffer';
  172. //断开检测
  173. ws.onclose = function(e) {
  174. alert("websocket连接断开");
  175. console.log('websocket 断开: ' + e.code + ' ' + e.reason + ' ' + e.wasClean);
  176. console.log(e);
  177. }
  178. ws.addEventListener('message', function(event) {
  179. //console.log("喂数据");
  180. //jmuxer.feed(data);
  181. var data = ParseProto(event.data); //JAVA服务器转发
  182. //var data = parse(event.data);//分离音视频数据,C#转发
  183. // console.log("推流详情============",event.data);
  184. var audioData = {
  185. audio: data.audio,
  186. video: null,
  187. duration: data.duration
  188. };
  189. var videoData = {
  190. audio: null,
  191. video: data.video,
  192. duration: data.duration
  193. };
  194. var streamTemp = myAudio.captureStream();
  195. if (myAudio.readyState == 2) {
  196. console.log("出现声音停止");
  197. //alert("出现声音停止");
  198. //console.log("出现声音停止,当今进度 %s, %s", myAudio.played.end(0), myAudio.buffered.end(0));
  199. myAudio.play();
  200. }
  201. if (data.audio != null) //喂音频
  202. {
  203. //console.log("feed audio");
  204. audioMuxer.feed(audioData);
  205. }
  206. if (data.video != null) //喂视频
  207. {
  208. if (isFeed) {
  209. jmuxer.feed(data);
  210. }
  211. //jmuxer.feed(videoData);
  212. }
  213. if (data.video) {
  214. if (new Date().getTime() - curTime >= 1000) {
  215. //console.log("fps %d", fpsCount);
  216. fpsCount = 0;
  217. curTime = new Date().getTime();
  218. } else {
  219. fpsCount++;
  220. }
  221. }
  222. });
  223. myVideo.onmousedown = function(event) {
  224. //放在此处只是为了方便演示,实际使用中查找横竖屏只要刚连接上时调用一次就好。
  225. //var checkBuffer = GetScreenState();
  226. //ws.send(checkBuffer);
  227. /*if(!isFeed)
  228. {
  229. console.log("重新申请I帧");
  230. requestTime = new Date().getTime();
  231. var buffer = RequestIFrame();
  232. ws.send(buffer);
  233. }*/
  234. //console.log("报文 %s", PrintArry(buffer));
  235. if (event.button == 0) {
  236. var posX = event.offsetX * 1080 * 1.0 / myVideo.clientWidth;
  237. var posY = event.offsetY * 1920 * 1.0 / myVideo.clientHeight;
  238. var buffer = ExexuteMouseDown(posX.toString(), posY.toString());
  239. ws.send(buffer);
  240. isDrag = true;
  241. }
  242. }
  243. myVideo.onmousemove = function(event) {
  244. if (isDrag && event.button == 0) {
  245. var posX = event.offsetX * 1080 * 1.0 / myVideo.clientWidth;
  246. var posY = event.offsetY * 1920 * 1.0 / myVideo.clientHeight;
  247. var buffer = ExexuteMouseMove(posX.toString(), posY.toString());
  248. ws.send(buffer);
  249. //console.log("移动位置 %d, %d", posX, posY);
  250. }
  251. }
  252. myVideo.onmouseup = function(event) {
  253. isDrag = false;
  254. var posX = event.offsetX * 1080 * 1.0 / myVideo.clientWidth;
  255. var posY = event.offsetY * 1920 * 1.0 / myVideo.clientHeight;
  256. var buffer = ExexuteMouseUp(posX.toString(), posY.toString());
  257. ws.send(buffer);
  258. }
  259. myVideo.onkeydown = function(event) {
  260. ExexuteKeyDown(e.keyCode);
  261. }
  262. }
  263. function Back() {
  264. if (event.button == 2) {
  265. //ExexuteKeyDown(4);
  266. }
  267. ExexuteKeyDown(4);
  268. window.event.returnValue = false;
  269. return false;
  270. }
  271. </script>
  272. <script type="text/javascript" src="jmuxer.js"></script>
  273. </body>
  274. </html>