手机视频聊天分享
在人际互动的手机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);@Overridepublic void onClose() {Log.i(TAG, "onClose");}@Overridepublic void onFinish() {Log.i(TAG, "onFinish");}@Overridepublic void onDeny(String permission, int position) {Log.i(TAG, "onDeny- permission:" + permission + " position:" + position);}@Overridepublic void onGuarantee(String permission, int position) {Log.i(TAG, "onGuarantee");}});} catch (Exception ex) {ex.printStackTrace();}}
当安卓手机首次进入该Demo时, 将弹窗提示获取设备权限:
注:若禁止了这两个权限,后续就无法进行正常的视频聊天了!
(2)屏幕录制权限
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() {@Overridepublic 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("正在等待对方接受邀请");}
}
4. 回复对方视频请求
当收到对方的视频聊天邀请时,将进入视频预览页面,显示视频邀请。
当点击“接听”或“挂断”按钮时,就会发送视频聊天回复消息:
//接听
answer.setOnClickListener(new View.OnClickListener() {@Overridepublic 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() {@Overridepublic 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() {Overridepublic void run() {//在这里关闭不能重新连接cameraConnector = new CameraConnector();cameraConnector.setOtherVideoPlayerSurfaceView(otherView);cameraConnector.setConnectorEventListener(new IConnectorEventListener() {@Overridepublic void connectEnded(ConnectResult connectResult) {final String connectFailStr = MainActivity.getConnectFailStr(connectResult);if (!StringHelper.isNullOrEmpty(connectFailStr)) {mHandler.post(new Runnable() {@Overridepublic void run() {tv_camera_failure_cause.setText("摄像头:" + connectFailStr);}});}boolean isMobilePhone = cameraConnector.getOwnerMachineType() == MachineType.Android || cameraConnector.getOwnerMachineType() == MachineType.IOS;cameraConnector.setVideoUniformScale(true, isMobilePhone); //false 表示小的那边留黑边,true表示裁剪大的那一边}@Overridepublic void disconnected(ConnectorDisconnectedType connectorDisconnectedType) {}});cameraConnector.beginConnect(loginID);microphoneConnector = new MicrophoneConnector();microphoneConnector.setConnectorEventListener(new IConnectorEventListener() {@Overridepublic void connectEnded(final ConnectResult connectResult) {mHandler.post(new Runnable() {@Overridepublic void run() {if (connectResult == ConnectResult.Succeed) {startTimer(SystemClock.elapsedRealtime());} else {String connectFailStr = MainActivity.getConnectFailStr(connectResult);tv_mic_failure_cause.setText("麦克风:" + connectFailStr);}}});}@Overridepublic void disconnected(ConnectorDisconnectedType connectorDisconnectedType) {}});microphoneConnector.beginConnect(loginID);}});thread2.start();} catch (Exception ex) {ex.printStackTrace();}
}
当摄像头和麦克风都连接成功后,就可以正常视频聊天了。
相关文章:
![](https://www.ngui.cc/images/no-images.jpg)
手机视频聊天分享
在人际互动的手机APP中,增加语音视频聊天功能是一个常见的需求。而现在,更进一步,在某些场景下,我们需要能将自己的手机屏幕分享给他人,或者是观看他人的手机屏幕。那么,这些常见的功能是如何实现的了&…...
![](https://www.ngui.cc/images/no-images.jpg)
神经网络小记-优化器
优化器是深度学习中用于优化神经网络模型的一类算法,其主要作用是根据模型的损失函数来调整模型的参数,使得模型能够更好地拟合训练数据,提高模型的性能和泛化能力。优化器在训练过程中通过不断更新模型的参数,使模型逐步接近最优…...
![](https://img-blog.csdnimg.cn/45aa9af60df1487d919aa8395ee4d059.gif)
200+行代码写一个简易的Qt界面贪吃蛇
照例先演示一下: 一个简单的Qt贪吃蛇,所有的图片都是我自己画的(得意)。 大致的运行逻辑和之前那个200行写一个C小黑窗贪吃蛇差不多,因此在写这个项目的时候,大多情况是在想怎么通过Qt给展现出来。 背景图…...
![](https://img-blog.csdnimg.cn/566ded8de54648968315fc74d442f80d.png)
redis中使用bloomfilter的白名单功能解决缓存穿透问题
一 缓存预热 1.1 缓存预热 将需要的数据提前缓存到缓存redis中,可以在服务启动时候,或者在使用前一天完成数据的同步等操作。保证后续能够正常使用。 1.2 缓存穿透 在redis中,查询redis缓存数据没有内容,接着查询mysql数据库&…...
Spring Boot 2.7.8以后mysql-connector-java与mysql-connector-j
错误信息 如果升级到Spring Boot 2.7.8,可以看到因为找不到mysql-connector-java依赖而出现错误。 配置: <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId>&l…...
![](https://www.ngui.cc/images/no-images.jpg)
03|「如何写好一个 Prompt」
前言 Prompt 文章目录 前言一、通用模板和范式1. 组成2. 要求1)文字描述2)注意标点符号 一、通用模板和范式 1. 组成 指令(角色) 生成主体 额外要求 指令:模型具体完成的任务描述。例如,翻译一段文字&…...
![](https://img-blog.csdnimg.cn/c332a78b30eb4e1cb571844d0fa9a81f.png)
关于提示词 Prompt
Prompt原则 原则1 提供清晰明确的指示 注意在提示词中添加正确的分割符号 prompt """ 请给出下面文本的摘要: <你的文本> """可以指定输出格式,如:Json、HTML提示词中可以提供少量实例,…...
![](https://img-blog.csdnimg.cn/ca8f9edfd4764a0189aeed57b9132eec.png)
【Linux多线程】线程的互斥与同步(附抢票案例代码+讲解)
线程的互斥与同步 💫 概念引入⭐️临界资源(Critical Resource):🌟临界区(Critical Section):✨互斥(Mutex): ⚡️结合代码看互斥☄️ 代码逻辑&a…...
![](https://img-blog.csdnimg.cn/87aa23655d944f8786e956ce372b3c47.png)
ajax概述
目录 1.什么是ajax 2.ja原生ajax 3.jQuery框架的ajax 4.综合案例 1.什么是ajax Ajax 即"Asynchronous Javascript And XML"(异步 JavaScript 和 XML),是指一种创建交互式网页应用的网页开发技术。Ajax 异步 JavaScript 和 XML&…...
![](https://img-blog.csdnimg.cn/32082e6af69f42359b12b1b729ab7731.png)
小白带你学习linux的mysql服务(主从mysql服务和读写分离三十一)
目录 二、MySQL Replication优点: 三、MySQL复制类型 1、异步复制(Asynchronous repication) 2、全同步复制(Fully synchronous replication) 3、半同步复制(Semisynchronous replication)…...
![](https://img-blog.csdnimg.cn/img_convert/6484acac377e9a5379e3c64f1769a0c6.webp?x-oss-process=image/format,png)
【低代码专题方案】iPaaS运维方案,助力企业集成平台智能化高效运维
01 场景背景 随着IT行业的发展和各家企业IT建设的需要,信息系统移动化、社交化、大数据、系统互联、数据打通等需求不断增多,企业集成平台占据各个企业领域,成为各业务系统数据传输的中枢。 集成平台承接的业务系统越多,集成平台…...
![](https://img-blog.csdnimg.cn/img_convert/2ec5f6d864d831a2a9df725ca070a38e.png)
Android SDK 上手指南||第一章 环境需求||第二章 IDE:Eclipse速览
第一章 环境需求 这是我们系列教程的第一篇,让我们来安装Android的开发环境并且把Android SDK运行起来! 介绍 欢迎来到Android SDK入门指南系列文章,如果你想开始开发Android App,这个系列将从头开始教你所须的技能。我们假定你…...
![](https://www.ngui.cc/images/no-images.jpg)
Amazon Linux上使用ec2-user来设置开机自启动的shell脚本
要在Amazon Linux上使用ec2-user来设置开机自启动的shell脚本,可以按照以下步骤操作: 1. 确保您拥有要设置自启动的shell脚本。假设脚本的路径是/home/ec2-user/myscript.sh。 2. 使用以下命令打开/etc/rc.d/rc.local文件: shell sudo nano /…...
![](https://img-blog.csdnimg.cn/52fb40c4c7bb4c06abf079ca01903fbb.png)
【Spring】Spring 下载及其 jar 包
根据 【动力节点】最新Spring框架教程,全网首套Spring6教程,跟老杜从零学spring入门到高级 以及老杜的原版笔记 https://www.yuque.com/docs/share/866abad4-7106-45e7-afcd-245a733b073f?# 《Spring6》 进行整理, 文档密码:mg9b…...
![](https://www.ngui.cc/images/no-images.jpg)
蓝桥杯2023年第十四届省赛-飞机降落
题目描述 N 架飞机准备降落到某个只有一条跑道的机场。其中第 i 架飞机在 Ti 时刻到达机场上空,到达时它的剩余油料还可以继续盘旋 Di 个单位时间,即它最早 可以于 Ti 时刻开始降落,最晚可以于 Ti Di 时刻开始降落。降落过程需要 Li个单位时…...
![](https://img-blog.csdnimg.cn/e9496aacb478424fb90d3cb008c6c5f6.png)
STM32 串口实验(学习一)
本章将实现如下功能:STM32通过串口和上位机对话,STM32在收到上位机发过来的字符串后,原原本本返回给上位机。 STM32 串口简介 串口作为MCU的重要外部接口,同时也是软件开发重要的调试手段,其重要性不言而喻。现在基本…...
![](https://img-blog.csdnimg.cn/c03a7f180665410faa66b66ca75cf381.png)
多臂治疗规则的 Qini 曲线(Stefan Wager)
英文题目: Qini Curves for Multi-Armed Treatment Rules 中文题目:多臂治疗规则的 Qini 曲线 单位:Stefan Wager 论文链接: 代码:GitHub - grf-labs/maq: Treatment rule evaluation via the multi-armed Qini …...
![](https://img-blog.csdnimg.cn/6af9638114964b68b090979245aa6e63.png)
NOSQL之Redis配置及优化
目录 一、关系型数据库 二、非关系型数据库 三、关系型数据库和非关系型数据库区别 1、数据存储方式不同 2、扩展方式不同 3、对事务性的支持不同 四、Redis简介 五、Redis优点 (1)具有极高的数据读写速度 (2)支持丰富的…...
![](https://img-blog.csdnimg.cn/img_convert/3c296170b403a5912fd24c0fae7232dc.jpeg)
植物一区HR | 植物生理组+转录组:揭示豆科植物响应干旱胁迫机制
PlantArray 植物高通量生理学表型监测系统 是一套以植物生理学为基础的高精度,高通量,自动化表型监测系统,集合实验设置、数据分析、决策工具于一身,能够高通量实时动态监测并进行全天候生理及环境参数采集,是进行植物…...
![](https://www.ngui.cc/images/no-images.jpg)
TCP粘包问题
TCP粘包问题 TCP粘包问题造成TCP粘包的原因发送方原因接收方原因 如何处理TCP粘包发送方接收方应用层 为什么UDP没有粘包问题 TCP粘包问题 TCP粘包就是指发送方发送的若干包数据到达接收方时粘成了一包,从接收缓冲区来看,后一包数据的头紧接着前一包数据…...
![](https://img-blog.csdnimg.cn/3e38f40af93a44afa8dcb95e3a3ea6d1.png)
QT【day1】
登录框: #include "mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent) {//窗口设置this->setFixedSize(600,600); //大小this->setWindowTitle("MUMU"); //文本内容this->setWindowOpacity(0.8); //透…...
![](https://img-blog.csdnimg.cn/d248e8f19b51463297f721a2641fc471.png)
【Golang】Golang进阶系列教程--为什么 Go 不支持 []T 转换为 []interface
文章目录 前言官方解释内存布局程序运行中的内存布局通用方法 前言 在 Go 中,如果 interface{} 作为函数参数的话,是可以传任意参数的,然后通过类型断言来转换。 举个例子: package mainimport "fmt"func foo(v inter…...
![](https://img-blog.csdnimg.cn/95148e11605f41f2b0017ba32b3fd5ef.png)
两数相加 II——力扣445
题目描述 法一 栈 本题旨在从后往前加,为了逆序处理所有数位,利用栈,把数字压入栈中,再依次取出相加,注意进位!进位是/10,另外需要注意栈的常用函数,push()、pop()、top()࿰…...
![](https://www.ngui.cc/images/no-images.jpg)
js获取上传视频的封面第一帧
代码如下:粘贴到这个在线编辑器里,可以测试效果。 菜鸟教程在线编辑器 <div><div style"flex: 1;border: 1px solid #999; position:relative;color: #333;background-color:#FFF2B8;"><span style"position: absolute…...
![](https://img-blog.csdnimg.cn/529448960ce844ce8b5746a72251ca10.png)
Nginx 高可用负载均衡(三种模式)
一、nginx普通集群负载均衡 1、安装keepalived (1)下载 https://www.keepalived.org/download.html(2)解压 tar -zxvf keepalived-2.0.18.tar.gz(3)使用configure命令配置安装目录与核心配置文件所在位置: ./configure --prefix/usr/local/keepalived --sysconf/e…...
![](https://www.ngui.cc/images/no-images.jpg)
Linux tail命令
在Linux中,tail命令用于查看文件的末尾内容。它可以显示文件的最后几行,默认情况下显示最后10行。 以下是一些常见的使用方式和示例: 显示文件的最后10行: tail filename将会显示名为filename的文件的最后10行内容。 显示文件…...
![](https://img-blog.csdnimg.cn/813d4e85537e4b30b71a6e7639fe814b.png#pic_center)
【屏幕适配发展介绍 Objective-C语言】
一、接下来,我们花一天时间,给大家介绍这个屏幕适配 1.那么,屏幕适配,是什么意思啊 我们说,写程序的时候,我们有时候要做 1)系统适配 2)屏幕适配 1)系统适配:是指的你写的这个代码,在iOS6、iOS7、iOS8,在不同的iOS系统下,是不是运行的效果,一致吧 这个指的是…...
![](https://www.ngui.cc/images/no-images.jpg)
linux中ls命令详解
ls 显示目录内容列表 补充说明 ls命令 就是list的缩写,用来显示目标列表,在Linux中是使用率较高的命令。ls命令的输出信息可以进行彩色加亮显示,以分区不同类型的文件。 语法 ls [选项] [文件名...][-1abcdfgiklmnopqrstuxABCDFGLNQRSUX…...
![](https://img-blog.csdnimg.cn/ad723e25ba764ff3a967c4f847a60911.png)
大盗阿福(记忆化搜索板子)
提供核心代码:(经典的记忆化搜索套路) int dfs(int pos){if(f[pos]!-1) return f[pos];//记忆化if(pos>n) return 0;//边界,越界int sum0;//模板int f10,f20;f1dfs(pos1);f2dfs(pos2)w[pos];summax(f1,f2);//模板f[pos]sum;//模…...
![](https://www.ngui.cc/images/no-images.jpg)
打卡力扣题目八
#左耳听风 ARST 打卡活动重启# 目录 一、问题 二、解题方法一 三、解题方法二 四、两种方法的区别 关于 ARTS 的释义 —— 每周完成一个 ARTS: ● Algorithm: 每周至少做一个 LeetCode 的算法题 ● Review: 阅读并点评至少一篇英文技术文章 ● Tips: 学习至少一…...
![](/images/no-images.jpg)
繁昌网站建设/微信管理系统登录入口
一、功能要求 登录接口小程序(需实现的功能如下) 1、输入用户名和密码 2、认证成功后显示欢迎信息 3、输错三次后锁定 二、逻辑思路分析: 定义一个文件用于存储用户名和密码:passwd.db 定义一个文件用于记录输入3次错误后的用户名,…...
![](http://images.cnitblog.com/blog/360778/201402/271056422044524.jpg)
吉林电商网站建设/百度推广费
按照http://blog.csdn.net/azkabannull/article/details/7872958中的方法,在cygwin中运行runbundler.sh; 按照http://oliver.zheng.blog.163.com/blog/static/1424115952011915113138431/中的方法,使用Bundle2PMVS.exe和prep_pmvs.sh&#x…...
![](/images/no-images.jpg)
深圳p2p网站建设/做销售有什么技巧和方法
一、忘记除SYS、SYSTEM用户之外的用户的登录密码。用SYS (或SYSTEM)用户登录: CONN SYS/PASS_WORD AS SYSDBA;使用如下语句修改用户的密码: ALTER USER user_name IDENTIFIED BY "newpass";注意:密码不能全是数字。并且不能是数字开头。否则会出现&#x…...
![](/images/no-images.jpg)
龙岗住房建设局网站/无锡百度竞价推广
JSON&XML: JSON----- //英译 Serialization:序列化 perform:执行 segue:继续 IOS5后 NSJSONSerialization解析 解析JSON SBJSON JSONKit touchJson的第三方库 性能:NSJSONSerial…...
![](http://blogsit.iteye.com/images/spinner.gif)
制作免费企业宣传视频/seo外包公司排名
1)修改开机默认壁纸 Android开机默认资源文件为:frameworks/base/core/res/res/values/config.xml 我们找到wallpaper行: <string name"default_wallpaper_component">null</string> 系统默认壁纸一般是一张静态图片&a…...
个人电脑做服务器网站/广告推销
点击上方“服务端思维”,选择“设为星标”回复”669“获取独家整理的精选资料集回复”加群“加入全国服务端高端社群「后端圈作者:终端研发部NO.1 Java.alng.NullPointerException这个异常大家肯定都经常遇到,异常的解释是 “程序遇上了空指…...