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

简易屏幕共享工具-基于WebSocket

前面写了两个简单的屏幕共享工具,不过那只是为了验证通过截屏的方式是否可行,因为通常手动截屏的频率很低,而对于视频来说它的帧率要求就很高了,至少要一秒30帧率左右。所以,经过实际的截屏工具验证,我了解了几个Python截屏库的特点和限制。例如,多数截屏库都不支持很高的截屏速度,并且截屏是典型的 CPU 密集任务(我尝试使用多线程截屏,发现速度更慢了,之后有时间我也会把这一点整理成文章发出来)。
所以,我的初始的想法其实是基于 WebSocket 来实现的。现在,就让我们对先前的代码进行重构,采用 WebSocket 来传输图片数据。不过我这里没有使用到它的双向传输的特性,只是将原来 HTTP 传输的图片换成通过 WebSocket 来传输了。不过这里后续还有很多东西可以开发,如果有时间的话,也可以基于这个做一些有趣的东西。

演示

我这个笔记本的性能可能不太行,我只要打开视频帧率就降低了很多,哈哈。

截取播放B站视频

在这里插入图片描述

截取摄像头画面

这样甚至可以远程共享画面了,如果两个人都布置一个,就可以各自看到对话了(不过没有声音,且效率低下,可能也就只能在局域网使用),不过这样它是相当于从服务器的地方获取的数据,而我们平时使用的视频通话工具都是从客户端获取的数据。

而且页面越多,帧率越低,这里可能要优化一下或者它就是这么累赘,只能个人使用。

在这里插入图片描述

说明

注意,这里的测试环境是 Windows,因为有些库依赖于 Windows 提供的特性,所以需要在 Windows 上运行它。在程序启动时,它会开启一个截屏的线程,不断去获取屏幕的截屏,如果有用户访问,它就会通过 WebSocket 连接,把获取的屏幕截图数据传送给前端,前端的逻辑就是将它绘制在 canvas 上,并添加帧率显示。

注意:这部分前端的代码是 AI 生成,对于验证小功能来说,AI 真是太完美了。而且,其实这整个部分都可以让 AI 来做,但是具体是哪些的组合还是自己选择的,毕竟每个人的偏好和技术栈不同。

代码

所有的代码都在这里了,大概60行代码,我把前端压缩成一行代码了。如果要运行代码先要安装 flask, flask_sock, pillow, dxcam 库。

import time
from flask import Flask
from flask_sock import Sock
from io import BytesIO
from PIL import Image
import dxcam# 创建应用
app = Flask(__name__)
sockets = Sock(app)
# 整个应用只创建一个即可
camera = dxcam.create(device_idx=0, output_idx=1)  # output_idx 0 是第一块屏幕,1 是第二块屏幕
camera.start(target_fps=60, video_mode=True)
JPEG_QUALITY = 80 # 默认的jpeg图片质量,越高需要的计算量越大,同时越清晰# 直接前后端写一起了,这个只是一个演示的demo
INDEX_HTML = """
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Screen Sharing</title> <style> body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f0f0f0; } canvas { border: 1px solid black; } </style> </head> <body> <canvas id="screenCanvas" width="960" height="540"></canvas> <script> const canvas = document.getElementById('screenCanvas'); const ctx = canvas.getContext('2d'); const ws = new WebSocket('ws://'+window.location.host+'/remote_desktop'); ws.onopen = () => { console.log("WebSocket connected"); }; ws.onerror = (event) => { console.error("WebSocket error:", event); }; let lastFrameTime = performance.now(); let frameCount = 0; let fps = 0; ws.onmessage = (event) => { const image = new Image(); image.src = URL.createObjectURL(event.data); image.onload = () => { ctx.drawImage(image, 0, 0, canvas.width, canvas.height); frameCount++; const now = performance.now(); const elapsed = now - lastFrameTime; if (elapsed >= 1000) { fps = frameCount / (elapsed / 1000); frameCount = 0; lastFrameTime = now; } displayFPS(); }; }; function displayFPS() { ctx.font = '30px Arial'; ctx.fillStyle = 'red'; ctx.fillText(`FPS: ${Math.round(fps)}`, 50, 50); } </script> </body> </html>
"""@app.route('/', methods=['GET'])
def index():"""简单的前端"""return INDEX_HTML@sockets.route('/remote_desktop')
def get_desktop(ws):"""获取一帧图片,并发送给前端"""buffer = BytesIO()fps = 0    # 计算后端生成的帧率frame_count = 0last_frame_time = time.perf_counter()while True:reset_buffer(buffer)  # 每次重置buffer,方便复用img_data = get_frame(buffer)frame_count += 1elapsed = time.perf_counter() - last_frame_timeif elapsed >= 1:fps = frame_count // elapsedlast_frame_time = time.perf_counter()frame_count = 0print("backend real frame fps: ", fps)ws.send(img_data)def get_frame(buffer):"""获取一帧,然后处理成jpeg并返回二进制数据"""image_array = camera.get_latest_frame()Image.fromarray(image_array).save(buffer, format="JPEG", quality=JPEG_QUALITY)return buffer.getvalue()def reset_buffer(buffer):# 重置buffer,方便复用buffer.truncate(0)buffer.seek(0)if __name__ == '__main__':    print("remote desktop server starting...")app.run("0.0.0.0", 9000)

