Android WebRTC save remote stream

2019-06-12 16:34发布

问题:

Hi I'm trying to save the remote stream received from the webrtc, I followed some code sample from git, tried various approach to get remote stream but,not able to get stream from socket or any other way, if some one have any idea please suggest here is snippet of my Class of WebRTCClient

here is my class:

package fr.pchab.webrtcclient;

import android.app.Activity;
import android.util.Log;
import android.widget.Toast;

import com.github.nkzawa.emitter.Emitter;
import com.github.nkzawa.socketio.client.IO;
import com.github.nkzawa.socketio.client.Socket;

import org.json.JSONException;
import org.json.JSONObject;
import org.webrtc.AudioSource;
import org.webrtc.DataChannel;
import org.webrtc.IceCandidate;
import org.webrtc.MediaConstraints;
import org.webrtc.MediaStream;
import org.webrtc.PeerConnection;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.SdpObserver;
import org.webrtc.SessionDescription;
import org.webrtc.VideoCapturer;
import org.webrtc.VideoCapturerAndroid;
import org.webrtc.VideoSource;

import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.LinkedList;

public class WebRtcClient {
    private final static String TAG = "WebRtcClient";
    private final static int MAX_PEER = 2;
    private boolean[] endPoints = new boolean[MAX_PEER];
    private PeerConnectionFactory factory;
    private HashMap<String, Peer> peers = new HashMap<>();
    private LinkedList<PeerConnection.IceServer> iceServers = new LinkedList<>();
    private PeerConnectionParameters pcParams;
    private MediaConstraints pcConstraints = new MediaConstraints() {
    };
    private MediaStream localMS;
    private VideoSource videoSource;
    private RtcListener mListener;
    private Socket client;

    /**
     * Implement this interface to be notified of events.
     */
    public interface RtcListener {
        void onCallReady(String callId);

        void onStatusChanged(String newStatus);

        void onLocalStream(MediaStream localStream);

        void onAddRemoteStream(MediaStream remoteStream, int endPoint);

        void onRemoveRemoteStream(int endPoint);
    }

    private interface Command {
        void execute(String peerId, JSONObject payload) throws JSONException;
    }

    private class CreateOfferCommand implements Command {
        public void execute(String peerId, JSONObject payload) throws JSONException {
            Log.e(TAG, "CreateOfferCommand");
            Peer peer = peers.get(peerId);
            peer.pc.createOffer(peer, pcConstraints);
        }
    }

    private class CreateAnswerCommand implements Command {
        public void execute(String peerId, JSONObject payload) throws JSONException {
            Log.e(TAG, "CreateAnswerCommand");
            Peer peer = peers.get(peerId);
            SessionDescription sdp = new SessionDescription(
                    SessionDescription.Type.fromCanonicalForm(payload.getString("type")),
                    payload.getString("sdp")
            );
            peer.pc.setRemoteDescription(peer, sdp);
            peer.pc.createAnswer(peer, pcConstraints);
        }
    }

    private class SetRemoteSDPCommand implements Command {
        public void execute(String peerId, JSONObject payload) throws JSONException {
            Log.e(TAG, "SetRemoteSDPCommand");
            Peer peer = peers.get(peerId);
            SessionDescription sdp = new SessionDescription(
                    SessionDescription.Type.fromCanonicalForm(payload.getString("type")),
                    payload.getString("sdp")
            );
            peer.pc.setRemoteDescription(peer, sdp);
        }
    }

    private class AddIceCandidateCommand implements Command {
        public void execute(String peerId, JSONObject payload) throws JSONException {
            Log.e(TAG, "AddIceCandidateCommand");
            PeerConnection pc = peers.get(peerId).pc;
            if (pc.getRemoteDescription() != null) {
                IceCandidate candidate = new IceCandidate(
                        payload.getString("id"),
                        payload.getInt("label"),
                        payload.getString("candidate")
                );
                pc.addIceCandidate(candidate);
            }
        }
    }

