https://cornbro.tistory.com/3에서 이어지는 내용입니다.

 

먼저 RTCPeerConnection은 비디오 및 오디오 데이터 교환을 위한 API입니다.

해당 예제는 실제 외부접속은 아니며 PeerConnection API 작동 방식을 이해하기위한 단순 로컬 예제입니다.

 

work/index.html에 다음과 같이 내용을 추가합니다.

<!DOCTYPE html>
<html>

<head>

  <title>Realtime communication with WebRTC</title>

  <link rel="stylesheet" href="css/main.css" />

</head>

<body>

  <h1>Realtime communication with WebRTC</h1>
  
    <video id="localVideo" autoplay playsinline></video>
	<video id="remoteVideo" autoplay playsinline></video>
	
	<div>
		<button id="startButton">Start</button>
		<button id="callButton">Call</Button>
		<button id="hangupButton">Hang Up</button>
		<button id="stopButton">Stop</button>
	</div>	

  <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
  <script src="js/main.js"></script>

</body>

</html>

2개의 비디오 구성과 4개의 컨트롤 버튼을 추가하였습니다.

 "localVideo"는 getUserMedia()를 통해 비디오 스트림을 가져오고,  "remoteVideo"는 RTCPeerConnection통해 동일한 비디오 스트림을 가져옵니다.

 그리고 최신 버전의 adapter.js shim 링크를 연결해줍니다.

더보기

 adapter.js : 규격화를 통해 대부분의 프로토콜은 동일하게 구성되었지만 다른 브라우져간에 생기는 일부 다른 부분(ex. prefix)을 호환 시켜주기 위해 사용하게 됩니다.

step-2 폴더에 있는 main.js를 work 폴더로 옮겨서 열어봅니다.

개인적으로 로컬 스트림도 닫을수 있게 stop버튼을 추가했습니다.

 

변수 생성 및 상태 로그 메세지에 대한 내용은 넘어가도록 하겠습니다.

 

start 버튼에서 로컬 미디어 스트림을 가져옵니다. => gotLocalMediaStream()

// Handles start button action: creates local MediaStream.
function startAction() {
  startButton.disabled = true;
  navigator.mediaDevices.getUserMedia(mediaStreamConstraints)
    .then(gotLocalMediaStream).catch(handleLocalMediaStreamError);
  trace('Requesting local stream.');
}

//--------------------------------------------------------------------------------
      // Sets the MediaStream as the video element src.
      function gotLocalMediaStream(mediaStream) {
        localVideo.srcObject = mediaStream;
        localStream = mediaStream;
        trace('Received local stream.');
        callButton.disabled = false;  // Enable call button.
        stopButton.disabled = false;
      }
//--------------------------------------------------------------------------------

call 버튼에서 Peer Connection을 통해 end to end 연결을 하고 => handleConnection()

로컬에서 원격으로 비디오 스트림을 가져옵니다. => gotRemoteMediaStream()

실제에서는 메시징 서비스를 통해 외부로 연결되겠지만, 해당 스텝에서는 같은 페이지에서 직접 통신을 합니다.

WebRTC Peer는 해상도 및 코덱 기능과 같은 미디어 정보를 교환해야 합니다.

SDP 프로토콜의 Offer<->Answer 를 통해 메타 데이터의 Blob을 교환합니다. => createdOffer/createdAnswer

