Android-WebRTC-双人视频

news2024/12/24 0:19:16

省略开启本机摄像头的过程
以下和WebSocket通信的时候,是通过Gson转对象为字符串的方式传输的数据

  • 整个过程
    WEBRTC过程
  • layout_rtc.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
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;
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/779355.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

汽车ECU刷机笔记

汽车ECU刷机笔记 ECU简介ECU刷机的意义点火提前角点火延迟角调整意义 常见刷ECU的方法:成本价格1.通过obd汽车诊断口读写数据2.bdm后台调试模式3.BENCH刷写4.BOOT需要拆开电脑板&#xff0c;焊接电路 ECU刷写程序读取数据OBD tools(汽车诊断器)蓝牙ODB诊断器&#xff1a; 读写设…

<j-editable-table 隐藏一列表格数据

&#xff1c;j-editable-table 隐藏一列表格数据 隐藏条码 本来列代码是这样的 {title: 条码, key: barCode, width: 15%, type: FormTypes.input, defaultValue: , placeholder: 请输入${title},validateRules: [{ required: true, message: ${title}不能为空 },// { pattern…

二分搜索树的特性

一、顺序性 二分搜索树可以当做查找表的一种实现。 我们使用二分搜索树的目的是通过查找 key 马上得到 value。minimum、maximum、successor&#xff08;后继&#xff09;、predecessor&#xff08;前驱&#xff09;、floor&#xff08;地板&#xff09;、ceil&#xff08;天…

leetcode 699. 掉落的方块(java)

掉落的方块 leetcode 699. 掉落的方块题目描述线段树解法代码演示 leetcode 699. 掉落的方块 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;https://leetcode.cn/problems/falling-squares 题目描述 在二维平面上的 x 轴上&#xff0c;放置着一些方块…

jmeter软件测试实验(附源码以及配置)

jmeter介绍 JMeter是一个开源的性能测试工具&#xff0c;由Apache软件基金会开发和维护。它主要用于对Web应用程序、Web服务、数据库和其他类型的服务进行性能测试。JMeter最初是为测试Web应用程序而设计的&#xff0c;但现在已经扩展到支持更广泛的应用场景。 JMeter 可对服务…

Linux系统入门之-系统编程【open、close函数】

继上一篇环境配置后就正式开始系统编程 RK3568开发板入门之-tftp&nfs的配置 open的使用&#xff0c;使用之前可以先在Ubuntu下查看帮助&#xff0c;了解open的使用和语法&#xff0c;如下&#xff1a; man 2 open对于open函数 *pathname&#xff1a;要打开的文件路径 f…

【JavaWeb】Tomcat底层机制和Servlet运行原理

&#x1f384;欢迎来到dandelionl_的csdn博文&#xff0c;本文主要讲解Java web中Tomcat底层机制和Servlet的运行原理的相关知识&#x1f384; &#x1f308;我是dandelionl_&#xff0c;一个正在为秋招和算法竞赛做准备的学生&#x1f308; &#x1f386;喜欢的朋友可以关注一…

Revisiting Personalized Federated Learning:Robustness Against Backdoor Attacks

ACM SIGKDD Conference on Knowledge Discovery and Data Mining 2023 秦泽钰&#xff0c;香港科技大学计算机科学与工程系博士生&#xff0c;联邦学习中个性化的额外增益&#xff1a;对于后门攻击的鲁棒性 https://github.com/alibaba/FederatedScope/tree/backdoor-bench 摘要…

MySQL两种安装方式

数据库&#xff1a; 一、 二、 三、 四、 五、MySQL的两种安装方式 .zip包的安装方式&#xff1a; .msi方式安装的需要到控制面板卸载 密码默认是空密码 5.7和8.0开始&#xff1a;加强安全相关操作&#xff0c;安装有所不同 前三步一样&#xff0c;第四步在cmd&#xff0c;以…

windows系统安装异构虚拟机arm版的银河麒麟服务器

下载qemu UEFI&#xff08;BIOS的替代方案&#xff09;的下载地址&#xff1a;http://releases.linaro.org/components/kernel/uefi-linaro/16.02/release/qemu64/QEMU_EFI.fd QEMU 软件的下载地址&#xff1a;https://qemu.weilnetz.de/w64/2021/qemu-w64-setup-20210505.exe …

Docker详解及常用命令介绍

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…

【5】构建手写数字模型

1 mnist数据集 下载数据集&#xff1a; 手动下载&#xff1a;MNIST handwritten digit database, Yann LeCun, Corinna Cortes and Chris Burges tf程序下载&#xff1a; tensorflow2.x将数据集集成在Keras中。tensorflow2.0&#xff0c;更新删除了input_data的函数。 import…

19.主题时钟

主题时钟 html部分 <div class"btn">黑色</div><div class"clock-container"><div class"time">21</div><div class"date">21</div><div class"clock"><div class&qu…

经典面试题(力扣,接雨水)

接雨水 方法一思路测试代码复杂度测试结果 方法二思路测试代码复杂度测试结果 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1]…

Java基础——Stream类

文章目录 1 概述2 获取Stream对象3 常用API4 收集Stream流5 总结 1 概述 Stream类用于简化集合和数组操作的API。 Stream类提供了很多可以简化集合操作的api&#xff0c;比如过滤元素 示例如下&#xff1a; 假如一个List集合中存储着字符串&#xff0c;从这些字符串中找到以“…

Java反射 -- 详细介绍 (框架核心)

反射 是 Java框架 的核心 &#xff0c;无论是Tomcat、SpringMVC、Spring IOC、Spring AOP、动态代理 &#xff0c;都使用到了 反射 反射的作用简单讲就是 无需 new 对象&#xff0c;就可以动态获取到一个类的全部信息&#xff0c;包括 属性、方法&#xff0c;构造器&#xff0…

iOS--属性关键字

定义 chat&#xff1a; 在iOS开发中&#xff0c;属性关键字是用于声明类的属性&#xff08;实例变量&#xff09;的修饰符。属性关键字可以影响属性的访问权限、内存管理和生成相关的getter和setter方法。 属性关键字有哪些&#xff1f; 分类属性关键字原子性atomic、nonato…

dpdpdp

这里写目录标题 139. 单词拆分322. 零钱兑换300. 最长递增子序列120. 三角形最小路径和64. 最小路径和63. 不同路径 II5. 最长回文子串&#xff08;回文dp&#xff09;⭐97. 交错字符串⭐&#xff08;抽象成路径问题&#xff09;221. 最大正方形⭐ 139. 单词拆分 class Soluti…

实用便捷!一站式BI系统推荐

在企业数字化转型过程中&#xff0c;BI系统可以建立业务、数据的双驱引擎&#xff0c;形成业务、数据的互补作用&#xff0c;通过建立数字化技术架构&#xff0c;明确企业的战略定位和业务目标&#xff0c;从而支撑实现这个目标。而一站式BI系统&#xff0c;则是指可以轻松从数…

使用Soft-RoCE实践RDMA

RDMA介绍 RDMA&#xff08; Remote Direct Memory Access &#xff09;意为远程直接地址访问&#xff0c;通过RDMA&#xff0c;本端节点可以“直接”访问远端节点的内存。所谓直接&#xff0c;指的是可以像访问本地内存一样&#xff0c;绕过传统以太网复杂的TCP/IP网络协议栈读…