    /**
     * Send a message through the signaling server
     *
     * @param to      id of recipient
     * @param type    type of message
     * @param payload payload of message
     * @throws JSONException
     */
    public void sendMessage(String to, String type, JSONObject payload) throws JSONException {
        JSONObject message = new JSONObject();
        message.put("to", to);
        message.put("type", type);
        message.put("payload", payload);
        client.emit("message", message);
    }

    private class MessageHandler {
        private HashMap<String, Command> commandMap;

        private MessageHandler() {
            this.commandMap = new HashMap<>();
            commandMap.put("init", new CreateOfferCommand());
            commandMap.put("offer", new CreateAnswerCommand());
            commandMap.put("answer", new SetRemoteSDPCommand());
            commandMap.put("candidate", new AddIceCandidateCommand());
        }

        private Emitter.Listener onMessage = new Emitter.Listener() {
            @Override
            public void call(Object... args) {
                JSONObject data = (JSONObject) args[0];
                try {
                    String from = data.getString("from");
                    String type = data.getString("type");
                    JSONObject payload = null;
                    if (!type.equals("init")) {
                        payload = data.getJSONObject("payload");
                    }
                    // if peer is unknown, try to add him
                    if (!peers.containsKey(from)) {
                        // if MAX_PEER is reach, ignore the call
                        int endPoint = findEndPoint();
                        if (endPoint != MAX_PEER) {
                            Peer peer = addPeer(from, endPoint);
                            peer.pc.addStream(localMS);
                            commandMap.get(type).execute(from, payload);
                        }
                    } else {
                        commandMap.get(type).execute(from, payload);
                    }

                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        };

        private Emitter.Listener onId = new Emitter.Listener() {
            @Override
            public void call(Object... args) {
                String id = (String) args[0];
                mListener.onCallReady(id);
            }
        };
    }

    private class Peer implements SdpObserver, PeerConnection.Observer, DataChannel.Observer {
        private PeerConnection pc;
        private String id;
        private int endPoint;

        @Override
        public void onCreateSuccess(final SessionDescription sdp) {
            // TODO: modify sdp to use pcParams prefered codecs
            JSONObject payload = null;
            try {
                payload = new JSONObject();
                payload.put("type", sdp.type.canonicalForm());
                payload.put("sdp", sdp.description);
                sendMessage(id, sdp.type.canonicalForm(), payload);
                pc.setLocalDescription(Peer.this, sdp);
            } catch (JSONException e) {
                e.printStackTrace();
            }
            Log.e("WebRtcClient", "WebRtcClient onCreateSuccess:" + payload);

        }

        @Override
        public void onSetSuccess() {
            Log.e("WebRtcClient", "WebRtcClient onSetSuccess:");
        }

        @Override
        public void onCreateFailure(String s) {
            Log.e("WebRtcClient", "WebRtcClient onCreateFailure:" + s);
        }

        @Override
        public void onSetFailure(String s) {
            Log.e("WebRtcClient", "WebRtcClient onSetFailure:" + s);
        }

        @Override
        public void onSignalingChange(PeerConnection.SignalingState signalingState) {
            Log.e("WebRtcClient", "WebRtcClient onSignalingChange:" + signalingState.name());
        }

        @Override
        public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
            if (iceConnectionState == PeerConnection.IceConnectionState.DISCONNECTED) {
                removePeer(id);
                mListener.onStatusChanged("DISCONNECTED");
            } else if (iceConnectionState == PeerConnection.IceConnectionState.CONNECTED) {
            }
            Log.e("WebRtcClient", "WebRtcClient onIceConnectionChange:" + iceConnectionState);
        }

        @Override
        public void onIceConnectionReceivingChange(boolean b) {
            Log.e("WebRtcClient", "WebRtcClient onIceConnectionReceivingChange:" + b);
        }

        @Override
        public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
            Log.e("WebRtcClient", "WebRtcClient onIceGatheringChange:" + iceGatheringState);
        }

