Android-WebRTC-双人视频

省略开启本机摄像头的过程

以下和WebSocket通信的时候,是通过Gson转对象为字符串的方式传输的数据

  • 整个过程
  • layout_rtc.xml
xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="@dimen/dp_20"
    >

    <org.webrtc.SurfaceViewRenderer
        android:id="@+id/localView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintHeight_percent="0.5"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <org.webrtc.SurfaceViewRenderer
        android:id="@+id/remoteView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintHeight_percent="0.5"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        />

</androidx.constraintlayout.widget.ConstraintLayout>
  • RtcActivity
java 复制代码
package com.dream.app.activity;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

import androidx.annotation.Nullable;

import com.dream.app.adapter.PeerConnectionAdapter;
import com.dream.app.adapter.SdpAdapter;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.webrtc.Camera1Enumerator;
import org.webrtc.DefaultVideoDecoderFactory;
import org.webrtc.DefaultVideoEncoderFactory;
import org.webrtc.EglBase;
import org.webrtc.IceCandidate;
import org.webrtc.MediaConstraints;
import org.webrtc.MediaStream;
import org.webrtc.PeerConnection;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.SessionDescription;
import org.webrtc.SurfaceTextureHelper;
import org.webrtc.SurfaceViewRenderer;
import org.webrtc.VideoCapturer;
import org.webrtc.VideoSource;
import org.webrtc.VideoTrack;

import java.lang.reflect.Type;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.Random;

public class RtcActivity extends Activity {

    private static final String TAG = "RtcRemoteActivity";

