index.html 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8" />
  5. <title>NodePlayer.js Demo</title>
  6. <meta
  7. name="viewport"
  8. content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
  9. />
  10. <style>
  11. body {
  12. padding: 0;
  13. margin: 0;
  14. }
  15. .box {
  16. margin: 8px 0;
  17. display: flex;
  18. }
  19. .fl1 {
  20. flex: 1;
  21. }
  22. .fl0 {
  23. flex: 0;
  24. }
  25. </style>
  26. </head>
  27. <body>
  28. <div style="max-width: 980px; margin: 0 auto">
  29. <h3>
  30. <a href="http://www.nodemedia.cn" target="_blank">NodePlayer.js Demo</a>
  31. </h3>
  32. <div
  33. style="
  34. width: 100%;
  35. padding-bottom: 56%;
  36. position: relative;
  37. overflow: hidden;
  38. "
  39. >
  40. <canvas
  41. id="video1"
  42. style="
  43. width: 150%;
  44. height: 150%;
  45. position: absolute;
  46. background-color: black;
  47. "
  48. ></canvas>
  49. </div>
  50. <div>
  51. <div class="box">
  52. <input
  53. id="url"
  54. class="fl1"
  55. value="https://liveplay.fis.plus/live/v6up727idvci5o9feckkhfwkw8.flv"
  56. />
  57. <button class="fl0" onclick="startFunc()">Play</button>
  58. <button class="fl0" onclick="stopFunc()">Stop</button>
  59. <button class="fl0" onclick="fullFunc()">Full</button>
  60. <button class="fl0" onclick="screenshot()">ScreenShot</button>
  61. <button class="fl0" onclick="initCapturer()">InitCapturer</button>
  62. <button class="fl0" onclick="setClipSize()">setClipSize</button>
  63. <button class="fl0" onclick="capture()">CaptureOneFrame</button>
  64. </div>
  65. <div>
  66. <input
  67. type="range"
  68. id="slider1"
  69. name="slider1"
  70. min="0"
  71. max="1920"
  72. value="1920"
  73. />
  74. <label for="slider1">ClipWidth:</label>
  75. <span id="slider1Value">1920</span>
  76. <br />
  77. <input
  78. type="range"
  79. id="slider2"
  80. name="slider2"
  81. min="0"
  82. max="1080"
  83. value="1080"
  84. />
  85. <label for="slider2">ClipHeight:</label>
  86. <span id="slider2Value">1080</span>
  87. <br />
  88. </div>
  89. <div class="box">
  90. <div class="fl1">
  91. <label>Volume:</label>
  92. <select onchange="volumeChange(event);">
  93. <option>300</option>
  94. <option>200</option>
  95. <option selected>100</option>
  96. <option>75</option>
  97. <option>50</option>
  98. <option>25</option>
  99. <option>0</option>
  100. </select>
  101. </div>
  102. <div class="fl1">
  103. <label>BufferTime:</label>
  104. <select id="buffertime" onchange="bufferChange(event);">
  105. <option>0</option>
  106. <option>100</option>
  107. <option>300</option>
  108. <option>500</option>
  109. <option selected>1000</option>
  110. <option>2000</option>
  111. <option>3000</option>
  112. </select>
  113. </div>
  114. <div class="fl1">
  115. <label>ScaleMode:</label>
  116. <select onchange="scaleModeChange(event);">
  117. <option selected>0</option>
  118. <option>1</option>
  119. <option>2</option>
  120. </select>
  121. </div>
  122. <div class="fl1">
  123. <label>CryptoKey:</label>
  124. <input id="key" class="fl1" value="" />
  125. </div>
  126. </div>
  127. <!-- /input-group -->
  128. </div>
  129. <div style="margin-top: 20px"></div>
  130. <div style="display: flex">
  131. <div style="width: 30%; padding-bottom: 20%; position: relative">
  132. <canvas
  133. id="camera1"
  134. style="
  135. width: 100%;
  136. height: 100%;
  137. position: absolute;
  138. background-color: black;
  139. transform: scaleY(-1);
  140. "
  141. ></canvas>
  142. </div>
  143. <div style="margin-right: 20px"></div>
  144. <div style="width: 30%; padding-bottom: 20%; position: relative">
  145. <canvas
  146. id="camera2"
  147. style="
  148. width: 100%;
  149. height: 100%;
  150. position: absolute;
  151. background-color: black;
  152. transform: scaleY(-1);
  153. "
  154. ></canvas>
  155. </div>
  156. <div style="margin-right: 20px"></div>
  157. <div style="width: 30%; padding-bottom: 20%; position: relative">
  158. <canvas
  159. id="camera3"
  160. style="
  161. width: 100%;
  162. height: 100%;
  163. position: absolute;
  164. background-color: black;
  165. transform: scaleY(-1);
  166. "
  167. ></canvas>
  168. </div>
  169. </div>
  170. </div>
  171. <script src="https://cdn.bootcdn.net/ajax/libs/vConsole/3.3.4/vconsole.min.js"></script>
  172. <script
  173. type="text/javascript"
  174. src="./nodeplayer/NodePlayer.min.js"
  175. ></script>
  176. <script
  177. type="text/javascript"
  178. src="./video_processor/webgl_video_processor.js"
  179. ></script>
  180. <script
  181. type="text/javascript"
  182. src="./video_capturer/rtmp_video_capturer.js"
  183. ></script>
  184. <script>
  185. if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent)) {
  186. //手机开启vconsole,便于查看控制台调试信息,正式部署时无需使用,包括上面的vconsole.min.js也不用引入
  187. var vConsole = new VConsole();
  188. }
  189. var url = document.getElementById("url");
  190. var key = document.getElementById("key");
  191. var argurl = getQueryVariable("url");
  192. if (argurl) {
  193. url.value = argurl;
  194. }
  195. var argkey = getQueryVariable("key");
  196. if (argkey) {
  197. key.value = argkey;
  198. }
  199. /**
  200. * 是否打印debug信息
  201. */
  202. // NodePlayer.debug(true);
  203. //v0.5.70版之后,在Android手机端推荐使用以下音频引擎
  204. if (/(Android)/i.test(navigator.userAgent)) {
  205. NodePlayer.activeAudioEngine(true);
  206. }
  207. /// 创建视频裁切处理器(设置裁切方式)
  208. const videoProcessor = new VideoProcessor([
  209. { type: "scaleOnly", x: 2 / 3, y: 0.0, w: 1 / 3, h: 4 / 9 },
  210. { type: "threePart", x: 0.0, y: 2 / 3, w: 1, h: 4 / 27 },
  211. { type: "threePart", x: 0.0, y: 22 / 27, w: 1, h: 4 / 27 },
  212. ]);
  213. // 初始化(传入id)
  214. function initVideoProcessor() {
  215. videoProcessor.initializeById("video1", [
  216. "camera1",
  217. "camera2",
  218. "camera3",
  219. ]);
  220. }
  221. // 在播放器每一帧调用此方法(绘制裁切得到的部分)
  222. function onPlayerFrame() {
  223. videoProcessor.drawFrame();
  224. }
  225. var player;
  226. // 0.5.28之后, 为了统一asm与wasm版本api差异,现统一采用回调格式加载.
  227. NodePlayer.load(() => {
  228. player = new NodePlayer();
  229. /**
  230. * 自动测试浏览器是否支持MSE播放,如不支持,仍然使用软解码。
  231. * 紧随 new 后调用
  232. * 不调用则只使用软解
  233. */
  234. // player.useMSE();
  235. /**
  236. * 开启屏幕常亮
  237. * 在手机浏览器上,canvas标签渲染视频并不会像video标签那样保持屏幕常亮
  238. * 如果需要该功能, 可以调用此方法
  239. * H5目前在chrome\edge 84, android chrome 84及以上有原生亮屏API, 需要是https页面
  240. * 其余平台为模拟实现,非全兼容。
  241. */
  242. player.setKeepScreenOn();
  243. /**
  244. * 传入 canvas视图的id,当使用mse时,自动转换为video标签
  245. */
  246. player.setView("video1");
  247. /**
  248. * 设置最大缓冲时长,单位毫秒,只在软解时有效
  249. */
  250. player.setBufferTime(1000);
  251. /**
  252. * 设置超时时长, 单位秒,只在软解时有效
  253. * 在连接成功之前和播放中途,如果超过设定时长无数据返回,则回调timeout事件
  254. */
  255. // player.setTimeout(10);
  256. player.on("start", () => {
  257. console.log("player on start");
  258. });
  259. player.on("stop", () => {
  260. console.log("player on stop");
  261. });
  262. player.on("error", (e) => {
  263. console.log("player on error", e);
  264. });
  265. player.on("videoInfo", (w, h, codec) => {
  266. console.log(
  267. "player on video info width=" +
  268. w +
  269. " height=" +
  270. h +
  271. " codec=" +
  272. codec
  273. );
  274. initVideoProcessor();
  275. });
  276. player.on("audioInfo", (r, c, codec) => {
  277. console.log(
  278. "player on audio info samplerate=" +
  279. r +
  280. " channels=" +
  281. c +
  282. " codec=" +
  283. codec
  284. );
  285. });
  286. // player.on("metadata", (metadata) => {
  287. // var m = NodePlayer.AMF.parseScriptData(metadata.buffer, 0, metadata.length);
  288. // console.log(m);
  289. // });
  290. // player.on("videoSei", (sei, pts) => {
  291. // console.log("player on video sei=" + sei + " pts=" + pts);
  292. // });
  293. player.on("videoFrame", (pts) => {
  294. /// 每一帧执行一次视频处理
  295. onPlayerFrame();
  296. });
  297. // player.on("timeout", () => {
  298. // console.log("player on timeout");
  299. // player.stop();
  300. // });
  301. // player.on("buffer", (state) => {
  302. // console.log("player on buffer state=" + state);
  303. // });
  304. player.on("stats", (stats) => {
  305. // console.log("player on stats=", stats);
  306. });
  307. });
  308. function startFunc() {
  309. /**
  310. * 设置解密密码,必须是16字节
  311. */
  312. player.setCryptoKey(key.value);
  313. /**
  314. * 开始播放,参数为 http-flv或 websocket-flv 的url
  315. */
  316. player.start(url.value);
  317. }
  318. function stopFunc() {
  319. /**
  320. * 停止播放
  321. */
  322. player.stop();
  323. //按需清理画布为黑色背景
  324. // player.clearView();
  325. }
  326. function fullFunc() {
  327. player.fullscreen();
  328. }
  329. function volumeChange(event) {
  330. /**
  331. * 设置音量
  332. * 0.0 ~~ 3.0
  333. * 当为0.0时完全静音; 当为1.0时为正常音量; 继续增大之后进行音量增益,最大3.0
  334. */
  335. player.setVolume(event.target.value / 100.0);
  336. }
  337. function bufferChange(event) {
  338. player.setBufferTime(event.target.value);
  339. }
  340. function scaleModeChange(event) {
  341. /**
  342. * 视频缩放模式, 当视频分辨率比例与Canvas显示区域比例不同时,缩放效果不同:
  343. * 0 视频画面完全填充canvas区域,画面会被拉伸 --- 默认值
  344. * 1 视频画面做等比缩放后,对齐Canvas区域,画面不被拉伸,但有黑边
  345. * 2 视频画面做等比缩放后,完全填充Canvas区域,画面不被拉伸,没有黑边,但画面显示不全
  346. * 软解时有效
  347. */
  348. player.setScaleMode(event.target.value);
  349. }
  350. function screenshot() {
  351. // player.screenshot("np_screenshot.png", "png");
  352. player.screenshot("np_screenshot.jpeg", "jpeg", 0.8);
  353. }
  354. // 初始化截图器
  355. function initCapturer() {
  356. let videoSize = initRTMPVideoCapture("video1");
  357. console.log("videoSize", videoSize);
  358. }
  359. // 设置裁切区域
  360. function setClipSize() {
  361. var width = document.getElementById("slider1").value;
  362. var height = document.getElementById("slider2").value;
  363. setRTMPClipSize(width, height);
  364. }
  365. // 捕获一帧
  366. function capture() {
  367. captureRTMPOneFrame();
  368. }
  369. function getQueryVariable(variable) {
  370. var query = window.location.search.substring(1);
  371. var vars = query.split("&");
  372. for (var i = 0; i < vars.length; i++) {
  373. var pair = vars[i].split("=");
  374. if (pair[0] == variable) {
  375. return pair[1];
  376. }
  377. }
  378. return false;
  379. }
  380. // 获取 DOM 元素
  381. const slider1 = document.getElementById("slider1");
  382. const slider1Value = document.getElementById("slider1Value");
  383. const slider2 = document.getElementById("slider2");
  384. const slider2Value = document.getElementById("slider2Value");
  385. const showValuesButton = document.getElementById("showValuesButton");
  386. // 监听 input 事件
  387. slider1.addEventListener("input", () => {
  388. slider1Value.innerHTML = slider1.value;
  389. });
  390. slider2.addEventListener("input", () => {
  391. slider2Value.innerHTML = slider2.value;
  392. });
  393. </script>
  394. <script>
  395. (function () {
  396. var script = document.createElement("script");
  397. script.onload = function () {
  398. var stats = new Stats();
  399. stats.dom.style.position = "fixed";
  400. stats.dom.style.left = null;
  401. stats.dom.style.right = "0px";
  402. stats.dom.style.top = "0px";
  403. document.body.appendChild(stats.dom);
  404. requestAnimationFrame(function loop() {
  405. stats.update();
  406. requestAnimationFrame(loop);
  407. });
  408. };
  409. script.src = "https://mrdoob.github.io/stats.js/build/stats.min.js";
  410. document.head.appendChild(script);
  411. })();
  412. </script>
  413. </body>
  414. </html>