        @Override
        public void onIceCandidate(final IceCandidate candidate) {
            JSONObject payload = null;
            try {
                payload = new JSONObject();
                payload.put("label", candidate.sdpMLineIndex);
                payload.put("id", candidate.sdpMid);
                payload.put("candidate", candidate.sdp);
                sendMessage(id, "candidate", payload);
            } catch (JSONException e) {
                e.printStackTrace();
            }
            Log.e("WebRtcClient", "WebRtcClient onIceCandidate:" + payload);
        }

        @Override
        public void onAddStream(MediaStream mediaStream) {
            Log.e(TAG, "onAddStream " + mediaStream.label());
            // remote streams are displayed from 1 to MAX_PEER (0 is localStream)
            mListener.onAddRemoteStream(mediaStream, endPoint + 1);
            Log.e("WebRtcClient", "WebRtcClient onAddStream:" + mediaStream.label());

        }

        @Override
        public void onRemoveStream(MediaStream mediaStream) {
            Log.e(TAG, "onRemoveStream " + mediaStream.label());
            Log.e("WebRtcClient", "WebRtcClient onRemoveStream:" + mediaStream.label());
            removePeer(id);
        }


        @Override
        public void onRenegotiationNeeded() {
            Log.e("WebRtcClient", "WebRtcClient onRenegotiationNeeded:");
        }

        @Override
        public void onDataChannel(final DataChannel dataChannel) {
            Log.e("onDataChannel", "onDataChannel:" + dataChannel.label());
            dataChannel.registerObserver(this);
        }

        @Override
        public void onMessage(DataChannel.Buffer buffer) {
            ByteBuffer data = buffer.data;
            byte[] bytes = new byte[data.remaining()];
            data.get(bytes);
            String command = new String(bytes);
            Log.e(TAG, " onDataChannel-DcObserver " + command);
        }

        @Override
        public void onStateChange() {
            Log.e(TAG, "onDataChannel -DcObserver " + "onStateChange");
        }

        @Override
        public void onBufferedAmountChange(long arg0) {
            Log.e(TAG, " DcObserver " + arg0);
        }

        public Peer(String id, int endPoint) {
            Log.e(TAG, "WebRtcClient new Peer: " + id + " " + endPoint);
            this.pc = factory.createPeerConnection(iceServers, pcConstraints, this);
            createDataChannel();
            this.id = id;
            this.endPoint = endPoint;
            pc.addStream(localMS); //, new MediaConstraints()
            mListener.onStatusChanged("CONNECTING");
        }


        private void createDataChannel() {
            DataChannel.Init dcInit = new DataChannel.Init();
            dcInit.id = 1;
            dataChannel = pc.createDataChannel("sendDataChannel", dcInit);
            dataChannel.registerObserver(this);
        }


    }

    DataChannel dataChannel;

    private Peer addPeer(String id, int endPoint) {
        Peer peer = new Peer(id, endPoint);
        peers.put(id, peer);
        endPoints[endPoint] = true;
        return peer;
    }

    private void removePeer(String id) {
        Peer peer = peers.get(id);
        mListener.onRemoveRemoteStream(peer.endPoint);
        peer.pc.close();
        peers.remove(peer.id);
        endPoints[peer.endPoint] = false;
    }

    private Activity mContext;

