]> git.sesse.net Git - vlc/blob - lib/event.c
wasapi: add loopback mode
[vlc] / lib / event.c
1 /*****************************************************************************
2  * event.c: New libvlc event control API
3  *****************************************************************************
4  * Copyright (C) 2007-2010 VLC authors and VideoLAN
5  * $Id $
6  *
7  * Authors: Filippo Carone <filippo@carone.org>
8  *          Pierre d'Herbemont <pdherbemont # videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation; either version 2.1 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #define  LIBVLC_EVENT_TYPES_KEEP_DEFINE
30 #include <vlc/libvlc.h>
31
32 #include "libvlc_internal.h"
33 #include "event_internal.h"
34 #include <assert.h>
35 #include <errno.h>
36
37 typedef struct libvlc_event_listeners_group_t
38 {
39     libvlc_event_type_t event_type;
40     vlc_array_t listeners;
41     bool b_sublistener_removed;
42 } libvlc_event_listeners_group_t;
43
44 /*
45  * Private functions
46  */
47
48 static bool
49 group_contains_listener( libvlc_event_listeners_group_t * group,
50                          libvlc_event_listener_t * searched_listener )
51 {
52     int i;
53     for( i = 0; i < vlc_array_count(&group->listeners); i++ )
54     {
55         if( listeners_are_equal(searched_listener, vlc_array_item_at_index(&group->listeners, i)) )
56             return true;
57     }
58     return false;
59 }
60
61 /*
62  * Internal libvlc functions
63  */
64
65 /**************************************************************************
66  *       libvlc_event_manager_new (internal) :
67  *
68  * Init an object's event manager.
69  **************************************************************************/
70 libvlc_event_manager_t *
71 libvlc_event_manager_new( void * p_obj, libvlc_instance_t * p_libvlc_inst )
72 {
73     libvlc_event_manager_t * p_em;
74
75     p_em = malloc(sizeof( libvlc_event_manager_t ));
76     if( !p_em )
77     {
78         libvlc_printerr( "Not enough memory" );
79         return NULL;
80     }
81
82     p_em->p_obj = p_obj;
83     p_em->p_obj = p_obj;
84     p_em->async_event_queue = NULL;
85     p_em->p_libvlc_instance = p_libvlc_inst;
86
87     libvlc_retain( p_libvlc_inst );
88     vlc_array_init( &p_em->listeners_groups );
89     vlc_mutex_init( &p_em->object_lock );
90     vlc_mutex_init_recursive( &p_em->event_sending_lock );
91     return p_em;
92 }
93
94 /**************************************************************************
95  *       libvlc_event_manager_release (internal) :
96  *
97  * Release an object's event manager.
98  **************************************************************************/
99 void libvlc_event_manager_release( libvlc_event_manager_t * p_em )
100 {
101     libvlc_event_listeners_group_t * p_lg;
102     int i,j ;
103
104     libvlc_event_async_fini(p_em);
105
106     vlc_mutex_destroy( &p_em->event_sending_lock );
107     vlc_mutex_destroy( &p_em->object_lock );
108
109     for( i = 0; i < vlc_array_count(&p_em->listeners_groups); i++)
110     {
111         p_lg = vlc_array_item_at_index( &p_em->listeners_groups, i );
112
113         for( j = 0; j < vlc_array_count(&p_lg->listeners); j++)
114             free( vlc_array_item_at_index( &p_lg->listeners, j ) );
115
116         vlc_array_clear( &p_lg->listeners );
117         free( p_lg );
118     }
119     vlc_array_clear( &p_em->listeners_groups );
120     libvlc_release( p_em->p_libvlc_instance );
121     free( p_em );
122 }
123
124 /**************************************************************************
125  *       libvlc_event_manager_register_event_type (internal) :
126  *
127  * Init an object's event manager.
128  **************************************************************************/
129 void libvlc_event_manager_register_event_type(
130         libvlc_event_manager_t * p_em,
131         libvlc_event_type_t event_type )
132 {
133     libvlc_event_listeners_group_t * listeners_group;
134     listeners_group = xmalloc(sizeof(libvlc_event_listeners_group_t));
135     listeners_group->event_type = event_type;
136     vlc_array_init( &listeners_group->listeners );
137
138     vlc_mutex_lock( &p_em->object_lock );
139     vlc_array_append( &p_em->listeners_groups, listeners_group );
140     vlc_mutex_unlock( &p_em->object_lock );
141 }
142
143 /**************************************************************************
144  *       libvlc_event_send (internal) :
145  *
146  * Send a callback.
147  **************************************************************************/
148 void libvlc_event_send( libvlc_event_manager_t * p_em,
149                         libvlc_event_t * p_event )
150 {
151     libvlc_event_listeners_group_t * listeners_group = NULL;
152     libvlc_event_listener_t * listener_cached;
153     libvlc_event_listener_t * listener;
154     libvlc_event_listener_t * array_listeners_cached = NULL;
155     int i, i_cached_listeners = 0;
156
157     /* Fill event with the sending object now */
158     p_event->p_obj = p_em->p_obj;
159
160     vlc_mutex_lock( &p_em->event_sending_lock );
161     vlc_mutex_lock( &p_em->object_lock );
162     for( i = 0; i < vlc_array_count(&p_em->listeners_groups); i++)
163     {
164         listeners_group = vlc_array_item_at_index(&p_em->listeners_groups, i);
165         if( listeners_group->event_type == p_event->type )
166         {
167             if( vlc_array_count( &listeners_group->listeners ) <= 0 )
168                 break;
169
170             /* Cache a copy of the listener to avoid locking issues,
171              * and allow that edition of listeners during callbacks will garantee immediate effect. */
172             i_cached_listeners = vlc_array_count(&listeners_group->listeners);
173             array_listeners_cached = malloc(sizeof(libvlc_event_listener_t)*(i_cached_listeners));
174             if( !array_listeners_cached )
175             {
176                 vlc_mutex_unlock( &p_em->object_lock );
177                 vlc_mutex_unlock( &p_em->event_sending_lock );
178                 fprintf(stderr, "Can't alloc memory in libvlc_event_send" );
179                 return;
180             }
181
182             listener_cached = array_listeners_cached;
183             for( i = 0; i < vlc_array_count(&listeners_group->listeners); i++)
184             {
185                 listener = vlc_array_item_at_index(&listeners_group->listeners, i);
186                 memcpy( listener_cached, listener, sizeof(libvlc_event_listener_t) );
187                 listener_cached++;
188             }
189             break;
190         }
191     }
192
193     if( !listeners_group )
194     {
195         free( array_listeners_cached );
196         vlc_mutex_unlock( &p_em->object_lock );
197         vlc_mutex_unlock( &p_em->event_sending_lock );
198         return;
199     }
200
201     /* Track item removed from *this* thread, with a simple flag. Indeed
202      * event_sending_lock is a recursive lock. This has the advantage of
203      * allowing to remove an event listener from within a callback */
204     listeners_group->b_sublistener_removed = false;
205
206     vlc_mutex_unlock( &p_em->object_lock );
207
208     listener_cached = array_listeners_cached;
209     for( i = 0; i < i_cached_listeners; i++ )
210     {
211         if(listener_cached->is_asynchronous)
212         {
213             /* The listener wants not to block the emitter during event callback */
214             libvlc_event_async_dispatch(p_em, listener_cached, p_event);
215         }
216         else
217         {
218             /* The listener wants to block the emitter during event callback */
219
220             listener_cached->pf_callback( p_event, listener_cached->p_user_data );
221             listener_cached++;
222
223             if( listeners_group->b_sublistener_removed )
224             {
225                 /* If a callback was removed, this gets called */
226                 bool valid_listener;
227                 vlc_mutex_lock( &p_em->object_lock );
228                 valid_listener = group_contains_listener( listeners_group, listener_cached );
229                 vlc_mutex_unlock( &p_em->object_lock );
230                 if( !valid_listener )
231                 {
232                     listener_cached++;
233                     continue;
234                 }
235             }
236         }
237     }
238     vlc_mutex_unlock( &p_em->event_sending_lock );
239
240     free( array_listeners_cached );
241 }
242
243 /*
244  * Public libvlc functions
245  */
246
247 #define DEF( a ) { libvlc_##a, #a, },
248
249 typedef struct
250 {
251     int type;
252     const char name[40];
253 } event_name_t;
254
255 static const event_name_t event_list[] = {
256     DEF(MediaMetaChanged)
257     DEF(MediaSubItemAdded)
258     DEF(MediaDurationChanged)
259     DEF(MediaParsedChanged)
260     DEF(MediaFreed)
261     DEF(MediaStateChanged)
262     DEF(MediaSubItemTreeAdded)
263
264     DEF(MediaPlayerMediaChanged)
265     DEF(MediaPlayerNothingSpecial)
266     DEF(MediaPlayerOpening)
267     DEF(MediaPlayerBuffering)
268     DEF(MediaPlayerPlaying)
269     DEF(MediaPlayerPaused)
270     DEF(MediaPlayerStopped)
271     DEF(MediaPlayerForward)
272     DEF(MediaPlayerBackward)
273     DEF(MediaPlayerEndReached)
274     DEF(MediaPlayerEncounteredError)
275     DEF(MediaPlayerTimeChanged)
276     DEF(MediaPlayerPositionChanged)
277     DEF(MediaPlayerSeekableChanged)
278     DEF(MediaPlayerPausableChanged)
279     DEF(MediaPlayerTitleChanged)
280     DEF(MediaPlayerSnapshotTaken)
281     DEF(MediaPlayerLengthChanged)
282     DEF(MediaPlayerVout)
283     DEF(MediaPlayerScrambledChanged)
284     DEF(MediaPlayerESAdded)
285     DEF(MediaPlayerESDeleted)
286     DEF(MediaPlayerESSelected)
287
288     DEF(MediaListItemAdded)
289     DEF(MediaListWillAddItem)
290     DEF(MediaListItemDeleted)
291     DEF(MediaListWillDeleteItem)
292
293     DEF(MediaListViewItemAdded)
294     DEF(MediaListViewWillAddItem)
295     DEF(MediaListViewItemDeleted)
296     DEF(MediaListViewWillDeleteItem)
297
298     DEF(MediaListPlayerPlayed)
299     DEF(MediaListPlayerNextItemSet)
300     DEF(MediaListPlayerStopped)
301
302     DEF(MediaDiscovererStarted)
303     DEF(MediaDiscovererEnded)
304
305     DEF(VlmMediaAdded)
306     DEF(VlmMediaRemoved)
307     DEF(VlmMediaChanged)
308     DEF(VlmMediaInstanceStarted)
309     DEF(VlmMediaInstanceStopped)
310     DEF(VlmMediaInstanceStatusInit)
311     DEF(VlmMediaInstanceStatusOpening)
312     DEF(VlmMediaInstanceStatusPlaying)
313     DEF(VlmMediaInstanceStatusPause)
314     DEF(VlmMediaInstanceStatusEnd)
315     DEF(VlmMediaInstanceStatusError)
316 };
317 #undef DEF
318
319 static const char unknown_event_name[] = "Unknown Event";
320
321 static int evcmp( const void *a, const void *b )
322 {
323     return (*(const int *)a) - ((event_name_t *)b)->type;
324 }
325
326 const char * libvlc_event_type_name( int event_type )
327 {
328     const event_name_t *p;
329
330     p = bsearch( &event_type, event_list,
331                  sizeof(event_list)/sizeof(event_list[0]), sizeof(*p),
332                  evcmp );
333     return p ? p->name : unknown_event_name;
334 }
335
336 /**************************************************************************
337  *       event_attach (internal) :
338  *
339  * Add a callback for an event.
340  **************************************************************************/
341 static
342 int event_attach( libvlc_event_manager_t * p_event_manager,
343                   libvlc_event_type_t event_type,
344                   libvlc_callback_t pf_callback, void *p_user_data,
345                   bool is_asynchronous )
346 {
347     libvlc_event_listeners_group_t * listeners_group;
348     libvlc_event_listener_t * listener;
349     int i;
350
351     listener = malloc(sizeof(libvlc_event_listener_t));
352     if( unlikely(listener == NULL) )
353         return ENOMEM;
354
355     listener->event_type = event_type;
356     listener->p_user_data = p_user_data;
357     listener->pf_callback = pf_callback;
358     listener->is_asynchronous = is_asynchronous;
359
360     vlc_mutex_lock( &p_event_manager->object_lock );
361     for( i = 0; i < vlc_array_count(&p_event_manager->listeners_groups); i++ )
362     {
363         listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
364         if( listeners_group->event_type == listener->event_type )
365         {
366             vlc_array_append( &listeners_group->listeners, listener );
367             vlc_mutex_unlock( &p_event_manager->object_lock );
368             return 0;
369         }
370     }
371     vlc_mutex_unlock( &p_event_manager->object_lock );
372
373     free(listener);
374     fprintf( stderr, "This object event manager doesn't know about '%s' events",
375              libvlc_event_type_name(event_type) );
376     vlc_assert_unreachable();
377     return -1;
378 }
379
380 /**************************************************************************
381  *       libvlc_event_attach (public) :
382  *
383  * Add a callback for an event.
384  **************************************************************************/
385 int libvlc_event_attach( libvlc_event_manager_t * p_event_manager,
386                          libvlc_event_type_t event_type,
387                          libvlc_callback_t pf_callback,
388                          void *p_user_data )
389 {
390     return event_attach(p_event_manager, event_type, pf_callback, p_user_data,
391                         false /* synchronous */);
392 }
393
394 /**************************************************************************
395  *       libvlc_event_attach (public) :
396  *
397  * Add a callback for an event.
398  **************************************************************************/
399 void libvlc_event_attach_async( libvlc_event_manager_t * p_event_manager,
400                          libvlc_event_type_t event_type,
401                          libvlc_callback_t pf_callback,
402                          void *p_user_data )
403 {
404     event_attach(p_event_manager, event_type, pf_callback, p_user_data,
405                  true /* asynchronous */);
406 }
407
408 /**************************************************************************
409  *       libvlc_event_detach (public) :
410  *
411  * Remove a callback for an event.
412  **************************************************************************/
413 void libvlc_event_detach( libvlc_event_manager_t *p_event_manager,
414                                      libvlc_event_type_t event_type,
415                                      libvlc_callback_t pf_callback,
416                                      void *p_user_data )
417 {
418     libvlc_event_listeners_group_t * listeners_group;
419     libvlc_event_listener_t * listener;
420     int i, j;
421     bool found = false;
422
423     vlc_mutex_lock( &p_event_manager->event_sending_lock );
424     vlc_mutex_lock( &p_event_manager->object_lock );
425     for( i = 0; i < vlc_array_count(&p_event_manager->listeners_groups); i++)
426     {
427         listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
428         if( listeners_group->event_type == event_type )
429         {
430             for( j = 0; j < vlc_array_count(&listeners_group->listeners); j++)
431             {
432                 listener = vlc_array_item_at_index(&listeners_group->listeners, j);
433                 if( listener->event_type == event_type &&
434                     listener->pf_callback == pf_callback &&
435                     listener->p_user_data == p_user_data )
436                 {
437                     /* that's our listener */
438
439                     /* Mark this group as edited so that libvlc_event_send
440                      * will recheck what listener to call */
441                     listeners_group->b_sublistener_removed = true;
442
443                     free( listener );
444                     vlc_array_remove( &listeners_group->listeners, j );
445                     found = true;
446                     break;
447                 }
448             }
449         }
450     }
451     vlc_mutex_unlock( &p_event_manager->object_lock );
452     vlc_mutex_unlock( &p_event_manager->event_sending_lock );
453
454     /* Now make sure any pending async event won't get fired after that point */
455     libvlc_event_listener_t listener_to_remove;
456     listener_to_remove.event_type  = event_type;
457     listener_to_remove.pf_callback = pf_callback;
458     listener_to_remove.p_user_data = p_user_data;
459     listener_to_remove.is_asynchronous = true;
460
461     libvlc_event_async_ensure_listener_removal(p_event_manager, &listener_to_remove);
462
463     assert(found);
464 }