0
# Screenshots and Overlays
1
2
Screenshot capture with multiple output formats and overlay system for displaying images and text on top of video content. Supports various image formats, positioning, and dynamic overlay management.
3
4
## Capabilities
5
6
### Screenshot Capture
7
8
Capture screenshots in various formats and configurations.
9
10
```python { .api }
11
def screenshot(self, includes: str = 'subtitles', mode: str = 'single'):
12
"""
13
Take a screenshot and save to default location.
14
15
Parameters:
16
- includes: What to include ('subtitles', 'video', 'window')
17
- mode: Screenshot mode ('single', 'each-frame')
18
'single': Take one screenshot
19
'each-frame': Screenshot every frame
20
"""
21
22
def screenshot_to_file(self, filename: str, includes: str = 'subtitles'):
23
"""
24
Take a screenshot and save to specific file.
25
26
Parameters:
27
- filename: Output filename (extension determines format)
28
- includes: What to include ('subtitles', 'video', 'window')
29
"""
30
31
def screenshot_raw(self, includes: str = 'subtitles'):
32
"""
33
Take a screenshot and return raw image data.
34
35
Parameters:
36
- includes: What to include ('subtitles', 'video', 'window')
37
38
Returns:
39
Dictionary with image data, width, height, stride, and format information
40
41
Note: Requires Pillow for image processing if includes != 'video'
42
"""
43
```
44
45
### Overlay Management
46
47
Create and manage image and text overlays on video content.
48
49
```python { .api }
50
def allocate_overlay_id(self) -> int:
51
"""
52
Allocate a unique overlay ID.
53
54
Returns:
55
Integer overlay ID for use with overlay functions
56
"""
57
58
def free_overlay_id(self, overlay_id: int):
59
"""
60
Free an allocated overlay ID.
61
62
Parameters:
63
- overlay_id: ID to free for reuse
64
"""
65
66
def remove_overlay(self, overlay_id: int):
67
"""
68
Remove an overlay by ID.
69
70
Parameters:
71
- overlay_id: ID of overlay to remove
72
"""
73
```
74
75
### Image Overlays
76
77
Display images on top of video content with positioning control.
78
79
```python { .api }
80
def create_image_overlay(self, img=None, pos=(0, 0)) -> 'ImageOverlay':
81
"""
82
Create an image overlay.
83
84
Parameters:
85
- img: PIL Image object or image data
86
- pos: (x, y) position tuple
87
88
Returns:
89
ImageOverlay object for managing the overlay
90
"""
91
92
def overlay_add(self, overlay_id: int, x: int, y: int, file_or_fd, offset: int, fmt: str, w: int, h: int, stride: int):
93
"""
94
Add a low-level overlay.
95
96
Parameters:
97
- overlay_id: Overlay identifier
98
- x, y: Position coordinates
99
- file_or_fd: File path or file descriptor
100
- offset: Byte offset in file
101
- fmt: Image format ('bgra', 'rgba', etc.)
102
- w, h: Image dimensions
103
- stride: Bytes per row
104
"""
105
106
def overlay_remove(self, overlay_id: int):
107
"""
108
Remove a low-level overlay.
109
110
Parameters:
111
- overlay_id: Overlay identifier to remove
112
"""
113
```
114
115
### File-Based Overlays
116
117
Display images from files with efficient file-based rendering.
118
119
```python { .api }
120
def create_file_overlay(self, filename: str = None, size: tuple = None, stride: int = None, pos: tuple = (0, 0)) -> 'FileOverlay':
121
"""
122
Create a file-based overlay.
123
124
Parameters:
125
- filename: Path to image file
126
- size: (width, height) tuple
127
- stride: Bytes per row (auto-calculated if not provided)
128
- pos: (x, y) position tuple
129
130
Returns:
131
FileOverlay object for managing the overlay
132
"""
133
```
134
135
### OSD Overlays
136
137
Display text and graphics using mpv's On-Screen Display system.
138
139
```python { .api }
140
def osd_overlay(self, overlay_id: int, data: str, res_x: int = 0, res_y: int = 720, z: int = 0, hidden: bool = False):
141
"""
142
Create an OSD overlay with ASS subtitle formatting.
143
144
Parameters:
145
- overlay_id: Overlay identifier
146
- data: ASS-formatted text/graphics data
147
- res_x: X resolution (0 for auto)
148
- res_y: Y resolution (720 default)
149
- z: Z-order (higher values on top)
150
- hidden: Whether overlay starts hidden
151
"""
152
153
def osd_overlay_remove(self, overlay_id: int):
154
"""
155
Remove an OSD overlay.
156
157
Parameters:
158
- overlay_id: Overlay identifier to remove
159
"""
160
```
161
162
## Overlay Classes
163
164
### ImageOverlay Class
165
166
```python { .api }
167
class ImageOverlay:
168
"""Manages image-based overlays with PIL integration."""
169
170
def __init__(self, m: 'MPV', overlay_id: int, img=None, pos: tuple = (0, 0)):
171
"""
172
Initialize image overlay.
173
174
Parameters:
175
- m: MPV instance
176
- overlay_id: Unique overlay identifier
177
- img: PIL Image object or None
178
- pos: (x, y) position tuple
179
"""
180
181
def update(self, img=None, pos: tuple = None):
182
"""
183
Update overlay image and/or position.
184
185
Parameters:
186
- img: New PIL Image object (None to keep current)
187
- pos: New (x, y) position (None to keep current)
188
"""
189
190
def remove(self):
191
"""Remove this overlay from the video."""
192
```
193
194
### FileOverlay Class
195
196
```python { .api }
197
class FileOverlay:
198
"""Manages file-based overlays for efficient image display."""
199
200
def __init__(self, m: 'MPV', overlay_id: int, filename: str = None, size: tuple = None, stride: int = None, pos: tuple = (0, 0)):
201
"""
202
Initialize file overlay.
203
204
Parameters:
205
- m: MPV instance
206
- overlay_id: Unique overlay identifier
207
- filename: Path to image file
208
- size: (width, height) image dimensions
209
- stride: Bytes per row
210
- pos: (x, y) position tuple
211
"""
212
213
def update(self, filename: str = None, size: tuple = None, stride: int = None, pos: tuple = None):
214
"""
215
Update overlay file and/or properties.
216
217
Parameters:
218
- filename: New image file path
219
- size: New (width, height) dimensions
220
- stride: New bytes per row
221
- pos: New (x, y) position
222
"""
223
224
def remove(self):
225
"""Remove this overlay from the video."""
226
```
227
228
## Usage Examples
229
230
### Basic Screenshots
231
232
```python
233
import mpv
234
235
player = mpv.MPV()
236
player.play('/path/to/video.mp4')
237
player.wait_until_playing()
238
239
# Take screenshot to default location
240
player.screenshot()
241
242
# Save to specific file
243
player.screenshot_to_file('/path/to/screenshot.png')
244
245
# Different screenshot modes
246
player.screenshot(includes='video') # Video only, no subtitles
247
player.screenshot(includes='window') # Entire window
248
player.screenshot(includes='subtitles') # Video with subtitles (default)
249
250
# Save in different formats
251
player.screenshot_to_file('capture.jpg') # JPEG format
252
player.screenshot_to_file('capture.png') # PNG format
253
player.screenshot_to_file('capture.webp') # WebP format
254
```
255
256
### Raw Screenshot Data
257
258
```python
259
# Get raw screenshot data for processing
260
screenshot_data = player.screenshot_raw()
261
262
print(f"Image size: {screenshot_data['width']}x{screenshot_data['height']}")
263
print(f"Format: {screenshot_data['format']}")
264
print(f"Stride: {screenshot_data['stride']}")
265
266
# Access raw pixel data
267
pixel_data = screenshot_data['data']
268
269
# Convert to PIL Image (if Pillow is available)
270
if 'pil_image' in screenshot_data:
271
pil_img = screenshot_data['pil_image']
272
pil_img.save('processed_screenshot.png')
273
274
# Apply image processing
275
from PIL import ImageEnhance
276
enhancer = ImageEnhance.Brightness(pil_img)
277
bright_img = enhancer.enhance(1.5)
278
bright_img.save('bright_screenshot.png')
279
```
280
281
### Image Overlays
282
283
```python
284
from PIL import Image
285
286
player = mpv.MPV()
287
player.play('/path/to/video.mp4')
288
289
# Create image overlay
290
logo = Image.open('/path/to/logo.png')
291
overlay = player.create_image_overlay(logo, pos=(50, 50))
292
293
# Update overlay position
294
overlay.update(pos=(100, 100))
295
296
# Update overlay image
297
new_logo = Image.open('/path/to/new_logo.png')
298
overlay.update(img=new_logo)
299
300
# Remove overlay
301
overlay.remove()
302
```
303
304
### File-Based Overlays
305
306
```python
307
# More efficient for static images
308
overlay = player.create_file_overlay(
309
filename='/path/to/watermark.png',
310
pos=(10, 10)
311
)
312
313
# Update file overlay
314
overlay.update(
315
filename='/path/to/different_watermark.png',
316
pos=(20, 20)
317
)
318
319
# Clean up
320
overlay.remove()
321
```
322
323
### OSD Text Overlays
324
325
```python
326
# Simple text overlay
327
overlay_id = player.allocate_overlay_id()
328
player.osd_overlay(
329
overlay_id,
330
"Hello World!",
331
res_y=720,
332
z=1
333
)
334
335
# Formatted text with ASS styling
336
ass_text = "{\\an7}{\\fs24}{\\c&H00FF00&}Green Text in Top-Left"
337
player.osd_overlay(overlay_id, ass_text)
338
339
# Complex OSD with positioning and styling
340
complex_osd = """
341
{\\an1}{\\pos(10,500)}{\\fs20}{\\c&HFFFFFF&}Title: {\\c&H00FFFF&}Video Name
342
{\\an1}{\\pos(10,530)}{\\fs16}{\\c&HCCCCCC&}Duration: 01:23:45
343
{\\an1}{\\pos(10,550)}{\\fs16}{\\c&HCCCCCC&}Quality: 1080p
344
"""
345
player.osd_overlay(overlay_id, complex_osd)
346
347
# Remove OSD overlay
348
player.osd_overlay_remove(overlay_id)
349
player.free_overlay_id(overlay_id)
350
```
351
352
### Dynamic Overlays
353
354
```python
355
import time
356
import threading
357
from PIL import Image, ImageDraw, ImageFont
358
359
class LiveOverlay:
360
def __init__(self, player):
361
self.player = player
362
self.overlay = None
363
self.running = False
364
365
def start_time_overlay(self):
366
"""Display current time as overlay."""
367
self.overlay = self.player.create_image_overlay(pos=(10, 10))
368
self.running = True
369
370
def update_time():
371
while self.running:
372
# Create time image
373
img = Image.new('RGBA', (200, 50), (0, 0, 0, 128))
374
draw = ImageDraw.Draw(img)
375
376
current_time = time.strftime('%H:%M:%S')
377
draw.text((10, 15), current_time, fill='white')
378
379
# Update overlay
380
self.overlay.update(img=img)
381
time.sleep(1)
382
383
self.thread = threading.Thread(target=update_time)
384
self.thread.start()
385
386
def stop(self):
387
self.running = False
388
if hasattr(self, 'thread'):
389
self.thread.join()
390
if self.overlay:
391
self.overlay.remove()
392
393
# Usage
394
player = mpv.MPV()
395
live_overlay = LiveOverlay(player)
396
397
player.play('/path/to/video.mp4')
398
live_overlay.start_time_overlay()
399
400
# Later...
401
live_overlay.stop()
402
```
403
404
### Multi-Layer Overlays
405
406
```python
407
# Create multiple overlays with different Z-orders
408
overlays = []
409
410
# Background overlay (lowest Z-order)
411
bg_overlay_id = player.allocate_overlay_id()
412
player.osd_overlay(bg_overlay_id,
413
"{\\pos(10,10)}{\\c&H000000&}{\\1a&H80&}Background",
414
z=0)
415
416
# Foreground overlay (higher Z-order)
417
fg_overlay_id = player.allocate_overlay_id()
418
player.osd_overlay(fg_overlay_id,
419
"{\\pos(15,15)}{\\c&HFFFFFF&}Foreground Text",
420
z=1)
421
422
# Image overlay (highest priority)
423
logo_overlay = player.create_image_overlay(
424
Image.open('/path/to/logo.png'),
425
pos=(200, 200)
426
)
427
428
overlays.extend([bg_overlay_id, fg_overlay_id, logo_overlay])
429
430
# Clean up all overlays
431
for overlay_id in [bg_overlay_id, fg_overlay_id]:
432
player.osd_overlay_remove(overlay_id)
433
player.free_overlay_id(overlay_id)
434
logo_overlay.remove()
435
```
436
437
### Screenshot-Based Processing
438
439
```python
440
# Automated screenshot processing
441
def process_screenshots():
442
player.play('/path/to/video.mp4')
443
player.wait_until_playing()
444
445
# Take screenshots at intervals
446
duration = player.duration
447
interval = 10 # Every 10 seconds
448
449
screenshots = []
450
for pos in range(0, int(duration), interval):
451
player.seek(pos, reference='absolute')
452
time.sleep(0.1) # Wait for seek
453
454
# Capture and process
455
data = player.screenshot_raw(includes='video')
456
if 'pil_image' in data:
457
img = data['pil_image']
458
# Apply processing
459
processed = img.resize((320, 240))
460
screenshots.append(processed)
461
462
# Create thumbnail montage
463
if screenshots:
464
montage_width = 5 * 320 # 5 thumbnails wide
465
montage_height = ((len(screenshots) + 4) // 5) * 240
466
montage = Image.new('RGB', (montage_width, montage_height))
467
468
for i, thumb in enumerate(screenshots):
469
x = (i % 5) * 320
470
y = (i // 5) * 240
471
montage.paste(thumb, (x, y))
472
473
montage.save('video_montage.png')
474
475
# Usage
476
process_screenshots()
477
```
478
479
### Interactive Overlay Controls
480
481
```python
482
class InteractiveOverlay:
483
def __init__(self, player):
484
self.player = player
485
self.overlay_id = player.allocate_overlay_id()
486
self.visible = True
487
488
def toggle_info_display(self):
489
"""Toggle information overlay."""
490
if self.visible:
491
self.hide_info()
492
else:
493
self.show_info()
494
495
def show_info(self):
496
"""Show current playback information."""
497
pos = self.player.time_pos or 0
498
duration = self.player.duration or 0
499
filename = self.player.filename or "Unknown"
500
501
info_text = f"""
502
{{\\an7}}{{\\pos(10,10)}}{{\\fs16}}{{\\c&HFFFFFF&}}
503
File: {{\\c&H00FFFF&}}{filename}
504
Time: {{\\c&H00FF00&}}{pos:.1f}s / {duration:.1f}s
505
Volume: {{\\c&HFF8000&}}{self.player.volume}%
506
"""
507
508
self.player.osd_overlay(self.overlay_id, info_text, z=10)
509
self.visible = True
510
511
def hide_info(self):
512
"""Hide information overlay."""
513
self.player.osd_overlay_remove(self.overlay_id)
514
self.visible = False
515
516
def cleanup(self):
517
"""Clean up overlay resources."""
518
if self.visible:
519
self.hide_info()
520
self.player.free_overlay_id(self.overlay_id)
521
522
# Bind to key press
523
interactive = InteractiveOverlay(player)
524
525
@player.key_binding('i')
526
def toggle_info():
527
interactive.toggle_info_display()
528
529
# Clean up when done
530
# interactive.cleanup()
531
```