👊 Setting 
implementation  ' org . webrtc : google - webrtc: 1.0 .+ ' 
 
로 추가 가능하나, 블로그 대부분의 예시가 arr파일로 되어있어서 나도 arr 파일을 사용하였다 😎
  아래 코드는 audio를 가져오는 코드로, video도 생성하고 싶은 경우 VideoTrack을 이용하면 쉽게 구현할 수 있다. 
 
1. WebRTC 세팅 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 < uses - feature  android: name = "android.hardware.camera"  /> 
< uses - feature  android: name = "android.hardware.camera.autofocus"  /> 
< uses - feature 
    android: glEsVersion = "0x00020000" 
    android: required = "true"  /> 
< uses - permission  android: name = "android.permission.INTERNET"  /> 
< uses - permission  android: name = "android.permission.ACCESS_NETWORK_STATE"  /> 
< uses - permission  android: name = "android.permission.CHANGE_NETWORK_STATE"  /> 
< uses - permission  android: name = "android.permission.READ_EXTERNAL_STORAGE"  /> 
< uses - permission  android: name = "android.permission.WRITE_EXTERNAL_STORAGE"  /> 
< uses - permission  android: name = "android.permission.RECORD_AUDIO"  /> 
< uses - permission  android: name = "android.permission.MODIFY_AUDIO_SETTINGS"  /> 
< uses - permission  android: name = "android.permission.CAMERA"  /> 
 
  먼저, 필요한 permission을 AndroiMenifest.xml에 추가한다. 
  다음으로, WebRTC에 필요한 생성 및 초기화를 진행한다. 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 import  java.util.* ; 
/**
 *
 * @author HEESOO
 *
 */ 
public  void  initWebRTC (){ 
    PeerConnectionFactory . initializeAndroidGlobals ( this ,  true ,  false ,  true ); 
    PeerConnectionFactory . Options  options  =  new  PeerConnectionFactory . Options (); 
    peerConnectionFactory  =  new  PeerConnectionFactory ( options ); 
    audioConstraints  =  new  MediaConstraints (); 
    audioSource  =  peerConnectionFactory . createAudioSource ( audioConstraints ); 
    localAudioTrack  =  peerConnectionFactory . createAudioTrack ( "101" ,  audioSource ); 
    localAudioTrack . setEnabled ( true ); 
    sdpConstraints  =  new  MediaConstraints (); 
    sdpConstraints . mandatory . add ( new  MediaConstraints . KeyValuePair ( "offerToReceiveAudio" ,  "true" )); 
    sdpConstraints . mandatory . add ( new  MediaConstraints . KeyValuePair ( "offerToReceiveVideo" ,  "false" ));  
    stream  =  peerConnectionFactory . createLocalMediaStream ( "102" ); 
    stream . addTrack ( localAudioTrack ); 
} 
 
  audioSource를 생성하기 위해 PeerConnectionFactory을 생성 및 초기화한다. 
  audioConstraints를 통해 audio를 생성하고, localAudioTrack으로 audio를 setEnable한다. 
  sdpConstraints는 SDP 정보를 생성하는 것으로, 연결하고자 하는 Peer간의 미디어와 네트워크에 관한 정보를 이해하기 위해 사용된다. 
  마지막으로 stream을 통해 생성한 audioTrack을 넣으면 WebRTC 초기 세팅이 끝난다. 
 
2. SDP와 IceCandidate 전달 
  두 Peer가 서버와 연결되면, 같은 dest를 가지고 있는 Peer들은 sdp와 candidate를 교환해야 한다. 
  SDP(Session Description Protocol)은 미디어 정보를 서로 교환한다. 
  sdp는 (연결하고자 하는) A가 만든 sdp(createOffer())와, 상대방(A)의 sdp를 받고 제대로 받았음을 send할 B의 sdp(createAnswer())가 필요하다. 
 
createOffer() 
1
2
3
4
5
6
7
8
9
10
11
12
13
 public  void  setPeer (){ 
    ... 
    if ( initCall )  { 
            newPeer . createOffer ( new  CustomSdpObserver ( "newPeerCreateOffer" )  { 
                @Override 
                public  void  onCreateSuccess ( SessionDescription  sessionDescription )  { 
                    super . onCreateSuccess ( sessionDescription ); 
                    createdDescription ( sessionDescription ,  peerUuid ); 
                } 
            },  sdpConstraints ); 
        } 
    ... 
} 
 
  createOffer()로 sdp를 생성하면, onCreateSuccess()의 파라미터 sessionDescription으로 생성된 sdp를 확인할 수 있다. 이제 생성된 sdp를 서버로 전송해주면 된다. 
 
