Android WebRTC保存远程流

时间:2017-05-03 14:47:38

标签: android video-streaming webrtc

您好我正在尝试保存从webrtc收到的远程流,我从git中获取了一些代码示例,尝试了各种方法来获取远程流,但是,无法从套接字或任何其他方式获取流,如果有人有任何想法请在这里建议我的WebRTCClient类的片段

这是我的班级:

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");
            }
        });
    }

}

引自link

1 个答案:

答案 0 :(得分:0)

目前没有选项可以在Android上保存远程媒体流。您必须实现一个媒体服务器,它可以节省媒体流并在两个设备之间传递流。 Kurento是一个开源媒体服务器,我知道它提供了功能,但我没有使用它