|
@@ -4,7 +4,7 @@ import { grpcClientOptions } from './grpc-scene.options';
|
|
|
import { Logger } from '@nestjs/common';
|
|
|
import { DataChannel } from 'node-datachannel';
|
|
|
import * as path from 'path';
|
|
|
-import { BehaviorSubject } from 'rxjs';
|
|
|
+import { BehaviorSubject, filter, ignoreElements, take } from 'rxjs';
|
|
|
// import * as streamBuffers from 'stream-buffers';
|
|
|
import { ActionType } from './actionType';
|
|
|
import { CacheService } from 'src/cache/cache.service';
|
|
@@ -13,6 +13,7 @@ import { InjectQueue } from '@nestjs/bull';
|
|
|
import { Queue } from 'bull';
|
|
|
import { RotateService } from 'src/rotate/rotate.service';
|
|
|
import { DelayQueue, RxQueue, ThrottleQueue, DebounceQueue } from 'rx-queue';
|
|
|
+import { MoveService } from 'src/move/move.service';
|
|
|
|
|
|
@Injectable()
|
|
|
export class SceneService implements OnModuleInit, OnModuleDestroy {
|
|
@@ -20,8 +21,9 @@ export class SceneService implements OnModuleInit, OnModuleDestroy {
|
|
|
private cacheService: CacheService,
|
|
|
private streamService: StreamService,
|
|
|
private rotateService: RotateService,
|
|
|
+ private moveService: MoveService,
|
|
|
@InjectQueue('rotate') private rotateQueue: Queue,
|
|
|
- ) { }
|
|
|
+ ) {}
|
|
|
@Client(grpcClientOptions) private readonly client: ClientGrpc;
|
|
|
private sceneGrpcService: SceneGrpcService;
|
|
|
|
|
@@ -34,16 +36,22 @@ export class SceneService implements OnModuleInit, OnModuleDestroy {
|
|
|
private user_id: string;
|
|
|
private roomId: string;
|
|
|
private onSteaming = false;
|
|
|
- private testFrame = -1;
|
|
|
private rotateframeCnt = -1;
|
|
|
+ private moveframeCnt = -1;
|
|
|
private mockserverTime = Date.now() - 1653000000478;
|
|
|
private lastRenderMedia = '';
|
|
|
private frameCnt = new BehaviorSubject<number>(-1);
|
|
|
private frameCntSubscription: any;
|
|
|
private roQueueSubscription: any;
|
|
|
+ private moveQueueSubscription: any;
|
|
|
+
|
|
|
private streamServiceSub: any;
|
|
|
- private roQueue: RxQueue = new ThrottleQueue(50);
|
|
|
+ private roQueue: RxQueue = new ThrottleQueue(120);
|
|
|
+ private moveQueue: RxQueue = new DelayQueue(1000);
|
|
|
private rotateTimeStamp: number;
|
|
|
+ private lastMoveCnt = -1;
|
|
|
+ private currentMoveMaker = '';
|
|
|
+ private onMoving = false;
|
|
|
|
|
|
onModuleInit(): void {
|
|
|
this.sceneGrpcService =
|
|
@@ -72,12 +80,12 @@ export class SceneService implements OnModuleInit, OnModuleDestroy {
|
|
|
}
|
|
|
|
|
|
resumeStream(value: number) {
|
|
|
- this.frameCnt.next(value);
|
|
|
clearTimeout(this._frameTimeout);
|
|
|
clearInterval(this._frameInteval);
|
|
|
+ this.frameCnt.next(value);
|
|
|
this._frameTimeout = setTimeout(() => {
|
|
|
this._frameInteval = setInterval(async () => {
|
|
|
- const next = this.frameCnt.value + 1;
|
|
|
+ const next = this.frameCnt.getValue() + 1;
|
|
|
this.frameCnt.next(next);
|
|
|
}, 1000);
|
|
|
}, 1000);
|
|
@@ -86,16 +94,23 @@ export class SceneService implements OnModuleInit, OnModuleDestroy {
|
|
|
stopStream(): void {
|
|
|
if (this.frameCntSubscription) {
|
|
|
this.frameCntSubscription.unsubscribe();
|
|
|
+ this.frameCntSubscription = null;
|
|
|
}
|
|
|
if (this.roQueueSubscription) {
|
|
|
this.roQueueSubscription.unsubscribe();
|
|
|
+ this.roQueueSubscription = null;
|
|
|
+ }
|
|
|
+ if (this.moveQueueSubscription) {
|
|
|
+ this.moveQueueSubscription.unsubscribe();
|
|
|
+ this.moveQueueSubscription = null;
|
|
|
}
|
|
|
this.frameCnt.next(-1);
|
|
|
clearInterval(this._frameInteval);
|
|
|
this.rotateframeCnt = -1;
|
|
|
+ this.moveframeCnt = -1;
|
|
|
}
|
|
|
|
|
|
- setConfig(user_id: string, roomId: string) {
|
|
|
+ setConfig(user_id: string, roomId: string): void {
|
|
|
this.user_id = user_id;
|
|
|
this.roomId = roomId;
|
|
|
}
|
|
@@ -120,6 +135,7 @@ export class SceneService implements OnModuleInit, OnModuleDestroy {
|
|
|
// console.log('initReply', reply);
|
|
|
// });
|
|
|
this.rotateService.init(request.app_id, request.user_id);
|
|
|
+ this.moveService.init(request.app_id, request.user_id);
|
|
|
} catch (error) {
|
|
|
console.log('error', error);
|
|
|
}
|
|
@@ -141,19 +157,24 @@ export class SceneService implements OnModuleInit, OnModuleDestroy {
|
|
|
// this.rotateQueue.add(request, {
|
|
|
// jobId: request.trace_id,
|
|
|
// });
|
|
|
+ if (!this.roQueueSubscription) {
|
|
|
+ this.handleRotateStream();
|
|
|
+ }
|
|
|
if (!this.onSteaming) {
|
|
|
+ this.onMoving = false;
|
|
|
const redisMeta = await this.rotateService.rotate(request);
|
|
|
if (redisMeta && 'mediaSrc' in redisMeta) {
|
|
|
const mediaSrc: string = redisMeta.mediaSrc || '';
|
|
|
if (mediaSrc.length > 0) {
|
|
|
let src = mediaSrc.split('?')[0];
|
|
|
// 临时本地替换路经
|
|
|
- src = src.replace('/0000000001/100/', '');
|
|
|
+ src = src.replace('/0000000001/', '');
|
|
|
// 判断不是同一条源时才推出
|
|
|
if (this.lastRenderMedia !== src) {
|
|
|
console.log('[media]', src);
|
|
|
// console.log('不同源');
|
|
|
// this.frameCnt += 1;
|
|
|
+
|
|
|
this.holdSteam();
|
|
|
this.lastRenderMedia = src;
|
|
|
const clipPath = path.join(__dirname, `../ws/video/${src}`);
|
|
@@ -255,99 +276,39 @@ export class SceneService implements OnModuleInit, OnModuleDestroy {
|
|
|
// this.streamService.pushFrameToSteam(stream);
|
|
|
}
|
|
|
|
|
|
- walking(request) {
|
|
|
+ async walking(request) {
|
|
|
console.log('walking', request);
|
|
|
- console.log('walking-onSteaming', this.onSteaming);
|
|
|
-
|
|
|
- const walk1 = {
|
|
|
- traceIds: [request.trace_id],
|
|
|
- vehicle: null,
|
|
|
- newUserStates: [
|
|
|
- {
|
|
|
- userId: request.user_id,
|
|
|
- playerState: {
|
|
|
- roomTypeId: '',
|
|
|
- person: 0,
|
|
|
- avatarId: '',
|
|
|
- skinId: '',
|
|
|
- roomId: '',
|
|
|
- isHost: false,
|
|
|
- isFollowHost: false,
|
|
|
- skinDataVersion: '',
|
|
|
- avatarComponents: '',
|
|
|
- nickName: '',
|
|
|
- movingMode: 0,
|
|
|
- attitude: '',
|
|
|
- areaName: '',
|
|
|
- pathName: '',
|
|
|
- pathId: '',
|
|
|
- avatarSize: 1,
|
|
|
- extra: '',
|
|
|
- prioritySync: false,
|
|
|
- avatarURL: '',
|
|
|
- micStatus: 0,
|
|
|
- player: {
|
|
|
- position: { x: -755, y: -1450, z: -34 },
|
|
|
- angle: { pitch: 0, yaw: 348, roll: 4.5912e-41 },
|
|
|
- },
|
|
|
- camera: {
|
|
|
- position: { x: -1075, y: -1450, z: 86 },
|
|
|
- angle: { pitch: 0, yaw: 0, roll: 0 },
|
|
|
- },
|
|
|
- cameraCenter: { x: -755, y: -1450, z: -34 },
|
|
|
- },
|
|
|
- renderInfo: {
|
|
|
- renderType: 0,
|
|
|
- videoFrame: null,
|
|
|
- cameraStateType: 3,
|
|
|
- isMoving: 1,
|
|
|
- needIfr: 0,
|
|
|
- isVideo: 0,
|
|
|
- stillFrame: 0,
|
|
|
- isRotating: 0,
|
|
|
- isFollowing: 0,
|
|
|
- clientPanoTitlesBitmap: [],
|
|
|
- clientPanoTreceId: '',
|
|
|
- prefetchVideoId: '',
|
|
|
- noMedia: false,
|
|
|
- },
|
|
|
- event: null,
|
|
|
- relation: 1,
|
|
|
- },
|
|
|
- ],
|
|
|
- actionResponses: [
|
|
|
- {
|
|
|
- actionType: 1,
|
|
|
- pointType: 0,
|
|
|
- extra: '',
|
|
|
- traceId: request.trace_id,
|
|
|
- packetId: '',
|
|
|
- nps: [],
|
|
|
- peopleNum: 0,
|
|
|
- zoneId: '',
|
|
|
- echoMsg: '',
|
|
|
- reserveDetail: null,
|
|
|
- userWithAvatarList: [],
|
|
|
- newUserStates: [],
|
|
|
- code: 0,
|
|
|
- msg: '',
|
|
|
- },
|
|
|
- ],
|
|
|
- getStateType: 0,
|
|
|
- code: 0,
|
|
|
- msg: 'OK',
|
|
|
- };
|
|
|
- const nextframe = this.frameCnt.value + 1;
|
|
|
-
|
|
|
- const stream: StreamFrameType = {
|
|
|
- frame: nextframe,
|
|
|
- clipPath: path.join(__dirname, `../ws/video/2.h264`),
|
|
|
- metaData: JSON.stringify(walk1),
|
|
|
- serverTime: this.mockserverTime,
|
|
|
- DIR: 3,
|
|
|
- };
|
|
|
- console.log('walking', this.frameCnt, stream.clipPath);
|
|
|
- this.streamService.pushFrameToSteam(stream);
|
|
|
+
|
|
|
+ const walkingRes = await this.moveService.move(request);
|
|
|
+ if (walkingRes) {
|
|
|
+ this.onMoving = true;
|
|
|
+ if (!this.moveQueueSubscription) {
|
|
|
+ this.handleMoveSteam();
|
|
|
+ }
|
|
|
+ const res = Object.keys(walkingRes).map((item) => {
|
|
|
+ console.log('item', item);
|
|
|
+ return Array.from(walkingRes[item]).map((i) => {
|
|
|
+ i['marker'] = item;
|
|
|
+ return i;
|
|
|
+ });
|
|
|
+ });
|
|
|
+ const seqs = Array.from(res).flat();
|
|
|
+ this.lastMoveCnt = this.frameCnt.value + seqs.length;
|
|
|
+ console.log('lastMoveCnt', this.lastMoveCnt);
|
|
|
+ seqs.forEach((frame: StreamReplyType) => {
|
|
|
+ const mediaSrc = frame.mediaSrc;
|
|
|
+ delete frame.mediaSrc;
|
|
|
+ const stream: StreamFrameType = {
|
|
|
+ frame: -1,
|
|
|
+ clipPath: mediaSrc,
|
|
|
+ metaData: JSON.stringify(frame),
|
|
|
+ serverTime: this.mockserverTime,
|
|
|
+ DIR: 1,
|
|
|
+ };
|
|
|
+ this.moveQueue.next(stream);
|
|
|
+ });
|
|
|
+ this.holdSteam();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
handleBreath(request) {
|
|
@@ -499,6 +460,7 @@ export class SceneService implements OnModuleInit, OnModuleDestroy {
|
|
|
pushFirstRender(clipPath: string, metaData: string): Promise<boolean> {
|
|
|
return new Promise<boolean>(async (resolve, reject) => {
|
|
|
try {
|
|
|
+ this.onSteaming = true;
|
|
|
const streamData: StreamFrameType = {
|
|
|
frame: 1,
|
|
|
clipPath: clipPath,
|
|
@@ -519,12 +481,14 @@ export class SceneService implements OnModuleInit, OnModuleDestroy {
|
|
|
console.log('frame', frame);
|
|
|
if (frame === 1) {
|
|
|
const redisData = await this.rotateService.echo(this.user_id);
|
|
|
+ this.onSteaming = true;
|
|
|
+ this.holdSteam();
|
|
|
if (redisData && 'mediaSrc' in redisData) {
|
|
|
const mediaSrc: string = redisData.mediaSrc || '';
|
|
|
if (mediaSrc.length > 0) {
|
|
|
let src = mediaSrc.split('?')[0];
|
|
|
// 临时本地替换路经
|
|
|
- src = src.replace('/0000000001/100/', '');
|
|
|
+ src = src.replace('/0000000001/', '');
|
|
|
const clipPath = path.join(__dirname, `../ws/video/${src}`);
|
|
|
delete redisData.mediaSrc;
|
|
|
|
|
@@ -532,8 +496,6 @@ export class SceneService implements OnModuleInit, OnModuleDestroy {
|
|
|
`user:${this.user_id}:first render stream` +
|
|
|
JSON.stringify({ path: clipPath, meta: redisData }),
|
|
|
);
|
|
|
-
|
|
|
- this.holdSteam();
|
|
|
const status = await this.pushFirstRender(
|
|
|
clipPath,
|
|
|
JSON.stringify(redisData),
|
|
@@ -546,35 +508,35 @@ export class SceneService implements OnModuleInit, OnModuleDestroy {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ console.log(`空白流::${frame}-onSteaming`, this.onSteaming);
|
|
|
if (frame > 1 && !this.onSteaming) {
|
|
|
const redisDataAuto = await this.rotateService.echo(this.user_id);
|
|
|
if (redisDataAuto) {
|
|
|
+ 'mediaSrc' in redisDataAuto && delete redisDataAuto.mediaSrc;
|
|
|
const streamMeta: StreamMetaType = {
|
|
|
frame: frame,
|
|
|
metaData: JSON.stringify(redisDataAuto),
|
|
|
};
|
|
|
this.streamService.pushMetaDataToSteam(streamMeta);
|
|
|
- } else {
|
|
|
}
|
|
|
}
|
|
|
} catch (error) {
|
|
|
this.logger.error('handleStream', error);
|
|
|
}
|
|
|
});
|
|
|
-
|
|
|
+ }
|
|
|
+ handleRotateStream() {
|
|
|
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(
|
|
|
- 'roQueueSubscription \n',
|
|
|
- this.rotateframeCnt,
|
|
|
- stream.metaData,
|
|
|
+ this.logger.log(
|
|
|
+ `roQueueSubscription:frame:${this.rotateframeCnt} ` +
|
|
|
+ JSON.stringify(stream.metaData),
|
|
|
);
|
|
|
await this.streamService.pushFrameToSteam(stream);
|
|
|
setTimeout(() => {
|
|
@@ -583,9 +545,66 @@ export class SceneService implements OnModuleInit, OnModuleDestroy {
|
|
|
const next = this.rotateframeCnt + 1;
|
|
|
this.resumeStream(next);
|
|
|
this.rotateframeCnt = -1;
|
|
|
+ this.onMoving = false;
|
|
|
}
|
|
|
}, 300);
|
|
|
},
|
|
|
);
|
|
|
}
|
|
|
+
|
|
|
+ cleanMoveSteam() {
|
|
|
+ if (this.moveQueueSubscription) {
|
|
|
+ this.moveQueueSubscription.unsubscribe();
|
|
|
+ this.moveframeCnt = -1;
|
|
|
+ this.lastMoveCnt = -1;
|
|
|
+ this.moveQueueSubscription = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ handleMoveSteam() {
|
|
|
+ this.moveQueueSubscription = this.moveQueue.subscribe(
|
|
|
+ async (stream: StreamFrameType) => {
|
|
|
+ const metaData: StreamReplyType = JSON.parse(stream.metaData);
|
|
|
+ this.holdSteam();
|
|
|
+ console.log('handleMoveSteam-onSteaming', this.onSteaming);
|
|
|
+ if (this.onMoving) {
|
|
|
+ this.currentMoveMaker = metaData.marker;
|
|
|
+ } else {
|
|
|
+ if (
|
|
|
+ this.currentMoveMaker &&
|
|
|
+ this.currentMoveMaker !== metaData.marker
|
|
|
+ ) {
|
|
|
+ console.log('kill Queue');
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // console.log('moveQueueSubscription', stream, metaData.marker);
|
|
|
+ if (this.moveframeCnt === -1) {
|
|
|
+ this.moveframeCnt = this.frameCnt.value;
|
|
|
+ }
|
|
|
+ this.moveframeCnt += 1;
|
|
|
+ console.log('this.moveframeCnt', this.moveframeCnt);
|
|
|
+ let src = stream.clipPath.split('?')[0];
|
|
|
+ // // 临时本地替换路经
|
|
|
+ src = src.replace('/0000000001/', '');
|
|
|
+ const clipPath = path.join(__dirname, `../ws/video/${src}`);
|
|
|
+
|
|
|
+ const streamData: StreamFrameType = {
|
|
|
+ frame: this.moveframeCnt,
|
|
|
+ clipPath: clipPath,
|
|
|
+ metaData: stream.metaData,
|
|
|
+ serverTime: this.mockserverTime,
|
|
|
+ DIR: 3,
|
|
|
+ };
|
|
|
+ await this.streamService.pushFrameToSteam(streamData);
|
|
|
+
|
|
|
+ if (this.lastMoveCnt == this.moveframeCnt) {
|
|
|
+ const next = this.moveframeCnt + 1;
|
|
|
+ console.log('last', next);
|
|
|
+ this.resumeStream(next);
|
|
|
+ this.cleanMoveSteam();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ );
|
|
|
+ }
|
|
|
}
|