Android

안드로이드 WebRTC 시작하기 -3

park_juyoung 2018. 12. 25. 17:57

 


  p2p에서의 다음과 같은 시나리오를 생각해 봅시다.



  • 처음에 peer 연결과 offer SDP를 생성합니다. 이 offer에는 호출하는 peer에 대한 데이터가 들어 있으며 peer의 코덱 및 기타 엔터티를 식별하는데 사용됩니다.
  • 그런 다음 이 offer는 호출 peer에 "Local Description"으로 저장되고 일부 신호 메커니즘을 통해 호출 수신자에게 전송됩니다. 일반적으로 시스템의 대부분은 신호 매체로 웹 소켓을 사용하며 사용 및 요구 사항에 따라 다를 수 있습니다.
  • 수신자 측이 offer를 받아 "Remote Description"으로 저장하고 해당 offer에 대한 Answer SDP를 만듭니다. 
  • 이 Answer SDP는 호출자의 offer SDP와 비슷하지만 해당 피어의 세부정보를 가지고 있습니다.
  • 수신자 측은 Answer SDP를 "Local Description"으로 저장한다음 신호 채널을 통해 발신자에게 보냅니다.
  • 발신자는 answer를 받고 그것을 "Remote Description"으로 저장합니다.
  • 발진사와 호출원은 신호 채널을 통해 그들과 관련된 Ice Candidates를 전송합니다. 
  • Ice candidate는 네트워크 정보를 담는 데이터이다.
  • 이 ice candidate를 상대방이 받으면 상대방은 addIceCandidate 함수를 호출하여 상대방의 네트워크 정보를 추가하면 된다. 

이러한 peer간의 관계를 단일 장치에서 작동하는 모습을 코드로 설명하겠습니다.
단일 장치는 로컬 피어 및 원격 피어 모두로 작동하므로 제공 및 응답 부분을 모두 수행합니다. 실용적인 용도는 없지만,이 단계를 거치면서 어떻게 작동 하는지를 깊이 이해하는 것이 좋습니다.

public void start() {
//Initialize PeerConnectionFactory globals.
//Params are context, initAudio,initVideo and videoCodecHwAcceleration
PeerConnectionFactory.initializeAndroidGlobals(this, true, true, true);

//Create a new PeerConnectionFactory instance.
PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
peerConnectionFactory = new PeerConnectionFactory(options);

//Now create a VideoCapturer instance. Callback methods are there if you want to do something! Duh!
VideoCapturer videoCapturerAndroid = getVideoCapturer(new CustomCameraEventsHandler());

//Create MediaConstraints - Will be useful for specifying video and audio constraints.
audioConstraints = new MediaConstraints();
videoConstraints = new MediaConstraints();

//Create a VideoSource instance
videoSource = peerConnectionFactory.createVideoSource(videoCapturerAndroid, videoConstraints);
localVideoTrack = peerConnectionFactory.createVideoTrack("100", videoSource);

//create an AudioSource instance
audioSource = peerConnectionFactory.createAudioSource(audioConstraints);
localAudioTrack = peerConnectionFactory.createAudioTrack("101", audioSource);
localVideoView.setVisibility(View.VISIBLE);

//create a videoRenderer based on SurfaceViewRenderer instance
localRenderer = new VideoRenderer(localVideoView);
// And finally, with our VideoRenderer ready, we
// can add our renderer to the VideoTrack.
localVideoTrack.addRenderer(localRenderer);
}

