or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-rendering.mdcore-playback.mdevent-handling.mdindex.mdinput-keybinding.mdplaylist-media.mdproperty-management.mdscreenshots-overlays.mdstreaming.md

advanced-rendering.mddocs/

0

# Advanced Rendering

1

2

OpenGL render context management for custom rendering scenarios and integration with graphics frameworks. Enables advanced video output control, custom rendering pipelines, and integration with OpenGL applications.

3

4

## Capabilities

5

6

### Render Context Creation

7

8

Create and manage OpenGL render contexts for custom video output.

9

10

```python { .api }

11

class MpvRenderContext:

12

"""OpenGL render context for advanced rendering scenarios."""

13

14

def __init__(self, mpv: 'MPV', api_type: str, **kwargs):

15

"""

16

Initialize render context.

17

18

Parameters:

19

- mpv: MPV player instance

20

- api_type: Render API type ('opengl')

21

- kwargs: API-specific parameters

22

23

For OpenGL API:

24

- get_proc_address: Function to retrieve OpenGL function pointers

25

- gl_params: MpvOpenGLInitParams object

26

"""

27

28

def render(self, **kwargs) -> int:

29

"""

30

Render current frame to OpenGL context.

31

32

Parameters:

33

- kwargs: Render parameters (fbo, viewport, etc.)

34

35

Returns:

36

Render result code (0 for success)

37

"""

38

39

def update(self) -> bool:

40

"""

41

Update render context and check for new frames.

42

43

Returns:

44

True if new frame is available for rendering

45

"""

46

47

def report_swap(self):

48

"""Report that frame buffer swap has occurred."""

49

50

def free(self):

51

"""Free render context resources."""

52

```

53

54

### Filter Commands

55

56

Send commands to audio and video filter chains for dynamic filter control.

57

58

```python { .api }

59

def vf_command(self, label: str, command: str, argument):

60

"""

61

Send command to a video filter.

62

63

Parameters:

64

- label: Filter label/name in the filter chain

65

- command: Command name to send to the filter

66

- argument: Command argument/parameter

67

"""

68

69

def af_command(self, label: str, command: str, argument):

70

"""

71

Send command to an audio filter.

72

73

Parameters:

74

- label: Filter label/name in the filter chain

75

- command: Command name to send to the filter

76

- argument: Command argument/parameter

77

"""

78

```

79

80

### OpenGL Parameter Classes

81

82

Parameter classes for configuring OpenGL rendering.

83

84

```python { .api }

85

class MpvOpenGLInitParams:

86

"""OpenGL initialization parameters."""

87

88

def __init__(self, get_proc_address):

89

"""

90

Initialize OpenGL parameters.

91

92

Parameters:

93

- get_proc_address: Function pointer to get OpenGL function addresses

94

"""

95

96

class MpvOpenGLFBO:

97

"""OpenGL framebuffer object parameters."""

98

99

def __init__(self, w: int, h: int, fbo: int = 0, internal_format: int = 0):

100

"""

101

Initialize FBO parameters.

102

103

Parameters:

104

- w, h: Framebuffer dimensions

105

- fbo: Framebuffer object ID (0 for default framebuffer)

106

- internal_format: Internal format (0 for auto)

107

"""

108

109

class MpvRenderFrameInfo:

110

"""Information about rendered frame."""

111

112

def as_dict(self) -> dict:

113

"""

114

Get frame information as dictionary.

115

116

Returns:

117

Dictionary with frame metadata

118

"""

119

120

class MpvRenderParam:

121

"""Generic render parameter container."""

122

123

def __init__(self, name: str, value=None):

124

"""

125

Initialize render parameter.

126

127

Parameters:

128

- name: Parameter name

129

- value: Parameter value

130

"""

131

132

# Parameter type constants

133

TYPES = {

134

'invalid': 0,

135

'api_type': 1,

136

'opengl_init_params': 2,

137

'opengl_fbo': 3,

138

'flip_y': 4,

139

'depth': 5,

140

'icc_profile': 6,

141

'ambient_light': 7,

142

'x11_display': 8,

143

'wl_display': 9,

144

'advanced_control': 10,

145

'next_frame_info': 11,

146

'block_for_target_time': 12,

147

'skip_rendering': 13,

148

'opengl_drm_params': 14,

149

'opengl_drm_draw_surface_size': 15,

150

'opengl_drm_params_v2': 16,

151

'sw_size': 17,

152

'sw_format': 18,

153

'sw_stride': 19,

154

'sw_pointer': 20

155

}

156

```

