当前位置: 首页 > news >正文

手机视频聊天分享

在人际互动的手机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();}
}

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

相关文章:

手机视频聊天分享

在人际互动的手机APP中&#xff0c;增加语音视频聊天功能是一个常见的需求。而现在&#xff0c;更进一步&#xff0c;在某些场景下&#xff0c;我们需要能将自己的手机屏幕分享给他人&#xff0c;或者是观看他人的手机屏幕。那么&#xff0c;这些常见的功能是如何实现的了&…...

神经网络小记-优化器

优化器是深度学习中用于优化神经网络模型的一类算法&#xff0c;其主要作用是根据模型的损失函数来调整模型的参数&#xff0c;使得模型能够更好地拟合训练数据&#xff0c;提高模型的性能和泛化能力。优化器在训练过程中通过不断更新模型的参数&#xff0c;使模型逐步接近最优…...

200+行代码写一个简易的Qt界面贪吃蛇

照例先演示一下&#xff1a; 一个简单的Qt贪吃蛇&#xff0c;所有的图片都是我自己画的&#xff08;得意&#xff09;。 大致的运行逻辑和之前那个200行写一个C小黑窗贪吃蛇差不多&#xff0c;因此在写这个项目的时候&#xff0c;大多情况是在想怎么通过Qt给展现出来。 背景图…...

redis中使用bloomfilter的白名单功能解决缓存穿透问题

一 缓存预热 1.1 缓存预热 将需要的数据提前缓存到缓存redis中&#xff0c;可以在服务启动时候&#xff0c;或者在使用前一天完成数据的同步等操作。保证后续能够正常使用。 1.2 缓存穿透 在redis中&#xff0c;查询redis缓存数据没有内容&#xff0c;接着查询mysql数据库&…...

Spring Boot 2.7.8以后mysql-connector-java与mysql-connector-j

错误信息 如果升级到Spring Boot 2.7.8&#xff0c;可以看到因为找不到mysql-connector-java依赖而出现错误。 配置&#xff1a; <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId>&l…...

03|「如何写好一个 Prompt」

前言 Prompt 文章目录 前言一、通用模板和范式1. 组成2. 要求1&#xff09;文字描述2&#xff09;注意标点符号 一、通用模板和范式 1. 组成 指令&#xff08;角色&#xff09; 生成主体 额外要求 指令&#xff1a;模型具体完成的任务描述。例如&#xff0c;翻译一段文字&…...

关于提示词 Prompt

Prompt原则 原则1 提供清晰明确的指示 注意在提示词中添加正确的分割符号 prompt """ 请给出下面文本的摘要&#xff1a; <你的文本> """可以指定输出格式&#xff0c;如&#xff1a;Json、HTML提示词中可以提供少量实例&#xff0c;…...

【Linux多线程】线程的互斥与同步(附抢票案例代码+讲解)

线程的互斥与同步 &#x1f4ab; 概念引入⭐️临界资源&#xff08;Critical Resource&#xff09;&#xff1a;&#x1f31f;临界区&#xff08;Critical Section&#xff09;&#xff1a;✨互斥&#xff08;Mutex&#xff09;&#xff1a; ⚡️结合代码看互斥☄️ 代码逻辑&a…...

ajax概述

目录 1.什么是ajax 2.ja原生ajax 3.jQuery框架的ajax 4.综合案例 1.什么是ajax Ajax 即"Asynchronous Javascript And XML"&#xff08;异步 JavaScript 和 XML&#xff09;&#xff0c;是指一种创建交互式网页应用的网页开发技术。Ajax 异步 JavaScript 和 XML&…...

小白带你学习linux的mysql服务(主从mysql服务和读写分离三十一)

目录 二、MySQL Replication优点&#xff1a; 三、MySQL复制类型 1、异步复制&#xff08;Asynchronous repication&#xff09; 2、全同步复制&#xff08;Fully synchronous replication&#xff09; 3、半同步复制&#xff08;Semisynchronous replication&#xff09;…...

【低代码专题方案】iPaaS运维方案,助力企业集成平台智能化高效运维

01 场景背景 随着IT行业的发展和各家企业IT建设的需要&#xff0c;信息系统移动化、社交化、大数据、系统互联、数据打通等需求不断增多&#xff0c;企业集成平台占据各个企业领域&#xff0c;成为各业务系统数据传输的中枢。 集成平台承接的业务系统越多&#xff0c;集成平台…...

Android SDK 上手指南||第一章 环境需求||第二章 IDE:Eclipse速览

