Skip to content

Commit

Permalink
[Release] 6차 배포 (12.06)
Browse files Browse the repository at this point in the history
[Release] 6차 배포 (12.06)
  • Loading branch information
seoko97 authored Dec 5, 2024
2 parents 261d00c + 452887b commit 8d63721
Show file tree
Hide file tree
Showing 31 changed files with 250 additions and 108 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@

## 프론트엔드

<h4><a href='https://simeunseo.notion.site/5d4f6f4ab2124d19940e21e19ac3f04b?pvs=4'>🔗 네트워크 상태에 따른 스트림 품질 변경</a></h4>
<h4><a href='https://simeunseo.notion.site/14a599a6f0d2800cb40add7d399525aa?pvs=4'>🔗 스트리밍을 최적화 해보자</a></h4>

> 최대한 많은 유저가 들어와 화상 서비스를 이용하는 것을 목적으로 하는 만큼 소켓 이벤트, 기존 할당된 자원에 대한 관리를 진행하며 최대한 적은 자원으로 나은 환경을 제공하기 위해 최적화를 진행하였습니다.
Expand Down
3 changes: 2 additions & 1 deletion apps/api/src/dashboard/dashboard.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

import { Applicant } from '@/entity/applicant.entity';
import { Summary } from '@/entity/summary.entity';
import { Ticle } from '@/entity/ticle.entity';

import { DashboardController } from './dashboard.controller';
import { DashboardService } from './dashboard.service';

@Module({
imports: [TypeOrmModule.forFeature([Ticle, Applicant])],
imports: [TypeOrmModule.forFeature([Ticle, Applicant, Summary])],
controllers: [DashboardController],
providers: [DashboardService],
})
Expand Down
30 changes: 26 additions & 4 deletions apps/api/src/dashboard/dashboard.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Repository } from 'typeorm';
import { ErrorMessage, TicleStatus } from '@repo/types';

import { Applicant } from '@/entity/applicant.entity';
import { Summary } from '@/entity/summary.entity';
import { Ticle } from '@/entity/ticle.entity';

@Injectable()
Expand All @@ -12,7 +13,9 @@ export class DashboardService {
@InjectRepository(Ticle)
private readonly ticleRepository: Repository<Ticle>,
@InjectRepository(Applicant)
private readonly applicantRepository: Repository<Applicant>
private readonly applicantRepository: Repository<Applicant>,
@InjectRepository(Summary)
private readonly summaryRepository: Repository<Summary>
) {}

