1 /*
2  * Copyright (C) 2000-2018 the xine project
3  *
4  * This file is part of xine, a free video player.
5  *
6  * xine is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * xine is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19  *
20  * post plugin definitions
21  */
22 
23 #ifndef XINE_POST_H
24 #define XINE_POST_H
25 
26 #include <pthread.h>
27 
28 #include <xine.h>
29 #include <xine/attributes.h>
30 #include <xine/video_out.h>
31 #include <xine/audio_out.h>
32 #include <xine/xine_internal.h>
33 #include <xine/xineutils.h>
34 
35 struct plugin_node_s;
36 
37 #define POST_PLUGIN_IFACE_VERSION 10
38 
39 
40 typedef struct post_class_s post_class_t;
41 typedef struct post_plugin_s post_plugin_t;
42 typedef struct post_in_s post_in_t;
43 typedef struct post_out_s post_out_t;
44 
45 struct post_class_s {
46 
47   /*
48    * open a new instance of this plugin class
49    */
50   post_plugin_t* (*open_plugin) (post_class_t *this_gen, int inputs,
51 				 xine_audio_port_t **audio_target,
52 				 xine_video_port_t **video_target);
53 
54   /**
55    * @brief short human readable identifier for this plugin class
56    */
57   const char *identifier;
58 
59   /**
60    * @brief human readable (verbose = 1 line) description for this plugin class
61    *
62    * The description is passed to gettext() to internationalise.
63    */
64   const char *description;
65 
66   /**
67    * @brief Optional non-standard catalog to use with dgettext() for description.
68    */
69   const char *text_domain;
70 
71   /*
72    * free all class-related resources
73    */
74 
75   void (*dispose) (post_class_t *this_gen);
76 };
77 
78 #define default_post_class_dispose (void (*) (post_class_t *this_gen))free
79 
80 struct post_plugin_s {
81 
82   /* public part of the plugin */
83   xine_post_t         xine_post;
84 
85   /*
86    * the connections announced by the plugin
87    * the plugin must fill these with xine_post_{in,out}_t on init
88    */
89   xine_list_t        *input;
90   xine_list_t        *output;
91 
92   /*
93    * close down, free all resources
94    */
95   void (*dispose) (post_plugin_t *this_gen);
96 
97   /* plugins don't have to init the stuff below */
98 
99   /*
100    * the running ticket
101    *
102    * the plugin must assure to check for ticket revocation in
103    * intervals of finite length; this means that you must release
104    * the ticket before any operation that might block;
105    * note that all port functions are safe in this respect
106    *
107    * the running ticket is assigned to you by the engine
108    */
109   xine_ticket_t      *running_ticket;
110 
111   /* this is needed by the engine to decrement the reference counter
112    * on disposal of the plugin, but since this is useful, we expose it */
113   xine_t             *xine;
114 
115   /* used when the user requests a list of all inputs/outputs */
116   const char        **input_ids;
117   const char        **output_ids;
118 
119   /**
120    * @brief Pointer to the loaded plugin node.
121    *
122    * Used by the plugins loader. It's an opaque type when using the
123    * structure outside of xine's build.
124    */
125   struct plugin_node_s *node XINE_PRIVATE_FIELD;
126 
127   /* has dispose been called */
128   int                 dispose_pending;
129 };
130 
131 /* helper function to initialize a post_plugin_t */
132 void _x_post_init(post_plugin_t *post, int num_audio_inputs, int num_video_inputs) XINE_PROTECTED;
133 
134 struct post_in_s {
135 
136   /* public part of the input */
137   xine_post_in_t   xine_in;
138 
139   /* backward reference so that you have access to the post plugin */
140   post_plugin_t   *post;
141 
142   /* you can fill this to your liking */
143   void            *user_data;
144 };
145 
146 struct post_out_s {
147 
148   /* public part of the output */
149   xine_post_out_t  xine_out;
150 
151   /* backward reference so that you have access to the post plugin */
152   post_plugin_t   *post;
153 
154   /* you can fill this to your liking */
155   void            *user_data;
156 };
157 
158 
159 /* Post plugins work by intercepting calls to video or audio ports
160  * in the sense of the decorator design pattern. They reuse the
161  * functions of a given target port, but add own functionality in
162  * front of that port by creating a new port structure and filling in
163  * the function pointers with pointers to own functions that
164  * would do something and then call the original port function.
165  *
166  * Much the same is done with video frames which have their own
167  * set of functions attached that you might need to decorate.
168  */
169 
170 
171 /* helper structure for intercepting video port calls */
172 typedef struct post_video_port_s post_video_port_t;
173 struct post_video_port_s {
174 
175   /* the new public port with replaced function pointers */
176   xine_video_port_t         new_port;
177 
178   /* the original port to call its functions from inside yours */
179   xine_video_port_t        *original_port;
180 
181   /* if you want to decide yourself, whether a given frame should
182    * be intercepted, fill in this function; get_frame() acts as
183    * a template method and asks your function; return a boolean;
184    * the default is to intercept all frames */
185   int (*intercept_frame)(post_video_port_t *self, vo_frame_t *frame);
186 
187   /* the new frame function pointers */
188   vo_frame_t               *new_frame;
189 
190   /* if you want to decide yourself, whether the preprocessing functions
191    * should still be routed when draw is intercepted, fill in this
192    * function; _x_post_intercept_video_frame() acts as a template method
193    * and asks your function; return a boolean; the default is _not_ to
194    * route preprocessing functions when draw is intercepted */
195   int (*route_preprocessing_procs)(post_video_port_t *self, vo_frame_t *frame);
196 
197   /* if you want to decide yourself, whether the overlay manager should
198    * be intercepted, fill in this function; get_overlay_manager() acts as
199    * a template method and asks your function; return a boolean;
200    * the default is _not_ to intercept the overlay manager */
201   int (*intercept_ovl)(post_video_port_t *self);
202 
203   /* the new public overlay manager with replaced function pointers */
204   video_overlay_manager_t  *new_manager;
205 
206   /* the original manager to call its functions from inside yours */
207   video_overlay_manager_t  *original_manager;
208 
209   /* usage counter: how many objects are floating around that need
210    * these pointers to exist */
211   int                       usage_count;
212   pthread_mutex_t           usage_lock;
213 
214   /* the stream we are being fed by; NULL means no stream is connected;
215    * this may be an anonymous stream */
216   xine_stream_t            *stream;
217 
218   /* point to a mutex here, if you need some synchronization */
219   pthread_mutex_t          *port_lock;
220   pthread_mutex_t          *frame_lock;
221   pthread_mutex_t          *manager_lock;
222 
223   /* backward reference so that you have access to the post plugin
224    * when the call only gives you the port */
225   post_plugin_t            *post;
226 
227   /* you can fill this to your liking */
228   void                     *user_data;
229 
230 #ifdef POST_INTERNAL
231   /* some of the above members are to be directly included here, but
232    * adding the structures would mean that post_video_port_t becomes
233    * depended of the sizes of these structs; solution: we add pointers
234    * above and have them point into the memory provided here;
235    * note that the overlay manager needs to be first so that we can
236    * reconstruct the post_video_port_t* from overlay manager calls */
237 
238   /* any change here requires a change in _x_post_ovl_manager_to_port()
239    * below! */
240 
241   video_overlay_manager_t   manager_storage;
242   vo_frame_t                frame_storage;
243 
244   /* this is used to keep a linked list of free vo_frame_t's */
245   vo_frame_t               *free_frame_slots;
246   pthread_mutex_t           free_frames_lock;
247 #endif
248 };
249 
250 /* use this to create a new decorated video port in which
251  * port functions will be replaced with own implementations;
252  * for convenience, this can also create a related post_in_t and post_out_t */
253 post_video_port_t *_x_post_intercept_video_port(post_plugin_t *post, xine_video_port_t *port,
254 						post_in_t **input, post_out_t **output) XINE_PROTECTED;
255 
256 /* use this to decorate and to undecorate a frame so that its functions
257  * can be replaced with own implementations, decoration is usually done in
258  * get_frame(), undecoration in frame->free() */
259 vo_frame_t *_x_post_intercept_video_frame(vo_frame_t *frame, post_video_port_t *port) XINE_PROTECTED;
260 vo_frame_t *_x_post_restore_video_frame(vo_frame_t *frame, post_video_port_t *port) XINE_PROTECTED;
261 
262 /* when you want to pass a frame call on to the original issuer of the frame,
263  * you need to propagate potential changes up and down the pipe, so the usual
264  * procedure for this situation would be:
265  *
266  *   _x_post_frame_copy_down(frame, frame->next);
267  *   frame->next->function(frame->next);
268  *   _x_post_frame_copy_up(frame, frame->next);
269  */
270 void _x_post_frame_copy_down(vo_frame_t *from, vo_frame_t *to) XINE_PROTECTED;
271 void _x_post_frame_copy_up(vo_frame_t *to, vo_frame_t *from) XINE_PROTECTED;
272 
273 /* when you shortcut a frames usual draw() travel so that it will never reach
274  * the draw() function of the original issuer, you still have to do some
275  * housekeeping on the frame, before returning control up the pipe */
276 void _x_post_frame_u_turn(vo_frame_t *frame, xine_stream_t *stream) XINE_PROTECTED;
277 
278 /* use this to create a new, trivially decorated overlay manager in which
279  * port functions can be replaced with own implementations */
280 void _x_post_intercept_overlay_manager(video_overlay_manager_t *manager, post_video_port_t *port) XINE_PROTECTED;
281 
282 /* pointer retrieval functions */
_x_post_video_frame_to_port(vo_frame_t * frame)283 static inline post_video_port_t *_x_post_video_frame_to_port(vo_frame_t *frame) {
284   return (post_video_port_t *)frame->port;
285 }
286 
_x_post_ovl_manager_to_port(video_overlay_manager_t * manager)287 static inline post_video_port_t *_x_post_ovl_manager_to_port(video_overlay_manager_t *manager) {
288 #ifdef POST_INTERNAL
289   return (post_video_port_t *)( (uint8_t *)manager -
290     (uint8_t*)&(((post_video_port_t *)NULL)->manager_storage) );
291 #else
292   return (post_video_port_t *)( (uint8_t *)manager - sizeof(post_video_port_t) );
293 #endif
294 }
295 
296 
297 /* helper structure for intercepting audio port calls */
298 typedef struct post_audio_port_s post_audio_port_t;
299 struct post_audio_port_s {
300 
301   /* the new public port with replaced function pointers */
302   xine_audio_port_t  new_port;
303 
304   /* the original port to call its functions from inside yours */
305   xine_audio_port_t *original_port;
306 
307   /* the stream we are being fed by; NULL means no stream is connected;
308    * this may be an anonymous stream */
309   xine_stream_t     *stream;
310 
311   pthread_mutex_t    usage_lock;
312   /* usage counter: how many objects are floating around that need
313    * these pointers to exist */
314   int                usage_count;
315 
316   /* some values remembered by (port->open) () */
317   uint32_t           bits;
318   uint32_t           rate;
319   uint32_t           mode;
320 
321   /* point to a mutex here, if you need some synchronization */
322   pthread_mutex_t   *port_lock;
323 
324   /* backward reference so that you have access to the post plugin
325    * when the call only gives you the port */
326   post_plugin_t     *post;
327 
328   /* you can fill this to your liking */
329   void              *user_data;
330 };
331 
332 /* use this to create a new decorated audio port in which
333  * port functions will be replaced with own implementations */
334 post_audio_port_t *_x_post_intercept_audio_port(post_plugin_t *post, xine_audio_port_t *port,
335 						post_in_t **input, post_out_t **output) XINE_PROTECTED;
336 
337 
338 /* If you do intercept these decoder-called functions
339  * (that is, you do not use post defaults), please
340  * open ():
341  *   _x_post_inc_usage (port);
342  *   _x_post_rewire (port->post);
343  *   ...
344  * close ():
345  *   ...
346  *   _x_post_dec_usage (port);
347  * get_buffer ():
348  *   _x_post_inc_usage (port);
349  *   ...
350  *   port->original_port->get_buffer (port->original_port); and/or
351  *   while (not_done_yet) {
352  *     _x_post_rewire (port->post);
353  *     timed_wait (done);
354  *   }
355  *   ...
356  *   _x_post_dec_usage (port);
357  * get_frame ():
358  *   _x_post_inc_usage (port);
359  *   ...
360  *   port->original_port->get_frame (port->original_port, ...); and/or
361  *   while (not_done_yet) {
362  *     _x_post_rewire (port->post);
363  *     timed_wait (done);
364  *   }
365  *   ...
366  *   if (this_frame_is_not_intercepted)
367  *     _x_post_dec_usage (port);
368  * frame.free ():
369  *   ...
370  *   _x_post_dec_usage (port);
371  * this will allow pending rewire operations, while preventing your port from getting
372  * pulled from under your feet by the possible rewire. */
_x_post_rewire(post_plugin_t * post)373 static inline void _x_post_rewire(post_plugin_t *post) {
374   if (post->running_ticket->ticket_revoked)
375     post->running_ticket->renew(post->running_ticket, 1);
376 }
377 
378 /* with these functions you can switch interruptions like rewiring or engine pausing
379  * off for a block of code; use this only when really necessary */
_x_post_lock(post_plugin_t * post)380 static inline void _x_post_lock(post_plugin_t *post) {
381   post->running_ticket->acquire(post->running_ticket, 1);
382 }
_x_post_unlock(post_plugin_t * post)383 static inline void _x_post_unlock(post_plugin_t *post) {
384   post->running_ticket->release(post->running_ticket, 1);
385   _x_post_rewire(post);
386 }
387 
388 /* the standard disposal operation; returns 1 if the plugin is really
389  * disposed and you should free everything you malloc()ed yourself */
390 int _x_post_dispose(post_plugin_t *post) XINE_PROTECTED;
391 
392 
393 /* macros to handle usage counter */
394 
395 /* WARNING!
396  * note that _x_post_dec_usage() can call dispose, so be sure to
397  * not use any potentially already freed memory after this */
398 
399 #define _x_post_inc_usage(port)                                    \
400 do {                                                               \
401   pthread_mutex_lock(&(port)->usage_lock);                         \
402   (port)->usage_count++;                                           \
403   pthread_mutex_unlock(&(port)->usage_lock);                       \
404 } while(0)
405 
406 #define _x_post_dec_usage(port)                                    \
407 do {                                                               \
408   pthread_mutex_lock(&(port)->usage_lock);                         \
409   (port)->usage_count--;                                           \
410   if ((port)->usage_count == 0) {                                  \
411     if ((port)->post->dispose_pending) {                           \
412       pthread_mutex_unlock(&(port)->usage_lock);                   \
413       (port)->post->dispose((port)->post);                         \
414     } else                                                         \
415       pthread_mutex_unlock(&(port)->usage_lock);                   \
416   } else                                                           \
417     pthread_mutex_unlock(&(port)->usage_lock);                     \
418 } while(0)
419 
420 #ifdef POST_INTERNAL
421 /* try to recognize post ports, do the above, return new use count.
422  * otherwise, return -1. */
423 int _x_post_video_port_ref (xine_video_port_t *port_gen);
424 int _x_post_video_port_unref (xine_video_port_t *port_gen);
425 int _x_post_audio_port_ref (xine_audio_port_t *port_gen);
426 int _x_post_audio_port_unref (xine_audio_port_t *port_gen);
427 #endif
428 
429 /* macros to create parameter descriptors */
430 
431 #define START_PARAM_DESCR( param_t ) \
432 typedef param_t temp_t; \
433 static xine_post_api_parameter_t temp_p[] = {
434 
435 #ifndef offsetof
436 #include <stddef.h>
437 #endif
438 
439 #define PARAM_ITEM( param_type, var, enumv, min, max, readonly, descr ) \
440 { param_type, #var, sizeof(((temp_t*)0)->var),                        \
441   offsetof(temp_t, var), enumv, min, max, readonly, descr },
442 
443 #define END_PARAM_DESCR( name ) \
444   { POST_PARAM_TYPE_LAST, NULL, 0, 0, NULL, 0, 0, 1, NULL } \
445 }; \
446 static xine_post_api_descr_t name = { \
447   sizeof( temp_t ), \
448   temp_p \
449 };
450 
451 #endif
452