com.bitbreeds.webrtc.signaling.PeerConnection.java Source code

Java tutorial

Introduction

Here is the source code for com.bitbreeds.webrtc.signaling.PeerConnection.java

Source

package com.bitbreeds.webrtc.signaling;

import com.bitbreeds.webrtc.common.SignalUtil;
import com.bitbreeds.webrtc.datachannel.DataChannelImpl;
import com.bitbreeds.webrtc.dtls.KeyStoreInfo;
import org.apache.commons.codec.binary.Hex;
import org.pcollections.ConsPStack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sdp.MediaDescription;
import javax.sdp.SessionDescription;
import java.io.IOException;
import java.net.InetAddress;
import java.util.*;

/**
 * Copyright (c) 26/04/16, Jonas Waage
 * <p>
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
 * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * <p>
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 * <p>
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/**
 * A very poor mans PeerConnection hack, currently it only supports
 * connections initiated from another party.
 */
public class PeerConnection {

    private final static Logger logger = LoggerFactory.getLogger(PeerConnection.class);

    private ConsPStack<IceCandidate> candidates = ConsPStack.empty();
    private final Object candidateMutex = new Object();

    private KeyStoreInfo keyStoreInfo;

    public PeerConnection(KeyStoreInfo keyStoreInfo) {
        this.keyStoreInfo = keyStoreInfo;
    }

    public KeyStoreInfo getKeyStoreInfo() {
        return keyStoreInfo;
    }

    /**
     * Password and username for local user
     */
    private final UserData local = createLocalUser();

    /**
     * Password and usename for remote user
     */
    private UserData remote;

    /**
     *
     * @return local userdata
     */
    public UserData getLocal() {
        return local;
    }

    /**
     * @return the remote username and password
     */
    public UserData getRemote() {
        return remote;
    }

    /**
     * We must store the remote username/password when we receive it
     *
     * @param remote username/pass from answer
     */
    public void setRemote(UserData remote) {
        this.remote = remote;
    }

    /**
     * If we start an offer, we must create a datachannel with
     * no userdata for remote user.
     *
     * @return DataChannelImpl with no data for remote user, it can
     * not be started before this data is added.
     */
    public DataChannelImpl createDataChannel() throws IOException {
        return new DataChannelImpl(this);
    }

    /**
     * @param candidate the received candidate
     */
    private void addCandidate(IceCandidate candidate) {
        synchronized (candidateMutex) {
            candidates = candidates.plus(candidate);
        }
    }

    /**
     * @return A local user with randomly generated username and password.
     */
    private UserData createLocalUser() {
        String myUser = Hex.encodeHexString(SignalUtil.randomBytes(4));
        String myPass = Hex.encodeHexString(SignalUtil.randomBytes(16));
        return new UserData(myUser, myPass);
    }

    /**
     *
     * @param offer the received offer
     * @return The answer to respond with.
     */
    public Answer handleOffer(Offer offer) throws Exception {

        String fingerPrint = CertUtil.getCertFingerPrint(keyStoreInfo.getFilePath(), keyStoreInfo.getAlias(),
                keyStoreInfo.getPassword());

        SessionDescription sdp = offer.getSdp();
        sdp.setAttribute("fingerprint", fingerPrint);
        MediaDescription med = (MediaDescription) sdp.getMediaDescriptions(true).get(0);
        med.setAttribute("fingerprint", fingerPrint);

        String pwd = med.getAttribute("ice-pwd");
        String user = med.getAttribute("ice-ufrag");

        String cand = med.getAttribute("candidate");
        List<String> candData = Arrays.asList(cand.split(" "));

        String ip = candData.get(4);
        String port = candData.get(5);

        this.setRemote(new UserData(user, pwd));

        /**
         * TODO The below should be defined outside PeerConnection
         *
         * This is a huge hack now. Should follow browser API
         * and create datachannel from the outside.
         */
        DataChannelImpl conn = new DataChannelImpl(this);

        //Add handling of input
        conn.onOpen(() -> {
            logger.info("Running onOpen");
            conn.send("I'M SO OPEN!!!");
        });
        conn.onMessage((i) -> {
            String in = new String(i.getData());
            //logger.info("Running onMessage: " + in);
            conn.send("ECHO: " + in);
        });
        conn.onError((i) -> {
            logger.info("Received error", i.getError());
        });

        new Thread(conn).start();

        String localAddress = InetAddress.getLocalHost().getHostAddress();
        String address = System.getProperty("com.bitbreeds.ip", localAddress);
        logger.info("Adr: {}", address);
        med.setAttribute("ice-pwd", local.getPassword());
        med.setAttribute("ice-ufrag", local.getUserName());
        med.setAttribute("candidate", "1 1 UDP 2122252543 " + address + " " + conn.getPort() + " typ host");

        return new Answer(sdp);
    }

    /**
     * We sent an offer and here we handle the answer
     * @param answer the answer
     */
    public void handleAnswer(Answer answer) throws Exception {
        /**
         * Not implemented
         */
    }

    /**
     *
     * @param ice add the ice candidate to the ice list
     *
     */
    public void handleIce(IceCandidate ice) throws Exception {
        addCandidate(ice);
    }

}