scene.service.ts 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  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, ThrottleQueue, DebounceQueue } from 'rx-queue';
  15. import { MoveService } from 'src/move/move.service';
  16. import { GetRouterService } from 'src/get-router/get-router.service';
  17. import { join } from 'path';
  18. @Injectable()
  19. export class SceneService implements OnModuleInit, OnModuleDestroy {
  20. constructor(
  21. private cacheService: CacheService,
  22. private streamService: StreamService,
  23. private rotateService: RotateService,
  24. private moveService: MoveService,
  25. private getRouterService: GetRouterService, // @InjectQueue('rotate') private rotateQueue: Queue, // @InjectQueue('walking') private walkingQueue: Queue,
  26. ) { }
  27. @Client(grpcClientOptions) private readonly client: ClientGrpc;
  28. public _frameInteval: NodeJS.Timeout;
  29. public _frameTimeout: NodeJS.Timeout;
  30. public _rotateTimeout: NodeJS.Timeout;
  31. public _moveTimeout: NodeJS.Timeout;
  32. public startSteaming = new BehaviorSubject<boolean>(false);
  33. public onRotating = new BehaviorSubject<boolean>(false);
  34. public onMoving = new BehaviorSubject<boolean>(false);
  35. public frameCnt = new BehaviorSubject<number>(-1);
  36. private rotateframeCnt = -1;
  37. private moveframeCnt = -1;
  38. private sceneGrpcService: SceneGrpcService;
  39. private channel: DataChannel;
  40. private logger: Logger = new Logger('SceneService');
  41. private frameCntInterval = 1000;
  42. private user_id: string;
  43. private roomId: string;
  44. private onSteaming = false;
  45. private mockserverTime = Date.now() - 1653000000478;
  46. private lastRenderMedia = '';
  47. private frameCntSubscription: any;
  48. private roQueueSubscription: any;
  49. private moveQueueSubscription: any;
  50. private walkingSub: any;
  51. private joystickSub: any;
  52. private streamServiceSub: any;
  53. private roQueue: RxQueue = new DelayQueue(10);
  54. private clickQueue: RxQueue = new DebounceQueue(500);
  55. private moveQueue: RxQueue = new DelayQueue(100);
  56. private joystickQueue: RxQueue = new DebounceQueue(500);
  57. private rotateTimeStamp: number;
  58. private lastMoveCnt = -1;
  59. private firstRender = false;
  60. private latestBreakPointId: number;
  61. public lastMoveStreamFrame = new BehaviorSubject<StreamFrameType>({
  62. frame: -1,
  63. clipPath: '',
  64. metaData: '',
  65. });
  66. public users = {};
  67. initUsers(app_id, userId) {
  68. const user = {
  69. appId: null,
  70. userId: null,
  71. breakPointId: null,
  72. roomId: null,
  73. player: {
  74. position: { x: -700, y: 0, z: 0 },
  75. angle: {
  76. pitch: 0,
  77. yaw: 0,
  78. roll: 0,
  79. },
  80. },
  81. camera: {
  82. position: { x: -1145, y: 0, z: 160 },
  83. angle: {
  84. pitch: 0,
  85. yaw: 0,
  86. roll: 0,
  87. },
  88. },
  89. rotateInfo: {
  90. frameIndex: 0,
  91. horizontal_move: 0,
  92. mediaSrc: null,
  93. },
  94. moveInfo: {},
  95. // traceIds: [],
  96. // actionResponses:[]
  97. };
  98. user.appId = app_id;
  99. user.userId = userId;
  100. user.breakPointId = 100;
  101. this.users[userId] = user;
  102. }
  103. onModuleInit(): void {
  104. this.sceneGrpcService =
  105. this.client.getService<SceneGrpcService>('SceneGrpcService');
  106. this.logger.log('init SceneGrpcService');
  107. this.streamServiceSub = this.streamService.onSteaming.subscribe((val) => {
  108. this.onSteaming = val;
  109. });
  110. Number.prototype.padLeft = function (n, str) {
  111. return Array(n - String(this).length + 1).join(str || '0') + this;
  112. };
  113. }
  114. public getConfig() {
  115. return {
  116. userId: this.user_id,
  117. roomId: this.roomId,
  118. };
  119. }
  120. public startStream(): void {
  121. clearInterval(this._frameInteval);
  122. if (this.frameCnt.value === -1) {
  123. this._frameInteval = setInterval(async () => {
  124. const next = this.frameCnt.value + 1;
  125. this.frameCnt.next(next);
  126. }, 1000);
  127. }
  128. }
  129. public holdSteam(): void {
  130. clearInterval(this._frameInteval);
  131. }
  132. public resumeStream(): void {
  133. this.onMoving.next(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. // const initReply = this.sceneGrpcService.init(request);
  171. // initReply.subscribe((reply) => {
  172. // console.log('initReply', reply);
  173. // });
  174. this.rotateService.init(request.app_id, request.user_id);
  175. // this.moveService.init(request.app_id, request.user_id);
  176. // this.initUsers(request.app_id, request.user_id);
  177. } catch (error) {
  178. console.log('error', error);
  179. }
  180. }
  181. exit(request: ExitRequest) {
  182. this.frameCnt.next(-1);
  183. this.stopStream();
  184. // const exitReply = this.sceneGrpcService.exit(request);
  185. // exitReply.subscribe((reply) => {
  186. // console.log('exitReply', reply);
  187. // });
  188. }
  189. async rotate(request: RotateRequest) {
  190. try {
  191. if (this.firstRender) {
  192. if (!this.roQueueSubscription) {
  193. this.handleRotateStream();
  194. }
  195. let redisMeta: StreamReplyType;
  196. this.onRotating.next(true);
  197. if (this.onMoving.value) {
  198. this.onMoving.next(false);
  199. const lastStreamFrame = this.lastMoveStreamFrame.getValue();
  200. const metaData: StreamReplyType = JSON.parse(
  201. lastStreamFrame.metaData,
  202. ) as any as StreamReplyType;
  203. const newUserStates: NewUserStatesType = metaData.newUserStates.find(
  204. (item) => item.userId === this.user_id,
  205. );
  206. const trace_id = metaData.traceIds[0];
  207. const userId = newUserStates.userId;
  208. const breakPointId = metaData.breakPointId;
  209. const cameraAngle = newUserStates.playerState.camera.angle;
  210. const playerAngle = newUserStates.playerState.player.angle;
  211. console.log('stop-data', trace_id, userId, cameraAngle, cameraAngle);
  212. redisMeta = await this.moveService.stop(
  213. trace_id,
  214. userId,
  215. breakPointId,
  216. cameraAngle,
  217. playerAngle,
  218. );
  219. console.log('stop-redisMeta', redisMeta);
  220. // redisMeta = await this.rotateService.rotate(request);
  221. } else {
  222. redisMeta = await this.rotateService.rotate(request);
  223. }
  224. if (redisMeta && 'mediaSrc' in redisMeta) {
  225. const mediaSrc: string = redisMeta.mediaSrc || '';
  226. if (mediaSrc.length > 0) {
  227. let src = mediaSrc.split('?')[0];
  228. // 临时本地替换路经
  229. src = src.replace('/0000000001/', '');
  230. // 判断不是同一条源时才推出
  231. if (src.length > 0) {
  232. // console.log('不同源');
  233. // this.frameCnt += 1;
  234. this.holdSteam();
  235. this.lastRenderMedia = src;
  236. const clipPath = join(__dirname, `../ws/video/${src}`);
  237. // console.log('src-clipPath', src, clipPath);
  238. delete redisMeta.mediaSrc;
  239. // const nextFrame = this.frameCnt.getValue() + 1;
  240. // console.log('nextFrame', nextFrame);
  241. // this.frameCnt.next(nextFrame);
  242. const random_boolean = Math.random() < 0.3;
  243. const stream: StreamFrameType = {
  244. frame: -1,
  245. clipPath: clipPath,
  246. metaData: JSON.stringify(redisMeta),
  247. serverTime: this.mockserverTime,
  248. DIR: this.frameCnt.getValue() < 10 ? 3 : random_boolean ? 1 : 3,
  249. };
  250. // this.rotateQueue.add(stream, {
  251. // delay: 5,
  252. // jobId: `rotate:${this.user_id}:${this.frameCnt.getValue()}`,
  253. // removeOnComplete: true,
  254. // });
  255. clearTimeout(this._rotateTimeout);
  256. this.roQueue.next(stream);
  257. } else {
  258. // this.onRotating.next(false);
  259. }
  260. }
  261. }
  262. }
  263. } catch (error) {
  264. this.logger.error('rotate', error);
  265. console.log('error', error);
  266. }
  267. }
  268. async walking(req) {
  269. try {
  270. console.log('walking', req);
  271. this.clickQueue.next(req);
  272. this.walkingSub = this.clickQueue.subscribe(async (request) => {
  273. const user = this.moveService.users[this.user_id];
  274. const path = await this.getRouterService.searchRoad(
  275. user.appId,
  276. user.breakPointId,
  277. req.clicking_action.clicking_point,
  278. );
  279. const walkingRes = await this.moveService.move(path, request);
  280. // this.moveService.rotateForAngle();
  281. console.log('walkingRes', walkingRes);
  282. if (walkingRes && !this.onMoving.value) {
  283. console.log('walkingRes-front', walkingRes);
  284. // shift出前第一个镜头数据
  285. const rotateCamData = walkingRes.shift();
  286. // walkingRes marker to everybody
  287. const seqs = Array.from(
  288. walkingRes,
  289. ).flat() as any as StreamReplyType[];
  290. if (seqs?.length) {
  291. this.handleSeqMoving(seqs);
  292. } else {
  293. console.log('walking无数据');
  294. }
  295. // this.lastMoveCnt = this.frameCnt.value + seqs.length;
  296. }
  297. });
  298. } catch (error) {
  299. this.logger.error('walking', error);
  300. }
  301. }
  302. async joystick(request: JoystickRequest) {
  303. try {
  304. this.joystickQueue.next(request);
  305. if (!this.joystickSub) {
  306. this.joystickSub = this.joystickQueue.subscribe(
  307. async (joystickRequest) => {
  308. const joystickRes = await this.moveService.joystick(
  309. joystickRequest,
  310. );
  311. console.log('joystickRes-front', joystickRes);
  312. // 有数据 [0]是rotate数据,[1-infinity]是walking数据
  313. if (Array.isArray(joystickRes)) {
  314. // shift出前第一个镜头数据
  315. const rotateCamData = joystickRes.shift();
  316. console.log('rotateCamData', rotateCamData);
  317. console.log('joystickRes-end', joystickRes);
  318. if (rotateCamData?.length) {
  319. }
  320. const seqs = Array.from(
  321. joystickRes,
  322. ).flat() as any as StreamReplyType[];
  323. if (seqs?.length) {
  324. this.handleSeqMoving(seqs);
  325. } else {
  326. console.log('walking无数据');
  327. }
  328. } else {
  329. console.log('转交数据');
  330. this.streamService.pushNormalDataToStream(request);
  331. }
  332. },
  333. );
  334. }
  335. } catch (error) {
  336. this.logger.error('joystick', error);
  337. }
  338. }
  339. /**
  340. * 主要处理moving的序列动作
  341. * @param seqs StreamReplyType[]
  342. */
  343. handleSeqMoving(seqs: StreamReplyType[]) {
  344. if (!this.moveQueueSubscription) {
  345. this.handleMoveSteam();
  346. }
  347. console.log('walking-seqs', seqs);
  348. this.onMoving.next(true);
  349. this.holdSteam();
  350. this.moveframeCnt = this.frameCnt.value;
  351. seqs.forEach((frame: StreamReplyType) => {
  352. const mediaSrc = frame.mediaSrc;
  353. let src = mediaSrc.split('?')[0];
  354. // 临时本地替换路经
  355. src = src.replace('/0000000001/', '');
  356. const clipPath = join(__dirname, `../ws/video/${src}`);
  357. this.moveframeCnt += 1;
  358. delete frame.mediaSrc;
  359. const stream: StreamFrameType = {
  360. frame: this.moveframeCnt,
  361. clipPath: clipPath,
  362. metaData: JSON.stringify(frame),
  363. serverTime: this.mockserverTime,
  364. DIR: 1,
  365. };
  366. this.moveQueue.next(stream);
  367. });
  368. }
  369. cleanMoveSteam() {
  370. if (this.moveQueueSubscription) {
  371. this.moveQueueSubscription.unsubscribe();
  372. this.lastMoveCnt = -1;
  373. this.moveQueueSubscription = null;
  374. }
  375. if (this.walkingSub) {
  376. this.walkingSub.unsubscribe();
  377. this.walkingSub = null;
  378. }
  379. }
  380. handleMoveSteam() {
  381. this.moveQueueSubscription = this.moveQueue.subscribe(
  382. async (stream: StreamFrameType) => {
  383. const metaData: StreamReplyType = JSON.parse(stream.metaData);
  384. if (this.moveframeCnt === -1) {
  385. this.moveframeCnt = this.frameCnt.value;
  386. }
  387. this.moveframeCnt += 1;
  388. this.latestBreakPointId = metaData.breakPointId;
  389. // if (this.onMoving) {
  390. // this.frameCnt.next(this.moveframeCnt);
  391. // } else {
  392. // console.log(
  393. // 'handleMoveSteam stop',
  394. // this.moveframeCnt,
  395. // this.currentMoveMaker,
  396. // );
  397. // this.cleanMoveSteam();
  398. // this.resumeStream();
  399. // return;
  400. // }
  401. const streamData: StreamFrameType = {
  402. frame: this.moveframeCnt,
  403. clipPath: stream.clipPath,
  404. metaData: stream.metaData,
  405. serverTime: this.mockserverTime,
  406. DIR: 3,
  407. };
  408. this.lastMoveStreamFrame.next(streamData);
  409. const res = await this.streamService.pushFrameToSteam(streamData);
  410. if (res.done) {
  411. clearTimeout(this._moveTimeout);
  412. this._moveTimeout = setTimeout(() => {
  413. console.log('move 交权给空流', Date.now());
  414. console.log('move end');
  415. //TODO 每个结束点 updateUser metaData
  416. const lastFrame = this.lastMoveStreamFrame.getValue();
  417. const lastFrameMeta = JSON.parse(lastFrame.metaData);
  418. const userId = this.user_id;
  419. const breakPointId = lastFrameMeta.breakPointId;
  420. const lastReply = lastFrameMeta;
  421. this.moveService.updateUser(userId, breakPointId, lastReply);
  422. this.frameCnt.next(res.frame);
  423. this.resumeStream();
  424. this.rotateframeCnt = -1;
  425. this.onMoving.next(false);
  426. }, 300);
  427. }
  428. },
  429. );
  430. }
  431. handleDataChanelOpen(channel: DataChannel): void {
  432. this.channel = channel;
  433. this.streamService.setChannel(channel);
  434. this.startSteaming.next(true);
  435. this.startStream();
  436. this.handleStream();
  437. }
  438. handleDataChanelClose(): void {
  439. this.stopStream();
  440. this.startSteaming.next(false);
  441. this.streamService.closeChannel();
  442. const exitRequest: ExitRequest = {
  443. action_type: 1002,
  444. user_id: this.user_id,
  445. trace_id: '',
  446. };
  447. this.exit(exitRequest);
  448. }
  449. handleMessage(message: string | Buffer) {
  450. try {
  451. if (typeof message === 'string') {
  452. // wasm:特例, requestIframe
  453. if (message.includes('wasm:')) {
  454. const msg: RTCMessageRequest = JSON.parse(
  455. message.replace('wasm:', ''),
  456. );
  457. if (msg.MstType === 0) {
  458. this.logger.log('lost I frame');
  459. this.handleIframeRequest();
  460. }
  461. } else {
  462. const msg: RTCMessageRequest = JSON.parse(message);
  463. switch (msg.action_type) {
  464. case ActionType.walk:
  465. const walk = msg;
  466. this.walking(walk);
  467. break;
  468. case ActionType.joystick:
  469. const JoystickRequest = msg as any as JoystickRequest;
  470. this.joystick(JoystickRequest);
  471. break;
  472. case ActionType.breathPoint:
  473. this.handleBreath(msg);
  474. break;
  475. case ActionType.rotate:
  476. const rotateRequest: RotateRequest = msg;
  477. this.rotate(rotateRequest);
  478. break;
  479. case ActionType.userStatus:
  480. this.updateUserStatus(msg);
  481. break;
  482. case ActionType.status:
  483. this.updateStatus();
  484. break;
  485. default:
  486. break;
  487. }
  488. }
  489. }
  490. } catch (error) {
  491. this.logger.error('handleMessage:rtc--error', message);
  492. }
  493. }
  494. async handleIframeRequest() {
  495. const lastStreamFrame = this.streamService.lastStreamFrame.getValue();
  496. lastStreamFrame.DIR = 1;
  497. console.log('lastStreamFrame', lastStreamFrame);
  498. const nextFrame = this.frameCnt.getValue() + 1;
  499. lastStreamFrame.frame = nextFrame;
  500. this.frameCnt.next(nextFrame);
  501. this.streamService.pushFrameToSteam(lastStreamFrame);
  502. // const redisDataAuto = await this.rotateService.echo(this.user_id);
  503. // if (redisDataAuto) {
  504. // 'mediaSrc' in redisDataAuto && delete redisDataAuto.mediaSrc;
  505. // const streamMeta: StreamMetaType = {
  506. // frame: nextFrame,
  507. // metaData: JSON.stringify(redisDataAuto),
  508. // };
  509. // this.streamService.pushMetaDataToSteam(streamMeta);
  510. // }
  511. }
  512. async handleBreath(request) {
  513. const npsRes = await this.moveService.getBreakPoints(request);
  514. // console.log('npsRes', npsRes);
  515. this.streamService.pushNormalDataToStream(npsRes);
  516. }
  517. updateStatus() {
  518. const reply = {
  519. data: { action_type: 1009, echo_msg: { echoMsg: Date.now() } },
  520. track: false,
  521. };
  522. this.streamService.pushNormalDataToStream(reply);
  523. }
  524. async updateUserStatus(request) {
  525. try {
  526. const redisMeta = await this.rotateService.getNewUserStateRequest(
  527. request,
  528. );
  529. if (redisMeta) {
  530. redisMeta.actionType = 1024;
  531. this.streamService.pushNormalDataToStream(redisMeta);
  532. }
  533. } catch (error) {
  534. this.logger.error('updateUserStatus::function', error);
  535. }
  536. }
  537. pushFirstRender(clipPath: string, metaData: string): Promise<boolean> {
  538. return new Promise<boolean>(async (resolve, reject) => {
  539. try {
  540. const streamData: StreamFrameType = {
  541. frame: 1,
  542. clipPath: clipPath,
  543. metaData: metaData,
  544. serverTime: this.mockserverTime,
  545. DIR: 1,
  546. };
  547. const hasPush = await this.streamService.pushFrameToSteam(streamData);
  548. return resolve(hasPush.done);
  549. } catch (error) {
  550. return reject(false);
  551. }
  552. });
  553. }
  554. handleStream() {
  555. this.frameCntSubscription = this.frameCnt.subscribe(async (frame) => {
  556. try {
  557. console.log('frame', frame);
  558. if (frame === 1) {
  559. const redisData = await this.rotateService.echo(this.user_id);
  560. this.onSteaming = true;
  561. this.holdSteam();
  562. console.log('redisData', redisData);
  563. if (redisData && 'mediaSrc' in redisData) {
  564. const mediaSrc: string = redisData.mediaSrc || '';
  565. if (mediaSrc.length > 0) {
  566. let src = mediaSrc.split('?')[0];
  567. // 临时本地替换路经
  568. src = src.replace('/0000000001/', '');
  569. const clipPath = join(__dirname, `../ws/video/${src}`);
  570. delete redisData.mediaSrc;
  571. this.logger.log(
  572. `user:${this.user_id}:first render stream` +
  573. JSON.stringify({ path: clipPath, meta: redisData }),
  574. );
  575. const status = await this.pushFirstRender(
  576. clipPath,
  577. JSON.stringify(redisData),
  578. );
  579. if (status) {
  580. this.firstRender = true;
  581. this.frameCnt.next(2);
  582. this.resumeStream();
  583. } else {
  584. this.logger.error('first render problem', status);
  585. }
  586. }
  587. }
  588. }
  589. if (
  590. frame > 1 &&
  591. !this.onMoving.value &&
  592. !this.onRotating.value &&
  593. this.firstRender
  594. ) {
  595. const redisDataAuto = await this.rotateService.echo(this.user_id);
  596. if (redisDataAuto) {
  597. console.log(`空白流::有数据:${frame}`);
  598. 'mediaSrc' in redisDataAuto && delete redisDataAuto.mediaSrc;
  599. const streamMeta: StreamMetaType = {
  600. frame: frame,
  601. metaData: JSON.stringify(redisDataAuto),
  602. };
  603. this.streamService.pushMetaDataToSteam(streamMeta);
  604. } else {
  605. this.stopStream();
  606. throw new Error('空流无Redis数据');
  607. }
  608. }
  609. } catch (error) {
  610. this.stopStream();
  611. this.logger.error('handleStream', error);
  612. }
  613. });
  614. }
  615. handleRotateStream() {
  616. this.roQueueSubscription = this.roQueue.subscribe(
  617. async (stream: StreamFrameType) => {
  618. this.rotateTimeStamp = Date.now();
  619. if (this.rotateframeCnt === -1) {
  620. this.rotateframeCnt = this.frameCnt.value;
  621. }
  622. this.rotateframeCnt += 1;
  623. stream.frame = this.rotateframeCnt;
  624. console.log('[media-rotate]', stream.frame, stream.clipPath);
  625. // this.logger.log(
  626. // `roQueueSubscription:frame:${this.rotateframeCnt} ` +
  627. // JSON.stringify(stream.metaData),
  628. // );
  629. const res = await this.streamService.pushFrameToSteam(stream);
  630. if (res.done) {
  631. clearTimeout(this._rotateTimeout);
  632. this._rotateTimeout = setTimeout(() => {
  633. console.log('rotate 1秒', Date.now());
  634. console.log('rotate end');
  635. // const next = res.frame + 1;
  636. this.frameCnt.next(res.frame);
  637. this.resumeStream();
  638. this.rotateframeCnt = -1;
  639. this.onMoving.next(false);
  640. this.onRotating.next(false);
  641. }, 300);
  642. }
  643. },
  644. );
  645. }
  646. }