jswebrtc.min.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. var JSWebrtc = {
  2. Player: null,
  3. VideoElement: null,
  4. CreateVideoElements: function () {
  5. var elements = document.querySelectorAll(".jswebrtc");
  6. for (var i = 0; i < elements.length; i++) {
  7. new JSWebrtc.VideoElement(elements[i])
  8. }
  9. },
  10. FillQuery: function (query_string, obj) {
  11. obj.user_query = {};
  12. if (query_string.length == 0) return;
  13. if (query_string.indexOf("?") >= 0) query_string = query_string.split("?")[1];
  14. var queries = query_string.split("&");
  15. for (var i = 0; i < queries.length; i++) {
  16. var query = queries[i].split("=");
  17. obj[query[0]] = query[1];
  18. obj.user_query[query[0]] = query[1]
  19. }
  20. if (obj.domain) obj.vhost = obj.domain
  21. console.log(obj);
  22. },
  23. ParseUrl: function (rtmp_url) {
  24. var a = document.createElement("a");
  25. a.href = rtmp_url.replace("rtmp://", "http://").replace("webrtc://", "http://").replace("rtc://", "http://");
  26. var vhost = a.hostname;
  27. var app = a.pathname.substr(1, a.pathname.lastIndexOf("/") - 1);
  28. var stream = a.pathname.substr(a.pathname.lastIndexOf("/") + 1);
  29. app = app.replace("...vhost...", "?vhost=");
  30. if (app.indexOf("?") >= 0) {
  31. var params = app.substr(app.indexOf("?"));
  32. app = app.substr(0, app.indexOf("?"));
  33. if (params.indexOf("vhost=") > 0) {
  34. vhost = params.substr(params.indexOf("vhost=") + "vhost=".length);
  35. if (vhost.indexOf("&") > 0) {
  36. vhost = vhost.substr(0, vhost.indexOf("&"))
  37. }
  38. }
  39. }
  40. if (a.hostname == vhost) {
  41. var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
  42. if (re.test(a.hostname)) vhost = "__defaultVhost__"
  43. }
  44. var schema = "rtmp";
  45. if (rtmp_url.indexOf("://") > 0) schema = rtmp_url.substr(0, rtmp_url.indexOf("://"));
  46. var port = a.port;
  47. if (!port) {
  48. if (schema === "http") {
  49. port = 80
  50. } else if (schema === "https") {
  51. port = 443
  52. } else if (schema === "rtmp") {
  53. port = 1935
  54. } else if (schema === "webrtc" || schema === "rtc") {
  55. port = 1985
  56. }
  57. }
  58. var ret = {
  59. url: rtmp_url,
  60. schema: schema,
  61. server: a.hostname,
  62. port: port,
  63. vhost: vhost,
  64. app: app,
  65. stream: stream,
  66. eip: ''
  67. };
  68. JSWebrtc.FillQuery(a.search, ret);
  69. return ret
  70. },
  71. HttpPost: function (url, data) {
  72. return new Promise(function (resolve, reject) {
  73. var xhr = new XMLHttpRequest;
  74. xhr.onreadystatechange = function () {
  75. if (xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 300)) {
  76. var respone = JSON.parse(xhr.responseText);
  77. xhr.onreadystatechange = new Function;
  78. xhr = null;
  79. resolve(respone)
  80. } else {
  81. //JSWebrtc.Player.prototype.startLoading();
  82. }
  83. };
  84. xhr.ontimeout = function() {
  85. console.log('timeout...');
  86. xhr.open("POST", url, true);
  87. xhr.send(data);
  88. };
  89. xhr.open("POST", url, true);
  90. xhr.timeout = 1000;
  91. xhr.responseType = "text";
  92. xhr.setRequestHeader("Content-Type", "application/json");
  93. xhr.send(data)
  94. })
  95. }
  96. };
  97. if (document.readyState === "complete") {
  98. JSWebrtc.CreateVideoElements()
  99. } else {
  100. document.addEventListener("DOMContentLoaded", JSWebrtc.CreateVideoElements)
  101. }
  102. JSWebrtc.VideoElement = function () {
  103. "use strict";
  104. var VideoElement = function (element) {
  105. var url = element.dataset.url;
  106. if (!url) {
  107. throw "VideoElement has no `data-url` attribute"
  108. }
  109. var addStyles = function (element, styles) {
  110. for (var name in styles) {
  111. element.style[name] = styles[name]
  112. }
  113. };
  114. this.container = element;
  115. addStyles(this.container, {
  116. display: "inline-block",
  117. position: "relative",
  118. minWidth: "80px",
  119. minHeight: "80px"
  120. });
  121. this.video = document.createElement("video");
  122. this.video.width = 960;
  123. this.video.height = 540;
  124. addStyles(this.video, {
  125. display: "block",
  126. width: "100%"
  127. });
  128. this.container.appendChild(this.video);
  129. this.playButton = document.createElement("div");
  130. this.playButton.innerHTML = VideoElement.PLAY_BUTTON;
  131. addStyles(this.playButton, {
  132. zIndex: 2,
  133. position: "absolute",
  134. top: "0",
  135. bottom: "0",
  136. left: "0",
  137. right: "0",
  138. maxWidth: "75px",
  139. maxHeight: "75px",
  140. margin: "auto",
  141. opacity: "0.7",
  142. cursor: "pointer"
  143. });
  144. this.container.appendChild(this.playButton);
  145. var options = {
  146. video: this.video
  147. };
  148. for (var option in element.dataset) {
  149. try {
  150. options[option] = JSON.parse(element.dataset[option])
  151. } catch (err) {
  152. options[option] = element.dataset[option]
  153. }
  154. }
  155. this.player = new JSWebrtc.Player(url, options);
  156. element.playerInstance = this.player;
  157. if (options.poster && !options.autoplay) {
  158. options.decodeFirstFrame = false;
  159. this.poster = new Image;
  160. this.poster.src = options.poster;
  161. this.poster.addEventListener("load", this.posterLoaded);
  162. addStyles(this.poster, {
  163. display: "block",
  164. zIndex: 1,
  165. position: "absolute",
  166. top: 0,
  167. left: 0,
  168. bottom: 0,
  169. right: 0
  170. });
  171. this.container.appendChild(this.poster)
  172. }
  173. if (!this.player.options.streaming) {
  174. this.container.addEventListener("click", this.onClick.bind(this))
  175. }
  176. if (options.autoplay) {
  177. this.playButton.style.display = "none"
  178. }
  179. if (this.player.audioOut && !this.player.audioOut.unlocked) {
  180. var unlockAudioElement = this.container;
  181. if (options.autoplay) {
  182. this.unmuteButton = document.createElement("div");
  183. this.unmuteButton.innerHTML = VideoElement.UNMUTE_BUTTON;
  184. addStyles(this.unmuteButton, {
  185. zIndex: 2,
  186. position: "absolute",
  187. bottom: "10px",
  188. right: "20px",
  189. width: "75px",
  190. height: "75px",
  191. margin: "auto",
  192. opacity: "0.7",
  193. cursor: "pointer"
  194. });
  195. this.container.appendChild(this.unmuteButton);
  196. unlockAudioElement = this.unmuteButton
  197. }
  198. this.unlockAudioBound = this.onUnlockAudio.bind(this, unlockAudioElement);
  199. unlockAudioElement.addEventListener("touchstart", this.unlockAudioBound, false);
  200. unlockAudioElement.addEventListener("click", this.unlockAudioBound, true)
  201. }
  202. };
  203. VideoElement.prototype.onUnlockAudio = function (element, ev) {
  204. if (this.unmuteButton) {
  205. ev.preventDefault();
  206. ev.stopPropagation()
  207. }
  208. this.player.audioOut.unlock(function () {
  209. if (this.unmuteButton) {
  210. this.unmuteButton.style.display = "none"
  211. }
  212. element.removeEventListener("touchstart", this.unlockAudioBound);
  213. element.removeEventListener("click", this.unlockAudioBound)
  214. }.bind(this))
  215. };
  216. VideoElement.prototype.onClick = function (ev) {
  217. if (this.player.isPlaying) {
  218. this.player.pause();
  219. this.playButton.style.display = "block"
  220. } else {
  221. this.player.play();
  222. this.playButton.style.display = "none";
  223. if (this.poster) {
  224. this.poster.style.display = "none"
  225. }
  226. }
  227. };
  228. VideoElement.PLAY_BUTTON = '<svg style="max-width: 75px; max-height: 75px;" ' + 'viewBox="0 0 200 200" alt="Play video">' + '<circle cx="100" cy="100" r="90" fill="none" ' + 'stroke-width="15" stroke="#fff"/>' + '<polygon points="70, 55 70, 145 145, 100" fill="#fff"/>' + "</svg>";
  229. VideoElement.UNMUTE_BUTTON = '<svg style="max-width: 75px; max-height: 75px;" viewBox="0 0 75 75">' + '<polygon class="audio-speaker" stroke="none" fill="#fff" ' + 'points="39,13 22,28 6,28 6,47 21,47 39,62 39,13"/>' + '<g stroke="#fff" stroke-width="5">' + '<path d="M 49,50 69,26"/>' + '<path d="M 69,50 49,26"/>' + "</g>" + "</svg>";
  230. return VideoElement
  231. }();
  232. JSWebrtc.Player = function () {
  233. "use strict";
  234. var Player = function (url, options) {
  235. this.options = options || {};
  236. if (!url.match(/^webrtc?:\/\//)) {
  237. throw "JSWebrtc just work with webrtc"
  238. }
  239. if (!this.options.video) {
  240. throw "VideoElement is null"
  241. }
  242. this.urlParams = JSWebrtc.ParseUrl(url);
  243. this.pc = null;
  244. this.autoplay = !!options.autoplay || false;
  245. this.paused = true;
  246. if (this.autoplay) this.options.video.muted = true;
  247. this.startLoading()
  248. };
  249. Player.prototype.startLoading = function () {
  250. var _self = this;
  251. if (_self.pc) {
  252. _self.pc.close()
  253. }
  254. _self.pc = new RTCPeerConnection(null);
  255. _self.pc.ontrack = function (event) {
  256. _self.options.video["srcObject"] = event.streams[0]
  257. };
  258. _self.pc.addTransceiver("audio", {
  259. direction: "recvonly"
  260. });
  261. _self.pc.addTransceiver("video", {
  262. direction: "recvonly"
  263. });
  264. _self.pc.createOffer().then(function (offer) {
  265. return _self.pc.setLocalDescription(offer).then(function () {
  266. return offer
  267. })
  268. }).then(function (offer) {
  269. return new Promise(function (resolve, reject) {
  270. var port = _self.urlParams.port || 1985;
  271. var api = _self.urlParams.user_query.play || "/rtc/v1/play/";
  272. if (api.lastIndexOf("/") != api.length - 1) {
  273. api += "/"
  274. }
  275. var url = api;
  276. if (_self.urlParams.server && _self.urlParams.server != '') {
  277. url = "http://" + _self.urlParams.server + ":" + port + api;
  278. }
  279. //var url = "http://117.73.3.103:1985" + api;
  280. for (var key in _self.urlParams.user_query) {
  281. if (key != "api" && key != "play" && key != "server" && key != "port") {
  282. url += "&" + key + "=" + _self.urlParams.user_query[key]
  283. }
  284. }
  285. var data = {
  286. api: url,
  287. streamurl: _self.urlParams.url,
  288. clientip: null,
  289. sdp: offer.sdp
  290. };
  291. console.log("offer: " + JSON.stringify(data));
  292. JSWebrtc.HttpPost(url, JSON.stringify(data)).then(function (res) {
  293. console.log(res);
  294. if (_self.urlParams.eip && _self.urlParams.eip != '') {
  295. var ipreg = /((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}/g;
  296. res.sdp = res.sdp.replace(ipreg, _self.urlParams.eip);
  297. console.log('sdp中替换eip:');
  298. console.log(res);
  299. }
  300. if (res && res.sdp) {
  301. resolve(res.sdp);
  302. }
  303. },
  304. function (rej) {
  305. console.log('reject');
  306. reject(rej)
  307. })
  308. })
  309. }).then(function (answer) {
  310. return _self.pc.setRemoteDescription(new RTCSessionDescription({
  311. type: "answer",
  312. sdp: answer
  313. }))
  314. }).
  315. catch(function (reason) {
  316. throw reason
  317. });
  318. if (this.autoplay) {
  319. this.play()
  320. }
  321. };
  322. Player.prototype.play = function (ev) {
  323. if (this.animationId) {
  324. return
  325. }
  326. this.animationId = requestAnimationFrame(this.update.bind(this));
  327. this.paused = false
  328. };
  329. Player.prototype.pause = function (ev) {
  330. if (this.paused) {
  331. return
  332. }
  333. cancelAnimationFrame(this.animationId);
  334. this.animationId = null;
  335. this.isPlaying = false;
  336. this.paused = true;
  337. this.options.video.pause();
  338. if (this.options.onPause) {
  339. this.options.onPause(this)
  340. }
  341. };
  342. Player.prototype.stop = function (ev) {
  343. this.pause()
  344. };
  345. Player.prototype.destroy = function () {
  346. this.pause();
  347. this.pc && this.pc.close() && this.pc.destroy();
  348. this.audioOut && this.audioOut.destroy()
  349. };
  350. Player.prototype.update = function () {
  351. this.animationId = requestAnimationFrame(this.update.bind(this));
  352. if (this.options.video.readyState < 4) {
  353. return
  354. }
  355. if (!this.isPlaying) {
  356. this.isPlaying = true;
  357. this.options.video.play();
  358. if (this.options.onPlay) {
  359. this.options.onPlay(this)
  360. }
  361. }
  362. };
  363. return Player
  364. }();