157

158

### DRM (Direct Rendering Manager) Support

159

160

Classes for hardware-accelerated rendering on Linux.

161

162

```python { .api }

163

class MpvOpenGLDRMParams:

164

"""DRM parameters for direct hardware rendering."""

165

166

class MpvOpenGLDRMDrawSurfaceSize:

167

"""DRM draw surface size specification."""

168

169

class MpvOpenGLDRMParamsV2:

170

"""Enhanced DRM parameters with additional features."""

171

172

def __init__(self, crtc_id: int, connector_id: int, atomic_request_ptr, fd: int = -1, render_fd: int = -1):

173

"""

174

Initialize DRM parameters.

175

176

Parameters:

177

- crtc_id: CRTC (display controller) ID

178

- connector_id: Display connector ID

179

- atomic_request_ptr: Atomic mode setting request

180

- fd: DRM file descriptor

181

- render_fd: Render node file descriptor

182

"""

183

```

184

185

## Usage Examples

186

187

### Basic OpenGL Integration

188

189

```python

190

import mpv

191

import OpenGL.GL as gl

192

from OpenGL.GL import *

193

import ctypes

194

195

# OpenGL context setup (varies by platform/framework)

196

def setup_opengl_context():

197

"""Setup OpenGL context - implementation depends on your framework."""

198

# This is pseudocode - actual implementation depends on your OpenGL framework

199

# (pygame, GLFW, Qt, tkinter, etc.)

200

pass

201

202

def get_proc_address(name):

203

"""Get OpenGL function pointer."""

204

# Implementation depends on your OpenGL loading library

205

# For example, with GLFW:

206

# return glfw.get_proc_address(name.decode('utf-8'))

207

pass

208

209

# Initialize player and render context

210

player = mpv.MPV(vo='libmpv') # Use libmpv video output

211

212

# Create OpenGL initialization parameters

213

gl_init_params = mpv.MpvOpenGLInitParams(get_proc_address)

214

215

# Create render context

216

render_ctx = mpv.MpvRenderContext(

217

player,

218

'opengl',

219

opengl_init_params=gl_init_params

220

)

221

222

# Basic rendering loop

223

def render_frame():

224

"""Render mpv frame to current OpenGL context."""

225

226

# Check for new frame

227

if render_ctx.update():

228

# Get viewport dimensions

229

viewport = gl.glGetIntegerv(gl.GL_VIEWPORT)

230

width, height = viewport[2], viewport[3]

231

232

# Create FBO parameters for default framebuffer

233

fbo = mpv.MpvOpenGLFBO(width, height)

234

235

# Render frame

236

result = render_ctx.render(opengl_fbo=fbo)

237

238

# Report buffer swap

239

render_ctx.report_swap()

240

241

return result == 0 # Success

242

243

return False

244

245

# Usage in main loop

246

player.play('/path/to/video.mp4')

247

248

while True: # Your main loop

249

if render_frame():

250

# Frame was rendered

251

pass

252

253

# Handle other events, swap buffers, etc.

254

# This depends on your OpenGL framework

255

```

256

257

### Framebuffer Object Rendering

258

259

