투케이2K

201. (TWOK/WORK) [업무 이슈] AWS WebRTC 뷰어 실시간 영상 재생 중 네트워크 환경 변경 (Wifi To LTE) 으로 인한 영상 재생 중지 이슈 본문

투케이2K 업무정리

201. (TWOK/WORK) [업무 이슈] AWS WebRTC 뷰어 실시간 영상 재생 중 네트워크 환경 변경 (Wifi To LTE) 으로 인한 영상 재생 중지 이슈

투케이2K 2026. 1. 22. 19:25
728x90

[제 목]

타이틀 : 투케이 / 2k / 업무 정리

제목 : [업무 이슈] AWS WebRTC 뷰어 실시간 영상 재생 중 네트워크 환경 변경 (Wifi To LTE) 으로 인한 영상 재생 중지 이슈

 

[내 용]

------------------------------------------------------------------------------
[개발 및 테스트 환경]
------------------------------------------------------------------------------

- 제 목 : [업무 이슈] AWS WebRTC 뷰어 실시간 영상 재생 중 네트워크 환경 변경 (Wifi To LTE) 으로 인한 영상 재생 중지 이슈


- 테스트 환경 : AWS / KVS / WebRTC / Viewer


- 사전) WebRTC 설명 : 

  >> WebRTC 란 웹, 애플리케이션, 디바이스 간 중간자 없이 오디오나 영상 미디어를 포착하고 실시간 스트림할 뿐 아니라, 임의의 데이터도 교환할 수 있도록 하는 기술입니다

  >> WebRTC 는 간단한 API 를 통해 웹 브라우저, 모바일 애플리케이션 및 커넥티드 디바이스 간에 실시간 통신을 활성화할 수 있습니다

  >> WebRTC 주요 용어 : 

    - SDP (Session Description Protocol) : 오디오/비디오 코덱, 해상도, 포트 등 스트리밍 정보를 담은 텍스트 포맷
    - Offer / Answer : 통신 연결을 협상하기 위한 SDP 메시지 (초기 연결 설정)
    - ICE (Interactive Connectivity Establishment) : NAT/P2P 환경에서도 연결 가능한 경로(IP, 포트 등)를 찾기 위한 기술
    - Candidate : 가능한 연결 경로 (IP + Port 조합)

  >> WebRTC SDP 오퍼 생성 (뷰어) 및 응답 (마스터) 스트리밍 플로우 : 

    [Viewer → Signaling Server] -- SDP Offer --> [Master] : 뷰어는 마스터로 스트리밍 오퍼 신호 보낸다
    [Master] -- SDP Answer --> [Viewer] : 마스터는 특정 뷰어의 오퍼 신호 확인 후 응답을 보낸다

    [Viewer] -- ICE Candidate --> [Master] : 스트리밍을 할 수 있는 경로 확인
    [Master] -- ICE Candidate --> [Viewer] : 스트리밍을 할 수 있는 경로 확인

    P2P 연결 성립 → 스트리밍 시작

------------------------------------------------------------------------------





------------------------------------------------------------------------------
[이슈 사항]
------------------------------------------------------------------------------

1. WebRTC 뷰어 역할로 실시간 영상 확인 중 휴대폰 네트워크 환경이 변경 되어 영상 시청이 일시 중지 된다는 이슈 문의


2. 이슈 발생 환경 : 

  >> 사용자는 집안에서 와이파이를 사용해 WebRTC 영상 시청 중 인 상태

  >> 일정이 생겨 현관문을 열고 외부로 이동 수행

  >> 재생 중이던 WebRTC 영상이 일시 중지 된다 문의


3. 네트워크가 Wi‑Fi → LTE/5G 등으로 변경 될 시 참고 사항 : 

  >> 기존 WebRTC 피어 연결은 끊어지며, 'ICE 재시작(ICE restart)' 을 통해 재협상 필요

  >> 네트워크 인터페이스가 바뀌면 로컬 IP/포트가 달라지고, NAT 매핑도 바뀌어 기존 후보(pair)가 유효하지 않게 됩니다

    - ICE 재시작으로 새로운 ufrag/pwd를 가진 ICE 세션을 만들고, 새 후보들을 재수집해서 상대와 다시 연결 경로를 찾아야 합니다

------------------------------------------------------------------------------





------------------------------------------------------------------------------
[원인 파악 및 증상 재현]
------------------------------------------------------------------------------

1. WIFI 가 연결 된 상태에서 WebRTC 뷰어 연결 수행


2. 정상적으로 WebRTC 연결 완료 후 영상이 출력되는 것 확인


3. 휴대폰 상단 메뉴 스크롤을 내려 와이파이 기능 비활성 수행


4. 시청 중이던 영상이 일시 중지 되는 것 확인


