-
Notifications
You must be signed in to change notification settings - Fork 1
화질 조정 기능 구현
Seungheon Han edited this page Dec 1, 2024
·
1 revision
화질 조정 기능을 구현하기 위해 먼저 화질에 영향을 미치는 요소에 대해서 정리해 보았습니다.
- 초당 처리되는 데이터양을 의미.
- 고정 비트레이트(CBR)와 가변 비트레이트(VBR)가 있음.
- 비트레이트가 높을수록 품질은 좋아지지만
파일 크기가 커진다
.
- 영상의 가로x세로 픽셀 수입니다
- 보통 1920x1080(Full HD), 3840x2160(4K) 등으로 표현합니다
- 해상도가 높을수록 더 선명하지만
처리 부하가 증가
합니다
- 초당 표시되는 이미지 수
- 보통 24fps, 30fps, 60fps
- 프레임레이트가 높을수록 부드럽지만
데이터 양이 증가
합니다.
화질은 비트레이트, 해상도, 프레임레이트가 모두 맞물려서 결정됩니다. 또한, 화질별로 해상도가 정해져있고, 적정 비트레이트와 프레임레이트가 정해져있습니다.
화질별 권장 비트레이트
화질별 해상도
화질 기능을 구현하기 위해 위에서 학습한 화질별 비트레이트
, 해상도
, 프레임레이트
를 송출 클라이언트 producer에서 설정 해주어야 했습니다.
export const ENCODING_OPTIONS = [
{ maxBitrate: 750000, scaleResolutionDownBy: 2, maxFramerate: 30 },
{ maxBitrate: 2500000, scaleResolutionDownBy: 1.5, maxFramerate: 30 },
{ maxBitrate: 4000000, scaleResolutionDownBy: 1, maxFramerate: 30 },
];
const producerConfig: Record<string, unknown> = {
track: tracks[kind],
};
if (kind === 'video') {
producerConfig['encodings'] = ENCODING_OPTIONS;
producerConfig['codecOptions'] = {
videoGoogleStartBitrate: 1000,
};
}
transport
.current!.produce(producerConfig)
.then(producer => setProducers(prev => new Map(prev).set(kind, producer)));
- 방송 송출 스트림을 생성하기 위한 produce 함수를 호출 할 때 스트림 타입이 video일 경우 encoding 옵션을 설정하도록 코드를 변경해 주었습니다.
- encoidng 옵션은 각 화질별 비트레이트와 프레임레이트를 설정한 배열입니다.
- encoding 옵션에 배열을 담아 스트림을 송출하면 스트림을 수신하는 consumer는 3가지 인코딩 옵션 중 하나를 선택하여 스트림을 받을 수 있습니다.
서버에서는 스트림을 수신받는 consumer 생성 코드를 변경해주어야 합니다.
export const QUALITY = {
LOW: '480p',
MID: '720p',
HIGH: '1080p',
};
export const QUALITY_LAYER = {
[QUALITY.LOW]: 0,
[QUALITY.MID]: 1,
[QUALITY.HIGH]: 2,
};
const consumer = await transport.consume({
producerId: producer.id,
rtpCapabilities,
paused: false,
preferredLayers: {
spatialLayer: QUALITY_LAYER[QUALITY.MID], // 0,1,2 순으로 480p,720p,1080p. 기본값은 중간인 720p로 시작.
temporalLayer: 2, // fps: 0,1,2순으로 기본 fps의 1/4, 1/2, 1 로 들어감.
},
});
- consume 함수를 호출할때 preferredLayers 옵션을 추가하여 클라이언트 producer가 송출하는 비디오 인코딩 옵션 중 한가지를 선택해 받아볼 수 있도록 수정합니다.
- 초반부터 높은 화질을 선택하여 스트림을 받을 경우 과부화가 올 수 있기 때문에 중간 화질로 받아 볼 수 있도록 설정합니다.
@SubscribeMessage('setVideoQuality')
handleVideoQuality(@MessageBody() params: SetVideoQualityDto) {
this.sfuService.setVideoQuality(params);
}
setConsumerBitrate(params: SetVideoQualityDto) {
const { transportId, quality } = params;
const consumers = this.consumers.get(transportId);
const videoConsumer = consumers.find(consumer => consumer.kind === 'video');
videoConsumer.setPreferredLayers({
spatialLayer: QUALITY_LAYER[quality],
temporalLayer: 2,
});
}
- 클라이언트에서 화질 변경 버튼을 클릭했을때 화질을 변경하도록 socket 이벤트를 구현했습니다.
- consumer 객체의
setPreferredLayers
함수를 통해 3가지 인코딩 스트림중 요청받은 스트림으로 변경하도록 설정해주었습니다. - spatialLayer은 해상도와 관련된 옵션으로 비디오의 비트레이트와 해상도와 연관되어있으며, temporalLayer은 프레임레이트와 관련된 옵션입니다.
const handleVideoQuality = (videoQuality: VideoQuality) => {
if (!socket) return;
socket.emit('setVideoQuality', { transportId, quality: videoQuality });
setVideoQuality(videoQuality);
};
- 클라이언트에서 화질 변경 클릭이벤트가 발생할때 마다 서버에 구현한 setVideoQuality 이벤트가 발생하도록 코드를 수정해 주었습니다.
현재 화질 변경 기능은 정상적으로 동작하지만, 비디오의 화질 자체가 생각했던 것과는 조금 다른 모습이 있어 이 부분을 좀 더 개선해 봐야할 필요성을 느꼈습니다.
특히, 720p와 1080p 화질 차이가 육안으로 구분하기가 힘들고, 480p 화질은 육안으로 구분은 잘 되지만 초록 색으로 비디오가 부각되는 현상이 있어 영상 인코딩 정보들과 mediasoup 인코딩 옵션들에 대해서 더 학습하고 수정해봐야 할 것 같습니다.
- Mediasoup 포트 매핑 문제
- swagger 같은 응답 코드에 다양한 응답 보여주기
- Sudo가 계속 비밀번호를 요청함
- Docker 이미지가 너무 크다
- Git action에서 도커 이미지 빌드 시간을 단축시켜보자
- Docker compose를 이용해서 메모리 사용률을 줄여보자
- 방송 녹화 시 CPU 과부하 문제를 해결해보자
- Release 브랜치? 너 필요해?
- 로딩이 너무 짧아…!
- NestJS ORM으로 무엇을 사용해야 할까?
- WebRTC를 이용한 1:N 스트리밍 서비스에서 시그널링 서버가 필요할까?
- 실시간 채팅 구현: 인메모리 방식을 선택한 이유
- MySQL 아키텍처 개선: DB 의존성 분리와 서버 역할 명확화
- 브라우저 창이 최소화되면 비디오 송출이 안된다…!
- Mediasoup 기본 개념
- DLTS와 Signaling
- Tell, Don't Ask (TDA) 원칙이란
- VPC(Virtual Private Cloud) 학습 정리
- 순환참조: A 서비스 ‐ B 서비스 vs. A 서비스 ‐ B 레포지토리
- Dto 메서드 전략
- WebRTC란?
- 자바스크립트 패키지 매니저(npm, yarn, pnpm)
- shadcn/ui을 이용해 UI 개발 생산성 높이기
- React 이벤트 핸들러 네이밍(on vs handle)
- React-router-dom의 createBrowserRouter을 사용해보기
- fetch vs axios