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

极速网站制作推广平台收费标准

极速网站制作,推广平台收费标准,查看虚拟币行情的网站怎么做,记事本做网站怎么改字体大致流程为:客户端通过MediaProjectionManager进行屏幕共享,获取屏幕的截图,然后通过WebSocket发送到服务端,然后通过浏览器访问服务端查看传输的截图。 客户端代码分为3部分: ScreenShotShareActivity:作…

大致流程为:客户端通过MediaProjectionManager进行屏幕共享,获取屏幕的截图,然后通过WebSocket发送到服务端,然后通过浏览器访问服务端查看传输的截图。

客户端代码分为3部分:

  1. ScreenShotShareActivity:作用用户的交互界面,有2个按钮,一个是开启屏幕共享,另一个是结束屏幕共享,开启后会启动一个前台服务(这个是系统要求的,屏幕共享的逻辑必须在Service进行)
  2. ScreenShotEncoder:用于实现屏幕共享的图片截图处理,并设置结果回调,相当于一个工具类,会在Service中调用。
  3. ScreenCaptureService:前台服务,启动后会监听ScreenShotEncoder的结果,然后通过WebSocket将结果同步给远端服务器。

服务端代码分为2部分:
服务端通过Node js来实现,需要安装socket.io,2个代码文件:

  1. server.js:作为服务响应
  2. index.html:前端页面,用来展示客户端截图结果

客户端实现

清单文件配置

ScreenShotShareActivity + ScreenCaptureService 声明

<activity android:name=".demo.sharescreen.ScreenShotShareActivity" /><serviceandroid:name=".demo.sharescreen.ScreenCaptureService"android:exported="false"android:foregroundServiceType="mediaProjection" />

权限声明

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />

ScreenShotShareActivity

