import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common'; import { ClientGrpc, Client } from '@nestjs/microservices'; 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'; import { CacheService } from 'src/cache/cache.service'; import { StreamService } from './stream/stream.service'; const frameMetaReply = { traceIds: [], vehicle: null, newUserStates: [ { userId: 'a2aaaed1fffe6', 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, player: { position: { x: -755, y: -1450, z: -34 }, angle: { pitch: 0, yaw: 0, roll: 0 }, }, 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: 0, isMoving: 0, needIfr: 0, isVideo: 0, stillFrame: 0, isRotating: 0, isFollowing: 0, clientPanoTitlesBitmap: [], clientPanoTreceId: '', prefetchVideoId: '', noMedia: false, }, event: null, relation: 1, }, ], actionResponses: [], getStateType: 0, code: 0, msg: 'OK', }; @Injectable() export class SceneService implements OnModuleInit, OnModuleDestroy { constructor( private cacheService: CacheService, private streamService: StreamService, ) { } @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; public startSteaming = new BehaviorSubject(false); private user_id: string; private roomId: string; private onSteaming = false; private testFrame = -1; private RotateframeCnt = -1; 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; } } onModuleInit() { this.sceneGrpcService = this.client.getService('SceneGrpcService'); this.logger.log('init SceneGrpcService'); this.streamService.onSteaming.subscribe((val) => { this.onSteaming = val; }); Number.prototype.padLeft = function (n, str) { return Array(n - String(this).length + 1).join(str || '0') + this; }; } onModuleDestroy() { this.streamService.onSteaming.unsubscribe(); } getRoute(request: RouteRequest) { return this.sceneGrpcService.getRoute(request); } getBreakPoint(request: GetBreakPointRequest) { return this.sceneGrpcService.getBreakPoint(request); } init(request: InitRequest) { try { // const initReply = this.sceneGrpcService.init(request); // initReply.subscribe((reply) => { // console.log('initReply', reply); // }); } catch (error) { console.log('error', error); } } exit(request: ExitRequest) { // const exitReply = this.sceneGrpcService.exit(request); // exitReply.subscribe((reply) => { // console.log('exitReply', reply); // }); } move(request: MoveRequest) { return this.sceneGrpcService.move(request); } async rotate(request: RotateRequest) { try { // const reply = this.sceneGrpcService.rotate(request); if (!this.onSteaming) { this.frameCnt += 1; this.RotateframeCnt = this.frameCnt; this.testFrame += 1; 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: 754871824, DIR: 1, }; console.log('stream', this.frameCnt, stream.clipPath); this.streamService.pushFrameToSteam(stream); const redisMeta = await this.cacheService.rpop( `updateFrameMetadata:${this.user_id}`, ); // this.frameCnt += 1; if (redisMeta && redisMeta.length > 0) { console.log('this.user_id', this.user_id); const meta = JSON.parse(redisMeta); if ('mediaSrc' in meta) { console.log('meta', meta); console.log('mediaSrc', meta.mediaSrc); if (meta.mediaSrc.length > 0) { // const testclipPath = meta.mediaSrc.replace( // '/mnt/oss/metaverse/scene/0000000001/100/', // '', // ); // console.log('testclipPath', testclipPath); // // const demoPath = // // this.frameCnt > 10 // // ? path.join(__dirname, '../ws/video/53.h264') // // : path.join(__dirname, '../ws/video/2.h264'); // const demoPath = path.join( // __dirname, // `../ws/video/${testclipPath}`, // ); // delete meta.mediaSrc; // const stream: StreamFrameType = { // frame: this.frameCnt, // clipPath: demoPath, // metaData: JSON.stringify(meta), // serverTime: 754873824, // DIR: 3, // }; // this.streamService.pushFrameToSteam(stream); } } } } // reply.subscribe((res: NormalReply) => { // if (res.code === 200) { // } // }); } catch (error) { this.logger.error('rotate', error); } } joystick(request: JoystickRequest) { return this.sceneGrpcService.joystick(request); } handleDataChanelOpen(channel: DataChannel): void { this.channel = channel; this.streamService.setChannel(channel); this.handleStartCountingFrame(); this.startSteaming.next(true); } handleDataChanelClose(): void { this.stopCountingFrame(); this.startSteaming.next(false); this.streamService.closeChannel(); const exitRequest: ExitRequest = { action_type: 1002, user_id: this.user_id, trace_id: '', }; this.exit(exitRequest); } handleMessage(message: string | Buffer) { try { if (typeof message === 'string') { // console.log('toto', message); const msg: RTCMessageRequest = JSON.parse(message); switch (msg.action_type) { case ActionType.rotate: const rotateRequest: RotateRequest = msg; this.rotate(rotateRequest); break; case ActionType.userStatus: this.updateUserStatus(); break; case ActionType.status: this.updateStatus(); break; default: break; } } } catch (error) { this.logger.error('handleMessage:rtc', error); } } updateStatus() { const reply = { data: { action_type: 1009, echo_msg: { echoMsg: Date.now() } }, track: false, }; this.streamService.pushNormalDataToStream(reply); } async updateUserStatus() { const reply = { actionType: 1024, pointType: 100, extra: '', traceId: '65db8e1b-2261-42eb-8bc5-8dc97bfe0b0e', packetId: '', nps: [], peopleNum: 0, zoneId: '', echoMsg: '', reserveDetail: null, userWithAvatarList: [], newUserStates: [ { userId: 'e497b92704f5a', playerState: { roomTypeId: '', person: 0, avatarId: 'KGe_Boy', skinId: '10089', roomId: 'e629ef3e-022d-4e64-8654-703bb96410eb', isHost: false, isFollowHost: false, skinDataVersion: '1008900008', avatarComponents: '', nickName: 'e497b92704f5a', movingMode: 0, attitude: 'walk', areaName: '', pathName: 'thirdwalk', pathId: 'thirdwalk', avatarSize: 1, extra: '{"removeWhenDisconnected":true}', prioritySync: false, avatarURL: '', micStatus: 0, player: { position: { x: -755, y: -1450, z: -34 }, angle: { pitch: 0, yaw: 0, roll: 0 }, }, camera: null, cameraCenter: null, }, renderInfo: { renderType: 0, videoFrame: null, cameraStateType: 0, isMoving: 0, needIfr: 0, isVideo: 0, stillFrame: 0, isRotating: 0, isFollowing: 0, clientPanoTitlesBitmap: [], clientPanoTreceId: '', prefetchVideoId: '', noMedia: false, }, event: { id: '', type: 0, points: [], rotateEvent: null, removeVisitorEvent: null, }, relation: 0, }, ], code: 0, msg: '', }; const redisMeta = await this.cacheService.rpop( `updateFrameMetadata:${this.user_id}`, ); //TODO 接入redis数据 console.log('redisMeta', redisMeta); if (redisMeta && redisMeta.length > 0) { const meta = JSON.parse(redisMeta); 'mediaSrc' in meta && delete meta.mediaSrc; meta.action_type = 1024; this.streamService.pushNormalDataToStream(meta); } else { this.streamService.pushNormalDataToStream(reply); } // 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: 754871824, }; this.streamService.pushFrameToSteam(stream); } // console.log('padLeft', ); // if (this.frameCnt > 2) { // setInterval(() => { // this.frameCnt += 1; // const stream: StreamFrameType = { // frame: this.frameCnt, // clipPath: path.join( // __dirname, // `../ws/video/100/100.${this.frameCnt.padLeft(4, '0')}.h264`, // ), // metaData: JSON.stringify(frameMetaReply), // serverTime: 754871824, // }; // this.streamService.pushFrameToSteam(stream); // }, 1000 / 30); // }; 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; } }