云手机同步效果

现在市面上云手机 基本都是基于arm服务器+docker容器技术来实现,其中最核心的是gpu虚拟化,然后就是 远程画面操作。

今天和大家分享下如何在Android上把视频画面以及音频通过webrtc传输到客户端或者pc上,以及如何实现远程遥控操

1.首先我们需要一个信令服务用于转发信令消息,在推流与收流端正常通讯后。使用webrtc发送音视频,其中nat服务器使用的是cotrun。

2.在推流端实现推流关键代码如下

 private void start() {videoCapturer = createScreenCapturer();if (videoCapturer == null) {return;}eglBaseContext = EglBase.create().getEglBaseContext();surfaceViewRendererLocal.init(eglBaseContext, null);surfaceViewRendererLocal.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);surfaceViewRendererLocal.setKeepScreenOn(true);surfaceViewRendererRemote.init(eglBaseContext, null);surfaceViewRendererRemote.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);surfaceViewRendererRemote.setKeepScreenOn(true);peerConnectionFactory = createPeerConnectionFactory();//使用硬件PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();DefaultVideoDecoderFactory defaultVideoDecoderFactory = new DefaultVideoDecoderFactory(eglBaseContext);DefaultVideoEncoderFactory defaultVideoEncoderFactory = new DefaultVideoEncoderFactory(eglBaseContext, true, true);peerConnectionFactory = PeerConnectionFactory.builder().setOptions(options).setVideoDecoderFactory(defaultVideoDecoderFactory).setVideoEncoderFactory(defaultVideoEncoderFactory).createPeerConnectionFactory();MediaConstraints audioConstraints = new MediaConstraints();
//        //回声消除
//        audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googEchoCancellation", "true"));
//        //自动增益
//        audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googAutoGainControl", "true"));
//        //高音过滤
//        audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googHighpassFilter", "true"));
//        //噪音处理
//        audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googNoiseSuppression", "true"));AudioSource audioSource = peerConnectionFactory.createAudioSource(audioConstraints);audioTrack = peerConnectionFactory.createAudioTrack("101", audioSource);surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", eglBaseContext);VideoSource videoSource = peerConnectionFactory.createVideoSource(videoCapturer.isScreencast());videoCapturer.initialize(surfaceTextureHelper, this, videoSource.getCapturerObserver());videoTrack = peerConnectionFactory.createVideoTrack("102", videoSource);videoTrack.addSink(surfaceViewRendererLocal);btnCall.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {call();}});webSocketChannel = new WebSocketChannel();webSocketChannel.setWebSocketCallback(webSocketCallback);webSocketChannel.connect(WEBSOCKET_URL);}

3 收流端采用html我试过用腾讯的x5直接加载html也可

