scene.service.ts 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020
  1. import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
  2. import { ClientGrpc, Client } from '@nestjs/microservices';
  3. import { grpcClientOptions } from './grpc-scene.options';
  4. import { Logger } from '@nestjs/common';
  5. import { DataChannel } from 'node-datachannel';
  6. import { BehaviorSubject } from 'rxjs';
  7. // import * as streamBuffers from 'stream-buffers';
  8. import { ActionType } from './actionType';
  9. import { CacheService } from 'src/cache/cache.service';
  10. import { StreamService } from './stream/stream.service';
  11. // import { InjectQueue } from '@nestjs/bull';
  12. // import { Queue } from 'bull';
  13. import { RotateService } from 'src/rotate/rotate.service';
  14. import { DelayQueue, RxQueue, DebounceQueue } from 'rx-queue';
  15. import { MoveService } from 'src/move/move.service';
  16. import { GetRouterService } from 'src/get-router/get-router.service';
  17. import { ConfigService } from '@nestjs/config';
  18. @Injectable()
  19. export class SceneService implements OnModuleInit, OnModuleDestroy {
  20. constructor(
  21. private configService: ConfigService,
  22. private cacheService: CacheService,
  23. private streamService: StreamService,
  24. private rotateService: RotateService,
  25. private moveService: MoveService,
  26. private getRouterService: GetRouterService, // @InjectQueue('rotate') private rotateQueue: Queue, // @InjectQueue('walking') private walkingQueue: Queue,
  27. ) { }
  28. @Client(grpcClientOptions) private readonly client: ClientGrpc;
  29. public _frameInteval: NodeJS.Timeout;
  30. public _frameTimeout: NodeJS.Timeout;
  31. public _rotateTimeout: NodeJS.Timeout;
  32. public _moveTimeout: NodeJS.Timeout;
  33. public _JoyStickingTimeout: NodeJS.Timeout;
  34. public startSteaming = new BehaviorSubject<boolean>(false);
  35. public onRotating = new BehaviorSubject<boolean>(false);
  36. public onMoving = new BehaviorSubject<boolean>(false);
  37. public onJoysticking = new BehaviorSubject<boolean>(false);
  38. public frameCnt = new BehaviorSubject<number>(-1);
  39. private rotateframeCnt = -1;
  40. private moveframeCnt = -1;
  41. private joystickFrameCnt = -1;
  42. private rotateFirstIDR = true;
  43. private sceneGrpcService: SceneGrpcService;
  44. private channel: DataChannel;
  45. private logger: Logger = new Logger('SceneService');
  46. private frameCntInterval = 1000;
  47. private user_id: string;
  48. private roomId: string;
  49. private onSteaming = false;
  50. private mockserverTime = Date.now() - 1653000000478;
  51. private lastRenderMedia = '';
  52. private frameCntSubscription: any;
  53. private roQueueSubscription: any;
  54. private moveQueueSubscription: any;
  55. private walkingSub: any;
  56. private joystickSub: any;
  57. private clickQueueSub: any;
  58. private _rotateCurrentFame = -1;
  59. private streamServiceSub: any;
  60. // private roRequestQueue: RxQueue = new DelayQueue(20);
  61. private roQueue: RxQueue = new DelayQueue(
  62. Number(this.configService.get('queueConfig.rotate')) || 20,
  63. );
  64. private clickQueue: RxQueue = new DebounceQueue(500);
  65. private moveQueue: RxQueue = new DelayQueue(
  66. Number(this.configService.get('queueConfig.move')) || 20,
  67. );
  68. private joystickQueue: RxQueue = new DebounceQueue(500);
  69. private requestIFrameQueue: RxQueue = new DebounceQueue(2000);
  70. private requestIFrameQueueSub: any;
  71. private roRequestQueueSub: any;
  72. private rotateTimeStamp: number;
  73. private firstRender = false;
  74. private latestBreakPointId: number;
  75. private isHoldingStream = false;
  76. private lastMovingPointArray: MovingLastUpdateType[] = [];
  77. private latestWalkingRequest: any; // 最新waking的接收值
  78. private hasJoystickMoveRequest = false; // 最新joystick的接收值
  79. private moveSliceLastFrame = new BehaviorSubject<MovingLastUpdateType>(null);
  80. private moveSliceLastFrameSub: any;
  81. public lastMoveStreamFrame = new BehaviorSubject<StreamFrameType>({
  82. frame: -1,
  83. clipPath: '',
  84. metaData: '',
  85. });
  86. public users = {};
  87. // initUsers(app_id, userId) {
  88. // const user = {
  89. // appId: null,
  90. // userId: null,
  91. // breakPointId: null,
  92. // roomId: null,
  93. // player: {
  94. // position: { x: -700, y: 0, z: 0 },
  95. // angle: {
  96. // pitch: 0,
  97. // yaw: 0,
  98. // roll: 0,
  99. // },
  100. // },
  101. // camera: {
  102. // position: { x: -1145, y: 0, z: 160 },
  103. // angle: {
  104. // pitch: 0,
  105. // yaw: 0,
  106. // roll: 0,
  107. // },
  108. // },
  109. // rotateInfo: {
  110. // frameIndex: 0,
  111. // horizontal_move: 0,
  112. // mediaSrc: null,
  113. // },
  114. // moveInfo: {},
  115. // // traceIds: [],
  116. // // actionResponses:[]
  117. // };
  118. // user.appId = app_id;
  119. // user.userId = userId;
  120. // user.breakPointId = 100;
  121. // this.users[userId] = user;
  122. // }
  123. onModuleInit(): void {
  124. this.sceneGrpcService =
  125. this.client.getService<SceneGrpcService>('SceneGrpcService');
  126. this.logger.log('init SceneGrpcService');
  127. this.streamServiceSub = this.streamService.onSteaming.subscribe((val) => {
  128. this.onSteaming = val;
  129. });
  130. Number.prototype.padLeft = function (n, str) {
  131. return Array(n - String(this).length + 1).join(str || '0') + this;
  132. };
  133. this.logger.log('roQueue-period :' + Number(this.roQueue.period));
  134. this.logger.log('moveQueue-period :' + Number(this.moveQueue.period));
  135. }
  136. public isHeaderOrLast(index: number, length: number): boolean {
  137. if (index === 0 || index === length) {
  138. return true;
  139. } else {
  140. return false;
  141. }
  142. }
  143. public getConfig() {
  144. return {
  145. userId: this.user_id,
  146. roomId: this.roomId,
  147. };
  148. }
  149. public startStream(): void {
  150. clearInterval(this._frameInteval);
  151. if (this.frameCnt.value === -1) {
  152. this._frameInteval = setInterval(async () => {
  153. const next = this.frameCnt.value + 1;
  154. this.frameCnt.next(next);
  155. }, 1000);
  156. }
  157. }
  158. public holdSteam(): void {
  159. clearInterval(this._frameInteval);
  160. this.isHoldingStream = true;
  161. }
  162. public resumeStream(): void {
  163. this.onMoving.next(false);
  164. this.onRotating.next(false);
  165. this.isHoldingStream = false;
  166. this.moveframeCnt = -1;
  167. this.rotateframeCnt = -1;
  168. clearInterval(this._frameInteval);
  169. this._frameInteval = setInterval(async () => {
  170. const next = this.frameCnt.getValue() + 1;
  171. this.frameCnt.next(next);
  172. }, 1000);
  173. }
  174. public stopStream(): void {
  175. if (this.frameCntSubscription) {
  176. this.frameCntSubscription.unsubscribe();
  177. this.frameCntSubscription = null;
  178. }
  179. if (this.roQueueSubscription) {
  180. this.roQueueSubscription.unsubscribe();
  181. this.roQueueSubscription = null;
  182. }
  183. if (this.moveQueueSubscription) {
  184. this.moveQueueSubscription.unsubscribe();
  185. this.moveQueueSubscription = null;
  186. }
  187. this.frameCnt.next(-1);
  188. clearInterval(this._frameInteval);
  189. this.rotateframeCnt = -1;
  190. }
  191. setConfig(user_id: string, roomId: string): void {
  192. this.user_id = user_id;
  193. this.roomId = roomId;
  194. }
  195. onModuleDestroy() {
  196. if ('unsubscribe' in this.streamServiceSub) {
  197. this.streamService.onSteaming.unsubscribe();
  198. }
  199. }
  200. init(request: InitRequest) {
  201. try {
  202. this.rotateService.init(request.app_id, request.user_id);
  203. this.startSteaming.next(true);
  204. this.startStream();
  205. this.handleStream();
  206. // this.moveService.init(request.app_id, request.user_id);
  207. // this.initUsers(request.app_id, request.user_id);
  208. } catch (error) {
  209. this.logger.error('error', error);
  210. }
  211. }
  212. exit() {
  213. this.frameCnt.next(-1);
  214. this.rotateService.deleteUser(this.user_id);
  215. }
  216. async rotate(request: RotateRequest) {
  217. this.handleRotate(request);
  218. //this.logger.log('request', request)
  219. // this.roRequestQueue.next(request);
  220. // if (!this.roRequestQueueSub) {
  221. // this.handleRotate();
  222. // }
  223. }
  224. /**
  225. * rotate请求队列
  226. */
  227. async handleRotate(request) {
  228. // this.roRequestQueueSub = this.roRequestQueue.subscribe(
  229. // async (request: RotateRequest) => {
  230. // try {
  231. if (this.firstRender) {
  232. if (!this.roQueueSubscription) {
  233. this.handleRotateStream();
  234. }
  235. let redisMeta: StreamReplyType;
  236. this.onRotating.next(true);
  237. const start = performance.now();
  238. // 当move时处理
  239. if (this.onMoving.value) {
  240. const lastStreamFrame = this.lastMoveStreamFrame.getValue();
  241. const metaData: StreamReplyType = JSON.parse(
  242. lastStreamFrame.metaData,
  243. ) as any as StreamReplyType;
  244. console.log('stop-4', metaData.traceIds[0]);
  245. console.log('stop-5', request.trace_id);
  246. //判断request是否是新的
  247. if (metaData.traceIds.indexOf(request.trace_id) > -1) {
  248. return;
  249. }
  250. const newUserStates: NewUserStatesType = metaData.newUserStates.find(
  251. (item) => item.userId === this.user_id,
  252. );
  253. const trace_id = metaData.traceIds[0];
  254. const userId = newUserStates.userId;
  255. const breakPointId = metaData.endBreakPointId;
  256. const cameraAngle = newUserStates.playerState.camera.angle;
  257. const playerAngle = newUserStates.playerState.player.angle;
  258. this.logger.log(
  259. 'stop-data-0',
  260. trace_id,
  261. userId,
  262. cameraAngle,
  263. cameraAngle,
  264. );
  265. //debugger;
  266. redisMeta = await this.moveService.stop(
  267. trace_id,
  268. userId,
  269. breakPointId,
  270. cameraAngle,
  271. playerAngle,
  272. );
  273. this.logger.log('stop-redisMeta', redisMeta);
  274. this.onMoving.next(false);
  275. this.cleanMoveSteam();
  276. // redisMeta = await this.rotateService.rotate(request);
  277. } else {
  278. // 正常rotate
  279. redisMeta = await this.rotateService.seqExeRotate(request);
  280. }
  281. if (redisMeta && 'mediaSrc' in redisMeta) {
  282. const mediaSrc: string = redisMeta.mediaSrc || '';
  283. if (mediaSrc.length > 0) {
  284. const src = mediaSrc.split('?')[0];
  285. //this.logger.log('进入roQueue1', redisMeta.newUserStates[0].playerState.camera.angle.yaw);
  286. //this.logger.log('进入roQueue2', src);
  287. if (src.length > 0) {
  288. //this.logger.log('不同源');
  289. this.holdSteam();
  290. this.lastRenderMedia = src;
  291. const clipPath = this.configService.get('app.prefix') + src;
  292. //TODO 临时开出
  293. // delete redisMeta.mediaSrc;
  294. const stream: StreamFrameType = {
  295. frame: -1,
  296. clipPath: clipPath,
  297. metaData: JSON.stringify(redisMeta),
  298. serverTime: this.mockserverTime,
  299. DIR: 3,
  300. };
  301. //this.logger.log('rotate', stream, Date.now());
  302. clearTimeout(this._rotateTimeout);
  303. //this.logger.log('进入roQueue3', stream.clipPath);
  304. const stop = performance.now();
  305. const inMillSeconds = stop - start;
  306. const rounded = Number(inMillSeconds).toFixed(3);
  307. this.logger.log(`[timer]-rotate-入队列前: ${rounded}ms`);
  308. this.roQueue.next(stream);
  309. } else {
  310. // this.onRotating.next(false);
  311. }
  312. }
  313. }
  314. }
  315. // } catch (error) {
  316. // this.logger.error('rotate', error.message);
  317. // console.error('error', error);
  318. // }
  319. }
  320. async walking(request: MoveRequest) {
  321. this.latestWalkingRequest = request;
  322. this.logger.log('walking', request);
  323. // 进入正常walking流程
  324. if (!this.onMoving.getValue()) {
  325. this.latestWalkingRequest = null;
  326. this.handleWalking(request);
  327. }
  328. // 监听每小段最后一zhen
  329. if (!this.moveSliceLastFrameSub) {
  330. this.moveSliceLastFrameSub = this.moveSliceLastFrame.subscribe(
  331. async (frame: MovingLastUpdateType) => {
  332. //TODO 正在行走时,有新的reqest
  333. if (this.latestWalkingRequest && this.onMoving.value) {
  334. this.logger.log('stop-data-1', frame);
  335. // this.moveQueue.complete();
  336. // this.moveQueue.of('');
  337. // TODO 中断move队列 ?优化如何清空
  338. // this.moveQueue.pipe(ignoreElements());
  339. this.moveQueueSubscription.unsubscribe();
  340. this.moveQueueSubscription = null;
  341. //step1 执行stop方法
  342. const metaData: StreamReplyType = frame.metaData;
  343. const newUserStates: NewUserStatesType =
  344. metaData.newUserStates.find(
  345. (item) => item.userId === this.user_id,
  346. );
  347. const trace_id = metaData.traceIds[0];
  348. const userId = newUserStates.userId;
  349. const breakPointId = metaData.endBreakPointId;
  350. const cameraAngle = newUserStates.playerState.camera.angle;
  351. const playerAngle = newUserStates.playerState.player.angle;
  352. this.logger.log(
  353. 'stop-data-2',
  354. trace_id,
  355. userId,
  356. cameraAngle,
  357. cameraAngle,
  358. );
  359. const redisMeta = await this.moveService.stop(
  360. trace_id,
  361. userId,
  362. breakPointId,
  363. cameraAngle,
  364. playerAngle,
  365. );
  366. this.logger.log('stop-redisMeta', redisMeta);
  367. // 2. 中断重新walking
  368. this.handleReWalking(this.latestWalkingRequest);
  369. }
  370. },
  371. );
  372. }
  373. }
  374. /**
  375. * 行走队列处理器
  376. * @param request MoveRequest
  377. * @returns void
  378. */
  379. async handleWalking(request: MoveRequest): Promise<void> {
  380. try {
  381. // if (!this.onMoving.getValue()) {
  382. const start = performance.now();
  383. const user = this.moveService.users[this.user_id];
  384. this.logger.log('进入1 - searchRoad');
  385. this.logger.log('path-start' + user.breakPointId);
  386. const path = await this.getRouterService.searchRoad(
  387. user.appId,
  388. user.breakPointId,
  389. request.clicking_action.clicking_point,
  390. );
  391. this.logger.log('walking-path', path);
  392. if (!path) {
  393. this.logger.log('不存在--path', path);
  394. this.resumeStream();
  395. return;
  396. }
  397. // debugger;
  398. const walkingRes = await this.moveService.move(path, request);
  399. //this.logger.log('walking', walkingRes);
  400. // debugger;
  401. if (walkingRes && !this.onMoving.value) {
  402. //this.logger.log('walkingRes-front', walkingRes);
  403. // shift出前第一个镜头数据
  404. const rotateCamData = walkingRes[0];
  405. this.logger.log('rotateCamData', rotateCamData.length);
  406. if (rotateCamData?.length) {
  407. // 头数组[0] rotate 序列, 头是关键key
  408. walkingRes[0].forEach((item: StreamReplyType, index: number) => {
  409. item.mType = 'rotate';
  410. // item.DIR = index === 0 ? 1 : 3;
  411. const IDRflag = index % 5 === 0 ? 1 : 3;
  412. const dir = this.isHeaderOrLast(index, walkingRes[0].length - 1);
  413. item.DIR = dir ? 1 : IDRflag;
  414. });
  415. } else {
  416. this.logger.log('rotateCamData无数据');
  417. }
  418. // 二维数组 做move 序列, move类型
  419. console.log('move-walkingRes:' + JSON.stringify(walkingRes));
  420. if (walkingRes && walkingRes?.length >= 1) {
  421. for (let i = 1; i < walkingRes.length; i++) {
  422. Array.from(walkingRes[i]).forEach(
  423. (item: StreamReplyType, index: number) => {
  424. const IDRflag = index % 5 === 0 ? 1 : 3;
  425. const dir = this.isHeaderOrLast(
  426. index,
  427. walkingRes[i].length - 1,
  428. );
  429. item.DIR = dir ? 1 : IDRflag;
  430. //将每段最后一个推入lastMovingPointArray
  431. if (index === walkingRes[i].length - 1) {
  432. this.lastMovingPointArray.push({
  433. mediaSrc: item.mediaSrc,
  434. metaData: item,
  435. });
  436. }
  437. },
  438. );
  439. }
  440. }
  441. // walkingRes marker to everybody
  442. const seqs = Array.from(walkingRes).flat() as any as StreamReplyType[];
  443. if (seqs?.length) {
  444. this.logger.log('walking --总序列--seqs-2:' + seqs.length);
  445. const stop = performance.now();
  446. const inMillSeconds = stop - start;
  447. const rounded = Number(inMillSeconds).toFixed(3);
  448. this.logger.log(`[timer]-move-入队列前:-->${rounded}ms`);
  449. this.handleSeqMoving(seqs);
  450. } else {
  451. console.error('walking-move无数据');
  452. this.cleanMoveSteam();
  453. this.resumeStream();
  454. }
  455. // }
  456. }
  457. // });
  458. // }
  459. } catch (error) {
  460. this.logger.error('walking', error.message);
  461. this.cleanMoveSteam();
  462. this.resumeStream();
  463. }
  464. }
  465. /**
  466. * 改变路线后的walking队列处理(中转)
  467. * @param request MoveRequest
  468. */
  469. handleReWalking(request: MoveRequest) {
  470. this.latestWalkingRequest = null;
  471. this.handleWalking(request);
  472. }
  473. /***
  474. * joystick main core
  475. */
  476. async joystick(request: JoystickRequest) {
  477. // TODO hasJoystickMoveRequest中断
  478. this.logger.log('this.hasJoystickMoveRequest', this.hasJoystickMoveRequest);
  479. if (!this.hasJoystickMoveRequest) {
  480. this.handlejoystick(request);
  481. }
  482. }
  483. /***
  484. * joystick
  485. */
  486. async handlejoystick(request: JoystickRequest) {
  487. try {
  488. //const joystickRes = await this.moveService.joystick(request);
  489. const joystickRes = await this.moveService.seqExeJoystick(request);
  490. this.logger.log(
  491. 'joystick-breakPointId:' +
  492. this.moveService.users[this.user_id].breakPointId,
  493. );
  494. // 有数据 [0]是rotate数据,[1-infinity]是walking数据
  495. this.logger.log('joystickRes-1', joystickRes);
  496. this.onJoysticking.next(true);
  497. if (Array.isArray(joystickRes)) {
  498. // 处理第一个镜头数据
  499. const rotateCamData = joystickRes[0];
  500. this.logger.log('rotateCamData', rotateCamData.length);
  501. if (rotateCamData?.length) {
  502. // 头数组[0] rotate 序列, 头是关键key
  503. joystickRes[0].forEach((item: StreamReplyType, index: number) => {
  504. const IDRflag = index % 5 === 0 ? 1 : 3;
  505. const dir = this.isHeaderOrLast(index, joystickRes[0].length - 1);
  506. item.DIR = dir ? 1 : IDRflag;
  507. item.mType = 'rotate';
  508. });
  509. } else {
  510. this.logger.log('rotateCamData无数据');
  511. }
  512. // 二维数组 做move 序列, move类型
  513. if (joystickRes?.length >= 1) {
  514. for (let i = 1; i < joystickRes.length; i++) {
  515. this.logger.log('joystickRes-2', joystickRes[i]);
  516. Array.from(joystickRes[i]).forEach(
  517. (item: StreamReplyType, index: number) => {
  518. const IDRflag = index % 5 === 0 ? 1 : 3;
  519. const dir = this.isHeaderOrLast(
  520. index,
  521. joystickRes[i].length - 1,
  522. );
  523. item.DIR = dir ? 1 : IDRflag;
  524. // 将每段最后一个推入lastMovingPointArray
  525. if (index === joystickRes[i].length - 1) {
  526. this.lastMovingPointArray.push({
  527. mediaSrc: item.mediaSrc,
  528. metaData: item,
  529. });
  530. }
  531. //this.logger.log(
  532. // 'joystick:' +
  533. // JSON.stringify(
  534. // joystickRes[i][index]['newUserStates'][0].playerState
  535. // .camera.position,
  536. // ),
  537. // );
  538. },
  539. );
  540. }
  541. }
  542. const seqs = Array.from(joystickRes).flat() as any as StreamReplyType[];
  543. if (seqs?.length > 1) {
  544. this.logger.log('joystick:-seqs', seqs.length);
  545. //TODO joystick中断逻辑
  546. this.hasJoystickMoveRequest = true;
  547. this.handleSeqMoving(seqs);
  548. } else {
  549. console.warn('joystick-move无数据');
  550. }
  551. } else {
  552. this.logger.log('joystick-接收人物数据', this.onMoving.getValue());
  553. if (!this.onMoving.getValue()) {
  554. // 在非行走时接受
  555. this.holdSteam();
  556. if (this.joystickFrameCnt === -1) {
  557. this.joystickFrameCnt = this.frameCnt.getValue();
  558. }
  559. this.joystickFrameCnt += 1;
  560. const stream: StreamMetaType = {
  561. frame: this.joystickFrameCnt,
  562. metaData: JSON.stringify(joystickRes),
  563. };
  564. //this.logger.log('rotate', stream, Date.now());
  565. const res = await this.streamService.pushMetaDataToSteam(stream);
  566. if (res.done) {
  567. this.logger.log('joystick-frame', res.frame);
  568. this.frameCnt.next(res.frame);
  569. clearTimeout(this._JoyStickingTimeout);
  570. this._JoyStickingTimeout = setTimeout(() => {
  571. this.logger.log('joystick opt done');
  572. this.logger.log('joystick 交权给空流,当前pts', res.frame);
  573. // this.frameCnt.next(res.frame);
  574. this.onJoysticking.next(false);
  575. this.resumeStream();
  576. this.joystickFrameCnt = -1;
  577. }, 100);
  578. }
  579. }
  580. }
  581. } catch (error) {
  582. console.error('joystick错误', error);
  583. this.logger.error('joystick', error.message);
  584. }
  585. }
  586. /**
  587. * 主要处理moving的序列动作
  588. * @param seqs StreamReplyType[]
  589. */
  590. handleSeqMoving(seqs: StreamReplyType[]) {
  591. if (!this.moveQueueSubscription) {
  592. this.handleMoveSteam();
  593. }
  594. this.logger.log('moving-seqs', seqs.length);
  595. this.onMoving.next(true);
  596. this.holdSteam();
  597. //TODO Remove
  598. // clearTimeout(this._JoyStickingTimeout);
  599. seqs.forEach((frame: StreamReplyType) => {
  600. const mediaSrc = frame.mediaSrc;
  601. const src = mediaSrc.split('?')[0];
  602. const clipPath = this.configService.get('app.prefix') + src;
  603. const type = frame.mType?.length ? frame.mType.slice() : 'move';
  604. const stream: StreamFrameType = {
  605. frame: -1,
  606. clipPath: clipPath,
  607. metaData: JSON.stringify(frame),
  608. serverTime: this.mockserverTime,
  609. DIR: frame.DIR,
  610. mType: type,
  611. };
  612. this.moveQueue.next(stream);
  613. });
  614. }
  615. cleanMoveSteam() {
  616. if (this.moveQueueSubscription) {
  617. this.moveQueueSubscription.unsubscribe();
  618. this.moveQueueSubscription = null;
  619. }
  620. if (this.walkingSub) {
  621. this.walkingSub.unsubscribe();
  622. this.walkingSub = null;
  623. }
  624. // if (this.clickQueueSub) {
  625. // this.clickQueueSub.unsubscribe();
  626. // this.clickQueueSub = null;
  627. // }
  628. }
  629. handleMoveSteam() {
  630. this.moveQueueSubscription = this.moveQueue.subscribe(
  631. async (stream: StreamFrameType) => {
  632. const metaData: StreamReplyType = JSON.parse(stream.metaData);
  633. if (this.moveframeCnt === -1) {
  634. this.moveframeCnt = this.frameCnt.getValue();
  635. }
  636. this.moveframeCnt += 1;
  637. this.latestBreakPointId = metaData.endBreakPointId;
  638. const streamData: StreamFrameType = {
  639. frame: this.moveframeCnt,
  640. clipPath: stream.clipPath,
  641. metaData: stream.metaData,
  642. serverTime: this.mockserverTime,
  643. DIR: stream.DIR,
  644. };
  645. this.logger.log(
  646. '[media-move]: ' +
  647. ', moveframeCnt: ' +
  648. this.moveframeCnt +
  649. ', clipPath: ' +
  650. stream.clipPath +
  651. ', mType: ' +
  652. stream.mType +
  653. ', DIR: ' +
  654. stream.DIR,
  655. // stream.metaData,
  656. );
  657. this.logger.log(
  658. '[media-move-lastMovingPointArray]',
  659. this.lastMovingPointArray?.length,
  660. );
  661. this.lastMoveStreamFrame.next(streamData);
  662. const res = await this.streamService.pushFrameToSteam(streamData);
  663. const isLastFrameIndex = this.lastMovingPointArray.findIndex(
  664. (item) => item.mediaSrc === metaData.mediaSrc,
  665. );
  666. //this.logger.log('path-update-index', isLastFrameIndex);
  667. //每一段的最后一帧
  668. if (isLastFrameIndex > -1) {
  669. //this.logger.log('path-update-array', this.lastMovingPointArray);
  670. const currentMeta = this.lastMovingPointArray[isLastFrameIndex];
  671. const userId = this.user_id;
  672. const breakPointId = currentMeta.metaData.endBreakPointId;
  673. const lastReply = currentMeta.metaData;
  674. this.moveService.updateUser(userId, breakPointId, lastReply);
  675. //debugger
  676. this.lastMovingPointArray.splice(isLastFrameIndex, 1);
  677. //TODO 队列每一段最后one frame
  678. this.moveSliceLastFrame.next(currentMeta);
  679. }
  680. if (res.done) {
  681. clearTimeout(this._moveTimeout);
  682. this._moveTimeout = setTimeout(() => {
  683. this.logger.log('move 交权给空流,当前pts', res.frame);
  684. this.frameCnt.next(res.frame);
  685. this.resumeStream();
  686. this.rotateframeCnt = -1;
  687. this.onMoving.next(false);
  688. this.onJoysticking.next(false);
  689. this.cleanMoveSteam();
  690. this.lastMovingPointArray = [];
  691. this.hasJoystickMoveRequest = false;
  692. this.logger.log('move end');
  693. }, 300);
  694. }
  695. },
  696. );
  697. }
  698. handleDataChanelOpen(channel: DataChannel): void {
  699. this.channel = channel;
  700. this.streamService.setChannel(channel);
  701. }
  702. handleDataChanelClose(): void {
  703. this.stopStream();
  704. this.startSteaming.next(false);
  705. this.streamService.closeChannel();
  706. // const exitRequest: ExitRequest = {
  707. // action_type: 1002,
  708. // user_id: this.user_id,
  709. // trace_id: '',
  710. // };
  711. this.exit();
  712. }
  713. handleMessage(message: string | Buffer) {
  714. try {
  715. if (typeof message === 'string') {
  716. // wasm:特例, requestIframe
  717. if (message.includes('wasm:')) {
  718. const parseData = message
  719. ? String(message).replace('wasm:', '')
  720. : `{"MstType":1}`;
  721. const msg: RTCMessageRequest = JSON.parse(parseData);
  722. this.logger.error('lostIframe-message', JSON.stringify(msg));
  723. if (msg.MstType === 0) {
  724. this.handleIframeRequest();
  725. }
  726. } else {
  727. const msg: RTCMessageRequest = JSON.parse(message);
  728. console.log('msg.action_type:' + msg.action_type);
  729. switch (msg.action_type) {
  730. case ActionType.walk:
  731. const walk = msg as any as MoveRequest;
  732. this.walking(walk);
  733. break;
  734. case ActionType.joystick:
  735. const JoystickRequest = msg as any as JoystickRequest;
  736. this.joystick(JoystickRequest);
  737. break;
  738. case ActionType.breathPoint:
  739. this.handleBreath(msg);
  740. break;
  741. case ActionType.rotate:
  742. const rotateRequest: RotateRequest = msg;
  743. this.rotate(rotateRequest);
  744. break;
  745. case ActionType.userStatus:
  746. this.updateUserStatus(msg);
  747. break;
  748. case ActionType.status:
  749. this.updateStatus();
  750. break;
  751. default:
  752. break;
  753. }
  754. }
  755. }
  756. } catch (error) {
  757. this.logger.error('handleMessage:rtc--error', error.message);
  758. }
  759. }
  760. async handleIframeRequest() {
  761. //TODO Iframe 最终传什么?
  762. this.requestIFrameQueue.next(this.streamService.lastStreamFrame.getValue());
  763. if (!this.requestIFrameQueueSub) {
  764. this.requestIFrameQueueSub = this.requestIFrameQueue.subscribe(
  765. (frameData: StreamFrameType) => {
  766. const nextFrame = this.frameCnt.getValue() + 1;
  767. this.logger.warn('lostIframe', nextFrame);
  768. frameData.frame = nextFrame;
  769. this.streamService.pushFrameToSteam(frameData);
  770. this.frameCnt.next(nextFrame);
  771. this.resumeStream();
  772. },
  773. );
  774. }
  775. }
  776. handleBreath(request) {
  777. const npsRes = this.moveService.getBreakPoints(request);
  778. //this.logger.log('npsRes', npsRes.nps);
  779. this.streamService.pushNormalDataToStream(npsRes);
  780. }
  781. updateStatus(): void {
  782. const reply = {
  783. data: { action_type: 1009, echo_msg: { echoMsg: Date.now() } },
  784. track: false,
  785. };
  786. this.streamService.pushNormalDataToStream(reply);
  787. }
  788. updateUserStatus(request) {
  789. try {
  790. const usersData = this.rotateService.getNewUserStateRequest(request);
  791. if (usersData) {
  792. usersData.actionType = 1024;
  793. //this.logger.log(
  794. // 'joystick:->updateUserStatus' +
  795. // 'playerPosition:' +
  796. // JSON.stringify(
  797. // redisMeta['newUserStates'][0].playerState.player.position,
  798. // ),
  799. // );
  800. this.streamService.pushNormalDataToStream(usersData);
  801. } else {
  802. this.logger.error('updateUserStatus::function-empty');
  803. }
  804. } catch (error) {
  805. this.logger.error('updateUserStatus::function', error.message);
  806. }
  807. }
  808. /**
  809. * rotate 推送队列
  810. */
  811. handleRotateStream() {
  812. if (!this.roQueueSubscription) {
  813. this.roQueueSubscription = this.roQueue.subscribe(
  814. async (stream: StreamFrameType) => {
  815. this.rotateTimeStamp = Date.now();
  816. if (this.rotateframeCnt === -1) {
  817. this.rotateframeCnt = this.frameCnt.value;
  818. }
  819. this.rotateframeCnt += 1;
  820. stream.frame = this.rotateframeCnt;
  821. this._rotateCurrentFame += 1;
  822. const IDRflag = this._rotateCurrentFame % 5 === 0 ? 1 : 3;
  823. this.logger.log(
  824. `当前rotate ,mainframeCnt:${this.frameCnt.getValue()}, _rotateCurrentFame:${this._rotateCurrentFame
  825. } IDRflag:${IDRflag}`,
  826. );
  827. stream.DIR = this.rotateFirstIDR ? 1 : IDRflag;
  828. if (this.rotateFirstIDR) {
  829. this.rotateFirstIDR = false;
  830. }
  831. this.logger.log(
  832. '[media-rotate]: ' +
  833. ', frame: ' +
  834. stream.frame +
  835. ', rotateframeCnt: ' +
  836. this.rotateframeCnt +
  837. ', clipPath: ' +
  838. stream.clipPath,
  839. // stream.metaData,
  840. );
  841. // this.logger.log(
  842. // `roQueueSubscription:frame:${this.rotateframeCnt} ` +
  843. // JSON.stringify(stream.metaData),
  844. // );
  845. const res = await this.streamService.pushFrameToSteam(stream);
  846. if (res.done) {
  847. clearTimeout(this._rotateTimeout);
  848. this._rotateTimeout = setTimeout(() => {
  849. this.logger.log('rotate end', Date.now());
  850. this.frameCnt.next(res.frame);
  851. this.resumeStream();
  852. this.rotateframeCnt = -1;
  853. this._rotateCurrentFame = -1;
  854. this.onMoving.next(false);
  855. this.onRotating.next(false);
  856. this.rotateFirstIDR = true;
  857. //TODO rotate完后清除request队列
  858. if (this.roRequestQueueSub) {
  859. this.roRequestQueueSub.unsubscribe();
  860. this.roRequestQueueSub = null;
  861. }
  862. }, 300);
  863. }
  864. },
  865. );
  866. }
  867. }
  868. pushFirstRender(clipPath: string, metaData: string): Promise<boolean> {
  869. return new Promise<boolean>(async (resolve, reject) => {
  870. try {
  871. const streamData: StreamFrameType = {
  872. frame: 1,
  873. clipPath: clipPath,
  874. metaData: metaData,
  875. serverTime: this.mockserverTime,
  876. DIR: 1,
  877. };
  878. const hasPush = await this.streamService.pushFrameToSteam(streamData);
  879. return resolve(hasPush.done);
  880. } catch (error) {
  881. return reject(false);
  882. }
  883. });
  884. }
  885. handleStream() {
  886. this.logger.log('this.frameCntSubscription', this.frameCntSubscription);
  887. if (!this.frameCntSubscription) {
  888. this.frameCntSubscription = this.frameCnt.subscribe(async (frame) => {
  889. try {
  890. this.logger.log('frame', frame);
  891. if (frame === 1) {
  892. const redisData = await this.rotateService.echo(this.user_id, true);
  893. this.logger.log('获取-首屏', redisData);
  894. this.onSteaming = true;
  895. this.holdSteam();
  896. if (redisData && 'mediaSrc' in redisData) {
  897. const mediaSrc: string = redisData.mediaSrc || '';
  898. if (mediaSrc.length > 0) {
  899. const src = mediaSrc.split('?')[0];
  900. // 临时本地替换路经
  901. // src = src.replace('/10086/', '');
  902. // const clipPath = join(__dirname, `../ws/${src}`);
  903. const clipPath = this.configService.get('app.prefix') + src;
  904. delete redisData.mediaSrc;
  905. this.logger.log(
  906. `user:${this.user_id}:first render stream` +
  907. JSON.stringify({ path: clipPath, meta: redisData }),
  908. );
  909. const status = await this.pushFirstRender(
  910. clipPath,
  911. JSON.stringify(redisData),
  912. );
  913. if (status) {
  914. this.firstRender = true;
  915. this.frameCnt.next(2);
  916. this.resumeStream();
  917. } else {
  918. this.logger.error('first render problem', status);
  919. }
  920. }
  921. } else {
  922. this.logger.error(`首屏::无数据:${frame}`);
  923. }
  924. }
  925. if (
  926. frame > 1 &&
  927. !this.onMoving.value &&
  928. !this.onRotating.value &&
  929. !this.onJoysticking.value &&
  930. this.firstRender
  931. ) {
  932. const redisDataAuto = await this.rotateService.echo(
  933. this.user_id,
  934. false,
  935. );
  936. if (redisDataAuto) {
  937. this.logger.log(`空白流::有数据:${frame}`);
  938. 'mediaSrc' in redisDataAuto && delete redisDataAuto.mediaSrc;
  939. const streamMeta: StreamMetaType = {
  940. frame: frame,
  941. metaData: JSON.stringify(redisDataAuto),
  942. };
  943. this.streamService.pushMetaDataToSteam(streamMeta);
  944. } else {
  945. this.stopStream();
  946. this.logger.log('空流无Redis数据');
  947. throw new Error('空流无Redis数据');
  948. }
  949. }
  950. } catch (error) {
  951. this.stopStream();
  952. this.logger.error('handleStream', error.message);
  953. }
  954. });
  955. }
  956. }
  957. }