JavaScript
387. (javaScript) 자바스크립트 AWS Kvs WebRTC 디바이스 역할 (master) sdp answer 응답 및 실시간 비디오 스트림 전송 수행
투케이2K
2025. 5. 12. 20:09
728x90
[개발 환경 설정]
개발 툴 : Edit++
개발 언어 : JavaScript
[소스 코드]
-----------------------------------------------------------------------------------------
[사전 설명 및 설정 사항]
-----------------------------------------------------------------------------------------
- 개발 환경 : Web
- 개발 기술 : JavaScript (자바스크립트) / AWS / Kvs / WebRTC
-----------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------
[소스 코드]
-----------------------------------------------------------------------------------------
<!DOCTYPE HTML>
<html lang="ko">
<head>
<title>javaScriptTest</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 반응형 구조 만들기 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
<!-- 내부 CSS 스타일 지정 -->
<style>
html, body {
width: 100%;
height: 100%;
margin : 0 auto;
padding : 0;
border : none;
background-color: #666;
}
#container_remoteView {
width: 100%;
height: 98%;
margin: 0 auto;
position: relative;
top: 1%;
}
#remoteView {
width: 100%;
height: 100%;
margin: 0 auto;
background-color: #666;
}
.swal-custom-width { width: 280px !important; }
.swal-footer { text-align: center; }
.swal-modal { text-align: center; }
.swal-text { text-align: center; }
</style>
<!-- [CDN 주소 설정] -->
<script src="https://code.jquery.com/jquery-latest.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/spin.js/2.3.2/spin.js"></script>
<script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>
<script src="https://unpkg.com/amazon-kinesis-video-streams-webrtc/dist/kvs-webrtc.min.js"></script>
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.1416.0.min.js"></script>
<!-- [자바스크립트 코드 지정] -->
<script>
// --------------------------------------------------------------------------------------------------------------
/*
-----------------------------------------
[요약 설명]
-----------------------------------------
1. CDN 설치 방법 : CDN 의존성 코드 참고
-----------------------------------------
2. 참고 사이트 :
https://docs.aws.amazon.com/kinesisvideostreams-webrtc-dg/latest/devguide/kvswebrtc-sdk-js.html
https://github.com/awslabs/amazon-kinesis-video-streams-webrtc-sdk-js
https://github.com/aws/aws-sdk-js
https://www.npmjs.com/package/amazon-kinesis-video-streams-webrtc
https://www.jsdelivr.com/package/npm/amazon-kinesis-video-streams-webrtc-nodejs
https://developer.mozilla.org/ko/docs/Web/API/RTCPeerConnection
https://www.jsdelivr.com/package/npm/vlc-media-player
-----------------------------------------
*/
// --------------------------------------------------------------------------------------------------------------
// [전역 변수 선언]
var remotePlayTimer = null;
var remotePlayFlag = false;
var remotePlayCount = 0;
var remoteSrcArray = [];
var kinesisVideoClient = null;
var region = ''; // [AWS 리젼]
var accessKeyId = ''; // [실시간 영상 재생에 필요한 액세스 키]
var secretAccessKey = ''; // [실시간 영상 재생에 필요한 시크릿 키]
var channelARN = ''; // [실시간 스트리밍 채널 ARN 주소]
// --------------------------------------------------------------------------------------------------------------
// [html 최초 로드 및 이벤트 상시 대기 실시]
window.onload = async function() {
console.log("-");
console.log("=========================================");
console.log("[window onload] : [start]");
console.log("=========================================");
console.log("-");
// -----------------------------------------
// [JSON 데이터 삽입 수행]
// -----------------------------------------
var webJson = {
region : "ap-northeast-1",
accessKeyId : "AK..7Q",
secretAccessKey : "ZzQ..Kxj",
channelARN : "arn:aws:kinesisvideo:ap-northeast-1:095225280673:channel/.."
};
setSystem(JSON.stringify(webJson)); // [Web 자체 데이터 생성 호출]
};
// --------------------------------------------------------------------------------------------------------------
async function setSystem(jsonData){
console.log("-");
console.log("=========================================");
console.log("[setSystem] : 웹 데이터 전달 받음");
console.log("-----------------------------------------");
console.log("[데이터] : " + JSON.stringify(jsonData));
console.log("=========================================");
console.log("-");
try {
// -----------------------------------------
// [json 데이터 파싱 수행 실시]
// -----------------------------------------
var jsonObject = JSON.parse(jsonData);
region = String(jsonObject.region); // [AWS 리전]
accessKeyId = String(jsonObject.accessKeyId); // [실시간 영상 재생에 필요한 액세스 키]
secretAccessKey = String(jsonObject.secretAccessKey); // [실시간 영상 재생에 필요한 시크릿 키]
channelARN = String(jsonObject.channelARN); // [실시간 스트리밍 채널 ARN 주소]
console.log("-");
console.log("=========================================");
console.log("[setSystem] : 데이터 파싱 정보");
console.log("-----------------------------------------");
console.log("[region] : " + region);
console.log("-----------------------------------------");
console.log("[accessKeyId] : " + accessKeyId);
console.log("-----------------------------------------");
console.log("[secretAccessKey] : " + secretAccessKey);
console.log("-----------------------------------------");
console.log("[channelARN] : " + channelARN);
console.log("=========================================");
console.log("-");
// -----------------------------------------
// [비디오 플레이 컴포넌트 지정 및 이벤트 지정]
// -----------------------------------------
const remoteView = document.getElementById("remoteView");
remoteView.onerror = (event) => {
console.error("-");
console.error("=========================================");
console.error("[setSystem] : [remoteView] : 원격 비디오 뷰 컴포넌트 : onerror");
console.error("=========================================");
console.error("-");
// -----------------------------------------
// [플래그 값 변경]
// -----------------------------------------
remotePlayFlag = false;
remotePlayCount = 0;
};
remoteView.onwaiting = (event) => {
console.log("-");
console.log("=========================================");
console.log("[setSystem] : [remoteView] : 원격 비디오 뷰 컴포넌트 : onwaiting");
console.log("=========================================");
console.log("-");
// -----------------------------------------
// [플래그 값 변경]
// -----------------------------------------
remotePlayFlag = false;
remotePlayCount = 0;
};
remoteView.onpause = (event) => {
console.error("-");
console.error("=========================================");
console.error("[setSystem] : [remoteView] : 원격 비디오 뷰 컴포넌트 : onpause");
console.error("=========================================");
console.error("-");
// -----------------------------------------
// [플래그 값 변경]
// -----------------------------------------
remotePlayFlag = false;
remotePlayCount = 0;
};
remoteView.onplaying = (event) => {
console.warn("-");
console.warn("=========================================");
console.warn("[setSystem] : [remoteView] : 원격 비디오 뷰 컴포넌트 : onplaying");
console.warn("=========================================");
console.warn("-");
// -----------------------------------------
// [플래그 값 변경]
// -----------------------------------------
remotePlayFlag = true;
};
// -----------------------------------------
// [KVS 클라이언트 생성]
// -----------------------------------------
kinesisVideoClient = new AWS.KinesisVideo({
region,
accessKeyId,
secretAccessKey,
correctClockSkew: true,
});
// -----------------------------------------
// [신호 채널 끝점 가져오기]
// -----------------------------------------
// 각 신호 채널에는 데이터 플레인 작업을 위해 연결할 HTTPS 및 WSS 엔드포인트가 할당됩니다
// -----------------------------------------
const endpoints = await kinesisVideoClient.getSignalingChannelEndpoint({
ChannelARN: channelARN,
SingleMasterChannelEndpointConfiguration: {
Protocols: ['WSS', 'HTTPS'],
Role: KVSWebRTC.Role.MASTER,
},
})
.promise();
const endpointsByProtocol = endpoints.ResourceEndpointList.reduce((acc, endpoint) => {
acc[endpoint.Protocol] = endpoint.ResourceEndpoint;
return acc;
}, {});
// -----------------------------------------
// [KVS 시그널링 클라이언트 만들기]
// -----------------------------------------
// 응답 의 HTTPS 끝점은 GetSignalingChannelEndpoint이 클라이언트와 함께 사용됩니다
// -----------------------------------------
// 이 클라이언트는 실제 신호가 아닌 ICE 서버를 가져오는 데만 사용됩니다
// -----------------------------------------
const kinesisVideoSignalingChannelsClient = new AWS.KinesisVideoSignalingChannels({
region: region,
accessKeyId,
secretAccessKey,
endpoint: endpointsByProtocol.HTTPS,
correctClockSkew: true,
});
// -----------------------------------------
// [ICE 서버 구성 가져오기]
// -----------------------------------------
// 최상의 성능을 위해 STUN 및 TURN ICE 서버 구성을 수집합니다
// -----------------------------------------
// [사용 서버 정의]
// -----------------------------------------
const iceServers = [];
// [TUN]
const iceRes = await kinesisVideoSignalingChannelsClient
.getIceServerConfig({
ChannelARN: channelARN,
})
.promise();
const tunServerList = iceRes.IceServerList;
tunServerList?.forEach((iceServer) => {
iceServers.push({
urls: iceServer.Uris,
username: iceServer.Username,
credential: iceServer.Password,
});
});
// [STUN]
const stunIceServers = { urls: `stun:stun.kinesisvideo.${region}.amazonaws.com:443` };
iceServers.push(stunIceServers); // [추가]
console.log("-");
console.log("=========================================");
console.log("[setSystem] : [iceServers] : [서버 연결 정보 확인]");
console.log("-----------------------------------------");
console.log("[스턴 : stunIceServers] : " + JSON.stringify(stunIceServers));
console.log("-----------------------------------------");
console.log("[턴 : tunIceServers] : " + JSON.stringify(tunServerList));
console.log("-----------------------------------------");
console.log("[전체 : ALL] : " + JSON.stringify(iceServers));
console.log("=========================================");
console.log("-");
// -----------------------------------------
// [RTCPeerConnection 만들기]
// -----------------------------------------
// RTCPeerConnection은 웹 에서 WebRTC 통신을 위한 기본 인터페이스입니다
// -----------------------------------------
const config = {
iceServers: iceServers
};
const peerConnection = new RTCPeerConnection(config);
// -----------------------------------------
// [WebRTC 시그널링 클라이언트 만들기]
// -----------------------------------------
// 이것은 신호 채널을 통해 메시지를 보내는 데 사용되는 실제 클라이언트입니다
// -----------------------------------------
const signalingClient = new KVSWebRTC.SignalingClient({
channelARN,
channelEndpoint: endpointsByProtocol.WSS,
role: KVSWebRTC.Role.MASTER,
region,
credentials: {
accessKeyId : accessKeyId,
secretAccessKey : secretAccessKey,
},
systemClockOffset: kinesisVideoClient.config.systemClockOffset,
});
// -----------------------------------------
// [시그널링 클라이언트 이벤트 리스너 추가]
// -----------------------------------------
signalingClient.on('open', async () => {
console.log("-");
console.log("=========================================");
console.log("[setSystem] : [로컬 <-> 원격 연결 신호 확인] : [signalingClient] : [open] : [Start]");
console.log("-----------------------------------------");
console.log("[VIEWER] Connected to signaling service");
console.log("-----------------------------------------");
console.log("[로 직] : ", "신호 채널 연결 확인 >> Viewer 의 SDP Offer 수신 대기 수행");
console.log("=========================================");
console.log("-");
const localStream = await navigator.mediaDevices.getUserMedia({ // [휴대폰 카메라 사용 권한 요청]
video: true,
audio: false,
});
remoteView.srcObject = localStream; // [비디오 뷰에 카메라 및 오디오 스트림 지정]
localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream)); // [카메라 스트림 트랙에 추가]
});
// [Viewer 의 SDP Offer 수신 대기]
signalingClient.on('sdpOffer', async (offer, remoteClientId) => {
// [peerConnection remoteClientId 지정]
peerConnection.onicecandidate = event => {
if (event.candidate) {
signalingClient.sendIceCandidate(event.candidate, remoteClientId);
}
};
console.log(">>>>>>>>>>>>>>>>>>>>>>>> [sdpOffer] : [sendIceCandidate] : [Finish] >>>>>>>>>>>>>>>>>>>>>>>>");
// [상대방 오퍼를 내 Remote 에 추가]
await peerConnection.setRemoteDescription(offer);
console.log(">>>>>>>>>>>>>>>>>>>>>>>> [sdpOffer] : [setRemoteDescription] : [Finish] >>>>>>>>>>>>>>>>>>>>>>>>");
// [Answer 옵션 지정 및 생성]
const answer = await peerConnection.createAnswer();
console.log(">>>>>>>>>>>>>>>>>>>>>>>> [sdpOffer] : [createAnswer] : [Finish] >>>>>>>>>>>>>>>>>>>>>>>>");
// [Answer 를 내 local 로 저장]
await peerConnection.setLocalDescription(answer);
console.log(">>>>>>>>>>>>>>>>>>>>>>>> [sdpOffer] : [setLocalDescription] : [Finish] >>>>>>>>>>>>>>>>>>>>>>>>");
// [SDP Answer 응답 전송]
console.log(">>>>>>>>>>>>>>>>>>>>>>>> [sdpOffer] : [sendSdpAnswer] : ["+answer.type+"] >>>>>>>>>>>>>>>>>>>>>>>>");
//signalingClient.sendSdpAnswer(answer); // [클라이언트가 응답 받지 못함]
signalingClient.sendSdpAnswer(peerConnection.localDescription, remoteClientId); // [접속 시도한 기기 아이디를 지정해 줘야함]
console.log("-");
console.log("=========================================");
console.log("[setSystem] : [로컬 <-> 원격 연결 신호 확인] : [signalingClient] : [sdpOffer] : [Start]");
console.log("-----------------------------------------");
console.log("[VIEWER] Received SDP Offer from Viewer : ", remoteClientId);
console.log("-----------------------------------------");
console.log("[설 명] : 뷰어 SDP Offer 확인 및 Answer 응답 전송 수행");
console.log("=========================================");
console.log("-");
});
// [Viewer 의 ICE Candidate 수신 대기]
signalingClient.on('iceCandidate', candidate => {
console.log(">>>>>>>>>>>>>>>>>>>>>>>> [iceCandidate] : [원격 접속 연결 시도 리스트 추가] : ["+JSON.stringify(candidate)+"] >>>>>>>>>>>>>>>>>>>>>>>>");
/*
console.log("-");
console.log("=========================================");
console.log("[setSystem] : [로컬 <-> 원격 연결 신호 확인] : [signalingClient] : [iceCandidate] : [Start]");
console.log("-----------------------------------------");
console.log("[VIEWER] Received ICE candidate : ", JSON.stringify(candidate));
console.log("-----------------------------------------");
console.log("[설 명] : 원격 접속 연결 시도 리스트 추가");
console.log("-----------------------------------------");
console.log("[로 직] : ", "peerConnection.addIceCandidate 연결에 추가");
console.log("=========================================");
console.log("-");
// */
peerConnection.addIceCandidate(candidate);
});
// [ICE 종료 핸들러]
signalingClient.on('close', () => {
console.error("-");
console.error("=========================================");
console.error("[setSystem] : [로컬 <-> 원격 연결 신호 확인] : [signalingClient] : [close] : [Start]");
console.error("-----------------------------------------");
console.error("[VIEWER] Disconnected from signaling channel");
console.error("-----------------------------------------");
console.error("[설 명] 원격 연결 신호 끊김 >> 원격 비디오 재생 종료 됨");
console.error("=========================================");
console.error("-");
});
// [ICE 에러 핸들러]
signalingClient.on('error', error => {
console.error("-");
console.error("=========================================");
console.error("[setSystem] : [로컬 <-> 원격 연결 신호 확인] : [signalingClient] : [error] : [Start]");
console.error("-----------------------------------------");
console.error("[VIEWER] Signaling client error : ", JSON.stringify(error));
console.error("=========================================");
console.error("-");
});
// -----------------------------------------
// [신호 연결 수행]
// -----------------------------------------
signalingClient.open(); // [signalingClient 열기]
}
catch (exception) {
console.error("-");
console.error("=========================================");
console.error("[setSystem] : [Exception] : 예외 상황 발생");
console.error("-----------------------------------------");
console.error("[ERROR] : ", JSON.stringify(exception.message));
console.error("=========================================");
console.error("-");
}
};
// --------------------------------------------------------------------------------------------------------------
</script>
</head>
<body>
<!-- [컨테이너 생성] -->
<div id="container_remoteView">
<video autoplay="true" controls id="remoteView"></video>
</div>
</body>
</html>
-----------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------
[참고 사이트]
-----------------------------------------------------------------------------------------
[자바스크립트 AWS WebRTC 실시간 동영상 재생 수행 - KVS Stream Video]
https://blog.naver.com/kkh0977/223170500993?trackingCode=blog_bloghome_searchlist
[Aws Kinesis Video Streams] Aws Kvs WebRTC 실시간 영상 재생 관련 구성 요소 및 용어 정리
https://blog.naver.com/kkh0977/223858189791
[Aws Security Token Service] Aws STS 임시 보안 자격 증명 설명 정리
https://blog.naver.com/kkh0977/223846461194
[Aws AssumeRole] Aws AssumeRole 역할 전환 및 임시 자격 증명 공유 사용 설명 정리
https://blog.naver.com/kkh0977/223853566319
[Aws Kinesis Video Streams] Aws Kvs 구성 요소 용어 정리 - 생산자 , 소비자 , 청크 , 조각
https://blog.naver.com/kkh0977/223858193008
-----------------------------------------------------------------------------------------
728x90
반응형