```python

260

import numpy as np

261

262

class OffscreenRenderer:

263

def __init__(self, player, width, height):

264

self.player = player

265

self.width = width

266

self.height = height

267

268

# Create OpenGL resources

269

self.setup_fbo()

270

self.setup_render_context()

271

272

def setup_fbo(self):

273

"""Create framebuffer object for offscreen rendering."""

274

275

# Generate framebuffer

276

self.fbo = gl.glGenFramebuffers(1)

277

gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self.fbo)

278

279

# Create color texture

280

self.color_texture = gl.glGenTextures(1)

281

gl.glBindTexture(gl.GL_TEXTURE_2D, self.color_texture)

282

gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA8,

283

self.width, self.height, 0,

284

gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, None)

285

gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR)

286

gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR)

287

288

# Attach texture to framebuffer

289

gl.glFramebufferTexture2D(gl.GL_FRAMEBUFFER, gl.GL_COLOR_ATTACHMENT0,

290

gl.GL_TEXTURE_2D, self.color_texture, 0)

291

292

# Check framebuffer completeness

293

if gl.glCheckFramebufferStatus(gl.GL_FRAMEBUFFER) != gl.GL_FRAMEBUFFER_COMPLETE:

294

raise RuntimeError("Framebuffer not complete")

295

296

gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0)

297

298

def setup_render_context(self):

299

"""Setup mpv render context."""

300

gl_init_params = mpv.MpvOpenGLInitParams(get_proc_address)

301

302

self.render_ctx = mpv.MpvRenderContext(

303

self.player,

304

'opengl',

305

opengl_init_params=gl_init_params

306

)

307

308

def render_to_texture(self):

309

"""Render current frame to texture."""

310

311

if not self.render_ctx.update():

312

return False

313

314

# Bind our framebuffer

315

gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self.fbo)

316

gl.glViewport(0, 0, self.width, self.height)

317

318

# Clear framebuffer

319

gl.glClear(gl.GL_COLOR_BUFFER_BIT)

320

321

# Render mpv frame

322

fbo_params = mpv.MpvOpenGLFBO(self.width, self.height, self.fbo)

323

result = self.render_ctx.render(opengl_fbo=fbo_params)

324

325

# Report swap

326

self.render_ctx.report_swap()

327

328

# Restore default framebuffer

329

gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0)

330

331

return result == 0

332

333

def read_pixels(self):

334

"""Read rendered pixels from texture."""

335

336

gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self.fbo)

337

338

# Read pixels

339

pixels = gl.glReadPixels(0, 0, self.width, self.height,

340

gl.GL_RGBA, gl.GL_UNSIGNED_BYTE)

341

342

gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0)

343

344

# Convert to numpy array

345

pixel_array = np.frombuffer(pixels, dtype=np.uint8)

346

pixel_array = pixel_array.reshape((self.height, self.width, 4))

347

348

# Flip vertically (OpenGL convention)

349

pixel_array = np.flip(pixel_array, axis=0)

350

351

return pixel_array

352

353

def cleanup(self):

354

"""Clean up OpenGL resources."""

355

gl.glDeleteFramebuffers(1, [self.fbo])

356

gl.glDeleteTextures(1, [self.color_texture])

357

self.render_ctx.free()

358

359

# Usage

360

offscreen = OffscreenRenderer(player, 1920, 1080)

361

362

player.play('/path/to/video.mp4')

363

player.wait_until_playing()

364

365

# Render frames and capture

366

for i in range(100): # Capture 100 frames

367

if offscreen.render_to_texture():

368

pixels = offscreen.read_pixels()

369

370

# Save frame as image

371

from PIL import Image

372

img = Image.fromarray(pixels[:, :, :3]) # Remove alpha channel

373

img.save(f'frame_{i:04d}.png')

374

375

# Advance to next frame

376

player.frame_step()

377

378

offscreen.cleanup()

379

```

380

381

### Multi-Context Rendering

382

383

