import { Injectable, 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 { 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 onSteaming = new BehaviorSubject(false); public user_id: string; public roomId: string; onModuleInit() { this.sceneGrpcService = this.client.getService('SceneGrpcService'); this.logger.log('init SceneGrpcService'); } getRoute(request: RouteRequest) { return this.sceneGrpcService.getRoute(request); } getBreakPoint(request: GetBreakPointRequest) { return this.sceneGrpcService.getBreakPoint(request); } test(request: TestRequest) { return this.sceneGrpcService.test(request); } init(request: InitRequest) { try { const initReply = this.sceneGrpcService.init(request); initReply.subscribe((reply) => { console.log('initReply', reply); }); } catch (error) { console.log('error', error); } } move(request: MoveRequest) { return this.sceneGrpcService.move(request); } rotate(request: RotateRequest) { const reply = this.sceneGrpcService.rotate(request); console.log('reply', reply); reply.subscribe((res) => { console.log('rotate-reply', res); }); // console.log('this.frameCnt', this.frameCnt); // const stream: StreamFrameType = { // frame: this.frameCnt, // clipPath: path.join(__dirname, '../ws/video/254.h264'), // metaData: JSON.stringify(frameMetaReply), // }; // this.streamService.pushFrameToSteam(stream); } joystick(request: JoystickRequest) { return this.sceneGrpcService.joystick(request); } handleDataChanelOpen(channel: DataChannel): void { this.channel = channel; this.streamService.setChannel(channel); this.handleStartCountingFrame(); this.onSteaming.next(true); } handleDataChanelClose(): void { this.stopCountingFrame(); this.onSteaming.next(false); this.streamService.closeChannel(); } handleMessage(message: string | Buffer) { try { if (typeof message === 'string') { const msg: RTCMessageRequest = JSON.parse(message); switch (msg.action_type) { case ActionType.rotate: console.log('rotate', msg); const rotateRequest: RotateRequest = msg; this.rotate(rotateRequest); break; case ActionType.userStatus: this.updateUserStatus(); break; case ActionType.status: this.updateStatus(); break; default: break; } } // console.log('get rtc message', message); } catch (error) { this.logger.error('handleMessage:rtc', error); } } updateStatus() { const reply = { data: { action_type: 1009, echo_msg: { echoMsg: '1652857098550' } }, track: false, }; this.streamService.pushNormalDataToStream(reply); } 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: '', }; 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/254.h264'), metaData: JSON.stringify(frameMetaReply), }; this.streamService.pushFrameToSteam(stream); } // console.log('11', await this.redisService.get('test')); // console.log('data', data); if (this.frameCnt > 1) { 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; } // 首屏渲染 // pushTheFirstFrame() { // const chunk_size = 16000; // const block = 36; // console.log('首屏--数据'); // const paths = path.join(__dirname, '../ws/video'); // // const clipPath = paths + `/1.v2.h264`; // const testClipPath = paths + `/2.h264`; // const metaData = { // traceIds: [], // vehicle: null, // newUserStates: [ // { // userId: '1e3fa84d5c29a', // playerState: { // roomTypeId: '', // person: 0, // avatarId: 'KGe_Boy', // skinId: '10089', // roomId: '', // isHost: false, // isFollowHost: false, // skinDataVersion: '1008900008', // avatarComponents: '', // nickName: '1e3fa84d5c29a', // movingMode: 0, // attitude: 'walk', // areaName: 'LQC', // pathName: 'thirdwalk', // pathId: 'thirdwalk', // avatarSize: 1, // extra: '{"removeWhenDisconnected":true}', // 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: 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: 1, // }, // ], // actionResponses: [], // getStateType: 0, // code: 0, // msg: 'OK', // }; // const metaDataString = JSON.stringify(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(testClipPath); // // console.log('clipBuffer', clipBuffer); // // const fullBufferList = Buffer.concat([coordBuff, clipBuffer]); // // const steam = createReadStream(clipPath, { // // highWaterMark: chunk_size - block, // // }); // // console.log('fullBufferList', fullBufferList); // // const steam1 = createReadStream(fullBufferList.toString(), { // // highWaterMark: chunk_size - block, // // }); // const steam = new streamBuffers.ReadableStreamBuffer({ // frequency: 1, // in milliseconds. // chunkSize: chunk_size - block, // in bytes. // }); // steam.put(coordBuff); // steam.put(clipBuffer); // let steamByteLength = 0; // steam.on('data', (data: Buffer) => { // // console.log('data', data, data.byteLength); // // console.log('data-size', data); // const blockBuffStart = Buffer.alloc(block); // const packBuffer = Buffer.concat([blockBuffStart, data]); // // const isLastFrame = packBuffer.byteLength - chunk_size < 0; // // console.log('packBuffer', packBuffer.byteLength); // // if (isLastFrame) { // // // last frame // // packBuffer = Buffer.concat([packBuffer, coordBuff]); // // console.log('last frame', packBuffer.byteLength); // // } // const framePack = new DataView( // packBuffer.buffer.slice( // packBuffer.byteOffset, // packBuffer.byteOffset + packBuffer.byteLength, // ), // ); // // 16 bit slot // // framePack.setUint32(4) // framePack.setUint16(6, block); // framePack.setUint16(8, 1); // first render cnt // framePack.setUint16(10, 1); // 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, 754871824); //server_time // framePack.setUint32(24, 0); // framePack.setUint32(28, 0); // framePack.setUint32(block - 4, steamByteLength); // // console.log('statusPack', statusPack); // if (this.channel && this.channel.isOpen()) { // this.channel.sendMessageBinary(Buffer.from(framePack.buffer)); // } // steamByteLength += data.byteLength; // }); // steam.on('end', () => { // steamByteLength = 0; // // this.onSteaming = false; // }); // } }