WebRTC应用实现

171 阅读3分钟

WebRTC简介

 WebRTC(Web Real-Time Communication)是一种开放的实时通信技术,它使Web浏览器能够在不需要插件或其他软件的情况下进行实时音频、视频和数据传输。WebRTC基于标准的HTML5和JavaScript API,提供了一种简单易用的方式,使开发人员能够在网页上构建实时通信应用程序。
通过WebRTC,可以实现点对点(peer-to-peer)的实时音视频通信,无需服务器的中转。它使用了一些关键技术,如媒体捕获(Media Capture)、媒体传输(Media Transport)、媒体处理(Media Processing)和网络协商(Network Negotiation),以实现高质量、低延迟的音视频传输。
WebRTC不仅适用于视频会议和语音通话,还可以应用于各种实时通信场景,如在线教育、远程医疗、客服和社交媒体等。它支持跨平台和跨浏览器,可以在主流浏览器中使用,并且已经得到了广泛的支持和采用。

实践

框架介绍

Android端
集成WebRTC Android库,实现视频的接收,负责与WebSocket服务之间进行通信。
WebSocket服务
采用WebSocket实现服务端,WebSocket 是一种网络传输协议,可在单个 TCP 连接上进行全双工通信。
JavaScript
推送端采用JavaScript 调用WebRTC,实现流推送。

Android客户端 主要代码

//初始化PeerConnection
PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions.builder(context).createInitializationOptions());
// 创建 EglBase
mEglBase = EglBase.create();
// 创建 PeerConnectionFactory
mPeerConnectionFactory = createPeerConnectionFactory(mEglBase);


private PeerConnectionFactory createPeerConnectionFactory(EglBase eglBase) {
        VideoEncoderFactory videoEncoderFactory = new DefaultVideoEncoderFactory(eglBase.getEglBaseContext(), true, true);
        VideoDecoderFactory videoDecoderFactory = new DefaultVideoDecoderFactory(eglBase.getEglBaseContext());
        return PeerConnectionFactory.builder().setVideoEncoderFactory(videoEncoderFactory).setVideoDecoderFactory(videoDecoderFactory).createPeerConnectionFactory();
    }

//接收连接请求
private void receivedOffer(SessionDescription offer) {
        // 创建 PeerConnection
        mPeerConnection = createPeerConnection();
        // 为 PeerConnection 添加音轨、视轨
        //mPeerConnection.addTrack(mAudioTrack, STREAM_IDS);
        //mPeerConnection.addTrack(mVideoTrack, STREAM_IDS);
        // 将 offer sdp 作为参数 setRemoteDescription
        mPeerConnection.setRemoteDescription(new MySdpObserver() {
            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {

            }

            @Override
            public void onSetSuccess() {
                ShowLogUtil.verbose("set remote sdp success.");
                // 通过 PeerConnection 创建 answer,获取 sdp
                MediaConstraints mediaConstraints = new MediaConstraints();
                mPeerConnection.createAnswer(new MySdpObserver() {
                    @Override
                    public void onCreateSuccess(SessionDescription sessionDescription) {
                        ShowLogUtil.verbose("create answer success.");
                        // 将 answer sdp 作为参数 setLocalDescription
                        mPeerConnection.setLocalDescription(new MySdpObserver() {
                            @Override
                            public void onCreateSuccess(SessionDescription sessionDescription) {

                            }

                            @Override
                            public void onSetSuccess() {
                                ShowLogUtil.verbose("set local sdp success.");
                                // 发送 answer sdp
                                sendAnswer(sessionDescription);
                            }
                        }, sessionDescription);
                    }

                    @Override
                    public void onSetSuccess() {

                    }
                }, mediaConstraints);
            }
        }, offer);
    }

//创建配对
private PeerConnection createPeerConnection() {
        PeerConnection.RTCConfiguration rtcConfiguration = new PeerConnection.RTCConfiguration(new ArrayList<>());
        PeerConnection peerConnection = mPeerConnectionFactory.createPeerConnection(rtcConfiguration, new PeerConnection.Observer() {
            @Override
            public void onSignalingChange(PeerConnection.SignalingState signalingState) {

            }

            @Override
            public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
                if (iceConnectionState == PeerConnection.IceConnectionState.DISCONNECTED) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            hangUp();
                        }
                    });
                }
            }

            @Override
            public void onIceConnectionReceivingChange(boolean b) {

            }

            @Override
            public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {

            }

            @Override
            public void onIceCandidate(IceCandidate iceCandidate) {
                ShowLogUtil.verbose("onIceCandidate--->" + iceCandidate);
                sendIceCandidate(iceCandidate);
            }

            @Override
            public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) {

            }

            @Override
            public void onAddStream(MediaStream mediaStream) {
                ShowLogUtil.verbose("onAddStream--->" + mediaStream);
                if (mediaStream == null || mediaStream.videoTracks == null || mediaStream.videoTracks.isEmpty()) {
                    return;
                }
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Log.d("mars","setVideo");
                        //svrRemote = findViewById(R.id.svr_remote);
                        Log.d("mars",mediaStream.videoTracks.size()+"");
                        mediaStream.videoTracks.get(0).addSink(svrRemote);


                    }
                });
            }

            @Override
            public void onRemoveStream(MediaStream mediaStream) {

            }

            @Override
            public void onDataChannel(DataChannel dataChannel) {

            }

            @Override
            public void onRenegotiationNeeded() {

            }

            @Override
            public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) {

            }
        });
        return peerConnection;
    }

