Screen Capture¶
Simple RDP provides screen capture capabilities through the screenshot() method.
Basic Screenshot¶
import asyncio
from simple_rdp import RDPClient
async def main():
async with RDPClient(
host="192.168.1.100",
username="admin",
password="secret",
) as client:
# Wait for screen to render
await asyncio.sleep(2)
# Capture as PIL Image
img = await client.screenshot() # (1)!
print(f"Size: {img.size}") # (2)!
print(f"Mode: {img.mode}") # RGB
asyncio.run(main())
- Returns a
PIL.Image.Imagein RGB mode - Size matches the configured
widthandheight
Save Screenshot¶
# Save directly to file
await client.save_screenshot("screenshot.png")
# Or save the PIL Image
img = await client.screenshot()
img.save("screenshot.png")
img.save("screenshot.jpg", quality=90)
Continuous Capture¶
For video recording or monitoring:
import asyncio
from simple_rdp import RDPClient
async def capture_loop():
async with RDPClient(...) as client:
await asyncio.sleep(2) # Wait for initial render
frame_count = 0
while frame_count < 100:
img = await client.screenshot()
img.save(f"frames/frame_{frame_count:04d}.png")
frame_count += 1
await asyncio.sleep(0.1) # ~10 FPS # (1)!
asyncio.run(capture_loop())
- You can achieve up to 30 FPS with continuous capture
Performance
For higher frame rates, consider saving frames asynchronously.
Screenshot with Cursor¶
The cursor can be composited into screenshots. Access cursor state via:
# Get cursor position
x, y = client.pointer_position
# Check if cursor is visible
if client.pointer_visible:
# Get cursor image (PIL Image or None)
cursor_img = client.pointer_image
# Get cursor hotspot (click point offset)
hotspot_x, hotspot_y = client.pointer_hotspot
See Pointer/Cursor for more details.
Image Processing¶
Screenshots are returned as PIL Images, making it easy to process:
from PIL import Image
img = await client.screenshot()
# Crop a region
region = img.crop((100, 100, 500, 400))
# Resize
thumbnail = img.resize((480, 270))
# Convert to grayscale
gray = img.convert("L")
# Get pixel data
pixels = list(img.getdata())
# Convert to numpy array (with numpy installed)
import numpy as np
arr = np.array(img)
Performance Tips¶
Appropriate Resolution
Use only the resolution you need:
Screen Update Mechanism¶
How Screen Capture Works
Simple RDP maintains an internal screen buffer that is updated incrementally as the RDP server sends bitmap updates. The screenshot() method returns a copy of this buffer.
The screen buffer is updated in the background by a receive loop that processes:
- Fast-Path bitmap updates (compressed with RLE)
- Bitmap update PDUs
- Pointer updates
This means screenshots always reflect the current screen state, not just the initial connection.
Video Recording¶
The RDPClient has an integrated Display component for video recording. Frames are automatically captured as the screen updates.
Basic Video Recording¶
import asyncio
from simple_rdp import RDPClient
async def record_session():
async with RDPClient(
host="192.168.1.100",
username="admin",
password="secret",
) as client:
await asyncio.sleep(2) # Wait for initial render
# Start file recording (unlimited duration)
await client.start_file_recording("recording.ts") # (1)!
# Do some actions...
await client.mouse_move(500, 300)
await asyncio.sleep(5) # Record for 5 seconds
# Stop recording
await client.stop_file_recording() # (2)!
await client.stop_streaming()
# Check stats
stats = client.get_recording_stats()
print(f"Frames: {stats['frames_encoded']}")
asyncio.run(record_session())
- Starts ffmpeg encoder, records directly to file
- Stops recording and closes the file
Save Buffer as Video¶
For short clips, save the rolling frame buffer (~10 seconds):
import asyncio
from simple_rdp import RDPClient
async def save_clip():
async with RDPClient(
host="192.168.1.100",
username="admin",
password="secret",
) as client:
await asyncio.sleep(2)
# Frames are always captured in the background
# Wait and then save the last ~10 seconds
await asyncio.sleep(15)
# Save buffer as video
await client.save_video("clip.mp4")
asyncio.run(save_clip())
Access Display Directly¶
For advanced use cases, access the Display component directly:
# Access the integrated Display
display = client.display
# Get encoding statistics
print(f"Frames received: {display.stats['frames_received']}")
print(f"Buffer size: {display.video_buffer_size_mb:.2f} MB")
# Get latest frame as ScreenBuffer
latest = display.get_latest_frame()
if latest:
print(f"Frame size: {latest.width}x{latest.height}")
# Stream video chunks (for WebSocket, etc.)
async def stream_chunks():
await client.start_streaming()
while client.is_streaming:
chunk = await display.get_next_video_chunk(timeout=1.0)
if chunk:
yield chunk.data
See Also
For complete Display API reference, see Display API.