Version française de ma réponse sur Stack Overflow : Socket - Screen sharing in Python.
Comment partager son écran entre 2 machines ? Il existe des logiciels spécialisés comme AnyDesk, mais voyons une façon de faire en Python.
La partie "serveur", celle qui partagera son écran, utilisera le module MSS.
De l'autre côté, un "client" utilisera PyGame pour afficher ce que le serveur lui enverra.
Serveur
Grâce au module MSS, on peut atteindre de belles performances. Couplé à une compression de données suffisante, on énonomise la bande passante. Le code suivant utilise une compression ZLib, mais on pourrait aussi bien laisser sa chance à LZ4 ou un autre algorithme.
import socket
import threading
import zlib
import mss
# Dimensions des captures d'écran, plus petit = plus rapide
WIDTH = 1900
HEIGHT = 1000
def retreive_screenshot(conn):
""" Envoi des captures d'écran via un socket. """
# Si utilisation via SSH, il est possible de spécifier le $DISPLAY à utilser :
# with mss(display=':0.0') as sct:
with mss.mss() as sct:
# La région de l'écran à capturer
rect = {'top': 0, 'left': 0, 'width': WIDTH, 'height': HEIGHT}
while 'recording':
# Prendre la capture d'écran
img = sct.grab(rect)
# Ajuster le niveau de compression ici (0-9)
pixels = zlib.compress(img.rgb, 6)
# Envoie de la taille de la taille des pixels
size = len(pixels)
size_len = (size.bit_length() + 7) // 8
conn.send(bytes([size_len]))
# Envoie de la taille des pixels
size_bytes = size.to_bytes(size_len, 'big')
conn.send(size_bytes)
# Envoi des pixels compressés
conn.sendall(pixels)
def main(host='0.0.0.0', port=5000):
with socket.socket() as sock:
sock.bind((host, port))
sock.listen(5)
print('Server started.')
while 'connected':
conn, addr = sock.accept()
print('Client connected IP:', addr)
thread = threading.Thread(target=retreive_screenshot, args=(conn,))
thread.start()
if __name__ == '__main__':
import argparse
import sys
cli_args = argparse.ArgumentParser()
cli_args.add_argument('--port', default=5000, type=int)
options = cli_args.parse_args(sys.argv[1:])
main(port=options.port)
Client
Son utilisation est on ne peut plus simple : python3 client.py --host=192.168.0.10
.
import socket
import zlib
import pygame
# from server import WIDTH, HEIGHT
WIDTH = 1900
HEIGHT = 1000
def recvall(conn, length):
""" Récupération de tous les pixels. """
print(length % 1024, 'ko')
buf = b''
while len(buf) < length:
data = conn.recv(length - len(buf))
if not data:
return data
buf += data
return buf
def main(host='127.0.0.1', port=5000):
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
with socket.socket() as sock:
sock.connect((host, port))
watching = True
while watching:
for event in pygame.event.get():
if event.type == pygame.QUIT:
watching = False
break
# Récupération de la taille de la taille des pixels, la taille des pixels et les pixels
size_len = int.from_bytes(sock.recv(1), byteorder='big')
size = int.from_bytes(sock.recv(size_len), byteorder='big')
pixels = recvall(sock, size)
pixels = zlib.decompress(pixels)
# Création d'une Surface depuis les pixels brutes
img = pygame.image.fromstring(pixels, (WIDTH, HEIGHT), 'RGB')
# Affichage de l'image
screen.blit(img, (0, 0))
pygame.display.flip()
clock.tick(60)
if __name__ == '__main__':
import argparse
import sys
cli_args = argparse.ArgumentParser()
cli_args.add_argument('--host', default='127.0.0.1', type=str)
cli_args.add_argument('--port', default=5000, type=int)
options = cli_args.parse_args(sys.argv[1:])
main(host=options.host, port=options.port)