Quellcode durchsuchen

重构steam队列

gemercheung vor 3 Jahren
Ursprung
Commit
730b72924e
6 geänderte Dateien mit 302 neuen und 201 gelöschten Zeilen
  1. 2 3
      config.yaml
  2. 1 0
      package.json
  3. 1 1
      src/rotate/rotate.service.ts
  4. 189 114
      src/scene/scene.service.ts
  5. 86 81
      src/scene/stream/stream.service.ts
  6. 23 2
      yarn.lock

+ 2 - 3
config.yaml

@@ -16,12 +16,11 @@ grpc:
 #   db: 9
 
 redis:
-  port: 6379
-  host: '192.168.0.47' #远程调试需要设置bindip 为0.0.0.0 并且设置密码
+  port: 26379
+  host: '221.4.210.172' #远程调试需要设置bindip 为0.0.0.0 并且设置密码
   password: '' # 非远程不需要密码
   decode_responses: true
   db: 9
-
 stun:
   server: ['stun:172.18.156.41:3478', 'stun:120.24.252.95:3478']
   portRangeBegin: 52000

+ 1 - 0
package.json

@@ -42,6 +42,7 @@
     "redis": "^4",
     "reflect-metadata": "^0.1.13",
     "rimraf": "^3.0.2",
+    "rx-queue": "^1.0.5",
     "rxjs": "^7.2.0",
     "stream-buffers": "^3.0.2"
   },

+ 1 - 1
src/rotate/rotate.service.ts