相关文章:

简易屏幕共享工具-基于WebSocket

前面写了两个简单的屏幕共享工具&#xff0c;不过那只是为了验证通过截屏的方式是否可行&#xff0c;因为通常手动截屏的频率很低&#xff0c;而对于视频来说它的帧率要求就很高了&#xff0c;至少要一秒30帧率左右。所以&#xff0c;经过实际的截屏工具验证&#xff0c;我了解…...

Redis——主从复制模式

文章目录 1. 引入2. 主从复制模式2.1 概念2.2 配置2.3 原理2.3.1 建立连接阶段2.3.2 命令传播阶段2.3.3 心跳检测机制2.3.4 部分重同步机制(1) 主节点通过 复制积压缓冲区 记录写命令(2) 主节点通过 复制偏移量 判断从节点是否满足执行部分重同步的条件(3) 执行部分重同步操作 …...

简历_熟悉缓存高并发场景处理方法,如缓存穿透、缓存击穿、缓存雪崩

系列博客目录 文章目录 系列博客目录1.缓存穿透总结 2.缓存雪崩3.缓存击穿代码总结 1.缓存穿透 缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在&#xff0c;这样缓存永远不会生效&#xff0c;这些请求都会打到数据库。 常见的解决方案有两种&#xff1a; 缓存空对…...

阿里云电商平台用户行为分析与人群画像系统设计与实现

通过在阿里云&#xff08;https://baike.baidu.com/item/%E9%98%BF%E9%87%8C%E4%BA%91/297128&#xff09;上构建包含数据源层、数据存储层、数据处理层、数据分析层和数据应用层的系统架构&#xff0c;并设计合理的数据模型、ETL流程、数据质量与性能监控机制以及安全与合规性…...

Go语言的 的输入/输出流(I/O Streams)核心知识

Go语言的输入/输出流&#xff08;I/O Streams&#xff09;核心知识 前言 Go语言是一种现代编程语言&#xff0c;因其高效性、简洁性及强大的并发支持而受到开发者的喜爱。在开发应用程序时&#xff0c;输入/输出&#xff08;I/O&#xff09;操作是一个不可或缺的部分。无论是…...

57.在 Vue 3 中使用 OpenLayers 点击选择 Feature 设置特定颜色

在 Web 开发中&#xff0c;地图应用是非常常见的需求&#xff0c;而 OpenLayers 是一个非常强大的地图库&#xff0c;它提供了丰富的地图操作功能。今天&#xff0c;我们将一起学习如何在 Vue 3 中结合 OpenLayers 使用点击事件来选择地图上的 Feature&#xff0c;并设置特定的…...

数据结构C语言描述8(图文结合)--哈希、哈希冲突、开放地址法、链地址法等实现

前言 这个专栏将会用纯C实现常用的数据结构和简单的算法&#xff1b;有C基础即可跟着学习&#xff0c;代码均可运行&#xff1b;准备考研的也可跟着写&#xff0c;个人感觉&#xff0c;如果时间充裕&#xff0c;手写一遍比看书、刷题管用很多&#xff0c;这也是本人采用纯C语言…...

自动化立体库安全使用管理制度完整版

导语 大家好&#xff0c;我是社长&#xff0c;老K。专注分享智能制造和智能仓储物流等内容。欢迎大家到本文底部评论区留言。 新书《智能物流系统构成与技术实践》人俱乐部 完整版文件和更多学习资料&#xff0c;请球友到知识星球【智能仓储物流技术研习社】自行下载。 以下是《…...

云打印之拼多多打印组件交互协议

拼多多打印组件交互协议相关介绍如下&#xff1a; 1、打印组件下载地址 http://meta.pinduoduo.com/api/one/app/v1/lateststable?appIdcom.xunmeng.pddprint&platformwindows&subTypemain 2、socket连接端口 如果是http的话&#xff0c;端口是5000 socket new …...

TCP 演进之路:软硬件跷跷板与新征程

今天依旧是与 TCP 相关的一个短评。 先看软硬件间的胶着。晶体管诞生以来&#xff0c;硬件一直在突飞猛进发展&#xff0c;后来这个事被摩尔定律正则化&#xff0c;人们开始可以预测未来&#xff0c;但即便如此&#xff0c;软件依然跟不上来&#xff0c;不过几年&#xff0c;老…...

React最小状态管理Jotai