    public WebRtcClient(RtcListener listener, String host, PeerConnectionParameters params, Activity mContext) {
        mListener = listener;
        pcParams = params;
        this.mContext = mContext;
        PeerConnectionFactory.initializeAndroidGlobals(listener, true, true, true
                /*params.videoCodecHwAcceleration, mEGLcontext*/);
        factory = new PeerConnectionFactory();
        MessageHandler messageHandler = new MessageHandler();

        try {
            client = IO.socket(host);
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        client.on("id", messageHandler.onId);
        client.on("message", messageHandler.onMessage);
        client.connect();

        iceServers.add(new PeerConnection.IceServer("stun:23.21.150.121"));
        iceServers.add(new PeerConnection.IceServer("stun:stun.l.google.com:19302"));

        pcConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
        pcConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"));
        pcConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
        //   pcConstraints.optional.add(new MediaConstraints.KeyValuePair("RtpDataChannels", "false"));
    }

    /**
     * Call this method in Activity.onPause()
     */
    public void onPause() {
        if (videoSource != null) videoSource.stop();
    }

    /**
     * Call this method in Activity.onResume()
     */
    public void onResume() {
        if (videoSource != null) videoSource.restart();
    }

    /**
     * Call this method in Activity.onDestroy()
     */
    public void onDestroy() {
        for (Peer peer : peers.values()) {
            peer.pc.dispose();
        }
        videoSource.dispose();
        factory.dispose();
        client.disconnect();
        client.close();
    }

    private int findEndPoint() {
        for (int i = 0; i < MAX_PEER; i++) if (!endPoints[i]) return i;
        return MAX_PEER;
    }

    /**
     * Start the client.
     * <p>
     * Set up the local stream and notify the signaling server.
     * Call this method after onCallReady.
     *
     * @param name client name
     */
    public void start(String name) {
        setCamera();
        try {
            JSONObject message = new JSONObject();
            message.put("name", name);
            client.emit("readyToStream", message);
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    private void setCamera() {
        localMS = factory.createLocalMediaStream("ARDAMS");
        if (pcParams.videoCallEnabled) {
            MediaConstraints videoConstraints = new MediaConstraints();
            videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("maxHeight", Integer.toString(pcParams.videoHeight)));
            videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("maxWidth", Integer.toString(pcParams.videoWidth)));
            videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("maxFrameRate", Integer.toString(pcParams.videoFps)));
            videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("minFrameRate", Integer.toString(pcParams.videoFps)));

            VideoCapturer videoCapturer = getVideoCapturer();

            if (videoCapturer != null) {
                videoSource = factory.createVideoSource(videoCapturer, videoConstraints);
                localMS.addTrack(factory.createVideoTrack("ARDAMSv0", videoSource));
            } else {

            }


        }
        AudioSource audioSource = factory.createAudioSource(new MediaConstraints());
        localMS.addTrack(factory.createAudioTrack("ARDAMSa0", audioSource));
        mListener.onLocalStream(localMS);
    }

    private void showMessage(final String msg) {
        mContext.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
            }
        });
    }

    private VideoCapturer getVideoCapturer() {
        //Camera name empty will call the back cam bydefualt
        return VideoCapturerAndroid.create("", new VideoCapturerAndroid.CameraEventsHandler() {
            @Override
            public void onCameraError(String s) {
                Log.e("WebRtcClient", "WebRtcClient onCameraError:" + s);
            }

            @Override
            public void onCameraFreezed(String s) {
                Log.e("WebRtcClient", "WebRtcClient onCameraFreezed:" + s);
            }

            @Override
            public void onCameraOpening(int i) {
                Log.e("WebRtcClient", "WebRtcClient onCameraOpening:" + i);
                //  showMessage("Opening Camera id " + i);
            }

            @Override
            public void onFirstFrameAvailable() {
                Log.e("WebRtcClient", "WebRtcClient onFirstFrameAvailable:");
                // showMessage("Camera onFirstFrameAvailable:");
            }

            @Override
            public void onCameraClosed() {
                Log.e("WebRtcClient", "WebRtcClient onCameraClosed:");
                showMessage("Camera onCameraClosed");
            }
        });
    }

}

Referenced from link

回答1:

Currently there is no option to save remote media stream on android.You will have to implement a media server which would save media stream and pass stream between two devices. Kurento is an open source media server which I know provides the functionality but I haven't used it