scene.service.ts 40 KB

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