// Handles call button action: creates peer connection.
function callAction() {
  callButton.disabled = true;
  hangupButton.disabled = false;

  trace('Starting call.');
  startTime = window.performance.now();

  // Get local media stream tracks.
  const videoTracks = localStream.getVideoTracks();
  const audioTracks = localStream.getAudioTracks();
  if (videoTracks.length > 0) {
    trace(`Using video device: ${videoTracks[0].label}.`);
  }
  if (audioTracks.length > 0) {
    trace(`Using audio device: ${audioTracks[0].label}.`);
  }

  const servers = null;  // Allows for RTC server configuration.

  // Create peer connections and add behavior.
  localPeerConnection = new RTCPeerConnection(servers);
  trace('Created local peer connection object localPeerConnection.');

  localPeerConnection.addEventListener('icecandidate', handleConnection);
  localPeerConnection.addEventListener(
    'iceconnectionstatechange', handleConnectionChange);
  //---------------------------------------------------------------------    
        // Connects with new peer candidate.
        function handleConnection(event) {
          const peerConnection = event.target;
          const iceCandidate = event.candidate;

          if (iceCandidate) {
            const newIceCandidate = new RTCIceCandidate(iceCandidate);
            const otherPeer = getOtherPeer(peerConnection);

            otherPeer.addIceCandidate(newIceCandidate)
                .then(() => {
                	handleConnectionSuccess(peerConnection);
                }).catch((error) => {
                	handleConnectionFailure(peerConnection, error);
                });

            trace(`${getPeerName(peerConnection)} ICE candidate:\n` +
            	`${event.candidate.candidate}.`);
            }
        }
        
        // Gets the "other" peer connection.
        function getOtherPeer(peerConnection) {
          return (peerConnection === localPeerConnection) ?
              remotePeerConnection : localPeerConnection;
        }
        
        // Logs changes to the connection state.
        function handleConnectionChange(event) {
            const peerConnection = event.target;
            console.log('ICE state change event: ', event);
            trace(`${getPeerName(peerConnection)} ICE state: ` +
                  `${peerConnection.iceConnectionState}.`);
        }
  //---------------------------------------------------------------------    

  remotePeerConnection = new RTCPeerConnection(servers);
  trace('Created remote peer connection object remotePeerConnection.');

  remotePeerConnection.addEventListener('icecandidate', handleConnection);
  remotePeerConnection.addEventListener(
    'iceconnectionstatechange', handleConnectionChange);
  remotePeerConnection.addEventListener('addstream', gotRemoteMediaStream);
  
  //--------------------------------------------------------------------- 
  		// Handles remote MediaStream success by adding it as the remoteVideo src.
        function gotRemoteMediaStream(event) {
          const mediaStream = event.stream;
          remoteVideo.srcObject = mediaStream;
          remoteStream = mediaStream;
          trace('Remote peer connection received remote stream.');
        }
  //--------------------------------------------------------------------- 
  
  // Add local stream to connection and create offer to connect.
  localPeerConnection.addStream(localStream);
  trace('Added local stream to localPeerConnection.');

  trace('localPeerConnection createOffer start.');
  localPeerConnection.createOffer(offerOptions)
    .then(createdOffer).catch(setSessionDescriptionError);
    
  //--------------------------------------------------------------------- 
        // Logs offer creation and sets peer connection session descriptions.
        function createdOffer(description) {
          trace(`Offer from localPeerConnection:\n${description.sdp}`);

          trace('localPeerConnection setLocalDescription start.');
          localPeerConnection.setLocalDescription(description)
            .then(() => {
              setLocalDescriptionSuccess(localPeerConnection);
            }).catch(setSessionDescriptionError);

          trace('remotePeerConnection setRemoteDescription start.');
          remotePeerConnection.setRemoteDescription(description)
            .then(() => {
              setRemoteDescriptionSuccess(remotePeerConnection);
            }).catch(setSessionDescriptionError);

          trace('remotePeerConnection createAnswer start.');
          remotePeerConnection.createAnswer()
              .then(createdAnswer)
              .catch(setSessionDescriptionError);
        }
        
        // Logs answer to offer creation and sets peer connection session descriptions.
        function createdAnswer(description) {
          trace(`Answer from remotePeerConnection:\n${description.sdp}.`);

          trace('remotePeerConnection setLocalDescription start.');
          remotePeerConnection.setLocalDescription(description)
            .then(() => {
              setLocalDescriptionSuccess(remotePeerConnection);
            }).catch(setSessionDescriptionError);

          trace('localPeerConnection setRemoteDescription start.');
          localPeerConnection.setRemoteDescription(description)
            .then(() => {
              setRemoteDescriptionSuccess(localPeerConnection);
            }).catch(setSessionDescriptionError);
        }
  //--------------------------------------------------------------------- 
}

hangup 버튼에서 Peer Connection 연결을 닫아줍니다.

// Handles hangup action: ends up call, closes connections and resets peers.
function hangupAction() {
  localPeerConnection.close();
  remotePeerConnection.close();
  localPeerConnection = null;
  remotePeerConnection = null;
  hangupButton.disabled = true;
  callButton.disabled = false;
  trace('Ending call.');
}

stop 버튼에서 로컬 스트림을 닫아줍니다.

function stopAction() {
	if (hangupButton.disabled == false){
		hangupAction();
	}
	startButton.disabled = false;
	callButton.disabled = true;
	stopButton.disabled = true;
	
	localStream.getTracks().forEach((track) => {
		track.stop();
	});
}

 


파일 다운받아서 실행하면 화면처럼 똑같이 실행됩니다.

step_2.zip
0.02MB

 

 

'WebRTC' 카테고리의 다른 글

[WebRTC] 웹캠 영상 단순출력 예제  (0) 2020.01.12
[WebRTC] 개념정리  (1) 2020.01.07

+ Recent posts