Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix] Safari에서 비디오 스트림을 전송할 때 다른 브라우저에서 스트림 정보가 오지 않던 오류 수정 #375

Merged
merged 12 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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
23 changes: 13 additions & 10 deletions apps/media/src/mediasoup/mediasoup.service.ts
Original file line number Diff line number Diff line change
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
20 changes: 15 additions & 5 deletions apps/web/src/hooks/mediasoup/useNetworkMonitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const QUALITY_LEVEL = {
},
poor: {
quality: 0,
options: { packetLossRate: 5, jitter: 30, frameDropRate: 10, averageRTT: 300, nackCount: 50 },
options: { packetLossRate: 10, jitter: 30, frameDropRate: 10, averageRTT: 300, nackCount: 50 },
},
} as const;

Expand Down Expand Up @@ -89,12 +89,12 @@ const useNetworkMonitor = ({ streams }: UseNetworkMonitorProps) => {
[]
);

const checkNetworkQuality = async (streams: client.RemoteStream[]) => {
const checkNetworkQualities = async (streams: client.RemoteStream[]) => {
const networkQualities = await Promise.all(
streams.map(async (data) => {
const { consumer } = data;

if (!consumer) return;
if (!consumer || consumer.closed || consumer.paused) return;

let networkQuality = 2; // 0: poor, 1: average, 2: good

Expand Down Expand Up @@ -131,7 +131,15 @@ const useNetworkMonitor = ({ streams }: UseNetworkMonitorProps) => {
})
);

return networkQualities;
return networkQualities.filter(Boolean).reduce(
(acc, cur) => {
if (!cur) return acc;
if (acc.some((data) => data.consumerId === cur.consumerId)) return acc;

return [...acc, cur];
},
[] as { consumerId: string; networkQuality: number }[]
);
};

useEffect(() => {
Expand All @@ -142,7 +150,9 @@ const useNetworkMonitor = ({ streams }: UseNetworkMonitorProps) => {
const interval = setInterval(async () => {
const notPausedStreams = getNotPausedStreams(streams);

const networkQualities = await checkNetworkQuality(notPausedStreams);
const networkQualities = await checkNetworkQualities(notPausedStreams);

if (!networkQualities || !networkQualities.length) return;

socket.emit(SOCKET_EVENTS.changeConsumerPreferredLayers, {
roomId: ticleId,
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/hooks/mediasoup/useRemoteStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ const useRemoteStream = () => {
const newStreams = [...prevStreams];
const stream = newStreams.find((stream) => stream.consumer?.producerId === producerId);

if (!stream) {
if (!stream || stream.consumer?.closed) {
return prevStreams;
}

Expand Down Expand Up @@ -280,7 +280,7 @@ const useRemoteStream = () => {
const newStreams = [...prevStreams];
const stream = newStreams.find((stream) => stream.consumer?.producerId === producerId);

if (!stream) {
if (!stream || stream.consumer?.closed) {
return prevStreams;
}

Expand Down
16 changes: 11 additions & 5 deletions apps/web/src/hooks/useMediaTracks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const DEFAULT_LOCAL_STREAM = {

const getMediaDevices = (kind: MediaDeviceKind, devices: MediaDeviceInfo[]) => {
return devices
.filter((device) => device.kind === kind && device.deviceId && device.deviceId !== 'default')
.filter((device) => device.kind === kind && device.deviceId)
.map((device) => ({ label: device.label, value: device.deviceId }));
};

Expand Down Expand Up @@ -116,16 +116,22 @@ const useMediaTracks = () => {
setAudioDevices(audioInputs);
setAudioOutputDevices(audioOutputs);

if (videoInputs[0]) setSelectedVideoDeviceId(videoInputs[0].value);
if (audioInputs[0]) setSelectedAudioDeviceId(audioInputs[0].value);
if (audioOutputs[0]) setSelectedAudioOutputDeviceId(audioOutputs[0].value);
if (videoInputs[0] && !selectedVideoDeviceId) {
setSelectedVideoDeviceId(videoInputs[0].value);
}
if (audioInputs[0] && !selectedAudioDeviceId) {
setSelectedAudioDeviceId(audioInputs[0].value);
}
if (audioOutputs[0] && !selectedAudioOutputDeviceId) {
setSelectedAudioOutputDeviceId(audioOutputs[0].value);
}
} catch (_) {
toast('미디어 정보를 가져올 수 없습니다.');
}
};

fetchMediaDevices();
}, []);
}, [video, audio]);

return {
video,
Expand Down
31 changes: 16 additions & 15 deletions packages/mediasoup/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,25 +65,26 @@ export interface ResumeConsumersRes {
paused: boolean;
}

export const PRODUCER_OPTIONS: ProducerOptions = {
encodings: [
{ rid: 'r0', maxBitrate: 50000, scalabilityMode: 'S1T3' },
{ rid: 'r1', maxBitrate: 150000, scalabilityMode: 'S1T3' },
{ rid: 'r2', maxBitrate: 500000, scalabilityMode: 'S1T3' },
],
codecOptions: {
videoGoogleStartBitrate: 1000,
},
};

export const VIDEO_PRODUCER_OPTIONS: ProducerOptions = {
encodings: [
{ rid: 'r0', maxBitrate: 50000, scalabilityMode: 'S1T3' },
{ rid: 'r1', maxBitrate: 150000, scalabilityMode: 'S1T3' },
{ rid: 'r2', maxBitrate: 500000, scalabilityMode: 'S1T3' },
{
rid: 'r0',
maxBitrate: 750000,
maxFramerate: 30,
},
{
rid: 'r1',
maxBitrate: 2000000,
maxFramerate: 30,
},
{
rid: 'r2',
maxBitrate: 3500000,
maxFramerate: 30,
},
],
codecOptions: {
videoGoogleStartBitrate: 1000,
videoGoogleStartBitrate: 100000,
opusDtx: true,
},
};
Expand Down
11 changes: 0 additions & 11 deletions packages/mediasoup/src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,3 @@ export interface ChangeConsumerPreferredLayersDto {
roomId: string;
networkQualities: NetworkQualityDto[];
}

export const PRODUCER_OPTIONS = {
encodings: [
{ rid: 'r0', maxBitrate: 50000, scalabilityMode: 'S1T3', active: true, dtx: false },
{ rid: 'r1', maxBitrate: 150000, scalabilityMode: 'S1T3', active: true, dtx: false },
{ rid: 'r2', maxBitrate: 500000, scalabilityMode: 'S1T3', active: true, dtx: false },
],
codecOptions: {
videoGoogleStartBitrate: 1000,
},
};
Loading