Jotai 状态管理 1. 简介 Jotai 是一个基于原子 atom 概念的 React 状态管理库&#xff0c;它提供了简单且灵活的方式来管理应用状态, 而且非常轻量&#xff0c; 大厂用的非常多。 JotaiRedux适合单个页面&#xff0c;多次用到的属性适合全局公共属性超级轻量&#xff08;与use…...

计算机网络 —— 网络编程(TCP)

计算机网络 —— 网络编程&#xff08;TCP&#xff09; TCP和UDP的区别TCP (Transmission Control Protocol)UDP (User Datagram Protocol) 前期准备listen &#xff08;服务端&#xff09;函数原型返回值使用示例注意事项 accpect &#xff08;服务端&#xff09;函数原型返回…...

字玩FontPlayer开发笔记4 性能优化 首屏加载时间优化

字玩FontPlayer开发笔记4 性能优化 首屏加载时间优化 字玩FontPlayer是笔者开源的一款字体设计工具&#xff0c;使用Vue3 ElementUI开发&#xff0c;源代码&#xff1a; github: https://github.com/HiToysMaker/fontplayer gitee: https://gitee.com/toysmaker/fontplayer …...

RabbitMQ案例

1. 导入依赖 <!--AMQP依赖&#xff0c;包含RabbitMQ--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency> 发送消息 注入RabbitTemplate Autowired RabbitT…...

智能工厂的设计软件 应用场景的一个例子:为AI聊天工具添加一个知识系统 之13 方案再探之4:特定于领域的模板 之 div模型(完整版)

前景提要 整个“方案再探”篇 围绕着如何将项目附件文档中Part 1 部分中给出的零散问题讨论整理、重组为一个结构化的设计文档。为此提出了讨论题目&#xff1a; 特定于领域的模板--一个三套接的hoc结构 它是本项目actors 的剧本原型。其地位&#xff1a; 祖传代码脚本模板…...

WebRtc02:WebRtc架构、目录结构、运行机制

整体架构 WebRtc主要分为三层&#xff1a; CAPI层&#xff1a;外层调用Session管理核心层&#xff1a;包括视频引擎、音频引擎、网络传输 可由使用者重写视频引擎&#xff1a;编解码器、视频缓存、视频增强音频引擎&#xff1a;编解码器、音频缓存、回音消除、降噪传输&#x…...

数据结构复习 (顺序查找,对半查找,斐波那契查找,插值查找,分块查找)

查找&#xff08;检索&#xff09;&#xff1a; 定义&#xff1a;从给定的数据中找到对应的K 1&#xff0c;顺序查找&#xff1a; O(n)的从前向后的遍历 2&#xff0c;对半查找&#xff0c;要求有序 从中间开始查找&#xff0c;每次检查中间的是否正确&#xff0c;不正确就…...

el-input输入框需要支持多输入,最后传输给后台的字段值以逗号分割

需求&#xff1a;一个输入框字段需要支持多次输入&#xff0c;最后传输给后台的字段值以逗号分割 解决方案&#xff1a;结合了el-tag组件的动态编辑标签 那块的代码 //子组件 <template><div class"input-multiple-box" idinputMultipleBox><div>…...

C# 枚举格式字符串

总目录 前言 当前文章为 C# 中的格式设置(格式化字符串) 大全 中的一个小章节。 一、概述 1. 基本信息 可以使用 Enum.ToString 方法&#xff0c;新建表示枚举成员的数字值、十六进制值或字符串值的字符串对象。枚举格式说明符不区分大小写。 二、自定义数字格式说明符详解…...

【51单片机-零基础chapter1】

安装软件(配套的有,不多赘述) 1.管理员身份运行keil和破解软件kegen 将CID代码复制粘贴到 一定要管理员方式,不然会error 插入板子 我的电脑,管理 1.如果是拯救者,查看端口,如果没有则显示隐藏 2.苹果不知道,好像不可以 3.其他电脑在"其他设备找" (注:本人在校已…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

Leetcode 3577. Count the Number of Computer Unlocking Permutations

Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接&#xff1a;3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯&#xff0c;要想要能够将所有的电脑解锁&#x…...

2.Vue编写一个app

1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

Mac软件卸载指南,简单易懂!

刚和Adobe分手&#xff0c;它却总在Library里给你写"回忆录"&#xff1f;卸载的Final Cut Pro像电子幽灵般阴魂不散&#xff1f;总是会有残留文件&#xff0c;别慌&#xff01;这份Mac软件卸载指南&#xff0c;将用最硬核的方式教你"数字分手术"&#xff0…...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

[Java恶补day16] 238.除自身以外数组的乘积

给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O(n) 时间复杂度…...

高防服务器能够抵御哪些网络攻击呢?

高防服务器作为一种有着高度防御能力的服务器&#xff0c;可以帮助网站应对分布式拒绝服务攻击&#xff0c;有效识别和清理一些恶意的网络流量&#xff0c;为用户提供安全且稳定的网络环境&#xff0c;那么&#xff0c;高防服务器一般都可以抵御哪些网络攻击呢&#xff1f;下面…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...