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