scene.service.ts 40 KB

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