diff --git a/apps/media/src/mediasoup/mediasoup.service.ts b/apps/media/src/mediasoup/mediasoup.service.ts index d389c86b..5b4549dd 100644 --- a/apps/media/src/mediasoup/mediasoup.service.ts +++ b/apps/media/src/mediasoup/mediasoup.service.ts @@ -48,7 +48,7 @@ export class MediasoupService implements OnModuleInit { return worker; } - async createRoom(roomId: string) { + async createRoom(roomId: string, masterSocketId: string) { const isExistRoom = this.roomService.existRoom(roomId); if (isExistRoom) { return roomId; @@ -59,7 +59,7 @@ export class MediasoupService implements OnModuleInit { mediaCodecs: this.mediasoupConfig.router.mediaCodecs, }); - return this.roomService.createRoom(roomId, router); + return this.roomService.createRoom(roomId, router, masterSocketId); } joinRoom(roomId: string, socketId: string, nickname: string) { diff --git a/apps/media/src/record/record.service.ts b/apps/media/src/record/record.service.ts index 6873d333..e29a1b26 100644 --- a/apps/media/src/record/record.service.ts +++ b/apps/media/src/record/record.service.ts @@ -66,10 +66,6 @@ export class RecordService { return recordInfo; } - getRecordInfo(roomId: string) { - return this.recordInfos.get(roomId); - } - private async addPlainTransport(recordInfo: RecordInfo, router: types.Router) { const plainTransport = await this.mediasoupService.createPlainTransport(router); recordInfo.setPlainTransport(plainTransport); @@ -117,7 +113,7 @@ export class RecordService { return; } this.releasePort(recordInfo.port); - recordInfo.stopRecordProcess(); + recordInfo.clearStream(); this.recordInfos.delete(roomId); } diff --git a/apps/media/src/record/recordInfo.ts b/apps/media/src/record/recordInfo.ts index 62d2168c..0bc153a5 100644 --- a/apps/media/src/record/recordInfo.ts +++ b/apps/media/src/record/recordInfo.ts @@ -43,7 +43,7 @@ export class RecordInfo { this.recordConsumer.resume(); } - stopRecordProcess() { + clearStream() { if (this.recordConsumer) { this.recordConsumer.close(); this.recordConsumer = null; @@ -84,6 +84,7 @@ export class RecordInfo { this.ncpService.uploadFile(filePath, remoteFileName, roomId); unlinkSync(sdpFilePath); this.ffmpegProcess = null; + this.clearStream(); }) .save(filePath); diff --git a/apps/media/src/room/room.service.ts b/apps/media/src/room/room.service.ts index 848fc443..cecda26f 100644 --- a/apps/media/src/room/room.service.ts +++ b/apps/media/src/room/room.service.ts @@ -11,8 +11,8 @@ export class RoomService { constructor() {} - createRoom(roomId: string, router: Router) { - const room = new Room(roomId, router); + createRoom(roomId: string, router: Router, masterSocketId: string) { + const room = new Room(roomId, router, masterSocketId); this.rooms.set(roomId, room); return roomId; } @@ -31,14 +31,7 @@ export class RoomService { deletePeer(socketId: string) { for (const [roomId, room] of this.rooms) { - if (!room.removePeer(socketId)) continue; - - if (room.peers.size === 0) { - room.close(); - this.rooms.delete(roomId); - } - - return roomId; + if (room.removePeer(socketId)) return roomId; } } @@ -48,4 +41,28 @@ export class RoomService { this.rooms.delete(roomId); return roomId; } + + checkIsMaster(roomId: string, socketId: string) { + const room = this.rooms.get(roomId); + if (!room) { + return false; + } + return room.masterSocketId === socketId; + } + + checkRoomIsOpen(roomId: string) { + const room = this.rooms.get(roomId); + if (!room) { + return false; + } + return room.isOpen; + } + + setRoomIsOpen(roomId: string, isOpen: boolean) { + const room = this.rooms.get(roomId); + if (!room) { + return; + } + room.isOpen = isOpen; + } } diff --git a/apps/media/src/room/room.ts b/apps/media/src/room/room.ts index e2485668..224941cc 100644 --- a/apps/media/src/room/room.ts +++ b/apps/media/src/room/room.ts @@ -6,13 +6,17 @@ import { Peer } from './peer'; export class Room { id: string; + masterSocketId: string; router: Router; peers: Map; + isOpen: boolean; - constructor(roomId: string, router: Router) { + constructor(roomId: string, router: Router, masterSocketId: string) { this.id = roomId; this.router = router; + this.masterSocketId = masterSocketId; this.peers = new Map(); + this.isOpen = true; } getRouter() { diff --git a/apps/media/src/signaling/signaling.gateway.ts b/apps/media/src/signaling/signaling.gateway.ts index 047bbd49..89328120 100644 --- a/apps/media/src/signaling/signaling.gateway.ts +++ b/apps/media/src/signaling/signaling.gateway.ts @@ -13,6 +13,7 @@ import type { client, server } from '@repo/mediasoup'; import { MediasoupService } from '@/mediasoup/mediasoup.service'; import { RecordService } from '@/record/record.service'; +import { RoomService } from '@/room/room.service'; import { WSExceptionFilter } from '@/wsException.filter'; @WebSocketGateway() @@ -20,20 +21,21 @@ import { WSExceptionFilter } from '@/wsException.filter'; export class SignalingGateway implements OnGatewayDisconnect { constructor( private mediasoupService: MediasoupService, - private recordService: RecordService + private recordService: RecordService, + private roomService: RoomService ) {} @SubscribeMessage(SOCKET_EVENTS.createRoom) - async handleCreateRoom(@MessageBody('roomId') roomId: string) { - await this.mediasoupService.createRoom(roomId); + async handleCreateRoom(@ConnectedSocket() client: Socket, @MessageBody('roomId') roomId: string) { + await this.mediasoupService.createRoom(roomId, client.id); return { roomId }; } @SubscribeMessage(SOCKET_EVENTS.joinRoom) joinRoom(@ConnectedSocket() client: Socket, @MessageBody() joinRoomDto: server.JoinRoomDto) { const { roomId, nickname } = joinRoomDto; - client.join(roomId); const rtpCapabilities = this.mediasoupService.joinRoom(roomId, client.id, nickname); + client.join(roomId); client.to(roomId).emit(SOCKET_EVENTS.newPeer, { peerId: client.id, nickname }); return { rtpCapabilities }; } @@ -104,12 +106,18 @@ export class SignalingGateway implements OnGatewayDisconnect { handleDisconnect(@ConnectedSocket() client: Socket) { const roomId = this.mediasoupService.disconnect(client.id); - const recordInfo = this.recordService.getRecordInfo(roomId); - if (recordInfo && recordInfo.socketId === client.id) { + const isMaster = this.roomService.checkIsMaster(roomId, client.id); + if (isMaster) { + client.to(roomId).emit(SOCKET_EVENTS.roomClosed); this.recordService.stopRecord(roomId); + this.mediasoupService.closeRoom(roomId); + return; } - client.to(roomId).emit(SOCKET_EVENTS.peerLeft, { peerId: client.id }); + const isOpen = this.roomService.checkRoomIsOpen(roomId); + if (isOpen) { + client.to(roomId).emit(SOCKET_EVENTS.peerLeft, { peerId: client.id }); + } } @SubscribeMessage(SOCKET_EVENTS.closeProducer) @@ -188,7 +196,7 @@ export class SignalingGateway implements OnGatewayDisconnect { @SubscribeMessage(SOCKET_EVENTS.closeRoom) closeMeetingRoom(@ConnectedSocket() client: Socket, @MessageBody('roomId') roomId: string) { client.to(roomId).emit(SOCKET_EVENTS.roomClosed); - this.mediasoupService.closeRoom(roomId); + this.roomService.setRoomIsOpen(roomId, false); } @SubscribeMessage(SOCKET_EVENTS.startRecord) diff --git a/apps/media/src/signaling/signaling.module.ts b/apps/media/src/signaling/signaling.module.ts index a3bf7294..82c90837 100644 --- a/apps/media/src/signaling/signaling.module.ts +++ b/apps/media/src/signaling/signaling.module.ts @@ -2,11 +2,12 @@ import { Module } from '@nestjs/common'; import { MediasoupModule } from '@/mediasoup/mediasoup.module'; import { RecordModule } from '@/record/record.module'; +import { RoomModule } from '@/room/room.module'; import { SignalingGateway } from './signaling.gateway'; @Module({ - imports: [MediasoupModule, RecordModule], + imports: [MediasoupModule, RecordModule, RoomModule], providers: [SignalingGateway], exports: [SignalingGateway], })