Android实现两台手机屏幕共享和远程控制
Posted ZEGO即构开发者
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android实现两台手机屏幕共享和远程控制相关的知识,希望对你有一定的参考价值。
1 屏幕共享功能介绍
屏幕共享是指在视频通话或互动直播过程中将屏幕内容以视频的方式分享给其他的观众,以增强互动体验,提高沟通效率。屏幕共享解决方案提升了用户实时视频通话的沟通效率。
屏幕共享在如下场景中应用广泛:
- 视频会议场景中,屏幕共享可以将讲话者本地的文件、数据、网页、PPT 等画面分享给其他与会人;
- 在线课堂场景中,屏幕共享可以将老师的课件、笔记、讲课内容等画面展示给学生观看。
2 屏幕共享示例源码下载
请参考 下载示例源码 获取源码。
相关源码请查看 “/ZegoExpressExample/Others/src/main/java/com/example/others/screensharing” 目录下的文件。
others
...
├── screensharing
│ ├── CaptureScreenService.java //此文件实现了系统 Service 接口
│ ├── ScreenSharingActivity.java // 此文件主要完成了通过 ZegoExpress SDK 将屏幕画面数据流推送到远端的工作
│ ├── VideoCaptureScreen.java //此文件用于通过安卓系统接口创建 VirtualDisplay 实例,获取屏幕数据,并发送给 ZEGO Express SDK
│ └── ZegoVideoCaptureCallback.java //此文件实现了 ZegoExpress 的 IZegoCustomVideoCaptureHandler
...
3 屏幕共享功能实现准备工作-集成屏幕共享SDK
在实现屏幕共享功能之前,请确保:
- 已在项目中集成 ZEGO Express SDK,实现基本的实时音视频功能,详情请参考 快速开始 - 集成 和 快速开始 - 实现视频通话。
- 已在 ZEGO 控制台 创建项目,并申请有效的 AppID 和 AppSign,详情请参考 控制台 - 项目管理 中的“项目信息”。
4 屏幕共享实现流程-即构屏幕共享SDK
我们需要结合 android 系统 API 和 ZEGO Express SDK 的自定义视频采集来进行屏幕分享。
下图展示了 Android 平台实现屏幕共享的数据流转:
4.1 获取用户录制屏幕授权
在录制屏幕前需要获取用户的授权,不同版本下需要获取的权限如下:
- Android 4.4 及之前版本必须获取到 root 权限后才能实现屏幕录制,由于目前大部分设备的系统版本都高于 4.4,该场景此处不做赘述。
- Android 5.0 及以上版本,可以使用系统提供的 MediaProjection 和 MediaProjectionManager 进行屏幕录制。该版本下可以不获取 root 权限,但会弹窗提示用户是否允许应用录制屏幕,需要用户授权。
- Android 10.0 及以上版本,屏幕录制使用系统 API 时需要用到前台服务,详情请参考 官方文档。
public static MediaProjectionManager mMediaProjectionManager;
if (Build.VERSION.SDK_INT < 21)
Toast.makeText(ZGVideoCaptureOriginUI.this, getString(R.string.record_request), Toast.LENGTH_SHORT).show();
finish();
else
// 5.0及以上版本
// 请求录屏权限,等待用户授权
mMediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
startActivityForResult(mMediaProjectionManager.createScreenCaptureIntent(), REQUEST_CODE);
4.2 屏幕共享SDK-创建 MediaProjection 实例
- 在 AndroidManifest.xml 中添加相关配置。
为实现 Android 10.0 及以上版本应用的屏幕录制,需要在代码中开启前台服务,并在 AndroidManifest.xml 中注册 Service,添加 foregroundServiceType 属性。
<application>
<activity android:name="im.zego.videocapture.ui.ZGVideoCaptureDemoUI" />
<activity android:name="im.zego.videocapture.ui.ZGVideoCaptureOriginUI"></activity>
<service android:name=".service.CaptureScreenService"
android:enabled="true"
android:foregroundServiceType="mediaProjection"/>
</application>
- 用户授权后创建 MediaProjection 实例。
- 对于 Android 10.0 以下版,直接在授权成功后获取 MediaProjection
- 对于 Android 10.0 及以上版本,MediaProjection 实例的创建需要在前台服务的 onStartCommand 方法中执行。
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK)
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.Q)
//Target版本高于等于10.0需要使用前台服务,并在前台服务的onStartCommand方法中创建MediaProjection
service=new Intent(ZGVideoCaptureOriginUI.this, CaptureScreenService.class);
service.putExtra("code",resultCode);
service.putExtra("data",data);
startForegroundService(service);
else
//Target版本低于10.0直接获取MediaProjection
mMediaProjection = mMediaProjectionManager.getMediaProjection(resultCode, data);
创建一个类,实现 Service
接口,在 onStartCommand
中创建 MediaProjection
实例。
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class CaptureScreenService extends Service
...
@Override
public int onStartCommand(Intent intent, int flags, int startId)
···
//在这里获取MediaProjection
ZGVideoCaptureOriginUI.mMediaProjection = ZGVideoCaptureOriginUI.mMediaProjectionManager.getMediaProjection(mResultCode, Objects.requireNonNull(mResultData));
return super.onStartCommand(intent, flags, startId);
···
4.3 屏幕共享SDK-开启 ZegoExpress SDK 的自定义视频采集功能
调用 ZegoExpress SDK 的 enableCustomVideoCapture 开启自定义采集功能,详情请参考 自定义视频采集。
//VideoCaptureScreen继承IZegoCustomVideoCaptureHandler,用于监听自定义采集onStart和onStop回调
VideoCaptureScreen videoCapture = new VideoCaptureScreen(ZGVideoCaptureOriginUI.mMediaProjection, DEFAULT_VIDEO_WIDTH, DEFAULT_VIDEO_HEIGHT, mSDKEngine);
//监听自定义采集开始停止回调
mSDKEngine.setCustomVideoCaptureHandler(videoCapture);
ZegoCustomVideoCaptureConfig videoCaptureConfig=new ZegoCustomVideoCaptureConfig();
//使用SurfaceTexture类型进行自定义采集
videoCaptureConfig.bufferType=ZegoVideoBufferType.SURFACE_TEXTURE;
//开始自定义采集
mSDKEngine.enableCustomVideoCapture(true, videoCaptureConfig, ZegoPublishChannel.MAIN);
4.4 屏幕共享SDK-登录房间并开始推流
调用 loginRoom 接口,传入房间 ID 参数 “roomID” 和用户参数 “user”,登录房间。
调用 startPublishingStream 接口,传入流 ID 参数 “streamID”,向远端用户发送本端的音视频流。
/** 创建用户 */
ZegoUser user = new ZegoUser("user1");
/** 开始登录房间 */
mSDKEngine.loginRoom("room1", user);
/** 开始推流 */
mSDKEngine.startPublishingStream("stream1");
4.5 创建 VirtualDisplay 并给 ZEGO Express SDK 发送屏幕数据-
1、创建 ZegoVideoCaptureCallback 类继承 IZegoCustomVideoCaptureHandler。
2、创建 VideoCaptureScreen 类继承 ZegoVideoCaptureCallback。
当收到 onStart 回调后,开发者可以通过 MediaProjection 创建 VirtualDisplay 实例,用于获取屏幕数据,并发送给 ZEGO Express SDK。
3、通过 createVirtualDisplay 系统 API 将虚拟显示器的内容渲染到 Surface。
//ZegoVideoCaptureCallback继承于IZegoCustomVideoCaptureHandler
class VideoCaptureScreen extends ZegoVideoCaptureCallback
@Override
//当收到onStart回调后,就可以通过MediaProjection创建VirtualDisplay,并给ZEGO SDK塞屏幕数据
public void onStart(ZegoPublishChannel channel)
if (mZegoEngine != null && !mIsCapturing && mMediaProjection != null)
mIsCapturing = true;
//通过ZEGO API getCustomVideoCaptureSurfaceTexture获取SurfaceTexture,该接口默认使用主路通道进行推流
SurfaceTexture texture = mZegoEngine.getCustomVideoCaptureSurfaceTexture();
texture.setDefaultBufferSize(mCaptureWidth, mCaptureHeight);
//通过获取的SurfaceTexture创建Surface
mSurface = new Surface(texture);
//通过mSurface,完成将录屏数据塞给ZEGO SDK
mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCapture",
mCaptureWidth, mCaptureHeight, 1,
DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC, mSurface, null, mHandler);
至此,我们已完成采集屏幕数据并通过 ZegoExpress SDK 分享到远端的操作。
5 观看远端屏幕共享-远程控制
完成以上步骤之后,其他用户可以使用 startPlayingStream
接口拉取屏幕共享流,详细步骤可以参考 快速开始。
// 同样的,拉流播放的用户首先需要初始化 SDK 并登陆同一个房间
...
...
// 拉流播放,需传入发起屏幕共享的用户推流时所用的 streamID
mSDKEngine.startPlayingStream(streamID, new ZegoCanvas(playView));
6 获取屏幕共享SDK更多帮助
获取本文的Demo、开发文档、技术支持,访问即构文档中心
近期有开发规划的开发者可上即构官网查看,恰逢即构七周年全线音视频产品1折的优惠,联系商务获取RTC产品优惠;
android通过蓝牙实现两台手机传输数据
今天学习了android蓝牙方面的基础知识,包含了打开和关闭蓝牙的操作,以及两部手机之间通过蓝牙实现的数据传输。下面看代码:
首先,需要在清单文件里添加蓝牙操作的权限:
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
打开蓝牙的两种方式:
第一种:Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent,1);
这种方式会提示用户是否打开蓝牙
第二种:bluetoothAdapter.enable();
这种方式系统直接打开蓝牙
关闭蓝牙的操作:bluetoothAdapter.disable();
搜索附近蓝牙的基本操作:
1.BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();//得到默认的蓝牙适配器
2.if (bluetoothAdapter.isDiscovering()) //如果正好在搜索,则先取消搜索
bluetoothAdapter.cancelDiscovery();
bluetoothAdapter.startDiscovery();
第二部在搜索的时候,会发出一个广播BluetoothDevice.ACTION_FOUND
3.创建并注册BroadcastReceiver并在onReceive方法中进行操作,关键代码如下:
@Override
public void onReceive(Context arg0, Intent arg1)
// TODO Auto-generated method stub
String action = arg1.getAction();
//获得已经搜索到的蓝牙设备
if (BluetoothDevice.ACTION_FOUND.equals(action))
BluetoothDevice device = arg1.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//搜索到的设备不是已经绑定的设备
if (device.getBondState() != BluetoothDevice.BOND_BONDED)
//将搜索到的新蓝牙设备和名称显示到textview中
show.append(device.getName()+":"+device.getAddress()+"\\n");
else if (bluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) //说明搜索已经完成
setProgressBarIndeterminateVisibility(false);
setTitle("搜索蓝牙设备");
2.两个蓝牙设备之间的通信
在清单文件里添加蓝牙操作的权限:
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
A.在两个手机上蓝牙之间传输数据是通过BluetoothSocket和BluetoothServerSocket来实现的
B.创建BluetoothSocket和BluetoothServerSocket的方式如下:
clientSocket = device
.createRfcommSocketToServiceRecord(MY_UUID);
serverSocket = bluetoothAdapter
.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
这里需要注意的是:
UUID MY_UUID = UUID
.fromString("5dd231bf-d217-4e85-a26c-5e5cfda9aa0c");
5dd231bf-d217-4e85-a26c-5e5cfda9aa0c是有系统提供的UUID.randomUUID().toString();生成的可以用于蓝牙设备之间的传输身份证,这个是唯一的
NAME是一个字符串,是我们随便给的
B.创建接受数据的线程类:
private class AcceptThread extends Thread
private BluetoothServerSocket serverSocket;
private BluetoothSocket socket;
private InputStream is;
private OutputStream os;
public AcceptThread()
//创建BluetoothServerSocket对象
try
serverSocket = bluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord("name",UUID.fromString("5dd231bf-d217-4e85-a26c-5e5cfda9aa0c"));
catch (IOException e)
// TODO Auto-generated catch block
e.printStackTrace();
@Override
public void run()
// TODO Auto-generated method stub
//等待接受蓝牙客户端的请求
try
socket = serverSocket.accept();
is = socket.getInputStream();
os = socket.getOutputStream();
while(true)
byte[] buffer = new byte[128];
int count = is.read(buffer);
Message message = new Message();
message.obj = new String(buffer,0, count, "utf-8");
handler.sendMessage(message);
catch (IOException e)
// TODO Auto-generated catch block
e.printStackTrace();
得到已经绑定的蓝牙设备并用list显示出来
luetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();//得到默认的蓝牙适配器
C.Set<BluetoothDevice>paireDevices = bluetoothAdapter.getBondedDevices();//得到已经绑定的蓝牙设备
if (paireDevices.size() > 0) //若存在
String []data = new String[paireDevices.size()];
for (BluetoothDevice bluetoothDevice : paireDevices)
data[count++] = bluetoothDevice.getName()+":"+bluetoothDevice.getAddress();//得到绑定蓝牙设备的名称和地址
adapter = new ArrayAdapter<String>(MainActivity.this,android.R.layout.simple_expandable_list_item_1,data);
list.setAdapter(adapter);
为list的每一个列表项绑定监听事件:
list.setOnItemClickListener(new OnItemClickListener()
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id)
// TODO Auto-generated method stub
String s = adapter.getItem(position);
//获得要连接的蓝牙设备的地址
String address = s.substring(s.indexOf(":")+1).trim();
if (null == device)
//获得蓝牙设备,相当于网路客户端制定的socketip地址
device = bluetoothAdapter.getRemoteDevice(address);
if (null == clientSocket)
try
clientSocket = device.createInsecureRfcommSocketToServiceRecord(UUID.fromString("5dd231bf-d217-4e85-a26c-5e5cfda9aa0c"));
//开始连接蓝牙设备
clientSocket.connect();
os = clientSocket.getOutputStream();
catch (IOException e)
// TODO Auto-generated catch block
e.printStackTrace();
if (null != os)
//向服务器端发送一个字符串
try
os.write("这是另一台手机发送过来的数据".getBytes("utf-8"));
Toast.makeText(MainActivity.this,"发送成功",1000);
catch (Exception e)
// TODO Auto-generated catch block
Toast.makeText(MainActivity.this,"发送失败",1000);
e.printStackTrace();
);
分别将该应用部署到两部手机,实现蓝牙通信.注意目前这种方式只支持简单的数据传输,如果需要出书二进制文件,比如音频,还是不可以的。
以上是关于Android实现两台手机屏幕共享和远程控制的主要内容,如果未能解决你的问题,请参考以下文章
android 开发 ,控制整个屏幕的点击功能屏幕失去焦点功能问题: 求思路啊~~