首页 > 编程语言 >如何实现安卓屏幕分享及视频聊天?(源码)

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

时间:2023-07-25 12:34:14浏览次数:31  
标签:视频 安卓 public 源码 Override new 屏幕 void

在一些有人际互动的手机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 {
 
    /// <summary>
    /// 视频请求 0
    /// </summary>
    public static final int VideoRequest = 0;

    /// <summary>
    /// 回复视频请求的结果 1
    /// </summary>
    public static final int VideoResult = 1;

    /// <summary>
    /// 通知对方 挂断 视频连接 2
    /// </summary>
    public static final int CloseVideo = 2;

    /// <summary>
    /// 通知好友 网络原因,导致 视频中断 3
    /// </summary>
    public static final int NetReasonCloseVideo = 3;

    /// <summary>
    /// 通知对方(忙线中) 挂断 视频连接 4
    /// </summary>
    public static final int BusyLine = 4;

    /// <summary>
    /// 屏幕分享请求 5
    /// </summary>
    public static final int DesktopRequest = 5;

    /// <summary>
    /// 回复屏幕分享请求的结果 6
    /// </summary>
    public static final int DesktopResult = 6;

    /// <summary>
    ///  主动取消屏幕分享请求
    /// </summary>
    public static final int CancelDesktop = 7;

    /// <summary>
    ///  对方(主人端)主动断开屏幕分享
    /// </summary>
    public static final int OwnerCloseDesktop = 8;

    /// <summary>
    /// 客人端断开屏幕分享
    /// </summary>
    public static final int GuestCloseDesktop = 9;
}

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

2. 获取安卓系统权限

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

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

private void getPermission() {
        List<PermissionItem> permissionItems = new ArrayList<PermissionItem>();
        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端和安卓端是可以互通的,也就是可以相互视频通话,以及观看屏幕/桌面。

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

标签:视频,安卓,public,源码,Override,new,屏幕,void
From: https://www.cnblogs.com/zhuweisky/p/17579589.html

相关文章

  • Android 双击点亮屏幕
    1.修改配置:frameworks\base\services\core\java\com\android\server\power\PowerManagerService.javamSupportsDoubleTapWakeConfig=resources.getBoolean(com.android.internal.R.bool.config_supportDoubleTapWake);if(mSupportsDoubleTapWakeConfig){......
  • RTSP/Onvif视频服务器LntonNVR(源码版)平台鉴权功能的应用场景和意义
    LntonNVR平台是基于RTSP/Onvif协议的视频接入、处理及分发平台,可以分发出RTSP、RTMP、WS-FLV、HTTP-FLV、HLS、WebRTC等格式的视频流,还可以提供API接口,便于用户调用集成。用户也能够直接将流地址放到第三方播放器进行播放。感兴趣的朋友也可以去CSDN(https://blog.csdn.net/LNTON)......
  • 小程序蓝牙配网--安卓与IOS的区别
    1.getWifiList接口:IOS无法获取WiFi列表,只有安卓能够获取。IOS可以通过getConnectedWifi接口获取当前已连接的WIFI名,可以适当给予用户引导2.配网过程:安卓可以事先写死serviceId(0000FFFF-0000-1000-8000-00805F9B34FB)与characteristicId(0000FF02-0000-1000-8000-00805F9B34FB),以......
  • 爆款阿里P5到P7晋升之路,九大源码文档助我超神果然努力幸运并存
    前言相信有许多的程序员,工作了这么多年;但是依然不知道自己掌握的技术栈+项目,究竟达到了阿里的什么职级,还有薪资水平是什么样的;下面就给大家分享一波对标阿里p5到P7职级所掌握的技术栈和薪资水平的路线,后续还有P8到P9的路线。P5到P7p8到p9九大源码文档经过这套学习路线的学习,让我渐......
  • session设置源码
    setting中的SessionMiddleware必须设置 查看中间件源码 如果后面在试图函数使用request.session=像 SessionStore()这个函数中传递了Cookie如果视图函数定义了如下方法 执行clear清空_session_cache 至此process_request结束查看process_response下面两......
  • SpringBoot+Vue实现校园二手系统。前后端分离技术【完整功能介绍+实现详情+源码】
    前言       这个系统基本上可以改造为其它类似的系统。后台管理基本上一致。前台进行一些页面样式的改造就可以变成一个新的系统。有时间,做几个变体系统。       闲的无聊,把大学时候做的一个系统进行了重构。将项目拆分成完全前后端分离的形式。客户端采用一套、商家......
  • 国标GB28181视频平台LntonGBS(含源码)国标视频平台播放视频时偶尔出现播放失败的问题解
    LntonGBS是基于公安部推出的安防主流协议(国标GB28181协议)的视频接入、处理及分发平台,具有视频直播监控、云端录像、云存储、检索回放、智能告警、语音对讲等功能,能够涵盖所有监控领域的视频能力需求,已经在大量的项目中落地应用,如明厨亮灶、平安乡村、雪亮工程等。有用户反馈,在某项......
  • SpringBoot源码第四章-invokeBeanFactoryPostProcessors
    invokeBeanFactoryPostProcessors()实例化和调用所有BeanFactoryPostProcessorprotectedvoidinvokeBeanFactoryPostProcessors(ConfigurableListableBeanFactorybeanFactory){ PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory,getBeanFa......
  • 安卓嵌入穿山甲广告
    /***加载开屏广告*/privatevoidloadSplashAd(){Log.d("TAG",":loadSplashAd");TTAdNativemTTAdNative=TTAdSdk.getAdManager().createAdNative(this);AdSlotadSlot=newAdSlot.Builder().setCodeId("8883846......
  • 在Windows上编译Pytorch 源码
    在Windows上编译PyTorch源码作为一名经验丰富的开发者,我将向你介绍如何在Windows上编译PyTorch源码。编译PyTorch源码可以帮助你获得更多的灵活性,以及对PyTorch内部机制的更深入的了解。下面是整个过程的步骤:步骤操作1安装Git2安装CMake3安装Python4克隆Py......