WebSocket 服务器主要代码

public class WebSocketTest {
    private WebSocketServer mWebSocketServer;
    private final List<WebSocket> mWebSockets = new ArrayList<>();
    private static final String HOST_NAME = "192.168.1.162";
    private static final int PORT = 8888;

    public void start() {
        InetSocketAddress inetSocketAddress = new InetSocketAddress(HOST_NAME, PORT);
        mWebSocketServer = new WebSocketServer(inetSocketAddress) {

            @Override
            public void onOpen(WebSocket conn, ClientHandshake handshake) {
                System.out.println("onOpen--->" + conn);
                // 客户端连接时保存到集合中
                mWebSockets.add(conn);
            }

            @Override
            public void onClose(WebSocket conn, int code, String reason, boolean remote) {
                System.out.println("onClose--->" + conn);
                // 客户端断开时从集合中移除
                mWebSockets.remove(conn);
            }

            @Override
            public void onMessage(WebSocket conn, String message) {
//                System.out.println("onMessage--->" + message);
                // 消息直接透传给除发送方以外的连接
                for (WebSocket webSocket : mWebSockets) {
                    if (webSocket != conn) {
                        webSocket.send(message);
                    }
                }
            }

            @Override
            public void onError(WebSocket conn, Exception ex) {
                System.out.println("onError--->" + conn + ", ex--->" + ex);
                // 客户端连接异常时从集合中移除
                mWebSockets.remove(conn);
            }

            @Override
            public void onStart() {
                System.out.println("onStart");
            }
        };
        mWebSocketServer.start();
    }

    public void stop() {
        if (mWebSocketServer == null) {
            return;
        }
        for (WebSocket webSocket : mWebSocketServer.getConnections()) {
            webSocket.close();
        }
        try {
            mWebSocketServer.stop();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        mWebSocketServer = null;
    }

    public static void main(String[] args) {
        new WebSocketTest().start();
    }

JavaScript 推送端主要代码

function createPeerConnection() {
        let rtcPeerConnection = new RTCPeerConnection();

        //rtcPeerConnection.channel=rtcPeerConnection.createDataChannel('msgdatachannel');

        rtcPeerConnection.oniceconnectionstatechange = function (event) {
            if ("disconnected" == event.target.iceConnectionState) {
                hangUp();
            }
        }
        rtcPeerConnection.onicecandidate = function (event) {
            console.log("onicecandidate--->" + event.candidate);
            let iceCandidate = event.candidate;
            if (iceCandidate == null) {
                return;
            }
            sendIceCandidate(iceCandidate);
        }
        rtcPeerConnection.ontrack = function (event) {
            console.log("remote ontrack--->" + event.streams);
            let streams = event.streams;
            if (streams && streams.length > 0) {
                remoteView.srcObject = streams[0];
            }
        }


        return rtcPeerConnection
    }

    var displayMediaStreamConstraints = {
        video: true,
        audio: true// or pass HINTS
    };

    function share(){
        if (navigator.mediaDevices.getDisplayMedia) {
            navigator.mediaDevices.getDisplayMedia(displayMediaStreamConstraints).then(handleSuccess).catch(error);
        } else {
            navigator.getDisplayMedia(displayMediaStreamConstraints).then(handleSuccess).catch(error);
        }
    }

    function call() {
        // 创建 PeerConnection
        peerConnection = createPeerConnection();
        // 为 PeerConnection 添加音轨、视轨
        for (let i = 0; localStream != null && i < localStream.getTracks().length; i++) {
            const track = localStream.getTracks()[i];
            peerConnection.addTrack(track, localStream);
        }
        // 通过 PeerConnection 创建 offer,获取 sdp
        peerConnection.createOffer().then(function (sessionDescription) {
            console.log("create offer success.");
            // 将 offer sdp 作为参数 setLocalDescription;
            peerConnection.setLocalDescription(sessionDescription).then(function () {
                console.log("set local sdp success.");
                // 发送 offer sdp
                sendOffer(sessionDescription)
            })
        });

        const obj = JSON.stringify({
              'command': 'offer',
              'desc': 'desc'
            });
        peerConnection.sendChannel.send(obj);    
    }

    function sendOffer(offer) {
        var jsonObject = {
            "msgType": "sdp",
            "type": offer.type,
            "sdp": offer.sdp
        };
        send(JSON.stringify(jsonObject));
    }

    function receivedOffer(offer) {
        // 创建 PeerConnection
        peerConnection = createPeerConnection();
        // 为 PeerConnection 添加音轨、视轨
        for (let i = 0; localStream != null && i < localStream.getTracks().length; i++) {
            const track = localStream.getTracks()[i];
            peerConnection.addTrack(track, localStream);
        }
        // 将 offer sdp 作为参数 setRemoteDescription
        peerConnection.setRemoteDescription(offer).then(function () {
            console.log("set remote sdp success.");
            // 通过 PeerConnection 创建 answer,获取 sdp
            peerConnection.createAnswer().then(function (sessionDescription) {
                console.log("create answer success.");
                // 将 answer sdp 作为参数 setLocalDescription
                peerConnection.setLocalDescription(sessionDescription).then(function () {
                    console.log("set local sdp success.");
                    // 发送 answer sdp
                    sendAnswer(sessionDescription);
                })
            })
        })
    }