async getCreatedTicleList(
Expand All @@ -25,7 +28,15 @@ export class DashboardService {

const queryBuilder = this.ticleRepository
.createQueryBuilder('ticle')
.select(['ticle.id', 'ticle.title', 'ticle.startTime', 'ticle.endTime', 'ticle.ticleStatus'])
.leftJoin('ticle.summary', 'summary')
.select([
'ticle.id',
'ticle.title',
'ticle.startTime',
'ticle.endTime',
'ticle.ticleStatus',
'summary.id',
])
.where('ticle.speaker = :speakerId', { speakerId })
.skip(skip)
.take(pageSize);
Expand All @@ -40,7 +51,12 @@ export class DashboardService {
}
}

const [ticles, totalItems] = await queryBuilder.getManyAndCount();
const [ticle, totalItems] = await queryBuilder.getManyAndCount();

const ticles = ticle.map((ticle) => ({
...ticle,
summary: ticle.summary ? ticle.summary.id !== null : false,
}));

const totalPages = Math.ceil(totalItems / pageSize);

Expand All @@ -62,6 +78,7 @@ export class DashboardService {
const queryBuilder = this.applicantRepository
.createQueryBuilder('applicant')
.leftJoinAndSelect('applicant.ticle', 'ticle')
.leftJoin('ticle.summary', 'summary')
.select([
'applicant.id',
'ticle.id',
Expand All @@ -70,6 +87,7 @@ export class DashboardService {
'ticle.startTime',
'ticle.endTime',
'ticle.ticleStatus',
'summary.id',
])
.where('applicant.user = :userId', { userId })
.skip(skip)
Expand All @@ -87,7 +105,11 @@ export class DashboardService {

const [applicants, totalItems] = await queryBuilder.getManyAndCount();

const ticles = applicants.map((applicant) => applicant.ticle);
const ticles = applicants.map((applicant) => ({
...applicant.ticle,
summary: applicant.ticle.summary ? applicant.ticle.summary.id !== null : false,
}));

const totalPages = Math.ceil(totalItems / pageSize);

return {
Expand Down
2 changes: 1 addition & 1 deletion apps/api/src/entity/ticle.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class Ticle {
@Column({ type: 'varchar', name: 'speaker_email' })
speakerEmail: string;

@Column({ type: 'varchar', name: 'speaker_introduce' })
@Column({ type: 'text', name: 'speaker_introduce' })
speakerIntroduce: string;

@Column({ type: 'varchar' })
Expand Down
12 changes: 12 additions & 0 deletions apps/media/src/mediasoup/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,22 @@ export class MediasoupConfig {
useinbandfec: 1,
},
},
{
kind: 'video',
mimeType: 'video/H264',
clockRate: 90000,
parameters: {
'packetization-mode': 1,
'profile-level-id': '42e01f',
},
},
{
kind: 'video',
mimeType: 'video/VP8',
clockRate: 90000,
parameters: {
'x-google-start-bitrate': 10000,
},
},
] as RtpCodecCapability[],
};
Expand Down
27 changes: 15 additions & 12 deletions apps/media/src/mediasoup/mediasoup.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand Down Expand Up @@ -113,10 +113,6 @@ export class MediasoupService implements OnModuleInit {
const peer = room.getPeer(socketId);
const transport = peer.getTransport(transportId);

if (appData.mediaTypes !== 'audio') {
rtpParameters.encodings = server.PRODUCER_OPTIONS.encodings;
}

const producer = await transport.produce({
kind,
rtpParameters,
Expand Down Expand Up @@ -269,6 +265,8 @@ export class MediasoupService implements OnModuleInit {
const peer = room.peers.get(socketId);
const consumer = peer.getConsumer(consumerId);

if (!consumer) return;

consumer?.pause();

return { paused: true, consumerId, producerId: consumer.producerId };
Expand All @@ -279,6 +277,8 @@ export class MediasoupService implements OnModuleInit {
const peer = room.peers.get(socketId);
const consumer = peer.getConsumer(consumerId);

if (!consumer) return;

if (consumer?.producerPaused) {
return { paused: true, consumerId, producerId: consumer.producerId };
}
Expand All @@ -289,11 +289,15 @@ export class MediasoupService implements OnModuleInit {
}

pauseConsumers(socketId: string, roomId: string, consumerIds: string[]) {
return consumerIds.map((consumerId) => this.pauseConsumer(socketId, consumerId, roomId));
return consumerIds
.map((consumerId) => this.pauseConsumer(socketId, consumerId, roomId))
.filter(Boolean);
}

resumeConsumers(socketId: string, roomId: string, consumerIds: string[]) {
return consumerIds.map((consumerId) => this.resumeConsumer(socketId, consumerId, roomId));
return consumerIds
.map((consumerId) => this.resumeConsumer(socketId, consumerId, roomId))
.filter(Boolean);
}

changeConsumerPreferredLayers(
Expand All @@ -307,10 +311,9 @@ export class MediasoupService implements OnModuleInit {

const consumer = peer.getConsumer(consumerId);

consumer?.setPreferredLayers({
spatialLayer: networkQuality,
temporalLayer: networkQuality,
});
if (!consumer || consumer.closed || consumer.paused) return;

consumer.setPreferredLayers({ spatialLayer: networkQuality });
});
}

Expand Down
6 changes: 1 addition & 5 deletions apps/media/src/record/record.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -117,7 +113,7 @@ export class RecordService {
return;
}
this.releasePort(recordInfo.port);
recordInfo.stopRecordProcess();
recordInfo.clearStream();
this.recordInfos.delete(roomId);
}

Expand Down
3 changes: 2 additions & 1 deletion apps/media/src/record/recordInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export class RecordInfo {
this.recordConsumer.resume();
}

stopRecordProcess() {
clearStream() {
if (this.recordConsumer) {
this.recordConsumer.close();
this.recordConsumer = null;
Expand Down Expand Up @@ -84,6 +84,7 @@ export class RecordInfo {
this.ncpService.uploadFile(filePath, remoteFileName, roomId);
unlinkSync(sdpFilePath);
this.ffmpegProcess = null;
this.clearStream();
})
.save(filePath);

Expand Down
37 changes: 27 additions & 10 deletions apps/media/src/room/room.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
}
}

Expand All @@ -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;
}
}
6 changes: 5 additions & 1 deletion apps/media/src/room/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import { Peer } from './peer';

export class Room {
id: string;
masterSocketId: string;
router: Router;
peers: Map<string, Peer>;
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() {
Expand Down
24 changes: 16 additions & 8 deletions apps/media/src/signaling/signaling.gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,29 @@ 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()
@UseFilters(WSExceptionFilter)
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 };
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 8d63721

Please sign in to comment.