1
2
3
4
5
6
7
8
9
10
 public  void  createdDescription ( SessionDescription  sessionDescription ,  String  peerUuid ){ 
    Log . i ( TAG ,  "createdDescription" ); 
    CustomPeerConnection  tempPeer =  peerMap . get ( peerUuid ); 
    tempPeer . pc . setLocalDescription ( new  CustomSdpObserver ( "createdDescription" ),  sessionDescription ); 
    /**
      * createOffer()한 sdp를 서버로 전송
      */ 
} 
 
createAnswer() 
1
2
3
4
5
6
7
8
 tempPeer . pc . createAnswer ( new  CustomSdpObserver ( "offer createAnswer" )  { 
    @Override 
    public  void  onCreateSuccess ( SessionDescription  sessionDescription )  { 
        super . onCreateSuccess ( sessionDescription ); 
        tempPeer . pc . setLocalDescription ( new  CustomSdpObserver ( "offer setLocal" ),  sessionDescription ); 
        createdDescription ( sessionDescription ,  peerUuid ); 
    } 
},  new  MediaConstraints ()); 
 
  상대방의 sdp를 제대로 받았음을 전송하는 sdp이다. 
  sdp 교환이 끝나면, Ice Candidates를 교환한다. 
 
onIceCandidate() 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 PeerConnection  newPeer = peerConnectionFactory . createPeerConnection ( iceServerList ,  sdpConstraints , 
    new  CustomPeerConnectionObserver ( "newPeerCreation" )  { 
        @Override 
        public  void  onIceCandidate ( IceCandidate  iceCandidate )  { 
            Log . i ( TAG ,  "onIceCandidate" ); 
            super . onIceCandidate ( iceCandidate ); 
            /**
                * 생성된 iceCandidate(파라미터 값)을 서버로 전송
                */ 
            } 
        } 
        @Override 
        public  void  onAddStream ( MediaStream  mediaStream )  { 
            super . onAddStream ( mediaStream ); 
            gotRemoteStream ( mediaStream ); 
        } 
    } 
}); 
newPeer . addStream ( stream ); 
        
 
  onAddStream이 호출되면 Peer 연결에 성공했다는 뜻이다. 
  A와 B가 iceCandidate 교환에 성공하면, connection되어 audio 전송이 가능해진다. 
 
4. 참고 
CustomSdpObserver.java 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 class  CustomSdpObserver  implements  SdpObserver  { 
    private  String  tag  =  this . getClass (). getCanonicalName (); 
    CustomSdpObserver ( String  logTag )  { 
        this . tag  =  this . tag  +  " "  +  logTag ; 
    } 
    @Override 
    public  void  onCreateSuccess ( SessionDescription  sessionDescription )  { 
        Log . d ( tag ,  "onCreateSuccess() called with: sessionDescription = ["  +  sessionDescription  +  "]" ); 
    } 
    @Override 
    public  void  onSetSuccess ()  { 
        Log . d ( tag ,  "onSetSuccess() called" ); 
    } 
    @Override 
    public  void  onCreateFailure ( String  s )  { 
        Log . d ( tag ,  "onCreateFailure() called with: s = ["  +  s  +  "]" ); 
    } 
    @Override 
    public  void  onSetFailure ( String  s )  { 
        Log . d ( tag ,  "onSetFailure() called with: s = ["  +  s  +  "]" ); 
    } 
} 
 
  CustomSdpObserver는 SdpObserver를 implement하여, SDP와 관련된 이벤트들을 체크한다. 
  SdpObserver가 필요한 곳에 override method를 다 써줄 순 없으니까 Custom class를 하나 생성하여, 필요한 메소드가 있을 경우 override해준다. 
 
음량 조절 기능 
1
2
3
4
5
6
7
8
9
10
 public  void  muteAudio ( boolean  action ){ 
    Log . i ( TAG ,  "muteAudio" ); 
    stream . audioTracks . get ( 0 ). setEnabled ( action ); 
} 
public  void  setVolume ( long  uid ,  int  volume ){ 
    Log . i ( TAG ,  "setVolume " + uid + " " + volume ); 
    audioMap . get ( uid ). setVolume (( double ) volume ); 
} 
 
  audio, video는 MediaStream에서 관리한다. 
  
    initWebRTC()에서 audio, video를 세팅해주고 addTrack()으로 나의 audio 데이터를 담았다.
   
  참고로, MediaStream에는 audioTracks와 videoTracks가 존재한다(둘다 LinkedList). 
  그래서 내 audio는 media.audioTrack.get(0)이 된다. 
  
    음소거 기능은 setEnabled()로 관리하며, false가 음소거이다.
   
  audio는 LinkedList형태로 peer들이 들어올 때마다 뒤에서 삽입되므로 해당 인덱스를 찾는다면 특정 peer의 음량을 조절할 수 있다.