summaryrefslogtreecommitdiff
path: root/doc/hackersguide/output.sgml
blob: 64adbd2944584ff6204499830d53e13d4f03dcbd (plain)
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>&nbsp;&nbsp;&nbsp;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>&nbsp;&nbsp;&nbsp;char *get_identifier(post_class_t *class_gen);</programlisting>
    This function returns a short identifier describing the plugin.
   </para>
   <para>
    <programlisting>&nbsp;&nbsp;&nbsp;char *get_description(post_class_t *class_gen);</programlisting>
    This function returns a plaintext, one-line string describing the plugin.
   </para>
   <para>
    <programlisting>&nbsp;&nbsp;&nbsp;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>&nbsp;&nbsp;&nbsp;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>
&nbsp;&nbsp;&nbsp;if (_x_post_dispose(this))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;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>
&nbsp;&nbsp;&nbsp;post_video_port_t *_x_post_intercept_video_port(post_plugin_t *post,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;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-&gt;open()</function> and use the macro
     <function>_x_post_dec_usage()</function> after calling
     <function>original_port-&gt;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>&nbsp;&nbsp;&nbsp;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-&gt;new_manager</varname> and stores the original
     manager in <varname>port-&gt;original_manager</varname>.
    </para>
    <para>
     No matter how you intercepted the overlay manager, your own replacement
     functions will receive <varname>port-&gt;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>&nbsp;&nbsp;&nbsp;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>
&nbsp;&nbsp;&nbsp;vo_frame_t *_x_post_intercept_video_frame(vo_frame_t *frame, post_video_port_t *port);
&nbsp;&nbsp;&nbsp;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-&gt;next</varname>. This copy will be filled with your own
     frame functions from <varname>port-&gt;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-&gt;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>&nbsp;&nbsp;&nbsp;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-&gt;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>
&nbsp;&nbsp;&nbsp;_x_post_frame_copy_down(frame, frame-&gt;next);
&nbsp;&nbsp;&nbsp;frame-&gt;next-&gt;draw(frame-&gt;next, stream);
&nbsp;&nbsp;&nbsp;_x_post_frame_copy_up(frame, frame-&gt;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>
&nbsp;&nbsp;&nbsp;modified_frame = port-&gt;original_port-&gt;get_frame(port-&gt;original_port, &hellip;);
&nbsp;&nbsp;&nbsp;_x_post_frame_copy_down(frame, modified_frame);
&nbsp;&nbsp;&nbsp;copy_and_modify(frame, modified_frame);
&nbsp;&nbsp;&nbsp;skip = modified_frame-&gt;draw(modified_frame, stream);
&nbsp;&nbsp;&nbsp;_x_post_frame_copy_up(frame, modified_frame);
&nbsp;&nbsp;&nbsp;modified_frame-&gt;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-&gt;next-&gt;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>&nbsp;&nbsp;&nbsp;char *get_description(video_driver_class_t *this_gen);</programlisting>
    This function returns a plaintext, one-line string describing the plugin.
   </para>
   <para>
    <programlisting>&nbsp;&nbsp;&nbsp;char *get_identifier(video_driver_class_t *this_gen);</programlisting>
    This function returns a shorter identifier describing the plugin.
   </para>
   <para>
    <programlisting>&nbsp;&nbsp;&nbsp;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>&nbsp;&nbsp;&nbsp;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>&nbsp;&nbsp;&nbsp;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>
&nbsp;&nbsp;&nbsp;int get_property(vo_driver_t *self, int property);
&nbsp;&nbsp;&nbsp;int set_property(vo_driver_t *self, int property, int value);
&nbsp;&nbsp;&nbsp;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>&nbsp;&nbsp;&nbsp;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>&nbsp;&nbsp;&nbsp;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>&nbsp;&nbsp;&nbsp;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>&nbsp;&nbsp;&nbsp;void display_frame(vo_driver_t *self, vo_frame_t *vo_img);</programlisting>
    Renders a given frame to the output device.
   </para>
   <para>
    <programlisting>
&nbsp;&nbsp;&nbsp;void overlay_begin(vo_driver_t *self, vo_frame_t *vo_img, int changed);
&nbsp;&nbsp;&nbsp;void overlay_blend(vo_driver_t *self, vo_frame_t *vo_img, vo_overlay_t *overlay);
&nbsp;&nbsp;&nbsp;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>&nbsp;&nbsp;&nbsp;int redraw_needed(vo_driver_t *self);</programlisting>
    Queries the driver, if the current frame needs to be drawn again.
   </para>
   <para>
    <programlisting>&nbsp;&nbsp;&nbsp;void dispose(vo_driver_t *self);</programlisting>
    Releases all resources and frees the plugin.
   </para>
  </sect2>
 </sect1>

</chapter>