meta.gateway.ts 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. import {
  2. SubscribeMessage,
  3. WebSocketGateway,
  4. OnGatewayInit,
  5. WebSocketServer,
  6. OnGatewayConnection,
  7. OnGatewayDisconnect,
  8. } from '@nestjs/websockets';
  9. import { Server } from 'ws';
  10. import * as WebSocket from 'ws';
  11. import {
  12. PeerConnection,
  13. initLogger,
  14. DataChannel,
  15. cleanup,
  16. } from 'node-datachannel';
  17. import { Buffer } from 'buffer';
  18. import { Logger } from '@nestjs/common';
  19. import * as path from 'path';
  20. import { createReadStream } from 'fs';
  21. // import { SceneService } from './scene/scene.service';
  22. // 'Verbose' | 'Debug' | 'Info' | 'Warning' | 'Error' | 'Fatal';
  23. initLogger('Debug');
  24. @WebSocketGateway({
  25. transports: ['websocket'],
  26. cors: '*',
  27. // namespace: "ws",
  28. path: '/ws',
  29. })
  30. export class MetaGateway
  31. implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
  32. {
  33. // constructor(private readonly sceneService: SceneService) {}
  34. private logger: Logger = new Logger('MetaGateway');
  35. private peer: PeerConnection = null;
  36. private timer: NodeJS.Timeout;
  37. private _webrtcInterval: NodeJS.Timeout;
  38. private heartBeatFlag: number;
  39. private gameChanel: DataChannel;
  40. @WebSocketServer() server: Server;
  41. // @SubscribeMessage('message')
  42. // handleMessage(client: any, payload: any) {
  43. // this.logger.log(`payload: ${JSON.stringify(payload)}`);
  44. // }
  45. afterInit(server: Server) {
  46. this.logger.log('Init');
  47. // console.log('sceneService', this.sceneService);
  48. // console.log('gateway init', server);
  49. // this.sceneService.getService('SceneGrpcService');
  50. }
  51. @SubscribeMessage('init')
  52. handleInit(client: any, payload: any) {
  53. this.logger.log(`init: ${JSON.stringify(payload)}`);
  54. }
  55. @SubscribeMessage('heartbeat')
  56. handleHeartBeat(client: any, payload: any) {
  57. // this.logger.log(`heartbeat: ${JSON.stringify(payload)}`);
  58. // console.log('hb', payload);
  59. this.heartBeatFlag = payload;
  60. const pong = {
  61. channel_id: '',
  62. client_os: '',
  63. data: payload,
  64. fe_version: '',
  65. id: 'heartbeat',
  66. packet_id: '',
  67. room_id: '',
  68. session_id: '',
  69. trace_id: '',
  70. user_id: '',
  71. };
  72. // if (this.gameChanel.isOpen()) {
  73. // // console.log('this.gameChanel.isOpen()', this.gameChanel.isOpen());
  74. // // this.sendWertcHeartPack(this.gameChanel);
  75. // // const heartPack = new DataView(new ArrayBuffer(4));
  76. // // heartPack.setUint32(0, 2009889916);
  77. // // this.gameChanel.sendMessageBinary(Buffer.from(heartPack.buffer));
  78. // // return pong;
  79. // }
  80. return pong;
  81. }
  82. @SubscribeMessage('init_webrtc')
  83. handleInitWebRtc(client: any, payload: any): void {
  84. console.log('handleInitWebRtc');
  85. this.peer = new PeerConnection('roomTest', {
  86. // iceServers: ['stun:stun.l.google.com:19302'],
  87. portRangeBegin: 52000,
  88. portRangeEnd: 53000,
  89. iceServers: ['stun:172.18.156.41:3478', 'stun:120.24.252.95:3478'],
  90. });
  91. this.peer.onLocalDescription((sdp, type) => {
  92. console.warn('peer SDP:', sdp, ' Type:', type);
  93. const offer = { sdp, type };
  94. const offerFormat = {
  95. id: 'offer',
  96. data: Buffer.from(JSON.stringify(offer)).toString('base64'),
  97. };
  98. console.log('send', offerFormat);
  99. client.send(JSON.stringify(offerFormat));
  100. // return '';
  101. });
  102. const replaceToPublic = (candidate) => {
  103. console.warn('PRIVATE_IP', process.env.PRIVATE_IP);
  104. return candidate.replace(process.env.PRIVATE_IP, process.env.PUBLIC_IP);
  105. };
  106. this.peer.onLocalCandidate((candidate, mid) => {
  107. if (/172\./.test(candidate)) {
  108. console.error('private Ip process', candidate);
  109. if (candidate.includes(process.env.PRIVATE_IP)) {
  110. console.error('PRIVATE_IP', process.env.PRIVATE_IP);
  111. candidate = replaceToPublic(candidate);
  112. } else {
  113. return;
  114. }
  115. }
  116. if (/192.168\./.test(candidate)) {
  117. if (!/192.168.0\./.test(candidate)) {
  118. console.warn('不是192.168.0.测试网段', candidate);
  119. return;
  120. }
  121. // if (candidate.includes(process.env.PRIVATE_IP)) {
  122. // console.error('PRIVATE_IP', process.env.PRIVATE_IP);
  123. // candidate = replaceToPublic(candidate);
  124. // }
  125. }
  126. console.warn('onLocalCandidate last Candidate:', candidate);
  127. const iceRes = {
  128. candidate,
  129. sdpMid: mid,
  130. sdpMLineIndex: 0,
  131. };
  132. const res = {
  133. channel_id: '',
  134. client_os: '',
  135. data: Buffer.from(JSON.stringify(iceRes)).toString('base64'),
  136. fe_version: '',
  137. id: 'ice_candidate',
  138. packet_id: '',
  139. room_id: '',
  140. session_id: '',
  141. trace_id: '',
  142. user_id: '',
  143. };
  144. client.send(JSON.stringify(res));
  145. });
  146. this.peer.onStateChange((state) => {
  147. console.log('peer-State:', state);
  148. });
  149. this.peer.onGatheringStateChange((state) => {
  150. console.log('GatheringState:', state);
  151. });
  152. this.peer.onTrack((track) => {
  153. console.log('track', track);
  154. });
  155. this.gameChanel = this.peer.createDataChannel('game-input');
  156. this.peer.onDataChannel((dc) => {
  157. console.log('onDataChannel', dc);
  158. });
  159. this.gameChanel.onOpen(() => {
  160. console.log('channel is open');
  161. clearInterval(this.timer);
  162. const peers = this.peer.getSelectedCandidatePair();
  163. console.log('配对成功', peers);
  164. let i = 1;
  165. const paths = path.join(__dirname, '../ws/video/100');
  166. console.error('__dirname', __dirname);
  167. console.error('paths', paths);
  168. if (this.gameChanel.isOpen()) {
  169. console.log('gameChanel', this.gameChanel.isOpen());
  170. this.sendWertcHeartPack(this.gameChanel);
  171. }
  172. Number.prototype.padLeft = function (n, str) {
  173. return Array(n - String(this).length + 1).join(str || '0') + this;
  174. };
  175. this.timer = setInterval(() => {
  176. if (i < 30) {
  177. const steam = createReadStream(
  178. paths + `/100.${Number(i).padLeft(4, '0')}.h264`,
  179. );
  180. // const steam = createReadStream(paths + `/test2`);
  181. steam.on('data', (data: Buffer) => {
  182. // console.log(data.buffer);
  183. const frame = new DataView(data.buffer);
  184. frame.setUint32(0, 1437227610);
  185. // frame.setUint16(6, 36);
  186. // frame.setUint16(24, 0);
  187. // frame.setUint16(26, 0);
  188. // frame.setUint32(28, 0);
  189. this.gameChanel.sendMessageBinary(Buffer.from(frame.buffer));
  190. });
  191. }
  192. i++;
  193. }, 1000 / 30);
  194. });
  195. this.gameChanel.onClosed(() => {
  196. console.log('gameChanel close');
  197. this.stopSendWertcHeartPack();
  198. cleanup();
  199. });
  200. this.gameChanel.onMessage((event) => {
  201. console.log('gameChanel onMessage', event);
  202. });
  203. this.gameChanel.onError(() => {
  204. console.log('gameChanel onError');
  205. this.stopSendWertcHeartPack();
  206. });
  207. }
  208. sendWertcHeartPack(channel: DataChannel) {
  209. const heartPack = new DataView(new ArrayBuffer(4));
  210. heartPack.setUint32(0, 2009889916);
  211. this._webrtcInterval = setInterval(() => {
  212. if (channel.isOpen()) {
  213. channel.sendMessageBinary(Buffer.from(heartPack.buffer));
  214. }
  215. }, 200);
  216. }
  217. stopSendWertcHeartPack(): void {
  218. clearInterval(this._webrtcInterval);
  219. }
  220. @SubscribeMessage('ice_candidate')
  221. handlerIceCandidate(client: any, payload: any) {
  222. const iceCandidate = Buffer.from(payload, 'base64').toString('utf-8');
  223. const candidate = JSON.parse(iceCandidate);
  224. console.warn('收到ice_candidate', candidate);
  225. this.peer.addRemoteCandidate(candidate.candidate, candidate.sdpMid);
  226. }
  227. @SubscribeMessage('answer')
  228. handerAnswer(client: any, payload: any) {
  229. const answer = Buffer.from(payload, 'base64').toString('utf-8');
  230. console.log('answer', answer);
  231. const clientAnswer = JSON.parse(answer);
  232. this.peer.setLocalDescription(clientAnswer.sdp);
  233. this.peer.setRemoteDescription(clientAnswer.sdp, clientAnswer.type);
  234. }
  235. @SubscribeMessage('start')
  236. handlerWebrtcStart(client: any, payload: any) {
  237. console.log('start', payload);
  238. }
  239. handleConnection(client: WebSocket, ...args: any[]) {
  240. this.logger.log(`Client connected: ${args}`);
  241. const connected = {
  242. channel_id: '',
  243. client_os: '',
  244. data: '',
  245. fe_version: '',
  246. id: 'init',
  247. packet_id: '',
  248. room_id: '',
  249. session_id: '',
  250. trace_id: '',
  251. user_id: '',
  252. };
  253. const tt = JSON.stringify(connected);
  254. client.send(tt);
  255. }
  256. handleDisconnect(client: WebSocket) {
  257. this.logger.log(`Client disconnected: ${client.id}`);
  258. this.peer && this.peer.close();
  259. }
  260. }