    private WebSocketClient webSocketClient = null;
    private PeerConnection peerConnection = null;
    private static final Type sessionDescriptionType = new TypeToken<SessionDescription>(){}.getType();
    private static final Type iceCandidateType = new TypeToken<IceCandidate>(){}.getType();
    private VideoTrack videoTrack;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_rtc);

        //  factory static init
        PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions
                .builder(this)
                .createInitializationOptions());
        EglBase.Context eglBaseContext = EglBase.create().getEglBaseContext();
        PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();

        //  create video encoder/decoder
        DefaultVideoEncoderFactory defaultVideoEncoderFactory =
                new DefaultVideoEncoderFactory(eglBaseContext, true, true); // encoder
        DefaultVideoDecoderFactory defaultVideoDecoderFactory =
                new DefaultVideoDecoderFactory(eglBaseContext); // decoder

        //  factory create
        PeerConnectionFactory peerConnectionFactory = PeerConnectionFactory.builder()
                .setOptions(options)
                .setVideoEncoderFactory(defaultVideoEncoderFactory)
                .setVideoDecoderFactory(defaultVideoDecoderFactory)
                .createPeerConnectionFactory();

        // create videoCapturer
        Camera1Enumerator camera1Enumerator = new Camera1Enumerator(false);// false输出到surface
        String[] deviceNames = camera1Enumerator.getDeviceNames();
        VideoCapturer videoCapturer = null;
        for (String deviceName : deviceNames) {
            if (camera1Enumerator.isFrontFacing(deviceName)) {
                VideoCapturer capturer = camera1Enumerator.createCapturer(deviceName, null);
                if (capturer != null) {
                    videoCapturer = capturer;
                    break;
                }
                Log.e(TAG, "onCreate: create capturer fail");
                return;
            }
        }
        if (videoCapturer == null) {
            Log.e(TAG, "onCreate: create capturer fail");
            return;
        }

        //  create videoSource
        VideoSource videoSource = peerConnectionFactory.createVideoSource(videoCapturer.isScreencast());

        //  init videoCapturer
        SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create("surfaceTexture", eglBaseContext);
        videoCapturer.initialize(surfaceTextureHelper, this, videoSource.getCapturerObserver());
        videoCapturer.startCapture(480, 640, 30);// width, height, frame

        //  create videoTrack
        videoTrack = peerConnectionFactory.createVideoTrack("videoTrack-1", videoSource);

        //  get show view
        SurfaceViewRenderer localView = findViewById(R.id.localView);
        localView.setMirror(true);// 镜像
        localView.init(eglBaseContext, null);

        //  link track to view so that data show in view
        videoTrack.addSink(localView);

        Activity activity = this;

        // link websocket
        try {
            String uriStr = "ws://host/webrtc/" + Math.abs(new Random().nextInt()) % 100;
            Log.e(TAG, "onCreate: " + uriStr);
            webSocketClient = new WebSocketClient(new URI(uriStr)) {
                @Override
                public void onOpen(ServerHandshake handshakedata) {
                    //  create peerConnection
                    peerConnection = peerConnectionFactory.createPeerConnection(Collections.singletonList(PeerConnection.IceServer
                            .builder("turn:host:3478")
                            .setUsername("admin")
                            .setPassword("123456")
                            .createIceServer()), new PeerConnectionAdapter() {

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

                        @Override
                        public void onIceCandidate(IceCandidate iceCandidate) {
                            super.onIceCandidate(iceCandidate);
                            //TODO send to remote the local iceCandidate
                            Log.e(TAG, "onIceCandidate: ICE CANDIDATE");
                            webSocketClient.send(new Gson().toJson(iceCandidate, iceCandidateType));
                        }

                        @Override
                        public void onAddStream(MediaStream mediaStream) { // turn deal with
                            super.onAddStream(mediaStream);
                            Log.e(TAG, "onAddStream: ICE STREAM");
                            //  get remote videoTrack
                            VideoTrack remoteVideoTrack = mediaStream.videoTracks.get(0);
                            runOnUiThread(() -> {
                                SurfaceViewRenderer remoteView = findViewById(R.id.remoteView);
                                remoteView.setMirror(false);
                                remoteView.init(eglBaseContext, null);
                                //  link track to view so that data show in view
                                remoteVideoTrack.addSink(remoteView);
                            });
                        }
                    });

                    if (peerConnection == null) {
                        Log.e(TAG, "onCreate: peerConnection fail");
                        webSocketClient.close();
                        activity.finish();
                    }

                    //  create stream
                    MediaStream stream = peerConnectionFactory.createLocalMediaStream("stream");
                    stream.addTrack(videoTrack);
                    peerConnection.addStream(stream);
                }

                @Override
                public void onMessage(String message) {
                    Log.e(TAG, "onMessage: " + message);

                    /**
                     * SDP change
                     */
                    if ("offer".equals(message)) {
                        // peerConnection offer
                        peerConnection.createOffer(new SdpAdapter("offer") {
                            @Override
                            public void onCreateSuccess(SessionDescription sessionDescription) {
                                super.onCreateSuccess(sessionDescription);
                                peerConnection.setLocalDescription(new SdpAdapter("local set local sd"), sessionDescription);

                                //TODO send sessionDescription to remote
                                webSocketClient.send("offer-" + new Gson().toJson(sessionDescription, sessionDescriptionType));
                            }
                        }, new MediaConstraints());
                        return;
                    }
                    if (message.startsWith("answer-")) {

                        peerConnection.setRemoteDescription(new SdpAdapter()
                                , new Gson().fromJson(message.substring("answer-".length()), sessionDescriptionType));
                        peerConnection.createAnswer(new SdpAdapter() {
                            @Override
                            public void onCreateSuccess(SessionDescription sessionDescription) {
                                super.onCreateSuccess(sessionDescription);

                                Log.e(TAG, "onCreateSuccess: createAnswer");

                                peerConnection.setLocalDescription(new SdpAdapter(), sessionDescription);
                                // send local sd to call
                                webSocketClient.send("answer-offer-" + new Gson().toJson(sessionDescription, sessionDescriptionType));
                            }

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

                        }, new MediaConstraints());
                        return;
                    }
                    if (message.startsWith("offer-receiver-")) {
                        peerConnection.setRemoteDescription(new SdpAdapter()
                                , new Gson().fromJson(message.substring("offer-receiver-".length()), sessionDescriptionType));
                        return;
                    }
                    // ------------------------ sdp change -----------------------------------

                    /**
                     * ICE change
                     */
                    // get remote IceCandidate
                    IceCandidate remoteIceCandidate = new Gson().fromJson(message, iceCandidateType);
                    peerConnection.addIceCandidate(remoteIceCandidate);
                    // ------------------------ ice change -----------------------------------
                }

                @Override
                public void onClose(int code, String reason, boolean remote) {

                }

                @Override
                public void onError(Exception ex) {
                    ex.printStackTrace();
                }
            };
        } catch (URISyntaxException e) {
            Log.e(TAG, "onCreate: ", e);
            return;
        }
        webSocketClient.connect();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (webSocketClient.isOpen()) {
            webSocketClient.close();
            webSocketClient = null;
        }
        if (peerConnection != null) {
            peerConnection.dispose();
            peerConnection = null;
        }
        if (videoTrack != null) {
            videoTrack.dispose();
            videoTrack = null;
        }
    }
}
相关推荐
六毛的毛11 分钟前
Springboot开发常见注解一览
java·spring boot·后端
程序漫游人25 分钟前
centos8.5安装jdk21详细安装教程
java·linux
超级码.里奥.农1 小时前
零基础 “入坑” Java--- 七、数组(二)
java·开发语言
hqxstudying1 小时前
Java创建型模式---单例模式
java·数据结构·设计模式·代码规范
挺菜的1 小时前
【算法刷题记录(简单题)002】字符串字符匹配(java代码实现)
java·开发语言·算法
A__tao1 小时前
一键将 SQL 转为 Java 实体类,全面支持 MySQL / PostgreSQL / Oracle!
java·sql·mysql
一只叫煤球的猫2 小时前
真实事故复盘:Redis分布式锁居然失效了?公司十年老程序员踩的坑
java·redis·后端
猴哥源码2 小时前
基于Java+SpringBoot的农事管理系统
java·spring boot
花王江不语2 小时前
android studio 配置硬件加速 haxm
android·ide·android studio
面朝大海,春不暖,花不开2 小时前
Java网络编程:TCP/UDP套接字通信详解
java·网络·tcp/ip