private void call() {
//we already have video and audio tracks. Now create peerconnections
List<PeerConnection.IceServer> iceServers = new ArrayList<>();

//create sdpConstraints
sdpConstraints = new MediaConstraints();
sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveAudio", "true"));
sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveVideo", "true"));

//creating localPeer
localPeer = peerConnectionFactory.createPeerConnection(iceServers, sdpConstraints, new CustomPeerConnectionObserver("localPeerCreation") {
@Override
public void onIceCandidate(IceCandidate iceCandidate) {
super.onIceCandidate(iceCandidate);
onIceCandidateReceived(localPeer, iceCandidate);
}
});

//creating remotePeer
remotePeer = peerConnectionFactory.createPeerConnection(iceServers, sdpConstraints, new CustomPeerConnectionObserver("remotePeerCreation") {

@Override
public void onIceCandidate(IceCandidate iceCandidate) {
super.onIceCandidate(iceCandidate);
onIceCandidateReceived(remotePeer, iceCandidate);
}

@Override
public void onAddStream(MediaStream mediaStream) {
super.onAddStream(mediaStream);
gotRemoteStream(mediaStream);
}
});

//creating local mediastream
MediaStream stream = peerConnectionFactory.createLocalMediaStream("102");
stream.addTrack(localAudioTrack);
stream.addTrack(localVideoTrack);
localPeer.addStream(stream);

//creating Offer
localPeer.createOffer(new CustomSdpObserver("localCreateOffer"){
@Override
public void onCreateSuccess(SessionDescription sessionDescription) {
//we have localOffer. Set it as local desc for localpeer and remote desc for remote peer.
//try to create answer from the remote peer.
super.onCreateSuccess(sessionDescription);
localPeer.setLocalDescription(new CustomSdpObserver("localSetLocalDesc"), sessionDescription);
remotePeer.setRemoteDescription(new CustomSdpObserver("remoteSetRemoteDesc"), sessionDescription);
remotePeer.createAnswer(new CustomSdpObserver("remoteCreateOffer") {
@Override
public void onCreateSuccess(SessionDescription sessionDescription) {
//remote answer generated. Now set it as local desc for remote peer and remote desc for local peer.
super.onCreateSuccess(sessionDescription);
remotePeer.setLocalDescription(new CustomSdpObserver("remoteSetLocalDesc"), sessionDescription);
localPeer.setRemoteDescription(new CustomSdpObserver("localSetRemoteDesc"), sessionDescription);
}
},new MediaConstraints());
}
},sdpConstraints);
}


private void hangup() {
localPeer.close();
remotePeer.close();
localPeer = null;
remotePeer = null;
}

private void gotRemoteStream(MediaStream stream) {
//we have remote video stream. add to the renderer.
final VideoTrack videoTrack = stream.videoTracks.getFirst();
AudioTrack audioTrack = stream.audioTracks.getFirst();
runOnUiThread(new Runnable() {
@Override
public void run() {
try {
remoteRenderer = new VideoRenderer(remoteVideoView);
remoteVideoView.setVisibility(View.VISIBLE);
videoTrack.addRenderer(remoteRenderer);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}

public void onIceCandidateReceived(PeerConnection peer, IceCandidate iceCandidate) {
//we have received ice candidate. We can set it to the other peer.
if (peer == localPeer) {
remotePeer.addIceCandidate(iceCandidate);
} else {
localPeer.addIceCandidate(iceCandidate);
}
}
위의 코드를 살펴보면 3가지 방법이 있습니다.

start() 메서드는 기본적으로 로컬 오디오 및 비디오 소스를 만들고이를 SurfaceViewRenderer에 추가합니다.이 메서드는 WebRTC에서 제공하므로 비디오 프레임을이 뷰에 직접 그릴 수 있습니다.

hangup()메서드는 모든 PeerConnection 인스턴스를 지우는 단순한 코드 입니다.

 

call() 두 개의 피어 연결 인스턴스를 만듭니다 (하나는 로컬 피어에 대한 인스턴스이고 다른 하나는 원격 피어에 대한 인스턴스). 피어가 생성되면 로컬 피어가 해당 로컬 설명과 원격 피어의 원격 설명으로 설정된 제안을 만듭니다.

그런 다음 원격 피어가 로컬 설명과 로컬 피어의 원격 설명으로 설정된 응답을 생성하도록 만듭니다.


 onIceCandidateReceived() 이 메소드는 한 피어에서받은 Ice 후보를 다른 피어로 설정하는 것입니다.


이렇게 작성하면 첫 번째 부분에서했던 것처럼 화면상에 카메라의 영상을 볼 수 있습니다. 이번에는 앱이 피어 연결을 통해 영상을 보여줍니다.



이것으로 기본적인 설명을 하였습니다. 다음 글에서는 p2p를 지원하는 화상 통화를 설명하겠습니다.

'Android' 카테고리의 다른 글

안드로이드 4대 컴포넌트란 무엇인가?  (0) 2019.01.09
Parcelable vs Serializable  (0) 2019.01.09
안드로이드 WebRtc 시작하기 -2  (0) 2018.12.25
안드로이드 WebRTC 시작하기  (1) 2018.12.24
AsyncTask를 사용해보자  (0) 2018.11.20