@@ -189,7 +189,7 @@ export class RotateService {
 
       const redisData = await this.cacheService.get(key);
       const value = JSON.parse(redisData);
-      console.log('redis', value);
+      // console.log('redis', value);
       user.camera['position'] = value ? value.cameraPosition : '';
       user.camera['angle'] = value ? value.cameraAngle : '';
 

+ 189 - 114
src/scene/scene.service.ts

@@ -4,8 +4,6 @@ import { grpcClientOptions } from './grpc-scene.options';
 import { Logger } from '@nestjs/common';
 import { DataChannel } from 'node-datachannel';
 import * as path from 'path';
-// import { statSync, createReadStream, readFileSync } from 'fs';
-// import { Readable } from 'stream';
 import { BehaviorSubject } from 'rxjs';
 // import * as streamBuffers from 'stream-buffers';
 import { ActionType } from './actionType';
@@ -14,6 +12,7 @@ import { StreamService } from './stream/stream.service';
 import { InjectQueue } from '@nestjs/bull';
 import { Queue } from 'bull';
 import { RotateService } from 'src/rotate/rotate.service';
+import { DelayQueue, RxQueue, ThrottleQueue, DebounceQueue } from 'rx-queue';
 
 const frameMetaReply = {
   traceIds: [''],
@@ -81,12 +80,11 @@ export class SceneService implements OnModuleInit, OnModuleDestroy {
     private streamService: StreamService,
     private rotateService: RotateService,
     @InjectQueue('rotate') private rotateQueue: Queue,
-  ) {}
+  ) { }
   @Client(grpcClientOptions) private readonly client: ClientGrpc;
   private sceneGrpcService: SceneGrpcService;
 
   private logger: Logger = new Logger('SceneService');
-  private frameCnt = -1;
   private frameCntInterval = 1000;
   public _frameInteval: NodeJS.Timeout;
   private channel: DataChannel;
@@ -95,23 +93,14 @@ export class SceneService implements OnModuleInit, OnModuleDestroy {
   private roomId: string;
   private onSteaming = false;
   private testFrame = -1;
-  private RotateframeCnt = -1;
+  private rotateframeCnt = -1;
   private mockserverTime = Date.now() - 1653000000478;
   private lastRenderMedia = '';
-  // private rotateMap = new Map()<>;
-
-  setConfig(user_id: string, roomId: string) {
-    this.user_id = user_id;
-    this.roomId = roomId;
-  }
-  checkingIsRotating() {
-    console.log('async', this.frameCnt, this.RotateframeCnt);
-    if (this.frameCnt > 0 && this.frameCnt === this.RotateframeCnt) {
-      return true;
-    } else {
-      return false;
-    }
-  }
+  private frameCnt = new BehaviorSubject<number>(-1);
+  private frameCntSubscription: any;
+  private roQueueSubscription: any;
+  private roQueue: RxQueue = new ThrottleQueue(50);
+  private rotateTimeStamp: number;
 
   onModuleInit() {
     this.sceneGrpcService =
@@ -125,6 +114,44 @@ export class SceneService implements OnModuleInit, OnModuleDestroy {
     };
   }
 
+  startStream() {
+    if (this.frameCnt.value === -1) {
+      this._frameInteval = setInterval(async () => {
+        const next = this.frameCnt.value + 1;
+        this.frameCnt.next(next);
+      }, 1000);
+    }
+  }
+
+  holdSteam() {
+    clearInterval(this._frameInteval);
+  }
+
+  resumeStream(value: number) {
+    this.frameCnt.next(value);
+    this._frameInteval = setInterval(async () => {
+      const next = this.frameCnt.value + 1;
+      this.frameCnt.next(next);
+    }, 1000);
+  }
+
+  stopStream() {
+    if (this.frameCntSubscription) {
+      this.frameCntSubscription.unsubscribe();
+    }
+    if (this.roQueueSubscription) {
+      this.roQueueSubscription.unsubscribe();
+    }
+    this.frameCnt.next(-1);
+    clearInterval(this._frameInteval);
+    this.rotateframeCnt = -1;
+  }
+
+  setConfig(user_id: string, roomId: string) {
+    this.user_id = user_id;
+    this.roomId = roomId;
+  }
+
   onModuleDestroy() {
     this.streamService.onSteaming.unsubscribe();
   }
@@ -163,14 +190,13 @@ export class SceneService implements OnModuleInit, OnModuleDestroy {
     try {
       // const reply = this.sceneGrpcService.rotate(request);
       if (!this.onSteaming) {
-        this.RotateframeCnt = this.frameCnt;
         // const redisMeta = await this.cacheService.get(
         //   `updateFrameMetadata:${this.user_id}`,
         // );
         // console.log('rotate信息', this.user_id, request.sampleRate);
 
         const redisMeta = await this.rotateService.rotate(request);
-        console.log('rotate信息', redisMeta);
+        // console.log('rotate信息', redisMeta);
         // await this.rotateQueue.add('processFrame', request, {
         //   jobId: request.trace_id,
         // });
@@ -187,23 +213,25 @@ export class SceneService implements OnModuleInit, OnModuleDestroy {
             // 临时本地替换路经
             src = src.replace('/0000000001/100/', '');
             // 判断不是同一条源时才推出
-            console.log('[media]', this.lastRenderMedia, src);
             if (this.lastRenderMedia !== src) {
-              console.log('不同源');
-              this.frameCnt += 1;
-              console.log('src', src);
+              console.log('[media]', src);
+              // console.log('不同源');
+              // this.frameCnt += 1;
+              this.holdSteam();
               this.lastRenderMedia = src;
               const clipPath = path.join(__dirname, `../ws/video/${src}`);
-              console.log('src-clipPath', src, clipPath);
-              delete redisMeta.mediaSrc;
+              // console.log('src-clipPath', src, clipPath);
+              // delete redisMeta.mediaSrc;
               const stream: StreamFrameType = {
-                frame: this.frameCnt,
+                frame: -1,
                 clipPath: clipPath,
-                metaData: JSON.stringify(redisMeta),
+                metaData: JSON.stringify(frameMetaReply),
                 serverTime: this.mockserverTime,
                 DIR: 3,
               };
-              this.streamService.pushFrameToSteam(stream);
+              // console.log('stream', stream)
+              this.roQueue.next(stream);
+              // this.streamService.pushFrameToSteam(stream);
             }
           }
         }
@@ -218,51 +246,51 @@ export class SceneService implements OnModuleInit, OnModuleDestroy {
     }
   }
 
-  async rotate1(request: RotateRequest) {
-    try {
-      // const reply = this.sceneGrpcService.rotate(request);
-      // const res = await this.cacheService.publish(
-      //   'test',
-      //   JSON.stringify(request),
-      // );
+  // async rotate1(request: RotateRequest) {
+  //   try {
+  //     // const reply = this.sceneGrpcService.rotate(request);
+  //     // const res = await this.cacheService.publish(
+  //     //   'test',
+  //     //   JSON.stringify(request),
+  //     // );
 
-      // console.log('res', res);
+  //     // console.log('res', res);
 
-      if (!this.onSteaming) {
-        this.frameCnt += 1;
-        this.RotateframeCnt = this.frameCnt;
-        // this.cacheService
-        this.testFrame += 3;
-        this.mockserverTime += 1;
-        this.onSteaming = true;
-        if (this.testFrame > 358) this.testFrame = 0;
-        const stream: StreamFrameType = {
-          frame: this.frameCnt,
-          clipPath: path.join(
-            __dirname,
-            `../ws/video/100/100.${this.testFrame.padLeft(4, '0')}.h264`,
-          ),
-          metaData: JSON.stringify(frameMetaReply),
-          serverTime: this.mockserverTime,
-          DIR: 3,
-        };
-        console.log('stream', this.frameCnt, stream.clipPath);
-        this.streamService.pushFrameToSteam(stream);
-        // const redisMeta = await this.cacheService.rpop(
-        //   `updateFrameMetadata:${this.user_id}`,
-        // );
-      }
-      // reply.subscribe((res: NormalReply) => {
-      //   if (res.code === 200) {
-      //   }
-      // });
-    } catch (error) {
-      this.logger.error(
-        `rotate-${this.frameCnt},src:${this.testFrame}`,
-        JSON.stringify(error),
-      );
-    }
-  }
+  //     if (!this.onSteaming) {
+  //       this.frameCnt += 1;
+  //       this.RotateframeCnt = this.frameCnt;
+  //       // this.cacheService
+  //       this.testFrame += 3;
+  //       this.mockserverTime += 1;
+  //       this.onSteaming = true;
+  //       if (this.testFrame > 358) this.testFrame = 0;
+  //       const stream: StreamFrameType = {
+  //         frame: this.frameCnt,
+  //         clipPath: path.join(
+  //           __dirname,
+  //           `../ws/video/100/100.${this.testFrame.padLeft(4, '0')}.h264`,
+  //         ),
+  //         metaData: JSON.stringify(frameMetaReply),
+  //         serverTime: this.mockserverTime,
+  //         DIR: 3,
+  //       };
+  //       console.log('stream', this.frameCnt, stream.clipPath);
+  //       this.streamService.pushFrameToSteam(stream);
+  //       // const redisMeta = await this.cacheService.rpop(
+  //       //   `updateFrameMetadata:${this.user_id}`,
+  //       // );
+  //     }
+  //     // reply.subscribe((res: NormalReply) => {
+  //     //   if (res.code === 200) {
+  //     //   }
+  //     // });
+  //   } catch (error) {
+  //     this.logger.error(
+  //       `rotate-${this.frameCnt},src:${this.testFrame}`,
+  //       JSON.stringify(error),
+  //     );
+  //   }
+  // }
 
   joystick(request: JoystickRequest) {
     return this.sceneGrpcService.joystick(request);
@@ -271,12 +299,15 @@ export class SceneService implements OnModuleInit, OnModuleDestroy {
   handleDataChanelOpen(channel: DataChannel): void {
     this.channel = channel;
     this.streamService.setChannel(channel);
-    this.handleStartCountingFrame();
+    // this.handleStartCountingFrame();
     this.startSteaming.next(true);
+    this.startStream();
+    this.handleStream();
   }
 
   handleDataChanelClose(): void {
-    this.stopCountingFrame();
+    // this.stopCountingFrame();
+    this.stopStream();
     this.startSteaming.next(false);
     this.streamService.closeChannel();
     const exitRequest: ExitRequest = {
@@ -330,15 +361,15 @@ export class SceneService implements OnModuleInit, OnModuleDestroy {
   }
 
   handleIframeRequest() {
-    this.frameCnt += 1;
-    this.onSteaming = true;
-    const stream: StreamFrameType = {
-      frame: this.frameCnt,
-      clipPath: path.join(__dirname, '../ws/video/100/100.0000.h264'),
-      metaData: JSON.stringify(frameMetaReply),
-      serverTime: this.mockserverTime,
-    };
-    this.streamService.pushFrameToSteam(stream);
+    // this.frameCnt += 1;
+    // this.onSteaming = true;
+    // const stream: StreamFrameType = {
+    //   frame: this.frameCnt,
+    //   clipPath: path.join(__dirname, '../ws/video/100/100.0000.h264'),
+    //   metaData: JSON.stringify(frameMetaReply),
+    //   serverTime: this.mockserverTime,
+    // };
+    // this.streamService.pushFrameToSteam(stream);
   }
 
   walking(request) {
@@ -423,9 +454,10 @@ export class SceneService implements OnModuleInit, OnModuleDestroy {
       code: 0,
       msg: 'OK',
     };
-    this.frameCnt += 1;
+    const nextframe = this.frameCnt.value + 1;
+
     const stream: StreamFrameType = {
-      frame: this.frameCnt,
+      frame: nextframe,
       clipPath: path.join(__dirname, `../ws/video/2.h264`),
       metaData: JSON.stringify(walk1),
       serverTime: this.mockserverTime,
@@ -661,36 +693,79 @@ export class SceneService implements OnModuleInit, OnModuleDestroy {
     }
     // this.streamService.pushNormalDataToStream(reply);
   }
-  handleStartCountingFrame() {
-    this._frameInteval = setInterval(async () => {
-      this.frameCnt += 1;
-      try {
-        if (this.frameCnt === 1) {
-          // this.pushTheFirstFrame();
-          const stream: StreamFrameType = {
-            frame: 1,
-            clipPath: path.join(__dirname, '../ws/video/100/100.0000.h264'),
-            metaData: JSON.stringify(frameMetaReply),
-            serverTime: this.mockserverTime,
-          };
-          this.streamService.pushFrameToSteam(stream);
-        }
 
-        if (this.frameCnt > 1 && !this.onSteaming) {
-          const streamMeta: StreamMetaType = {
-            frame: this.frameCnt,
-            metaData: JSON.stringify(frameMetaReply),
-          };
-          this.streamService.pushMetaDataToSteam(streamMeta);
-        }
-      } catch (error) {
-        console.log('error', error);
+  handleStream() {
+    this.frameCntSubscription = this.frameCnt.subscribe((frame) => {
+      console.log('frame', frame);
+      if (frame === 1) {
+        const stream: StreamFrameType = {
+          frame: 1,
+          clipPath: path.join(__dirname, '../ws/video/100/100.0000.h264'),
+          metaData: JSON.stringify(frameMetaReply),
+          serverTime: this.mockserverTime,
+        };
+        this.streamService.pushFrameToSteam(stream);
       }
-    }, this.frameCntInterval);
-  }
+      if (frame > 1 && !this.onSteaming) {
+        const streamMeta: StreamMetaType = {
+          frame: frame,
+          metaData: JSON.stringify(frameMetaReply),
+        };
+        this.streamService.pushMetaDataToSteam(streamMeta);
+      }
+    });
 
-  stopCountingFrame(): void {
-    clearInterval(this._frameInteval);
-    this.frameCnt = -1;
+    this.roQueueSubscription = this.roQueue.subscribe(
+      async (stream: StreamFrameType) => {
+        this.rotateTimeStamp = Date.now();
+
+        if (this.rotateframeCnt === -1) {
+          this.rotateframeCnt = this.frameCnt.value;
+        }
+        this.rotateframeCnt += 1;
+        stream.frame = this.rotateframeCnt;
+        console.log('this.rotateframeCnt', this.rotateframeCnt);
+        await this.streamService.pushFrameToSteam(stream);
+        setTimeout(() => {
+          const now = Date.now();
+          if (now - this.rotateTimeStamp > 300) {
+            this.resumeStream(this.rotateframeCnt);
+          }
+        }, 300);
+      },
+    );
   }
+
+  // handleStartCountingFrame() {
+  //   this._frameInteval = setInterval(async () => {
+  //     this.frameCnt += 1;
+  //     try {
+  //       if (this.frameCnt === 1) {
+  //         // this.pushTheFirstFrame();
+  //         const stream: StreamFrameType = {
+  //           frame: 1,
+  //           clipPath: path.join(__dirname, '../ws/video/100/100.0000.h264'),
+  //           metaData: JSON.stringify(frameMetaReply),
+  //           serverTime: this.mockserverTime,
+  //         };
+  //         this.streamService.pushFrameToSteam(stream);
+  //       }
+
+  //       if (this.frameCnt > 1 && !this.onSteaming) {
+  //         const streamMeta: StreamMetaType = {
+  //           frame: this.frameCnt,
+  //           metaData: JSON.stringify(frameMetaReply),
+  //         };
+  //         this.streamService.pushMetaDataToSteam(streamMeta);
+  //       }
+  //     } catch (error) {
+  //       console.log('error', error);
+  //     }
+  //   }, this.frameCntInterval);
+  // }
+
+  // stopCountingFrame(): void {
+  //   clearInterval(this._frameInteval);
+  //   this.frameCnt = -1;
+  // }
 }

+ 86 - 81
src/scene/stream/stream.service.ts

@@ -5,6 +5,7 @@ import { readFileSync } from 'fs';
 import * as streamBuffers from 'stream-buffers';
 import { BehaviorSubject } from 'rxjs';
 import { CacheService } from 'src/cache/cache.service';
+import { resolve } from 'path';
 @Injectable()
 export class StreamService {
   private channel: DataChannel;
@@ -88,86 +89,90 @@ export class StreamService {
    * @param stream   meta Json and stream
    */
 
-  pushFrameToSteam(stream: StreamFrameType) {
-    try {
-      // if (!this.onSteaming) {
-      const clipPath = stream.clipPath;
-      const metaData = stream.metaData;
-      const frame = stream.frame;
-      const serverTime = stream.serverTime || 754871824;
-      const dir = stream.DIR || 1;
-      const metaDataString = metaData.replace(/\s/g, '');
-      const coordBuff = Buffer.from(metaDataString, 'utf-8');
-      // console.warn('coordBuff', coordBuff.byteLength);
-      // const steamStat = statSync(clipPath);
-      // const steamTotalSize = metaData.length + steamStat.size;
-      const clipBuffer = readFileSync(clipPath);
-      const steam = new streamBuffers.ReadableStreamBuffer({
-        frequency: 1, // in milliseconds.
-        chunkSize: this.chunk_size - this.block, // in bytes.
-      });
-      steam.put(coordBuff);
-      steam.put(clipBuffer);
-
-      let steamByteLength = 0;
-
-      steam.on('data', (data: Buffer) => {
-        this.onSteaming.next(true);
-        // console.log('data', data, data.byteLength);
-        const blockBuffStart = Buffer.alloc(this.block);
-        const packBuffer = Buffer.concat([blockBuffStart, data]);
-
-        const framePack = new DataView(
-          packBuffer.buffer.slice(
-            packBuffer.byteOffset,
-            packBuffer.byteOffset + packBuffer.byteLength,
-          ),
-        );
-
-        // 16 bit slot
-        // framePack.setUint32(4)
-        framePack.setUint16(6, this.block);
-        framePack.setUint16(8, frame); // first render cnt
-        framePack.setUint16(10, dir); // isDIR
-        framePack.setUint16(24, 0);
-        framePack.setUint16(26, 0);
-
-        // 32 bit slot
-        // statusPack.setUint32(12, buff.byteLength);
-        // console.log('metaLen', coordBuff.byteLength);
-        // console.log('metaLen', clipBuffer.byteLength);
-
-        framePack.setUint32(0, 1437227610);
-        framePack.setUint32(12, coordBuff.byteLength); // metaLen
-        framePack.setUint32(16, clipBuffer.byteLength); // mediaLen
-        framePack.setUint32(20, serverTime); //server_time
-        framePack.setUint32(24, 0);
-        framePack.setUint32(28, 0);
-        framePack.setUint32(this.block - 4, steamByteLength);
-        const isLastFrame = framePack.byteLength - this.chunk_size < 0;
-
-        // console.log('statusPack', statusPack);
-        if (this.channel && this.channel.isOpen()) {
-          this.channel.sendMessageBinary(Buffer.from(framePack.buffer));
-        }
-        steamByteLength += data.byteLength;
-        if (isLastFrame) {
-          // console.log('isLastFrame', isLastFrame);
-          // steamByteLength = 0;
-          // this.onSteaming.next(false);
-          steam.stop();
-        }
-      });
-      //TODO steam can't trigger end
-      steam.on('end', () => {
-        steamByteLength = 0;
-        console.log('stream end');
-        if (this.onSteaming) {
-          this.onSteaming.next(false);
-        }
-      });
-    } catch (error) {
-      this.logger.error(error);
-    }
+  pushFrameToSteam(stream: StreamFrameType): Promise<boolean> {
+    return new Promise((resolve, reject) => {
+      try {
+        // if (!this.onSteaming) {
+        const clipPath = stream.clipPath;
+        const metaData = stream.metaData;
+        const frame = stream.frame;
+        const serverTime = stream.serverTime || 754871824;
+        const dir = stream.DIR || 1;
+        const metaDataString = metaData.replace(/\s/g, '');
+        const coordBuff = Buffer.from(metaDataString, 'utf-8');
+        // console.warn('coordBuff', coordBuff.byteLength);
+        // const steamStat = statSync(clipPath);
+        // const steamTotalSize = metaData.length + steamStat.size;
+        const clipBuffer = readFileSync(clipPath);
+        const steam = new streamBuffers.ReadableStreamBuffer({
+          frequency: 1, // in milliseconds.
+          chunkSize: this.chunk_size - this.block, // in bytes.
+        });
+        steam.put(coordBuff);
+        steam.put(clipBuffer);
+
+        let steamByteLength = 0;
+
+        steam.on('data', (data: Buffer) => {
+          this.onSteaming.next(true);
+          // console.log('data', data, data.byteLength);
+          const blockBuffStart = Buffer.alloc(this.block);
+          const packBuffer = Buffer.concat([blockBuffStart, data]);
+
+          const framePack = new DataView(
+            packBuffer.buffer.slice(
+              packBuffer.byteOffset,
+              packBuffer.byteOffset + packBuffer.byteLength,
+            ),
+          );
+
+          // 16 bit slot
+          // framePack.setUint32(4)
+          framePack.setUint16(6, this.block);
+          framePack.setUint16(8, frame); // first render cnt
+          framePack.setUint16(10, dir); // isDIR
+          framePack.setUint16(24, 0);
+          framePack.setUint16(26, 0);
+
+          // 32 bit slot
+          // statusPack.setUint32(12, buff.byteLength);
+          // console.log('metaLen', coordBuff.byteLength);
+          // console.log('metaLen', clipBuffer.byteLength);
+
+          framePack.setUint32(0, 1437227610);
+          framePack.setUint32(12, coordBuff.byteLength); // metaLen
+          framePack.setUint32(16, clipBuffer.byteLength); // mediaLen
+          framePack.setUint32(20, serverTime); //server_time
+          framePack.setUint32(24, 0);
+          framePack.setUint32(28, 0);
+          framePack.setUint32(this.block - 4, steamByteLength);
+          const isLastFrame = framePack.byteLength - this.chunk_size < 0;
+
+          // console.log('statusPack', statusPack);
+          if (this.channel && this.channel.isOpen()) {
+            this.channel.sendMessageBinary(Buffer.from(framePack.buffer));
+          }
+          steamByteLength += data.byteLength;
+          if (isLastFrame) {
+            // console.log('isLastFrame', isLastFrame);
+            // steamByteLength = 0;
+            // this.onSteaming.next(false);
+            steam.stop();
+            resolve(true);
+          }
+        });
+        //TODO steam can't trigger end
+        steam.on('end', () => {
+          steamByteLength = 0;
+          console.log('stream end');
+          if (this.onSteaming) {
+            this.onSteaming.next(false);
+          }
+        });
+      } catch (error) {
+        this.logger.error(error);
+        reject(error);
+      }
+    });
   }
 }

+ 23 - 2
yarn.lock

@@ -1124,6 +1124,11 @@
   resolved "https://registry.npmmirror.com/@types/node/-/node-16.11.27.tgz"
   integrity sha512-C1pD3kgLoZ56Uuy5lhfOxie4aZlA3UMGLX9rXteq4WitEZH6Rl80mwactt9QG0w0gLFlN/kLBTFnGXtDVWvWQw==
 
+"@types/node@^13.7.4":
+  version "13.13.52"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.52.tgz#03c13be70b9031baaed79481c0c0cfb0045e53f7"
+  integrity sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==
+
 "@types/parse-json@^4.0.0":
   version "4.0.0"
   resolved "https://registry.npmmirror.com/@types/parse-json/-/parse-json-4.0.0.tgz"
@@ -3396,6 +3401,14 @@ iterare@1.2.1:
   resolved "https://registry.npmmirror.com/iterare/-/iterare-1.2.1.tgz"
   integrity sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==
 
+ix@^4.5.2:
+  version "4.5.2"
+  resolved "https://registry.yarnpkg.com/ix/-/ix-4.5.2.tgz#c91163d38805c5b427902d7cc2ee35ccee0364a5"
+  integrity sha512-Cm0uEpZd2qU+7DVeyyBQeceafggvPD3xe6LDKUU4YnmOzAFIz0CbxwufRfxywU/5easfCX1XEYcOAkDJUuUtiw==
+  dependencies:
+    "@types/node" "^13.7.4"
+    tslib "^2.3.0"
+
 jest-changed-files@^27.5.1:
   version "27.5.1"
   resolved "https://registry.npmmirror.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz"
@@ -4895,6 +4908,14 @@ run-parallel@^1.1.9:
   dependencies:
     queue-microtask "^1.2.2"
 
+rx-queue@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/rx-queue/-/rx-queue-1.0.5.tgz#17711076923ec8d84f5bb76bc5e5d030ce797c0a"
+  integrity sha512-upDUgE3pk1+JnyV9MBkVSSlpSIg33ygqXvczsQIMVyjubLWCX/QzoFNfQavEHxYxKw+9JNMqDTPnxZ3dnR+61g==
+  dependencies:
+    ix "^4.5.2"
+    rxjs "^7.5.5"
+
 rxjs@6.6.7, rxjs@^6, rxjs@^6.6.0:
   version "6.6.7"
   resolved "https://registry.npmmirror.com/rxjs/-/rxjs-6.6.7.tgz"
@@ -4902,7 +4923,7 @@ rxjs@6.6.7, rxjs@^6, rxjs@^6.6.0:
   dependencies:
     tslib "^1.9.0"
 
-rxjs@^7.2.0:
+rxjs@^7.2.0, rxjs@^7.5.5:
   version "7.5.5"
   resolved "https://registry.npmmirror.com/rxjs/-/rxjs-7.5.5.tgz"
   integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==
@@ -5512,7 +5533,7 @@ tslib@^1.8.1, tslib@^1.9.0:
   resolved "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz"
   integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
 
-tslib@^2.1.0:
+tslib@^2.1.0, tslib@^2.3.0:
   version "2.4.0"
   resolved "https://registry.npmmirror.com/tslib/-/tslib-2.4.0.tgz"
   integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==