```python

384

class MultiContextRenderer:

385

def __init__(self, player):

386

self.player = player

387

self.contexts = {}

388

389

def add_render_target(self, name, width, height, context_setup_func):

390

"""Add a new render target with its own context."""

391

392

# Setup context-specific OpenGL state

393

context_setup_func()

394

395

# Create render context for this target

396

gl_init_params = mpv.MpvOpenGLInitParams(get_proc_address)

397

render_ctx = mpv.MpvRenderContext(

398

self.player,

399

'opengl',

400

opengl_init_params=gl_init_params

401

)

402

403

self.contexts[name] = {

404

'render_ctx': render_ctx,

405

'width': width,

406

'height': height,

407

'setup_func': context_setup_func

408

}

409

410

def render_to_target(self, target_name, fbo_id=0):

411

"""Render to specific target."""

412

413

if target_name not in self.contexts:

414

return False

415

416

ctx_info = self.contexts[target_name]

417

render_ctx = ctx_info['render_ctx']

418

419

# Setup target context

420

ctx_info['setup_func']()

421

422

if render_ctx.update():

423

# Create FBO parameters

424

fbo_params = mpv.MpvOpenGLFBO(

425

ctx_info['width'],

426

ctx_info['height'],

427

fbo_id

428

)

429

430

# Render

431

result = render_ctx.render(opengl_fbo=fbo_params)

432

render_ctx.report_swap()

433

434

return result == 0

435

436

return False

437

438

def cleanup(self):

439

"""Clean up all render contexts."""

440

for ctx_info in self.contexts.values():

441

ctx_info['render_ctx'].free()

442

443

# Usage with multiple windows/contexts

444

multi_renderer = MultiContextRenderer(player)

445

446

# Add different render targets

447

def setup_main_window():

448

# Setup main window OpenGL context

449

pass

450

451

def setup_preview_window():

452

# Setup preview window OpenGL context

453

pass

454

455

multi_renderer.add_render_target('main', 1920, 1080, setup_main_window)

456

multi_renderer.add_render_target('preview', 320, 240, setup_preview_window)

457

458

# Render to different targets

459

player.play('/path/to/video.mp4')

460

461

while True: # Main loop

462

# Render to main window

463

multi_renderer.render_to_target('main')

464

465

# Render to preview window

466

multi_renderer.render_to_target('preview')

467

468

# Handle events, etc.

469

```

470

471

### Custom Render Pipeline

472

473

