1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
|
<chapter id="output">
<title>xine's output layer</title>
<sect1>
<title>Post plugin layer</title>
<para>
In this section you will learn, how the powerful post plugin architecture
works and how to write post plugins.
</para>
<sect2>
<title>General principle of post plugins</title>
<para>
The name "post plugin" comes from "postprocessing" which already describes
what these plugins are supposed to do: they take video frames, audio
buffers or subpicture planes as they leave the decoders and apply arbitrary
processing to them. Then they pass processed elements on to the output
loops. Post plugins can not only be chained to process the predecessor's
output and advance the data to their successor, they can form arbitrary trees,
since post plugins can have any number of inputs and outputs. Additionally,
the interconnection of the plugins currently inserted in such a tree can
be changed on the fly during playback. The public function
<function>xine_post_wire()</function> is used by frontends to form such
connections.
</para>
<para>
Due to the variety of possible applications, the interface post plugins have
to implement is just the basic foundation. The complexity comes from hooking
your post plugin into the engine data paths for video frames and audio buffers,
intercepting the data and performing your operation on them. This is done by
taking the interface of a video or audio port, replacing some of the functions
with your own ones and passing the interface to the decoder or predecessor
post plugin that is going to feed you with data by accessing this interface
and by doing that, calling the functions you provide. From there you can do
almost anything you want. Constructing video frames from audio buffers to
visualize sound is possible as well as just outputting an integer giving the
average brightness of an image. It is also possible to invent post plugins
with no output (not very useful, unless the plugin has some side-effect) or
no input at all; for the latter you have to create your own pthread, otherwise
your plugin will not do anything. An audio signal generator could be
implemented like this. The various data types, post plugins can
accept as input or offer as output are defined in <filename>xine.h</filename>
as <varname>XINE_POST_DATA_*</varname> defines.
</para>
<para>
Some terminology used in the following explanations:
<itemizedlist>
<listitem>
<para>
<varname>down direction</varname>:
The direction from the decoders to the output. This is the way video or audio
data (actual content and meta information) usually travels through the engine.
</para>
</listitem>
<listitem>
<para>
<varname>up direction</varname>:
The direction from the output to the decoders. This is the way some video or audio
metadata like metronom timestamps travel through the engine.
</para>
</listitem>
<listitem>
<para>
<varname>interception</varname>:
Post plugins are inserted into the engine data paths by the means of the decorator
design pattern. This works by taking engine structures with member funtions like
video or audio ports, video frames or overlay managers and inserting your own functions
into a copy of this structure. This is called interception. This modified structure
is then passed up to the plugin that uses it and thus calls your replaced functions.
</para>
</listitem>
</itemizedlist>
</para>
</sect2>
<sect2>
<title>Writing a xine post plugin</title>
<para>
The post plugin API is declared in <filename>src/xine-engine/post.h</filename>
The plugin info of post plugins contains the post plugin type, which is one of the
<varname>XINE_POST_TYPE_*</varname> defines and the init_class function of the plugin.
</para>
<para>
<programlisting> post_plugin_t *open_plugin(post_class_t *class_gen, int inputs, xine_audio_port_t **audio_target, xine_video_port_t **video_target);</programlisting>
Returns an instance of the plugin. Some post plugins evaluate <varname>inputs</varname>
to open a variable number of inputs. Since almost all post plugins have audio or video
outputs, you can hand in a NULL-terminated array of ports to connect to these outputs.
In this function you can also intercept these ports so that your plugin is actually used.
There is a helper function to initialize a <type>post_plugin_t</type>, which you are
encouraged to use: <function>_x_post_init()</function>.
</para>
<para>
<programlisting> char *get_identifier(post_class_t *class_gen);</programlisting>
This function returns a short identifier describing the plugin.
</para>
<para>
<programlisting> char *get_description(post_class_t *class_gen);</programlisting>
This function returns a plaintext, one-line string describing the plugin.
</para>
<para>
<programlisting> void dispose(post_class_t *class_gen);</programlisting>
This function frees the memory used by the video out plugin class object.
</para>
<para>
The <type>post_plugin_t</type> structure contains the publicly visible
part of the post plugin with the audio and video inputs and the type of
the post plugin. Not publicly visible are the lists of all inputs and outputs,
the <function>dispose()</function> function and some internal stuff which
plugins do not have to worry about.
</para>
<para>
<programlisting> void dispose(post_plugin_t *this_gen);</programlisting>
This function frees the memory used by the plugin instance, but not necessarily
immediately. Since post plugins enter their own functions into engine structures,
they might still be needed when <function>dispose()</function> is being called.
They maintain a usage counter to detect that. To check for such a condition, you
should use the <function>_x_post_dispose()</function> helper function like that:
<programlisting>
if (_x_post_dispose(this))
really_free(this);</programlisting>
<function>_x_post_dispose()</function> frees any ressources allocated by any of the
post plugin helper functions and returns boolean true, if the plugin is not needed
any more.
</para>
</sect2>
<sect2>
<title>Interception</title>
<para>
Currently, we have four engine structures which can be intercepted by post plugins:
video ports, video frames, overlay managers and audio ports. You could do this
interception manually, but since this is quite a complex process, there are helper
functions to assist you and their usage is encouraged.
</para>
<sect3>
<title>Intercepting a video port</title>
<para>
<programlisting>
post_video_port_t *_x_post_intercept_video_port(post_plugin_t *post,
xine_video_port_t *port, post_in_t **input, post_out_t **output);</programlisting>
This function will intercept <varname>port</varname> and returns a structure
for you to pass up. All functions in the port will be replaced with dummy
pass through functions that do nothing but relaying the call down to the original
port. If you want (that is, <varname>input</varname> or <varname>output</varname> are
not NULL), this function will also create the input and output descriptors complete
with rewiring functions and add them to the relevant lists.
This is required, if you want this port to be advertised by the plugin to the outside world.
</para>
<para>
<type>post_video_port_t</type> makes a variety of interception schemes very easy.
If you want to replace any of the default functions with your own, just enter it
into <varname>new_port</varname>. You can use <varname>original_port</varname>
from within your function to propagate calls down to the original port.
The constraint is that your functions have to ensure that every original
port held open scores one usage counter point, so that opened ports are always
closed before the plugin is disposed. Therefore, you should use the macro
<function>_x_post_inc_usage()</function> before calling
<function>original_port->open()</function> and use the macro
<function>_x_post_dec_usage()</function> after calling
<function>original_port->close()</function>. Note that <function>_x_post_dec_usage()</function>
might dispose the plugin, when <function>dispose()</function> has been called
earlier and usage count drops to zero, so do never touch plugin structures after
<function>_x_post_dec_usage()</function>. In addition, you must never call a port
function of the original port when the port is not open.
</para>
<para>
Intercepting video frames or the overlay manager of the port is even easier.
You do not have to reimplement <function>get_frame()</function> or
<function>get_overlay_manager()</function>. Just enter a <varname>intercept_frame</varname>
or <varname>intercept_ovl</varname> function which returns boolean true, if
you want to intercept. The functions to insert in the intercepted frame or overlay
manager are taken from <varname>new_frame</varname> and <varname>new_manager</varname>
respectively. Note that the defaults are reversed: If you do not enter such a
decision function for either one, all frames and no overlay manager will be intercepted.
</para>
<para>
For convenience <type>post_video_port_t</type> also contains pointers to the
current stream and to the current post plugin and a user_data pointer, where you
can put in anything you need in addition. If your port is used by more than one
thread, you can also enforce locking on the port, frame or overlay manager level
by entering a lock into <varname>port_lock</varname>, <varname>frame_lock</varname> or
<varname>manager_lock</varname> respectively.
</para>
</sect3>
<sect3>
<title>Intercepting an audio port</title>
<para>
Audio port interception is just a stripped down version of video port interception.
Everything related to frames and overlay manager is not needed and audio buffers
do not need to be intercepted, since they have no member functions. Everything else
of the above still applies.
</para>
</sect3>
<sect3>
<title>Intercepting overlay manager</title>
<para>
<programlisting> void _x_post_intercept_overlay_manager(video_overlay_manager_t *original, post_video_port_t *port);</programlisting>
Interception of the overlay manager is done automatically when your
<function>intercept_ovl()</function> decision function returns boolean true.
Should you ever decide not to use that, interception can be done with this helper
function, which simply creates an intercepted overlay manager with dummy
pass through functions in <varname>port->new_manager</varname> and stores the original
manager in <varname>port->original_manager</varname>.
</para>
<para>
No matter how you intercepted the overlay manager, your own replacement
functions will receive <varname>port->new_manager</varname> as the overlay manager
argument. But you most likely want to have access to the <type>post_video_port_t</type>
from within your functions. For that, there exists a pointer retrieval function:
<programlisting> post_video_port_t *_x_post_ovl_manager_to_port(video_overlay_manager_t *manager);</programlisting>
</para>
</sect3>
<sect3>
<title>Intercepting a video frame</title>
<para>
<programlisting>
vo_frame_t *_x_post_intercept_video_frame(vo_frame_t *frame, post_video_port_t *port);
vo_frame_t *_x_post_restore_video_frame(vo_frame_t *frame, post_video_port_t *port);</programlisting>
Interception of video frames is done automatically when your
<function>intercept_frame()</function> decision function returns boolean true or
when there is no such function in <type>post_video_port_t</type>.
Should you ever decide not to use that, interception can be done with the helper
function <function>_x_post_intercept_video_frame()</function>.
</para>
<para>
Since the same video frame can be in use in the decoder and in the output and in
any post plugin in between at the same time, simply modifying the frame
structure does not work, because every user of the frame needs to see his version
and the frame must always travel along the same path through the plugins for its
entire lifetime. To ensure that, <function>_x_post_intercept_video_frame()</function>
provides a shallow copy of the frame structure with the original frame attached to
<varname>copy->next</varname>. This copy will be filled with your own
frame functions from <varname>port->new_frame</varname> and with dummy pass
through functions for those you did not provide. This way, every part of xine
using this frame will see its own frame structure with a list of frame
contexts from down the data path attached to <varname>frame->next</varname>.
<function>_x_post_restore_video_frame()</function> reverses this and should be
used when the frame is freed or disposed.
</para>
<para>
Your own replacement functions will receive the copied frame as as argument.
But you most likely want to have access to the <type>post_video_port_t</type>
from within your functions. For that, there exists a pointer retrieval function:
<programlisting> post_video_port_t *_x_post_video_frame_to_port(vo_frame_t *frame);</programlisting>
The constraint is that your functions have to ensure that every intercepted
frame scores one usage counter point, so that these frames are always
freed or disposed before the plugin is disposed. Therefore, you should use the macro
<function>_x_post_inc_usage()</function> before calling
<function>_x_post_intercept_video_frame()</function> and use the macro
<function>_x_post_dec_usage()</function> after calling
<function>_x_post_restore_video_frame()</function>. Note that <function>_x_post_dec_usage()</function>
might dispose the plugin, when <function>dispose()</function> has been called
earlier and usage count drops to zero, so do never touch plugin structures after
<function>_x_post_dec_usage()</function>.
</para>
<para>
From within your own frame functions, you can propagate calls to the original
frame by calling a function on <varname>frame->next</varname>. Since
modifications to the frame can travel both upwards and downwards (decoders and
output can modify the frame), changes need to be copied between the frame
structure contexts. You should use the <function>_x_post_frame_copy_down()</function>
and <function>_x_post_frame_copy_up()</function> helper functions like that:
<programlisting>
_x_post_frame_copy_down(frame, frame->next);
frame->next->draw(frame->next, stream);
_x_post_frame_copy_up(frame, frame->next);</programlisting>
</para>
<para>
If your post plugin modifies the content of the frame, you have to modify
a deep copy of the frame, because the decoder might still use the frame as
a reference frame for future decoding. The usual procedure is:
<programlisting>
modified_frame = port->original_port->get_frame(port->original_port, …);
_x_post_frame_copy_down(frame, modified_frame);
copy_and_modify(frame, modified_frame);
skip = modified_frame->draw(modified_frame, stream);
_x_post_frame_copy_up(frame, modified_frame);
modified_frame->free(modified_frame);</programlisting>
</para>
<para>
When the output receives a frame via <function>draw()</function>,
it usually receives the stream where the frame
originates as well and modifies the state of this stream by passing
the frame through the stream's metronom. Sometimes this is unwanted.
For example, when you pass the same frame to the output more than once, it
will confuse metronom. To solve this, you can call
<function>frame->next->draw()</function> with NULL as the stream.
You might also want to prevent frames from being passed down to the output
completely, because your post plugin creates something else from these frames,
but does not need them to be drawn. In both these situations, you have
to call the helper function <function>_x_post_frame_u_turn()</function>
when the frame is drawn, because this does some housekeeping which the
decoder might expect to take place.
</para>
<para>
The following diagram summarizes the situations of a video frame passing
through a post plugin:
</para>
<mediaobject>
<imageobject>
<imagedata fileref="post_frame.png" format="PNG">
</imageobject>
<imageobject>
<imagedata fileref="post_frame.eps" format="EPS">
</imageobject>
<caption>
<para>video frame passing through a post plugin</para>
</caption>
</mediaobject>
</sect3>
<sect3>
<title>Summary of constraints</title>
<itemizedlist>
<listitem>
<para>
Call <function>_x_post_inc_usage()</function> before port <function>open()</function>
before any other port function.
</para>
</listitem>
<listitem>
<para>
Call <function>_x_post_inc_usage()</function> before issueing an intercepted frame.
</para>
</listitem>
<listitem>
<para>
Call <function>_x_post_dec_usage()</function> after port <function>close()</function>
and do not call any port functions after that.
</para>
</listitem>
<listitem>
<para>
Call <function>_x_post_dec_usage()</function> after restoring a frame.
</para>
</listitem>
<listitem>
<para>
When a frame is drawn by your plugin, it must either be drawn on the original
port with the correct stream as argument or U-turned (or passed through a
private metronom manually).
</para>
</listitem>
<listitem>
<para>
If your post plugin keeps locked frames, release them when your input port is being
closed.
</para>
</listitem>
<listitem>
<para>
Do not assume your plugin is alive after <function>_x_post_dec_usage()</function>.
</para>
</listitem>
</itemizedlist>
</sect3>
</sect2>
<sect2>
<title>Rewiring and the ticket system</title>
<para>
Rewiring is the reconnection of one post plugin's outputs and another post plugin's
inputs. This can happen on the fly during playback, which makes this a very delicate
process. In one such input to output connection, only the output is active by either
writing data directly to the connected input or by calling functions there. Therefore
we have to notify only the output, when it is rewired. This is done by calling the
<function>rewire()</function> member function of the corresponding
<type>xine_post_out_t</type> when the frontend issues a rewiring on this output.
This is done from the frontend thread for the rewire operation to return synchroneously.
But since this can happen on the fly, we have to assure that no other thread is relying
on the connection while we modify it. For this, threads working within post plugins
have to be accounted and on demand suspended in safe positions. For this, xine offers
a synchronization facility called "tickets".
</para>
<sect3>
<title>Ticket system principles and operations</title>
<para>
The idea of the ticket system is based on an extended read-write lock, where there can
be many readers in parallel, but only one exclusive writer. A certain interface might
require you to have a ticket before calling its functions. The ticket system now
allows multiple threads to operate within the component behind the interface by
granting as many tickets as needed. But should an outside operation require exclusive
access to the component, all granted tickets can be revoked and have to be given back
by the threads who hold them, which suspends the threads. After the exclusive
operation, tickets will be reissued so all suspended threads can continue where they
stopped. We will now look at the ticket primitives in detail:
</para>
<variablelist>
<varlistentry>
<term><function>acquire()</function></term>
<listitem>
<para>
You receive a new ticket. If the ticket is currently revoked, you can be blocked
until it is reissued. There is one exception to this: You can acquire a revoked
ticket, if you revoked it atomic yourself. You can also acquire a ticket irrevocably.
Between acquire and release of an irrevocable ticket, it is guaranteed that
you will not be blocked by ticket revocation.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>release()</function></term>
<listitem>
<para>
You give a ticket back when you do not need it any longer. If the ticket is
currently revoked, you can be blocked until it is reissued. If you acquired the
ticket irrevocably, you have to release it irrevocably as well.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>renew()</function></term>
<listitem>
<para>
You must only call this function, when the ticket has been revoked, so be
sure to check <varname>ticket_revoked</varname> before. You give the ticket
back and receive a new one. In between, you can be blocked until the ticket is
reissued. You have to renew irrevocably, if you cannot assure that the thread holds
no irrevocable tickets. If you can assure this, renew revocably.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>revoke()</function></term>
<listitem>
<para>
This function can only be called by the xine engine, plugins do not have access to it.
It revokes all tickets and after finite time, all threads will run into a
<function>acquire()</function>, <function>release()</function> or <function>renew()</function>
and will be suspended there. Then the revocation returns and you can modify structures
or call functions with the knowledge that all ticket holders will remain in safe
positions. When you additionally need exclusive access where no other revoker
can interfere, you have to revoke atomic.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>issue()</function></term>
<listitem>
<para>
This function can only be called by the xine engine, plugins do not have access to it.
It ends ticket revocation and hands out new tickets to all threads that applied with a
<function>acquire()</function> or <function>renew()</function>. If you revoked the
tickets atomic, you have to issue them atomic.
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
When you use the ticket system in any way, you have to obey to the following rules:
<itemizedlist>
<listitem>
<para>
Assure to release irrevocable tickets ater a finite time.
</para>
</listitem>
<listitem>
<para>
Assure to release or renew revocable tickets ater a finite time.
</para>
</listitem>
<listitem>
<para>
Assure to reissue tickets you revoked atomic after a finite time.
</para>
</listitem>
<listitem>
<para>
Pair calls properly.
</para>
</listitem>
<listitem>
<para>
Never revoke a ticket you hold.
</para>
</listitem>
<listitem>
<para>
Never acquire a ticket you revoked atomic before.
</para>
</listitem>
<listitem>
<para>
Never acquire a ticket revocable more than once.
</para>
</listitem>
</itemizedlist>
</para>
</sect3>
<sect3>
<title>Ticket handling in decoder and post plugins</title>
<para>
The contract of the video and audio port interfaces is that you need to have
the port ticket, when you want to call functions of these interfaces. The decoder
plugins do not have to worry about this at all, since the decoder loops inside the
engine handle the ticketing. Post plugins get the ticket assigned by the engine,
but usually, a post plugin does not create a new thread, it is called by the
decoder thread and therefore already owns the necessary ticket. All port functions
are also ticket-safe as they follow all the rules given above.
</para>
<para>
You only have to remember that tickets need to be renewed as soon as possible,
when the are revoked. For this, the helper function
<function>_x_post_rewire()</function> should be used in prominent locations
where it is safe to be suspended. Candidates for such locations are at the
beginning of the port's <function>open()</function> and
<function>get_frame()</function>/<function>get_buffer()</function> functions.
The default pass through implementations for intercepted ports already do this.
</para>
<para>
The port tickets are revoked, whenever a rewiring takes place or the engine
switches into pause mode. The tickets are reissued, when the rewiring is finished
or the engine switches from pause mode into playback. Some post plugins might
contain critical parts, where they must not be interrupted by a rewire or pause.
These sections can be enclosed in <function>_x_post_lock()</function> and
<function>_x_post_unlock()</function>, which will simply acquire and release an
irrevocable ticket for you. As long as you hold such a ticket, it is guaranteed
that you will never be interrupted by a pause or rewire.
</para>
</sect3>
</sect2>
</sect1>
<sect1>
<title>Video output</title>
<para>
In order to allow for device-dependant acceleration features, xine
calls upon the video output plugin for more than just displaying
images. The tasks performed by the video plugins are:
<itemizedlist>
<listitem>
<para>
Allocation of <type>vo_frame_t</type> structures and their
subsequent destruction.
</para>
</listitem>
<listitem>
<para>
Allocation of memory for use by one frame (this is to allow
for the ability of some video output plugins to map frames directly
into video-card memory hence removing the need for the frame to
be copied across the PCI/AGP bus at display time).
</para>
</listitem>
<listitem>
<para>
Most important, the ability to render/copy a given
frame to the output device.
</para>
</listitem>
<listitem>
<para>
Optionally the copying of the frame from a file dependant
colour-space and depth into the frame structure. This is to allow for
on-the fly colour-space conversion and scaling if required (e.g. the XShm
ouput plugin uses this mechanism).
</para>
</listitem>
</itemizedlist>
</para>
<para>
Although these extra responsibilities add great complexity to your
plugin it should be noted that they allow plugins to take full advantage
of any special hardware-acceleration without sacrificing flexibility.
</para>
<sect2>
<title>Writing a xine video out plugin</title>
<para>
The video out plugin API is declared in <filename>src/xine-engine/video_out.h</filename>
The plugin info of video out plugins contains the visual type, priority,
and the init_class function of the plugin.
</para>
<para>
The <varname>visual_type</varname> field is used by xine to
determine if the GUI used by the client is supported by the plugin
(e.g. X11 output plugins require the GUI to be running under the
X Windowing system) and also to determine the type of information passed to the
<function>open_plugin()</function> function as its <varname>visual</varname> parameter.
</para>
<para>
<programlisting> char *get_description(video_driver_class_t *this_gen);</programlisting>
This function returns a plaintext, one-line string describing the plugin.
</para>
<para>
<programlisting> char *get_identifier(video_driver_class_t *this_gen);</programlisting>
This function returns a shorter identifier describing the plugin.
</para>
<para>
<programlisting> void dispose(video_driver_class_t *this_gen);</programlisting>
This function frees the memory used by the video out plugin class object.
</para>
<para>
<programlisting> vo_driver_t *get_instance(video_driver_class_t *class_gen, const void *visual);</programlisting>
Returns an instance of the plugin.
The <varname>visual</varname> is a pointer to a visual-dependant
structure/variable. For example, if you had previously claimed your
plugin was of the VISUAL_TYPE_X11 type, this would be a pointer
to a <type>x11_visual_t</type>, which amongst other things hold the
<type>Display</type> variable associated with the
X-server xine should display to. See plugin source-code for other
VISUAL_TYPE_* constants and associated structures. Note that this
field is provided by the client application and so if you wish to add another visual
type you will either need to extend an existing client or write a new
one.
</para>
<para>
<programlisting> uint32_t get_capabilities(vo_driver_t *this_gen);</programlisting>
Returns a bit mask describing the output plugin's capabilities.
You may logically OR the <varname>VO_CAP_*</varname> constants together to get
a suitable bit-mask (via the '|' operator).
</para>
<para>
<programlisting>
int get_property(vo_driver_t *self, int property);
int set_property(vo_driver_t *self, int property, int value);
void get_property_min_max(vo_driver_t *self, int property, int *min, int *max);</programlisting>
Handle the getting, setting of properties and define their bounds.
Valid property IDs can be found in the <filename>video_out.h</filename>
header file.
</para>
<para>
<programlisting> int gui_data_exchange(vo_driver_t *self, int data_type, void *data);</programlisting>
Accepts various forms of data from the UI (e.g. the mouse has moved or the
window has been hidden). Look at existing plugins for examples of data
exchanges from various UIs.
</para>
<para>
<programlisting> vo_frame_t *alloc_frame(vo_driver_t *self);</programlisting>
Returns a pointer to a xine video frame.
Typically the video plugin will add private fields to the end of the
<type>vo_frame_t</type> structure which are used for internal purposes by the plugin.
</para>
<para>
The function pointers within the frame structure provide a mechanism for the
driver to retain full control of how the frames are managed and rendered to. If
the VO_CAP_COPIES_IMAGE flag was set in the plugins capabilities then the
copy field is required and will be called sequentially for each 16-pixel high
strip in the image. The plugin may then decide, based on the frame's format, how
this is copied into the frame.
</para>
<para>
<programlisting> void update_frame_format(vo_driver_t *self, vo_frame_t *img, uint32_t width, uint32_t height, double ratio, int format, int flags);</programlisting>
This function will be called each time the colour-depth/space or size of a frame changes.
Typically this function would allocate sufficient memory for the frame, assign the pointers
to the individual planes of the frame to the <varname>base</varname> field of the
frame and perform any driver-specific changes.
</para>
<para>
<programlisting> void display_frame(vo_driver_t *self, vo_frame_t *vo_img);</programlisting>
Renders a given frame to the output device.
</para>
<para>
<programlisting>
void overlay_begin(vo_driver_t *self, vo_frame_t *vo_img, int changed);
void overlay_blend(vo_driver_t *self, vo_frame_t *vo_img, vo_overlay_t *overlay);
void overlay_end(vo_driver_t *self, vo_frame_t *vo_img);</programlisting>
These are used to blend overlays on frames. <function>overlay_begin()</function> is called,
when the overlay appears for the first time, <function>overlay_blend()</function> is then
called for every subsequent frame and <function>overlay_end()</function> is called, when
the overlay should disappear again.
</para>
<para>
<programlisting> int redraw_needed(vo_driver_t *self);</programlisting>
Queries the driver, if the current frame needs to be drawn again.
</para>
<para>
<programlisting> void dispose(vo_driver_t *self);</programlisting>
Releases all resources and frees the plugin.
</para>
</sect2>
</sect1>
</chapter>
|