5. ✅ 참고 사항 : 마스터 입장에서 뷰어가 네트워크 환경이 전환 될 시 응답 되는 이벤트 콜백

  >> 대부분의 경우 : 마스터는 "뷰어가 잠시 끊김(disconnected)"으로만 감지

    - iceConnectionState = "disconnected" : 잠시 연결이 불안정해진 상태
    
    - 이후 뷰어가 ICE restart를 하면 다시 : "checking" → "connected" 로 회복 가능

  >> 뷰어가 ICE 재시작을 하지 않으면 마스터는 일정 시간이 지나 iceConnectionState = "failed" 또는 "closed" 상태로 넘어가며, 이때 비로소 뷰어가 끊긴 것처럼 인식하게 됩니다.

------------------------------------------------------------------------------






------------------------------------------------------------------------------
[조치 내용]
------------------------------------------------------------------------------

1. 휴대폰에서 WebRTC 영상 재생 시 연결이 완료 된 이후 일정 주기 (ex : 5초) 마다 네트워크 연결 상태 체크 로직 적용


2. 일정 주기 마다 네트워크 연결 상태를 체크해 > 네트워크 연결 상태가 변경 된 경우 알림 팝업창 노출

  >> '사용자 네트워크 상태가 변경되었습니다. 영상 시청을 재접속해주세요.'


3. ✅ 사용자가 [재접속] 버튼을 클릭한 경우 WebRTC 관련 객체 전체 초기화 후 다시 재접속 시도 수행

    function closeConnection(){

        try{

            // -----------------------------------------
            // [로컬 스트림 종료 처리 수행]
            // -----------------------------------------
            localStream.getTracks().forEach(track => track.stop());

        }
        catch (exception) {
            console.error("[ERROR] : ", exception.message);
        }


        try{

            // -----------------------------------------
            // [signalingClient close : ICE candidate 교환이나 SDP 메시지 전송이 중단]
            // -----------------------------------------

            if (signalingClient != null){
                signalingClient.close();                    
            }
        }
        catch (exception) {
            console.error("[ERROR] : ", exception.message);
        }


        try{

            // -----------------------------------------
            // [peerConnection removeTrack 처리 수행]
            // -----------------------------------------

            if (peerConnection != null){

                const transceivers = peerConnection.getTransceivers?.() || [];

                transceivers.forEach(t => {

                    try { if (typeof t.stop === 'function') t.stop(); } catch (e) { console.warn('[cleanup] transceiver.stop error:', e); }

                });

                const senders = peerConnection.getSenders?.() || [];

                senders.forEach(s => {

                    try { peerConnection.removeTrack(s); } catch (e) { console.warn('[cleanup] removeTrack error:', e); }

                });

            }
        }
        catch (exception) {
            console.error("[ERROR] : ", exception.message);
        }


        try{

            // -----------------------------------------
            // [peerConnection 연결 종료 처리 수행]
            // -----------------------------------------

            if (peerConnection != null){
                peerConnection.close(); // 미디어 스트림 종료
            }
        }
        catch (exception) {
            console.error("[ERROR] : ", exception.message);
        }


        try{

            // -----------------------------------------
            // [객체 null 초기화 수행]
            // -----------------------------------------

            signalingClient = null;

            peerConnection = null;

            localStream = null; // 양방향 통신 위해 초기화 중요

        }
        catch (exception) {
            console.error("[ERROR] : ", exception.message);
        }

    };
 
------------------------------------------------------------------------------





------------------------------------------------------------------------------
[참고 사이트]
------------------------------------------------------------------------------

[업무 이슈] AWS WebRTC 마스터 SDK 초기화 시 STS 임시 자격 증명 UserId 값에 특수 문자 포함으로 인증 에러 발생 이슈

https://kkh0977.tistory.com/8565

https://blog.naver.com/kkh0977/224145469350


[업무 이슈] AWS WebRTC 마스터 GetIceServerConfig ICE 서버 사용 유효 기간 만료 TURN 릴레이 서버 연결 불가능 이슈

https://kkh0977.tistory.com/8564


[업무 이슈] AWS WebRTC 뷰어에서 동일한 clientId 사용해 스트리밍 시청 시 AWS 세션 초기화 이슈로 60 초내외 재사용 필요 이슈

https://kkh0977.tistory.com/8442

https://blog.naver.com/kkh0977/224094606649


[Aws Kvs WebRTC 실시간 영상 재생 관련 구성 요소 및 용어 정리]

https://blog.naver.com/kkh0977/223858189791


[Aws Kinesis Video Streams] WebRTC remote sender clientId 클라이언트 아이디 설명, 규격 및 제한 정리

https://kkh0977.tistory.com/8415

https://blog.naver.com/kkh0977/224083731976


[Aws Kinesis Video Streams] WebRTC SDP 협상 과정 프로세스 정리 정리

https://blog.naver.com/kkh0977/224030054470

------------------------------------------------------------------------------
 
728x90
반응형
Comments