第一章 环境需求 这是我们系列教程的第一篇&#xff0c;让我们来安装Android的开发环境并且把Android SDK运行起来&#xff01; 介绍 欢迎来到Android SDK入门指南系列文章&#xff0c;如果你想开始开发Android App&#xff0c;这个系列将从头开始教你所须的技能。我们假定你…...

Amazon Linux上使用ec2-user来设置开机自启动的shell脚本

要在Amazon Linux上使用ec2-user来设置开机自启动的shell脚本&#xff0c;可以按照以下步骤操作&#xff1a; 1. 确保您拥有要设置自启动的shell脚本。假设脚本的路径是/home/ec2-user/myscript.sh。 2. 使用以下命令打开/etc/rc.d/rc.local文件&#xff1a; shell sudo nano /…...

【Spring】Spring 下载及其 jar 包

根据 【动力节点】最新Spring框架教程&#xff0c;全网首套Spring6教程&#xff0c;跟老杜从零学spring入门到高级 以及老杜的原版笔记 https://www.yuque.com/docs/share/866abad4-7106-45e7-afcd-245a733b073f?# 《Spring6》 进行整理&#xff0c; 文档密码&#xff1a;mg9b…...

蓝桥杯2023年第十四届省赛-飞机降落

题目描述 N 架飞机准备降落到某个只有一条跑道的机场。其中第 i 架飞机在 Ti 时刻到达机场上空&#xff0c;到达时它的剩余油料还可以继续盘旋 Di 个单位时间&#xff0c;即它最早 可以于 Ti 时刻开始降落&#xff0c;最晚可以于 Ti Di 时刻开始降落。降落过程需要 Li个单位时…...

STM32 串口实验(学习一)

本章将实现如下功能&#xff1a;STM32通过串口和上位机对话&#xff0c;STM32在收到上位机发过来的字符串后&#xff0c;原原本本返回给上位机。 STM32 串口简介 串口作为MCU的重要外部接口&#xff0c;同时也是软件开发重要的调试手段&#xff0c;其重要性不言而喻。现在基本…...

多臂治疗规则的 Qini 曲线(Stefan Wager)

英文题目&#xff1a; Qini Curves for Multi-Armed Treatment Rules 中文题目&#xff1a;多臂治疗规则的 Qini 曲线 单位&#xff1a;Stefan Wager 论文链接&#xff1a; 代码&#xff1a;GitHub - grf-labs/maq: Treatment rule evaluation via the multi-armed Qini …...

NOSQL之Redis配置及优化

目录 一、关系型数据库 二、非关系型数据库 三、关系型数据库和非关系型数据库区别 1、数据存储方式不同 2、扩展方式不同 3、对事务性的支持不同 四、Redis简介 五、Redis优点 &#xff08;1&#xff09;具有极高的数据读写速度 &#xff08;2&#xff09;支持丰富的…...

植物一区HR | 植物生理组+转录组:揭示豆科植物响应干旱胁迫机制

PlantArray 植物高通量生理学表型监测系统 是一套以植物生理学为基础的高精度&#xff0c;高通量&#xff0c;自动化表型监测系统&#xff0c;集合实验设置、数据分析、决策工具于一身&#xff0c;能够高通量实时动态监测并进行全天候生理及环境参数采集&#xff0c;是进行植物…...

TCP粘包问题

TCP粘包问题 TCP粘包问题造成TCP粘包的原因发送方原因接收方原因 如何处理TCP粘包发送方接收方应用层 为什么UDP没有粘包问题 TCP粘包问题 TCP粘包就是指发送方发送的若干包数据到达接收方时粘成了一包&#xff0c;从接收缓冲区来看&#xff0c;后一包数据的头紧接着前一包数据…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序

一、开发环境准备 ​​工具安装​​&#xff1a; 下载安装DevEco Studio 4.0&#xff08;支持HarmonyOS 5&#xff09;配置HarmonyOS SDK 5.0确保Node.js版本≥14 ​​项目初始化​​&#xff1a; ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

python执行测试用例,allure报乱码且未成功生成报告

allure执行测试用例时显示乱码&#xff1a;‘allure’ &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;ڲ&#xfffd;&#xfffd;&#xfffd;&#xfffd;ⲿ&#xfffd;&#xfffd;&#xfffd;Ҳ&#xfffd;&#xfffd;&#xfffd;ǿ&#xfffd;&am…...

C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

CSS | transition 和 transform的用处和区别

省流总结&#xff1a; transform用于变换/变形&#xff0c;transition是动画控制器 transform 用来对元素进行变形&#xff0c;常见的操作如下&#xff0c;它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...

FFmpeg:Windows系统小白安装及其使用

一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】&#xff0c;注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录&#xff08;即exe所在文件夹&#xff09;加入系统变量…...