class ScreenShotShareActivity : AppCompatActivity() {private var isBound = falseprivate var binder: ScreenCaptureService.LocalBinder? = nullprivate var imageView: ImageView? = nullprivate val screenCaptureLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->if (result.resultCode == Activity.RESULT_OK && result.data != null) {startScreenEncoding(result.resultCode, result.data!!)} else {Toast.makeText(this, "屏幕录制权限被拒绝", Toast.LENGTH_SHORT).show()}}private val connection = object : ServiceConnection {override fun onServiceConnected(name: ComponentName?, service: IBinder?) {binder = service as? ScreenCaptureService.LocalBinderbinder?.setImageCallback(object : ScreenShotCaptureCallback {override fun onJpegImageReady(jpegData: String) {// 采集端本地测试val decodedBytes = Base64.decode(jpegData, Base64.NO_WRAP)val bitmap = BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size)runOnUiThread {imageView?.setImageBitmap(bitmap)}}})isBound = true}override fun onServiceDisconnected(name: ComponentName?) {isBound = false}}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)imageView = ImageView(this).apply {scaleType = ImageView.ScaleType.FIT_CENTER}setContentView(LinearLayout(this).apply {orientation = LinearLayout.VERTICALaddView(Button(this@ScreenShotShareActivity).apply {text = "开始屏幕录制"click {requestScreenCapture()}})addView(Button(this@ScreenShotShareActivity).apply {text = "停止屏幕录制"click {stopScreenCapture()}})addView(imageView, LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT))})}private fun stopScreenCapture() {if (isBound) unbindService(connection)val intent = Intent(this@ScreenShotShareActivity, ScreenCaptureService::class.java)stopService(intent)}/*** 请求屏幕共享权限*/private fun requestScreenCapture() {val projectionManager = getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManagerval intent = projectionManager.createScreenCaptureIntent()screenCaptureLauncher.launch(intent)}/*** 开始屏幕共享,启动前台服务 ScreenCaptureService*/private fun startScreenEncoding(resultCode: Int, data: Intent) {val serviceIntent = Intent(this, ScreenCaptureService::class.java).apply {putExtra("resultCode", resultCode)putExtra("data", data)}ContextCompat.startForegroundService(this, serviceIntent)bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE)}
}

ScreenCaptureService


/*** 前台服务,需要添加权限:*   <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />*   <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />*/
class ScreenCaptureService : Service() {private var screenShotEncoder:ScreenShotEncoder? = nullprivate var screenShotCallback: ScreenShotCaptureCallback? = nullprivate val socket by lazy {val opts = IO.Options().apply {transports = arrayOf("websocket") // ⭐ 避免 xhr-poll 错误reconnection = truereconnectionAttempts = 5timeout = 5000}IO.socket("http://192.168.28.101:3000",opts)}private fun startForeground(notification: Notification) {if (VERSION.SDK_INT >= Build.VERSION_CODES.Q) {startForeground(1,notification,ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION)} else {startForeground(1, notification)}}override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {startForeground(createNotification())val resultCode = intent?.getIntExtra("resultCode", Activity.RESULT_CANCELED) ?: return START_NOT_STICKYval data = intent.getParcelableExtra<Intent>("data") ?: return START_NOT_STICKYinitSocketIO()screenShotEncoder = ScreenShotEncoder(this, resultCode, data)screenShotEncoder?.startCapturing { base64Jpeg ->// 回调给外部activity,可以用做测试或者打印logscreenShotCallback?.onJpegImageReady(base64Jpeg)// 发送给远端服务器socket.emit("frame", base64Jpeg)}return START_STICKY}private fun initSocketIO() {try {// 连接状态监听socket.on(Socket.EVENT_CONNECT) {Log.d("ScreenCaptureService", "✅ Socket 已连接到服务器")}socket.on(Socket.EVENT_CONNECT_ERROR) { args ->Log.e("ScreenCaptureService", "❌ Socket 连接失败: ${args.getOrNull(0)}")}socket.on(Socket.EVENT_DISCONNECT) {Log.w("ScreenCaptureService", "⚠️ Socket 已断开连接")}socket.connect()} catch (e: URISyntaxException) {Log.e("ScreenCaptureService", "❌ Socket URI 格式错误", e)}}private fun createNotification(): Notification {val channelId = "screen_capture"val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManagerif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {val channel = NotificationChannel(channelId, "屏幕共享", NotificationManager.IMPORTANCE_LOW)manager.createNotificationChannel(channel)}return NotificationCompat.Builder(this, channelId).setContentTitle("屏幕共享中").setContentText("正在采集屏幕内容").setSmallIcon(android.R.drawable.ic_menu_camera).build()}override fun onBind(intent: Intent?): IBinder? {return LocalBinder()}override fun onDestroy() {super.onDestroy()screenEncoder?.stopEncoding()screenShotEncoder?.stopCapturing()socket.disconnect()Log.d("ScreenCaptureService","服务销毁")}inner class LocalBinder : Binder() {fun stopCapture() {stopSelf()}fun setImageCallback(callback: ScreenShotCaptureCallback) {screenShotCallback  = callback}}
}interface ScreenShotCaptureCallback {fun onJpegImageReady(jpegData: String)
}

ScreenShotEncoder


class ScreenShotEncoder(private val context: Context,private val resultCode: Int,private val data: Intent,private val width: Int = 720,private val height: Int = 1280,private val dpi: Int = 320
) {private lateinit var mediaProjection: MediaProjectionprivate var imageReader: ImageReader? = nullprivate var handlerThread: HandlerThread? = nullprivate var handler: Handler? = nullprivate var isCapturing = falsefun startCapturing(onFrameReady: (String) -> Unit) {setupMediaProjection()setupImageReader(onFrameReady)isCapturing = true}fun stopCapturing() {isCapturing = falseimageReader?.close()mediaProjection.stop()handlerThread?.quitSafely()}private fun setupMediaProjection() {val projectionManager = context.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManagermediaProjection = projectionManager.getMediaProjection(resultCode, data)}private fun setupImageReader(onFrameReady: (String) -> Unit) {imageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 2)handlerThread = HandlerThread("ScreenShotThread").also { it.start() }handler = Handler(handlerThread!!.looper)mediaProjection.createVirtualDisplay("ScreenShotCapture",width,height,dpi,0,imageReader!!.surface,null,handler)handler?.post(object : Runnable {override fun run() {if (!isCapturing) returnval image = imageReader?.acquireLatestImage()if (image != null) {val planes = image.planesval buffer = planes[0].bufferval pixelStride = planes[0].pixelStrideval rowStride = planes[0].rowStrideval rowPadding = rowStride - pixelStride * widthval bitmap = createBitmap(width + rowPadding / pixelStride, height)bitmap.copyPixelsFromBuffer(buffer)image.close()val outputStream = ByteArrayOutputStream()bitmap.compress(Bitmap.CompressFormat.JPEG, 70, outputStream)val byteArray = outputStream.toByteArray()val base64Image = Base64.encodeToString(byteArray, Base64.NO_WRAP)onFrameReady(base64Image)bitmap.recycle()}handler?.postDelayed(this, 100) // 每 100ms 截图一帧(约10帧/s)}})}
}

启动App后,看到界面如下:
在这里插入图片描述

点击开始录制,会弹一个权限弹窗
在这里插入图片描述
同意后,进入录制
在这里插入图片描述

服务端实现

第一步:准备环境

安装 Node.js
如果你尚未安装,请去 https://nodejs.org/ 下载并安装 LTS 版本。
安装完成后,打开终端输入:

node -v
npm -v

确认安装成功。

第二步:创建项目文件夹

mkdir webrtc-server
cd webrtc-server
npm init -y
npm install express socket.io

第三步:创建信令服务器 server.js

在webrtc-server文件夹下创建server.js文件,代码如下:

// server.js
const express = require('express');
const app = express();
const http = require('http').createServer(app);
const io = require('socket.io')(http);  // v2.4.1 不需要 CORS 配置app.use(express.static(__dirname + '/public'));io.on('connection', socket => {console.log('🟢 Client connected:', socket.id);socket.on('frame', (data) => {console.log(`📥 接收到 frame,长度=${data.length}`);socket.broadcast.emit('frame', data);});socket.on('offer', (data) => {socket.broadcast.emit('offer', data);});socket.on('answer', (data) => {socket.broadcast.emit('answer', data);});socket.on('ice-candidate', (data) => {socket.broadcast.emit('ice-candidate', data);});socket.on('disconnect', () => {console.log('🔴 Client disconnected:', socket.id);});
});const PORT = 3000;
http.listen(PORT, '0.0.0.0', () => {console.log(`🚀 Server running at http://0.0.0.0:${PORT}`);
});

第四步:创建接收端前端页面

在 webrtc-server 目录下创建一个 public/index.html 文件:

<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><title>屏幕共享画面</title><style>#log {background: #f4f4f4;border: 1px solid #ccc;padding: 10px;margin-top: 10px;height: 100px;overflow-y: auto;font-family: monospace;font-size: 14px;}</style>
</head>
<body>
<h2>接收到的屏幕共享图像</h2>
<img id="screenImg"style="max-width: 100%; max-height: 600px; border: 1px solid #ccc; object-fit: contain;" /><div id="log"></div><script src="/socket.io/socket.io.js"></script>
<script>const socket = io();const img = document.getElementById("screenImg");const logEl = document.getElementById("log");function log(message) {const time = new Date().toLocaleTimeString();const entry = `[${time}] ${message}`;console.log(entry);logEl.innerText += entry + "\n";logEl.scrollTop = logEl.scrollHeight;}socket.on("connect", () => {log("✅ 已连接到服务器");});socket.on("disconnect", () => {log("❌ 与服务器断开连接");});socket.on("frame", (base64) => {log("🖼️ 收到一帧图像");img.src = "data:image/jpeg;base64," + base64;});socket.on("connect_error", (err) => {log("⚠️ 连接错误: " + err.message);});
</script>
</body>
</html>

第五步:运行服务器

node server.js

你会看到:
在这里插入图片描述
说明服务端运行成功了,并且有一个客户端加入了,这个就是我们的客户端发起的屏幕共享加入的。

验证效果

打开浏览器输入地址:http://192.168.28.101:3000/index.html
注意:这里的地址是局域网地址,我本机电脑的地址,和我手机是在同一个局域网内。

成功的话,可以看到客户端的共享内容:
在这里插入图片描述

http://www.dtcms.com/wzjs/87230.html

相关文章:

  • 做购物网站哪个cms好用b站新人视频怎么推广
  • 建网站没有实体公司能建站吗百度爱企查电话人工服务总部
  • 交互式网站设计怎么做互联网公司排名
  • 百度站长怎样添加网站android优化大师
  • 深圳b2c电子商务网站百度百度一下
  • wordpress 汉化软件seo综合查询接口
  • 餐饮网站做的比较好的是哪个5118关键词工具
  • 新网站如何做seo网站服务器怎么搭建
  • 苏州建设网站电话深圳seo优化公司搜索引擎优化方案
  • 北京市社会保险网站网站建设平台有哪些
  • 做设计开店的网站湖南株洲疫情最新情况
  • 湖北响应式网站建设企业合肥seo代理商
  • 高校建设网站的特色百度关键词排名联系方式
  • php网站如何做特效百度云官网登录首页
  • 贵阳网站如何推广百度排行榜
  • 在技校计算机网站建设游戏推广公司
  • 织梦cms手机网站源码怎么做好市场宣传和推广
  • 莱芜网站优化招聘网百度统计app
  • 苹果手机怎么做微电影网站今日头条热点新闻
  • 网站设计专业有前途吗端点seo博客
  • 两学一做测试网站新网seo关键词优化教程
  • 上海网站制作与推广seo的中文意思是什么
  • 网站建设宣传广告如何推广seo
  • 网站委托建设合同哪里有竞价推广托管
  • wordpress网站特效线上推广策划方案范文
  • 什么网站可以做问卷调查深圳seo优化外包
  • 咋制作网站seo文章是什么意思
  • 个人做网站设计什么软件可以发帖子做推广
  • 信誉好的镇江网站建设韩国比分预测
  • ru后缀的网站seo推广怎么入门