```python

474

class CustomRenderPipeline:

475

def __init__(self, player):

476

self.player = player

477

self.render_ctx = None

478

self.post_process_shaders = []

479

480

def setup_pipeline(self):

481

"""Setup custom rendering pipeline."""

482

483

# Create render context

484

gl_init_params = mpv.MpvOpenGLInitParams(get_proc_address)

485

self.render_ctx = mpv.MpvRenderContext(

486

self.player,

487

'opengl',

488

opengl_init_params=gl_init_params

489

)

490

491

# Create intermediate framebuffers for post-processing

492

self.setup_framebuffers()

493

494

def setup_framebuffers(self):

495

"""Create framebuffers for pipeline stages."""

496

497

# Main rendering FBO

498

self.main_fbo = gl.glGenFramebuffers(1)

499

self.main_texture = gl.glGenTextures(1)

500

501

# Post-processing FBOs

502

self.post_fbos = []

503

self.post_textures = []

504

505

for i in range(2): # Ping-pong buffers

506

fbo = gl.glGenFramebuffers(1)

507

texture = gl.glGenTextures(1)

508

509

gl.glBindTexture(gl.GL_TEXTURE_2D, texture)

510

gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA8,

511

1920, 1080, 0, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, None)

512

gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR)

513

gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR)

514

515

self.post_fbos.append(fbo)

516

self.post_textures.append(texture)

517

518

def add_post_process_shader(self, vertex_shader, fragment_shader):

519

"""Add post-processing shader to pipeline."""

520

521

# Compile and link shader program

522

program = self.compile_shader_program(vertex_shader, fragment_shader)

523

self.post_process_shaders.append(program)

524

525

def compile_shader_program(self, vertex_src, fragment_src):

526

"""Compile OpenGL shader program."""

527

528

# Vertex shader

529

vertex_shader = gl.glCreateShader(gl.GL_VERTEX_SHADER)

530

gl.glShaderSource(vertex_shader, vertex_src)

531

gl.glCompileShader(vertex_shader)

532

533

# Fragment shader

534

fragment_shader = gl.glCreateShader(gl.GL_FRAGMENT_SHADER)

535

gl.glShaderSource(fragment_shader, fragment_src)

536

gl.glCompileShader(fragment_shader)

537

538

# Program

539

program = gl.glCreateProgram()

540

gl.glAttachShader(program, vertex_shader)

541

gl.glAttachShader(program, fragment_shader)

542

gl.glLinkProgram(program)

543

544

# Cleanup individual shaders

545

gl.glDeleteShader(vertex_shader)

546

gl.glDeleteShader(fragment_shader)

547

548

return program

549

550

def render_with_pipeline(self):

551

"""Render frame through custom pipeline."""

552

553

if not self.render_ctx.update():

554

return False

555

556

# Render mpv frame to main FBO

557

gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self.main_fbo)

558

559

fbo_params = mpv.MpvOpenGLFBO(1920, 1080, self.main_fbo)

560

result = self.render_ctx.render(opengl_fbo=fbo_params)

561

562

if result != 0:

563

return False

564

565

# Apply post-processing pipeline

566

current_input = self.main_texture

567

568

for i, shader in enumerate(self.post_process_shaders):

569

# Use ping-pong buffers

570

output_idx = i % 2

571

output_fbo = self.post_fbos[output_idx]

572

output_texture = self.post_textures[output_idx]

573

574

# Bind output framebuffer

575

gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, output_fbo)

576

gl.glViewport(0, 0, 1920, 1080)

577

578

# Use shader

579

gl.glUseProgram(shader)

580

581

# Bind input texture

582

gl.glActiveTexture(gl.GL_TEXTURE0)

583

gl.glBindTexture(gl.GL_TEXTURE_2D, current_input)

584

gl.glUniform1i(gl.glGetUniformLocation(shader, "inputTexture"), 0)

585

586

# Render fullscreen quad

587

self.render_fullscreen_quad()

588

589

current_input = output_texture

590

591

# Final output to screen

592

gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0)

593

gl.glViewport(0, 0, 1920, 1080)

594

595

# Simple blit of final result

596

gl.glUseProgram(0)

597

gl.glActiveTexture(gl.GL_TEXTURE0)

598

gl.glBindTexture(gl.GL_TEXTURE_2D, current_input)

599

gl.glEnable(gl.GL_TEXTURE_2D)

600

601

# Render final quad

602

self.render_fullscreen_quad()

603

604

self.render_ctx.report_swap()

605

return True

606

607

def render_fullscreen_quad(self):

608

"""Render a fullscreen quad for post-processing."""

609

gl.glBegin(gl.GL_QUADS)

610

gl.glTexCoord2f(0, 0); gl.glVertex2f(-1, -1)

611

gl.glTexCoord2f(1, 0); gl.glVertex2f(1, -1)

612

gl.glTexCoord2f(1, 1); gl.glVertex2f(1, 1)

613

gl.glTexCoord2f(0, 1); gl.glVertex2f(-1, 1)

614

gl.glEnd()

615

616

# Usage

617

pipeline = CustomRenderPipeline(player)

618

pipeline.setup_pipeline()

619

620

# Add bloom effect shader

621

bloom_fragment = """

622

#version 330 core

623

uniform sampler2D inputTexture;

624

in vec2 texCoord;

625

out vec4 fragColor;

626

627

void main() {

628

vec3 color = texture(inputTexture, texCoord).rgb;

629

630

// Simple bloom effect

631

float brightness = dot(color, vec3(0.2126, 0.7152, 0.0722));

632

if (brightness > 0.8) {

633

color *= 1.5;

634

}

635

636

fragColor = vec4(color, 1.0);

637

}

638

"""

639

640

bloom_vertex = """

641

#version 330 core

642

layout(location = 0) in vec2 position;

643

out vec2 texCoord;

644

645

void main() {

646

texCoord = position * 0.5 + 0.5;

647

gl_Position = vec4(position, 0.0, 1.0);

648

}

649

"""

650

651

pipeline.add_post_process_shader(bloom_vertex, bloom_fragment)

652

653

# Render with custom pipeline

654

player.play('/path/to/video.mp4')

655

656

while True: # Main loop

657

pipeline.render_with_pipeline()

658

# Swap buffers, handle events, etc.

659

```