<!DOCTYPE html>
<html>
<head><title>webrtc demo</title>
</head>
<!--去除白边-->
<body style="border:0;margin:0;padding:0;"><div><div style="position:absolute;z-index:321;left:50%"><button style="background:#F00; color:#FFF" onclick="makeCall()">申请实例</button></div><div style="position:absolute;z-index:321;left:20%;top:10%"><button style="background:#F00; color:#FFF" onclick="sendText()">点击一下通知云手机</button></div><video id="remoteVideo" autoplay playsinline controls="false"style="width: 100%;height: 100%;object-fit: fill;position:relative;"/></div>
<script>// 官网 https://webrtc.org/getting-started/peer-connectionsconst LOCAL_USER_ID = 'bbbb';const RECEIVER_USER_ID = 'aaaa';var webSocket = new WebSocket('ws://221.7.79.213:5000/websocket');webSocket.onerror = function(event) {console.log('open error');};webSocket.onopen = function(event) {console.log("onopen---------->>>>fuck");// 注册自己到服务端webSocket.send(JSON.stringify({'event':'register','userId': LOCAL_USER_ID}));};webSocket.onclose = function(event) {console.log('websocket closed');};webSocket.onmessage = function(event) {console.log('onmessage '+event.data);// 回调onReceiveSocketMessage(JSON.parse(event.data));};// ice 服务器列表const configuration = {'iceServers': [{'urls': 'stun:stun.l.google.com:19302'},{'urls': 'stun:42.192.40.58:3478?transport=udp'},{'urls': 'turn:42.192.40.58:3478?transport=udp','username':'ddssingsong','credential':'123456'},{'urls': 'turn:42.192.40.58:3478?transport=tcp','username':'ddssingsong','credential':'123456'},{'urls': 'turn:221.7.79.213:3478?transport=udp','username':'admin','credential':'123456'},]}// 管道var peerConnection = new RTCPeerConnection(configuration); // 全局 PeerConnectionvar dataChannel;//datachannel 发送消息// Listen for local ICE candidates on the local RTCPeerConnectionpeerConnection.addEventListener('icecandidate', event => {console.log('local trickle  '+event);if (event.candidate) {const trickle = {event:'trickle',sender:LOCAL_USER_ID,receiver: RECEIVER_USER_ID,candidate:{sdpMid:event.candidate.sdpMid,sdpMLineIndex:event.candidate.sdpMLineIndex,sdp:event.candidate.candidate}};webSocket.send(JSON.stringify(trickle));// 发送本地 ICE candidate 到远端console.log('send local ice candidate to remote '+RECEIVER_USER_ID);}});// Listen for connectionstatechange on the local RTCPeerConnectionpeerConnection.addEventListener('connectionstatechange', event => {if (peerConnection.connectionState === 'connected') {// Peers connected! 会话连接完成console.log('p2p connected');// 初始化管道initDataChanel();}});// 处理java 的 signaling 消息async function onReceiveSocketMessage(message) {if ('sdp'===message.event) {if('offer'==message.type.toLowerCase()) {// 收到 offerconsole.log('收到 offer');let offerSdp = {type:'offer', sdp: message.description};peerConnection.setRemoteDescription(new RTCSessionDescription(offerSdp));// 创建 answerconst answer = await peerConnection.createAnswer();await peerConnection.setLocalDescription(answer);console.log('create answer and setLocalDescription');// 发送 answer 到对端webSocket.send(JSON.stringify({"event": "sdp","type": 'answer',"description":answer.sdp,"sender":LOCAL_USER_ID,"receiver":message.sender}));console.log('send answer to remote '+message.receiver);} else if ('answer'==message.type.toLowerCase()) {// 收到 answerconsole.log('收到 answer from '+message.sender);let answerSdp = {type:'answer', sdp: message.description};const remoteDesc = new RTCSessionDescription(answerSdp);await peerConnection.setRemoteDescription(remoteDesc);console.log('setRemoteDescription after reveiver answer');}} else if('trickle'===message.event) {let candidate = message.candidate;await peerConnection.addIceCandidate(new RTCIceCandidate({sdpMid:candidate.sdpMid,sdpMLineIndex:candidate.sdpMLineIndex,candidate:candidate.candidate}))console.log('add remote candidate');}};async function addStreams() {const remoteStream = new MediaStream();const remoteVideo = document.querySelector('video#remoteVideo');remoteVideo.srcObject = remoteStream;// 监听远程流peerConnection.addEventListener('track', async (event) => {console.log('监听远程流');remoteStream.addTrack(event.track, remoteStream);});}// 添加流addStreams();// 拨打async function makeCall() {// create offerconst offer = await peerConnection.createOffer();console.log('create offer');console.log(offer);await peerConnection.setLocalDescription(offer);console.log('set setLocalDescription offer');// 发送 offer 到对端webSocket.send(JSON.stringify({"event": "sdp","type": 'offer',"description":offer.sdp,"sender":LOCAL_USER_ID,"receiver":RECEIVER_USER_ID}));console.log('send offer to remote '+RECEIVER_USER_ID);}// 状态改变function dataChannelStateChange(){var readyState=dataChannel.readyState;console.log(' will send ttttttttt:'+readyState);if(readyState==='open'){sendText();}else{}}function dataChannelError(error){console.log("Data Channel Error:", error);}//文本对方传过来的数据function reveivemsg(e){var msg = e.data;console.log('recreived msg is :'+e.data);if(msg){chat.value += '->' + msg + '\r\n';}else{console.error('recreived msg is null');}}// 初始化datachannelfunction initDataChanel(){var dataChannelOptions = {ordered: true, //保证到达顺序};//文本聊天dataChannel=peerConnection.createDataChannel('dataChannel',dataChannelOptions);dataChannel.onmessage=reveivemsg; //收到消息dataChannel.onopen = dataChannelStateChange; //状态改变dataChannel.onclose = dataChannelStateChange; //dataChannel.onerror = dataChannelError; //错误}//发送文本function sendText(){dataChannel.send('ttttttttt');console.log('send ttttttttt');}</script></body>
</html>

当然原生的Android客户端也可以的

原生的Java收流端类似推流端 只是不发送本地音视频

以上就是公网的实时效果,由于时间限制所以没有录制视频

