[webRTC for android vol4] 複数台接続

Android で複数台接続いきます。

- 1:1はこちら → [webRTC for android vol3 trickle ICE](http://qiita.com/nakadoribooks/items/d30fdc5585657dcd8420)
- web版はこちら → [webRTC for web vol2 複数人での接続](http://qiita.com/nakadoribooks/items/d8a55811fe0b51b8401f)
- iOS版はこちら　→ [webRTC for ios vol4 複数台接続](http://qiita.com/nakadoribooks/items/243a98610c31730a8e90)

# 成果物

https://github.com/nakadoribooks/webrtc-android/releases/tag/v0.0.4

<img src="https://j.gifs.com/58mjx8.gif" height="500" />

↑ iOSとブラウザが相手。

# 環境

- android studio 2.3.3
- Nexus 5x android6.0.1


# 流れ

<img src="https://qiita-image-store.s3.amazonaws.com/0/159266/44d9f952-34fd-9460-07c4-0175a263a3ac.png" />

# 設計

![IMG_4767.JPG](https://qiita-image-store.s3.amazonaws.com/0/159266/39377489-4d14-4511-9cef-f8e2157a1411.jpeg)

# インタフェース

## Wamp

```java:WampInterface.java
enum Topic {

    Callme("com.nakadoribook.webrtc.[roomId].callme"),
    Close("com.nakadoribook.webrtc.[roomId].close"),
    Answer("com.nakadoribook.webrtc.[roomId].[userId].answer"),
    Offer("com.nakadoribook.webrtc.[roomId].[userId].offer"),
    Candidate("com.nakadoribook.webrtc.[roomId].[userId].candidate");

    private final String text;

    private Topic(final String text) {
        this.text = text;
    }

    public String getString() {
        return this.text;
    }
}

interface WampCallbacks {

    void onOpen();
    void onReceiveAnswer(String targetId, String sdp);
    void onReceiveOffer(String taretId, String sdp);
    void onIceCandidate(String targetId, String sdp, String sdpMid, int sdpMLineIndex);
    void onReceiveCallme(String targetId);
    void onCloseConnection(String targetId);

}

public interface WampInterface {

    void connect();
    void publishCallme();
    void publishOffer(String targetId, String sdp);
    void publishAnswer(String targetId, String sdp);
    void publishCandidate(String targetId, String candidate);

}
```

## WebRTC

```java:WebRTCInterface.java
interface WebRTCCallbacks{

    void onCreateOffer(String sdp);
    void onCreateAnswer(String sdp);
    void didReceiveRemoteStream(MediaStream mediaStream);
    void onIceCandidate(String sdp, String sdpMid, int sdpMLineIndex);

}

public interface WebRTCInterface {

    void createOffer();
    void receiveOffer(String sdp);
    void receiveAnswer(String sdp);
    void receiveCandidate(String sdp, String sdpMid, int sdpMLineIndex);
    void close();

}

```

## Connection

```java:ConnectionInterface.java
interface ConnectionCallbacks{

    void onAddedStream(MediaStream mediaStream);

}

public interface ConnectionInterface {

    String targetId();
    void publishOffer();
    void receiveOffer(String sdp);
    void receiveAnswer(String sdp);
    void receiveCandidate(String candidate, String sdpMid, int sdpMLineIndex);
    void close();

}
```


# 実装

## App(MainActivity.java)

### WampTopic監視

Wamp.callback → App の部分。
自分のuserId のトピックを監視する。

- openしたタイミングで　roomIDのみんなにCallme を送る
- その他はtargetIdから対象のConnection探してそのまま情報を流す

receiveOffer と、 receiveCallme のところで、Connectionを作る。

```java:MainActivity.java
private void setupWamp(){
    // wamp
    String roomId = "abcdef"; // とりあえず固定
    wamp = new Wamp(this, roomId, userId, new WampCallbacks() {
        @Override
        public void onOpen() {
            wamp.publishCallme();
        }

        @Override
        public void onReceiveAnswer(String targetId, String sdp) {

            Connection connection = findConnection(targetId);
            if(connection == null){
                Log.d("onReceiveAnswer", "not found connection");
                return;
            }

            connection.receiveAnswer(sdp);
        }

        @Override
        public void onReceiveOffer(String targetId, String sdp) {
            Connection connection = createConnection(targetId);
            connection.receiveOffer(sdp);
        }

        @Override
        public void onIceCandidate(String targetId, String candidate, String sdpMid, int sdpMLineIndex) {
            Connection connection = createConnection(targetId);
            connection.receiveCandidate(candidate, sdpMid, sdpMLineIndex);
        }

        @Override
        public void onReceiveCallme(String targetId) {
            Connection connection = createConnection(targetId);
            connection.publishOffer();
        }

        @Override
        public void onCloseConnection(String targetId) {

        }
    });
}

```

### 相手のストリームを表示

Connection.callback → app の部分。
相手のストリームをもらったらViewを作って表示する


```java:MainActivity.java
Connection connection = new Connection(userId, targetId, wamp, new ConnectionCallbacks() {
    @Override
    public void onAddedStream(MediaStream mediaStream) {

        final VideoTrack remoteVideoTrack = mediaStream.videoTracks.getFirst();

        MainActivity.this.runOnUiThread(new Runnable() {
            @Override
            public void run() {

                SurfaceViewRenderer remoteRenderer = new SurfaceViewRenderer(MainActivity.this);

                // 〜 略  〜 

                GridLayout remoteViewContainer = (GridLayout) MainActivity.this.findViewById(R.id.remote_view_container);
                remoteViewContainer.addView(remoteRenderer);

            }
        });
    }
});

```

## Connection

### webrtcのシグナリング

WebRTC.callback → Connection (→　Wamp) の部分。
Offer/Answer/Candidate がWebRTCから出てくるので、それをWampでpublishして相手に渡す.

```java:Connection.java
this.webRTC = new WebRTC(new WebRTCCallbacks(){

    @Override
    public void onCreateOffer(String sdp) {

        try{
            JSONObject json = new JSONObject();
            json.put("sdp", sdp);
            json.put("type", "offer");
            wamp.publishOffer(targetId, json.toString());
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void onCreateAnswer(String sdp) {

        try{
            JSONObject json = new JSONObject();
            json.put("sdp", sdp);
            json.put("type", "answer");
            wamp.publishAnswer(targetId, json.toString());
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void onIceCandidate(String sdp, String sdpMid, int sdpMLineIndex) {

        final JSONObject json = new JSONObject();
        try{
            json.put("type", "candidate");
            json.put("candidate", sdp);
            json.put("sdpMid", sdpMid);
            json.put("sdpMLineIndex", sdpMLineIndex);

            wamp.publishCandidate(targetId, json.toString());
        }catch (Exception e){
            e.printStackTrace();
        }
    }
});
```

そんなこんなやってるとそのうち

```java:Connection.java
@Override
public void didReceiveRemoteStream(MediaStream mediaStream) {
    callbacks.onAddedStream(mediaStream);
}
```

相手のストリームが拾えるので拾ったらConnectionに回す。

終わり。

# 次
まとめ