• 如何实现安卓屏幕分享及视频聊天?(源码)


    在一些有人际互动的手机APP中,增加语音视频聊天功能是一个常见的需求。而现在,更进一步,在某些场景下,我们需要能将自己的手机屏幕分享给他人,或者是观看他人的手机屏幕。那么,这些常见的功能是如何实现的了?
    我为此专门写了一个安卓版的Demo,并将源码放出来供大家参考,希望对大家有所帮助。

    一.功能介绍

    1. 视频聊天

    (1)每个登录的用户都可向其他任意在线用户发送视频聊天请求。

    (2)当收到来自其他在线用户的视频聊天邀请时,可接受或拒绝对方的请求。

    (3)当接受其他在线用户的视频聊天邀请时,就启动视频聊天。

    2.屏幕分享

    (1)每个登录的用户都可向其他任意在线用户发送屏幕分享请求;当对方未响应时,可主动取消屏幕分享请求。

    (2)当收到来自其他在线用户请求屏幕分享时,可接受或拒绝对方的请求。

    (3)当发送方收到其他在线用户同意屏幕分享时,即可观看其屏幕

    (4)被控端和主控端都可主动断开屏幕分享。

    二.开发环境

    1.开发工具:

    Android Studio 4.0

    2.开发语言:

    JAVA

    3.主要框架:

    Netty 、OMCS

    三.具体实现

    类似视频聊天或屏幕分享这样的功能,一般是C/S架构的。在这种应用中,服务端相对简单,其主要是在客户端之间转发消息。本Demo提供了一个非常简易的C#服务端(开发环境:VS 2022),直接运行起来即可。下面我们将主要介绍安卓端的实现。
    大家可以从文末下载安卓端的源码,在阅读本文时对照源码,就会更清楚些。
    首先,我们先要确定客户端之间相互通信的消息类型。

    1.自定义消息类型 InformationTypes

    public class InformationTypes {
     
        /// 
        /// 视频请求 0
        /// 
        public static final int VideoRequest = 0;
    
        /// 
        /// 回复视频请求的结果 1
        /// 
        public static final int VideoResult = 1;
    
        /// 
        /// 通知对方 挂断 视频连接 2
        /// 
        public static final int CloseVideo = 2;
    
        /// 
        /// 通知好友 网络原因,导致 视频中断 3
        /// 
        public static final int NetReasonCloseVideo = 3;
    
        /// 
        /// 通知对方(忙线中) 挂断 视频连接 4
        /// 
        public static final int BusyLine = 4;
    
        /// 
        /// 屏幕分享请求 5
        /// 
        public static final int DesktopRequest = 5;
    
        /// 
        /// 回复屏幕分享请求的结果 6
        /// 
        public static final int DesktopResult = 6;
    
        /// 
        ///  主动取消屏幕分享请求
        /// 
        public static final int CancelDesktop = 7;
    
        /// 
        ///  对方(主人端)主动断开屏幕分享
        /// 
        public static final int OwnerCloseDesktop = 8;
    
        /// 
        /// 客人端断开屏幕分享
        /// 
        public static final int GuestCloseDesktop = 9;
    }
    

    这里我们定义了为了实现第一部分“功能介绍”中的功能,所需要用到的消息类型。

    2. 获取安卓系统权限

    在安卓上进行视频聊天和屏幕分享,APP需要向安卓系统申请3个权限:麦克风、摄像头、屏幕录制。

    (1)获取相机、麦克风、存储权限

    private void getPermission() {
            List permissionItems = new ArrayList();
            permissionItems.add(new PermissionItem(Manifest.permission.CAMERA, "相机", R.drawable.permission_ic_camera));
            permissionItems.add(new PermissionItem(Manifest.permission.RECORD_AUDIO, "麦克风", R.drawable.permission_ic_micro_phone));
            permissionItems.add(new PermissionItem(Manifest.permission.WRITE_EXTERNAL_STORAGE, "存储", R.drawable.permission_ic_storage));
            permissionItems.add(new PermissionItem(Manifest.permission.READ_EXTERNAL_STORAGE, "", 0));
            try {
                HiPermission.create(LoginActivity.this)
                        .title("欢迎访问" + getString(R.string.app_name))
                        .permissions(permissionItems)
                        .checkMutiPermission(new PermissionCallback() {
    
                            String TAG = getString(R.string.app_name);
    
                            @Override
                            public void onClose() {
                                Log.i(TAG, "onClose");
                            }
    
                            @Override
                            public void onFinish() {
                                Log.i(TAG, "onFinish");
                            }
    
                            @Override
                            public void onDeny(String permission, int position) {
                                Log.i(TAG, "onDeny- permission:" + permission + "   position:" + position);
                            }
    
                            @Override
                            public void onGuarantee(String permission, int position) {
                                Log.i(TAG, "onGuarantee");
                            }
                        });
            } catch (Exception ex) {
                ex.printStackTrace();
            }
    
        }
    

    当安卓手机首次进入该Demo时, 将弹窗提示获取设备权限:

    注:若禁止了这两个权限,后续就无法进行正常的视频聊天了!

    (2)屏幕录制权限

    MultimediaManagerFactory.GetSingleton().setDesktopRecordActivity(MainActivity.this);
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
      super.onActivityResult(requestCode, resultCode, data);
      MultimediaManagerFactory.GetSingleton().setDesktopRecordActivityResult(requestCode,resultCode,data);
    }
    

    当收到其他在线用户的屏幕分享请求并回复同意时,将弹窗获取屏幕权限:

    注:若禁止该权限,后续对方就无法看到分享者的屏幕了。

    3. 发送视频聊天请求

    当发起视频聊天时,将显示视频聊天窗口,并打开手机摄像头预览画面,然后向对方发送视频通话请求:

    CameraSurfaceView2 myView = null;
    MultimediaManagerFactory.GetSingleton().getAudioMessageController().dispose();
    AndroidUtil.OpenSpeaker(this);
    try {
      MultimediaManagerFactory.GetSingleton().openCamera();
    } catch (Exception e) {
      e.printStackTrace();
    }
    this.tv_nick = (TextView) findViewById(R.id.tv_nick);
    myView = (CameraSurfaceView2) findViewById(R.id.local_surface);
    myView.setSurfaceEventLister(new CameraSurfaceView2.SurfaceEventLister() {
      @Override
      public void surfaceCreated(SurfaceHolder surfaceHolder) {
        setShowPreviewHolder(surfaceHolder);
      }
    });
    myView.setZOrderOnTop(true);
    MultimediaManagerFactory.GetSingleton().setCameraDeviceIndex(1);//设置为前置摄像头
    //设置摄像头打开成功回调函数
    MultimediaManagerFactory.GetSingleton().setCameraOpenCallBack(this);
    if (StringHelper.isNullOrEmpty(userId)) {
      isSender = true;
      //我向对方发起视频
      userId = getIntent().getStringExtra(TalkingID);
      if (StringHelper.isNullOrEmpty(userId)) {
        tv_nick.setText("未知requestID");
      } else {
        ll_to_callLayout.setVisibility(View.VISIBLE);
        coming_callLayout.setVisibility(View.GONE);
        hangup.setVisibility(View.VISIBLE);
        MainActivity.getInstance().sendMediaCommunicate(userId, CommunicateType.Request);
        tv_tips.setText("正在等待对方接受邀请");
      }
    }
    

    运行起来的UI截图如下所示:

    4. 回复对方视频请求

    当收到对方的视频聊天邀请时,将进入视频预览页面,显示视频邀请。

    当点击“接听”或“挂断”按钮时,就会发送视频聊天回复消息:

    //接听
    answer.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        try {
            MainActivity.getInstance().stopRingForCalling();
            coming_callLayout.setVisibility(View.GONE);
            ll_to_callLayout.setVisibility(View.VISIBLE);
            openConnector();
            MainActivity.getInstance().sendMediaCommunicate(userId, CommunicateType.Agree);
          } catch (Exception ex) {
             ex.printStackTrace();
          }
        }
    });
    //拒绝
    refuse.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
      try {
          MainActivity.getInstance().sendMediaCommunicate(userId, CommunicateType.Reject);
          MainActivity.getInstance().stopRingForCalling();
          finish();
        } catch (Exception ex) {
          ex.printStackTrace();
        }
      }
    });
    

    5. 相互连接对方的摄像头、麦克风

    当对方回复同意时,自己和对方将相互连接到对方的麦克风和摄像头。

    private void openConnector() {
      try {
        if (thread2 != null) {
          thread2.interrupt();
        }
        hangup.setVisibility(View.VISIBLE);
        switch_camera_layout.setVisibility(View.VISIBLE);
        ll_top_container.setVisibility(View.INVISIBLE);
    
        thread2 = new Thread(new Runnable() {
          Override
          public void run() {
            //在这里关闭不能重新连接
            cameraConnector = new CameraConnector();
            cameraConnector.setOtherVideoPlayerSurfaceView(otherView);
            cameraConnector.setConnectorEventListener(new IConnectorEventListener() {
              @Override
              public void connectEnded(ConnectResult connectResult) {
                final String connectFailStr = MainActivity.getConnectFailStr(connectResult);
                if (!StringHelper.isNullOrEmpty(connectFailStr)) {
                  mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                      tv_camera_failure_cause.setText("摄像头:" + connectFailStr);
                    }
                  });
                }
                boolean isMobilePhone = cameraConnector.getOwnerMachineType() == MachineType.Android || cameraConnector.getOwnerMachineType() == MachineType.IOS;
                cameraConnector.setVideoUniformScale(true, isMobilePhone); //false 表示小的那边留黑边,true表示裁剪大的那一边
              }
              @Override
              public void disconnected(ConnectorDisconnectedType connectorDisconnectedType) {
    
              }
            });
    
            cameraConnector.beginConnect(loginID);
            microphoneConnector = new MicrophoneConnector();
            microphoneConnector.setConnectorEventListener(new IConnectorEventListener() {
              @Override
              public void connectEnded(final ConnectResult connectResult) {
                mHandler.post(new Runnable() {
                  @Override
                  public void run() {
                    if (connectResult == ConnectResult.Succeed) {
                      startTimer(SystemClock.elapsedRealtime());
                    } else {
                      String connectFailStr = MainActivity.getConnectFailStr(connectResult);
                      tv_mic_failure_cause.setText("麦克风:" + connectFailStr);
                    }
                  }
                });
              }
    
              @Override
              public void disconnected(ConnectorDisconnectedType connectorDisconnectedType) {
    
              }
            });
            microphoneConnector.beginConnect(loginID);
          }
        });
        thread2.start();
      } catch (Exception ex) {
        ex.printStackTrace();
      }
    }
    

    当摄像头和麦克风都连接成功后,就可以正常视频聊天了。

    6. 屏幕分享功能实现

    屏幕分享功能的业务逻辑与视频聊天功能的业务逻辑是相似的,这里就不再赘述了,大家可以自行参看源码。

    四.部署运行

    关于Demo的源码介绍就这么多了,接下来我们看如何将Demo运行起来。

    1. 启动服务端

    解压 VideoChatMini.rar 后,进入解压目录,依次进入 VideoChatMini.Server -> bin -> debug 。
    双击 Oraycn.Demos.VideoChatMini.Server.exe ,即可启动视频聊天服务端。服务端运行界面如下所示:

    2. 运行安卓端

    解压安卓端源码压缩包 VideoChatMini.Android.rar,解压后,使用 Android Studio 打开并编译,将生成的apk发送到手机安装。

    我们可以用两部手机,启动并登录两个安卓客户端,登录的账号密码可以随便填。安卓端登录成功后,出现如下界面:

    我们在 “对方ID” 输入框中填上对方的登录账号,就可以发起视频聊天邀请了。对应的界面截图在前面已经贴出来了。
    对方同意视频邀请后,两个人就开启视频聊天了,运行效果如下所示:

    五.源码下载

    Android 端:VideoChatMini.Android.rar

    服务端 + PC 端:VideoChatMini.rar

    在这里,我也给出了PC端的源码,PC端项目对应的目录是 VideoChatMini.ClientWPF。服务端和PC端都是 C# 开发的(开发环境是 VS2022),PC端UI使用的是WPF。

    PC端和安卓端是可以互通的,也就是可以相互视频通话,以及观看屏幕/桌面。

    希望这篇文章会对你有所帮助,谢谢。

  • 相关阅读:
    软件测试之测试评估
    Insight h2database SQL like 查询
    阿里云云原生一体化数仓 — 离线实时一体化新能力解读
    C++ Python网易云音乐播放器
    肽核酸PNA规格信息|大豆过氧化酶标记肽核酸(Peptide nucleic acid,PNA)
    免杀对抗-反沙盒+反调试
    开源基于Rust编写的Web服务器
    Packet Tracer - 配置 IPv4 和 IPv6 接口
    MySQL8--Windows下使用压缩包安装的方法
    清除Ubuntu系统中的无法启动的Ubuntu 24实例
  • 原文地址:https://www.cnblogs.com/zhuweisky/p/17579589.html