Webrtc 实现Android群控实时推流相关推荐

  1. web端实现rtsp实时推流视频播放可行性方案分析

    1.webrtc 1.1 什么是WebRTC 百度概念:WebRTC (Web Real-Time Communications) 是一项实时通讯技术,它允许网络应用或者站点,在不借助中间媒介的情况下 ...

  2. Android端实时音视频开发指南

    简介 yun2win-sdk-Android提供Android端实时音视频完整解决方案,方便客户快速集成实时音视频功能. SDK 提供的能力如下: 发起 加入 AVClient Channel AVM ...

  3. OBS使用WebRTC进行腾讯云推流播流

    推流: 首先:OBS想要推送WebRTC格式的推流需要满足以下两点: 1:OBS版本在26及以上 2:需要给OBS安装腾讯云插件,而且只支持Windows版. OBS下载地址:Download | O ...

  4. android黑盒子调用,[原创]Android群控黑盒调用 - Sekiro食用手册

    0x0 前言 之前尝试用过virjar大佬的hermesagent, 后来大佬又迭代出新的基于长链接的Sekiro, 一直想看都被耽搁了, 今天正好抽空尝试一下, 顺便写篇笔记, 有错误的地方大佬们请 ...

  5. android获取ro._抖音数据采集教程,Android群控黑盒调用,Sekiro使用手册

    抖音数据采集教程,Android群控黑盒调用,Sekiro使用手册 0x0 前言 之前尝试用过virjar大佬的hermesagent, 后来大佬又迭代出新的基于长链接的Sekiro, 一直想看都被耽 ...

  6. 浅谈iOS和Android后台实时消息推送的原理和区别

    http://www.52im.net/thread-286-1-1.html 前言 iOS和Android上的实时消息推送差异很大,往小了说是技术实现的差异,往大了说是系统实现理念的不同.实时消息推 ...

  7. 使用WebRTC开发Android Messenger:第3部分

    这是一个由三部分组成的系列文章,内容涉及:利用WebRTC中的BUG和利用Messenger应用程序.本系列文章重点阐述了当应用程序不能应用于WebRTC补丁程序以及通信和安全问题通知中断时可能出问题 ...

  8. 使用WebRTC开发Android Messenger:第2部分

    这是一个由三部分组成的系列文章,内容涉及:利用WebRTC中的BUG和利用Messenger应用程序.本系列文章重点阐述了当应用程序不能应用于WebRTC补丁程序以及通信和安全问题通知中断时可能出问题 ...

  9. Android平台RTMP推流或轻量级RTSP服务(摄像头或同屏)编码前数据接入类型总结

    很多开发者在做Android平台RTMP推流或轻量级RTSP服务(摄像头或同屏)时,总感觉接口不够用,以大牛直播SDK为例 (Github) 我们来总结下,我们常规需要支持的编码前音视频数据有哪些类型 ...

最新文章

  1. Node webkit启动最大化窗口
  2. 记事本中写c/c++程序在Windows下执行
  3. html中如何让图片交错,HTML5/Canvas 光圈交错幻觉
  4. 【数据结构与算法】完结
  5. 163邮箱苹果设置不成功_苹果变安卓不是不可能,Corellium让iPhone成功安装安卓系统...
  6. sylixos pci
  7. 「开源资讯」浏览器中可以深度学习的框架Paddle.js 1.0 发布
  8. 华为hs8145v5刷机包_华为hs8145v5刷机包_华为P40 Pro(ELSAN00/TN00)官方11.0.0.155固件卡刷包强刷包救砖包...
  9. Apache RocketMQ源码学习之生产者发送消息
  10. redis入门(三)
  11. 通过 ffmpeg 无损剪切与拼接视频方法
  12. 计算机课程教学资源网,课程动态
  13. the installation cannot continue as the installer file may be damaged. Download the installer file
  14. python使用大数据分析师_Python+大数据分析师
  15. 发那科数据采集 (法兰克数据采集)
  16. malloc: *** error for object 0x1740a8340: pointer being freed was not allocated
  17. 文本处理器—sed工具
  18. 懒人必备设计导航网站
  19. python将excel转换成图片格式_利用python将图片转换成excel文档格式
  20. CX-UDY主题:简洁大方自适应多功能WordPress图片主题

热门文章

  1. 解读 Caliper 修改配置文件调整交易量进行性能测试(中文首发)
  2. 计算机桌面隔几秒闪一下,win7游戏窗口化隔一段时间就会闪一下屏幕怎么办
  3. ACCESS关联更新
  4. 红灯停绿灯行c语言编程,基于物联网应用的《C语言程序设计》教学模式研究
  5. 红帽子认证辅导教程(转)
  6. keras.layers.Embedding层介绍
  7. 打开深度学习的潘多拉魔盒(模型可视化)
  8. 网络最强CASE工具 + 经典UML学习电子资下载汇总,软件设计与开发人员必备
  9. Nwafu-OJ-1431 Problem b C语言实习题五——6.用指针实现子字符串提取
  10. 如何做好企业内部培训?