]> git.sesse.net Git - vlc/blob - src/misc/objects.c
Merge branch 'master' of git@git.videolan.org:vlc
[vlc] / src / misc / objects.c
1 /*****************************************************************************
2  * objects.c: vlc_object_t handling
3  *****************************************************************************
4  * Copyright (C) 2004-2008 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /**
25  * \file
26  * This file contains the functions to handle the vlc_object_t type
27  */
28
29
30 /*****************************************************************************
31  * Preamble
32  *****************************************************************************/
33 #ifdef HAVE_CONFIG_H
34 # include "config.h"
35 #endif
36
37 #include <vlc/vlc.h>
38
39 #include "../libvlc.h"
40 #include <vlc_vout.h>
41 #include <vlc_aout.h>
42 #include "audio_output/aout_internal.h"
43
44 #include <vlc_access.h>
45 #include <vlc_demux.h>
46 #include <vlc_stream.h>
47
48 #include <vlc_sout.h>
49 #include "stream_output/stream_output.h"
50
51 #include "vlc_interface.h"
52 #include "vlc_codec.h"
53 #include "vlc_filter.h"
54
55 #include "variables.h"
56 #ifndef WIN32
57 # include <unistd.h>
58 #else
59 # include <io.h>
60 # include <fcntl.h>
61 # include <errno.h> /* ENOSYS */
62 #endif
63 #include <assert.h>
64
65 /*****************************************************************************
66  * Constants
67  *****************************************************************************/
68
69 const vlc_destructor_t kVLCDestructor = NULL;
70
71 /*****************************************************************************
72  * Local prototypes
73  *****************************************************************************/
74 static int  DumpCommand( vlc_object_t *, char const *,
75                          vlc_value_t, vlc_value_t, void * );
76
77 static vlc_object_t * FindObject    ( vlc_object_t *, int, int );
78 static vlc_object_t * FindObjectName( vlc_object_t *, const char *, int );
79 static void           DetachObject  ( vlc_object_t * );
80 static void           PrintObject   ( vlc_object_t *, const char * );
81 static void           DumpStructure ( vlc_object_t *, int, char * );
82 static int            FindIndex     ( vlc_object_t *, vlc_object_t **, int );
83 static void           SetAttachment ( vlc_object_t *, bool );
84
85 static vlc_list_t   * NewList       ( int );
86 static void           ListReplace   ( vlc_list_t *, vlc_object_t *, int );
87 /*static void           ListAppend    ( vlc_list_t *, vlc_object_t * );*/
88 static int            CountChildren ( vlc_object_t *, int );
89 static void           ListChildren  ( vlc_list_t *, vlc_object_t *, int );
90
91 static void vlc_object_destroy( vlc_object_t *p_this );
92 static void vlc_object_yield_locked( vlc_object_t *p_this );
93
94 /*****************************************************************************
95  * Local structure lock
96  *****************************************************************************/
97 static vlc_mutex_t    structure_lock;
98
99 void *vlc_custom_create( vlc_object_t *p_this, size_t i_size,
100                          int i_type, const char *psz_type )
101 {
102     vlc_object_t *p_new;
103     vlc_object_internals_t *p_priv;
104
105     /* NOTE:
106      * VLC objects are laid out as follow:
107      * - first the LibVLC-private per-object data,
108      * - then VLC_COMMON members from vlc_object_t,
109      * - finally, the type-specific data (if any).
110      *
111      * This function initializes the LibVLC and common data,
112      * and zeroes the rest.
113      */
114     p_priv = calloc( 1, sizeof( *p_priv ) + i_size );
115     if( p_priv == NULL )
116         return NULL;
117
118     if( i_type == VLC_OBJECT_GLOBAL )
119         p_new = p_this;
120     else
121     {
122         assert (i_size >= sizeof (vlc_object_t));
123         p_new = (vlc_object_t *)(p_priv + 1);
124     }
125
126     p_new->p_internals = p_priv;
127     p_new->i_object_type = i_type;
128     p_new->psz_object_type = psz_type;
129
130     p_new->psz_object_name = NULL;
131
132     p_new->b_die = false;
133     p_new->b_error = false;
134     p_new->b_dead = false;
135     p_priv->b_attached = false;
136     p_new->b_force = false;
137
138     p_new->psz_header = NULL;
139
140     p_new->i_flags |= p_this->i_flags
141         & (OBJECT_FLAGS_NODBG|OBJECT_FLAGS_QUIET|OBJECT_FLAGS_NOINTERACT);
142
143     p_priv->p_vars = calloc( sizeof( variable_t ), 16 );
144
145     if( !p_priv->p_vars )
146     {
147         free( p_priv );
148         return NULL;
149     }
150
151     if( i_type == VLC_OBJECT_GLOBAL )
152     {
153         /* If i_type is global, then p_new is actually p_libvlc_global */
154         libvlc_global_data_t *p_libvlc_global = (libvlc_global_data_t *)p_new;
155         p_new->p_libvlc = NULL;
156
157         p_libvlc_global->i_counter = 0;
158         p_new->i_object_id = 0;
159
160         p_libvlc_global->i_objects = 1;
161         p_libvlc_global->pp_objects = malloc( sizeof(vlc_object_t *) );
162         p_libvlc_global->pp_objects[0] = p_new;
163         p_priv->b_attached = true;
164         vlc_mutex_init( p_new, &structure_lock );
165     }
166     else
167     {
168         libvlc_global_data_t *p_libvlc_global = vlc_global();
169         if( i_type == VLC_OBJECT_LIBVLC )
170         {
171             p_new->p_libvlc = (libvlc_int_t*)p_new;
172             p_priv->b_attached = true;
173         }
174         else
175         {
176             p_new->p_libvlc = p_this->p_libvlc;
177         }
178
179         vlc_mutex_lock( &structure_lock );
180
181         p_libvlc_global->i_counter++;
182         p_new->i_object_id = p_libvlc_global->i_counter;
183
184         /* Wooohaa! If *this* fails, we're in serious trouble! Anyway it's
185          * useless to try and recover anything if pp_objects gets smashed. */
186         TAB_APPEND( p_libvlc_global->i_objects, p_libvlc_global->pp_objects,
187                     p_new );
188
189         vlc_mutex_unlock( &structure_lock );
190     }
191
192     p_priv->i_refcount = 1;
193     p_priv->pf_destructor = kVLCDestructor;
194     p_priv->b_thread = false;
195     p_new->p_parent = NULL;
196     p_new->pp_children = NULL;
197     p_new->i_children = 0;
198
199     p_new->p_private = NULL;
200
201     /* Initialize mutexes and condvars */
202     vlc_mutex_init( p_new, &p_new->object_lock );
203     vlc_cond_init( p_new, &p_new->object_wait );
204     vlc_mutex_init( p_new, &p_priv->var_lock );
205     vlc_spin_init( &p_priv->spin );
206     p_priv->pipes[0] = p_priv->pipes[1] = -1;
207
208     if( i_type == VLC_OBJECT_LIBVLC )
209     {
210         var_Create( p_new, "list", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
211         var_AddCallback( p_new, "list", DumpCommand, NULL );
212         var_Create( p_new, "tree", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
213         var_AddCallback( p_new, "tree", DumpCommand, NULL );
214         var_Create( p_new, "vars", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
215         var_AddCallback( p_new, "vars", DumpCommand, NULL );
216     }
217
218     return p_new;
219 }
220
221
222 /**
223  * Allocates and initializes a vlc object.
224  *
225  * @param i_type known object type (all of them are negative integer values),
226  *               or object byte size (always positive).
227  *
228  * @return the new object, or NULL on error.
229  */
230 void * __vlc_object_create( vlc_object_t *p_this, int i_type )
231 {
232     const char   * psz_type;
233     size_t         i_size;
234
235     switch( i_type )
236     {
237         case VLC_OBJECT_LIBVLC:
238             i_size = sizeof(libvlc_int_t);
239             psz_type = "libvlc";
240             break;
241         case VLC_OBJECT_INTF:
242             i_size = sizeof(intf_thread_t);
243             psz_type = "interface";
244             break;
245         case VLC_OBJECT_DIALOGS:
246             i_size = sizeof(intf_thread_t);
247             psz_type = "dialogs";
248             break;
249         case VLC_OBJECT_DEMUX:
250             i_size = sizeof(demux_t);
251             psz_type = "demux";
252             break;
253         case VLC_OBJECT_ACCESS:
254             i_size = sizeof(access_t);
255             psz_type = "access";
256             break;
257         case VLC_OBJECT_DECODER:
258             i_size = sizeof(decoder_t);
259             psz_type = "decoder";
260             break;
261         case VLC_OBJECT_PACKETIZER:
262             i_size = sizeof(decoder_t);
263             psz_type = "packetizer";
264             break;
265         case VLC_OBJECT_ENCODER:
266             i_size = sizeof(encoder_t);
267             psz_type = "encoder";
268             break;
269         case VLC_OBJECT_FILTER:
270             i_size = sizeof(filter_t);
271             psz_type = "filter";
272             break;
273         case VLC_OBJECT_VOUT:
274             i_size = sizeof(vout_thread_t);
275             psz_type = "video output";
276             break;
277         case VLC_OBJECT_AOUT:
278             i_size = sizeof(aout_instance_t);
279             psz_type = "audio output";
280             break;
281         case VLC_OBJECT_SOUT:
282             i_size = sizeof(sout_instance_t);
283             psz_type = "stream output";
284             break;
285         case VLC_OBJECT_OPENGL:
286             i_size = sizeof( vout_thread_t );
287             psz_type = "opengl";
288             break;
289         case VLC_OBJECT_ANNOUNCE:
290             i_size = sizeof( announce_handler_t );
291             psz_type = "announce";
292             break;
293         case VLC_OBJECT_INTERACTION:
294             i_size = sizeof( interaction_t );
295             psz_type = "interaction";
296             break;
297         default:
298             i_size = i_type > (int)sizeof(vlc_object_t)
299                          ? i_type : (int)sizeof(vlc_object_t);
300             i_type = VLC_OBJECT_GENERIC;
301             psz_type = "generic";
302             break;
303     }
304
305     return vlc_custom_create( p_this, i_size, i_type, psz_type );
306 }
307
308
309 /**
310  ****************************************************************************
311  * Set the destructor of a vlc object
312  *
313  * This function sets the destructor of the vlc object. It will be called
314  * when the object is destroyed when the its refcount reaches 0.
315  * (It is called by the internal function vlc_object_destroy())
316  *****************************************************************************/
317 void __vlc_object_set_destructor( vlc_object_t *p_this,
318                                   vlc_destructor_t pf_destructor )
319 {
320     vlc_object_internals_t *p_priv = vlc_internals(p_this );
321
322     vlc_mutex_lock( &structure_lock );
323     p_priv->pf_destructor = pf_destructor;
324     vlc_mutex_unlock( &structure_lock );
325 }
326
327 /**
328  ****************************************************************************
329  * Destroy a vlc object (Internal)
330  *
331  * This function destroys an object that has been previously allocated with
332  * vlc_object_create. The object's refcount must be zero and it must not be
333  * attached to other objects in any way.
334  *****************************************************************************/
335 static void vlc_object_destroy( vlc_object_t *p_this )
336 {
337     vlc_object_internals_t *p_priv = vlc_internals( p_this );
338
339     /* Automatically detach the object from its parents */
340     if( p_this->p_parent ) vlc_object_detach( p_this );
341
342
343     /* Send a kill to the object's thread if applicable */
344     vlc_object_kill( p_this );
345
346     /* If we are running on a thread, wait until it ends */
347     if( p_priv->b_thread )
348         vlc_thread_join( p_this );
349
350     /* Call the custom "subclass" destructor */
351     if( p_priv->pf_destructor )
352         p_priv->pf_destructor( p_this );
353
354     /* Sanity checks */
355     if( p_this->i_children )
356     {
357         int i;
358
359         fprintf( stderr,
360                  "ERROR: cannot delete object (%i, %s) with %d children\n",
361                  p_this->i_object_id, p_this->psz_object_name,
362                  p_this->i_children );
363
364         for( i = 0; i < p_this->i_children; i++ )
365         {
366             fprintf( stderr,
367                      "ERROR: Remaining children object "
368                      "(id:%i, type:%s, name:%s)\n",
369                      p_this->pp_children[i]->i_object_id,
370                      p_this->pp_children[i]->psz_object_type,
371                      p_this->pp_children[i]->psz_object_name );
372         }
373         fflush(stderr);
374         /* Dump libvlc object to ease debugging */
375         vlc_object_dump( p_this->p_libvlc );
376         abort();
377     }
378
379     /* Destroy the associated variables, starting from the end so that
380      * no memmove calls have to be done. */
381     while( p_priv->i_vars )
382     {
383         var_Destroy( p_this, p_priv->p_vars[p_priv->i_vars - 1].psz_name );
384     }
385
386     free( p_priv->p_vars );
387     vlc_mutex_destroy( &p_priv->var_lock );
388
389     free( p_this->psz_header );
390
391     if( p_this->i_object_type == VLC_OBJECT_GLOBAL )
392     {
393         libvlc_global_data_t *p_global = (libvlc_global_data_t *)p_this;
394
395         /* Test for leaks */
396         if( p_global->i_objects > 0 )
397         {
398             int i;
399             for( i = 0; i < p_global->i_objects; i++ )
400             {
401                 /* We are leaking this object */
402                 fprintf( stderr,
403                          "ERROR: leaking object (id:%i, type:%s, name:%s)\n",
404                          p_global->pp_objects[i]->i_object_id,
405                          p_global->pp_objects[i]->psz_object_type,
406                          p_global->pp_objects[i]->psz_object_name );
407                 fflush(stderr);
408             }
409             /* Strongly abort, cause we want these to be fixed */
410             abort();
411         }
412
413         /* We are the global object ... no need to lock. */
414         free( p_global->pp_objects );
415         p_global->pp_objects = NULL;
416
417         vlc_mutex_destroy( &structure_lock );
418     }
419
420 #if defined(WIN32) || defined(UNDER_CE)
421     /* if object has an associated thread, close it now */
422     if( p_priv->thread_id.hThread )
423        CloseHandle(p_priv->thread_id.hThread);
424 #endif
425
426     vlc_mutex_destroy( &p_this->object_lock );
427     vlc_cond_destroy( &p_this->object_wait );
428     vlc_spin_destroy( &p_priv->spin );
429     if( p_priv->pipes[1] != -1 )
430         close( p_priv->pipes[1] );
431     if( p_priv->pipes[0] != -1 )
432         close( p_priv->pipes[0] );
433
434     free( p_priv );
435 }
436
437
438 /** Inter-object signaling */
439
440 void __vlc_object_lock( vlc_object_t *obj )
441 {
442     vlc_mutex_lock( &obj->object_lock );
443 }
444
445 void __vlc_object_unlock( vlc_object_t *obj )
446 {
447     vlc_assert_locked( &obj->object_lock );
448     vlc_mutex_unlock( &obj->object_lock );
449 }
450
451 #ifdef WIN32
452 # include <winsock2.h>
453 # include <ws2tcpip.h>
454
455 /**
456  * select()-able pipes emulated using Winsock
457  */
458 static int pipe (int fd[2])
459 {
460     SOCKADDR_IN addr;
461     int addrlen = sizeof (addr);
462
463     SOCKET l = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP), a,
464            c = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
465     if ((l == INVALID_SOCKET) || (c == INVALID_SOCKET))
466         goto error;
467
468     memset (&addr, 0, sizeof (addr));
469     addr.sin_family = AF_INET;
470     addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
471     if (bind (l, (PSOCKADDR)&addr, sizeof (addr))
472      || getsockname (l, (PSOCKADDR)&addr, &addrlen)
473      || listen (l, 1)
474      || connect (c, (PSOCKADDR)&addr, addrlen))
475         goto error;
476
477     a = accept (l, NULL, NULL);
478     if (a == INVALID_SOCKET)
479         goto error;
480
481     closesocket (l);
482     //shutdown (a, 0);
483     //shutdown (c, 1);
484     fd[0] = c;
485     fd[1] = a;
486     return 0;
487
488 error:
489     if (l != INVALID_SOCKET)
490         closesocket (l);
491     if (c != INVALID_SOCKET)
492         closesocket (c);
493     return -1;
494 }
495
496 #undef  read
497 #define read( a, b, c )  recv (a, b, c, 0)
498 #undef  write
499 #define write( a, b, c ) send (a, b, c, 0)
500 #undef  close
501 #define close( a )       closesocket (a)
502 #endif /* WIN32 */
503
504 /**
505  * Returns the readable end of a pipe that becomes readable once termination
506  * of the object is requested (vlc_object_kill()).
507  * This can be used to wake-up out of a select() or poll() event loop, such
508  * typically when doing network I/O.
509  *
510  * Note that the pipe will remain the same for the lifetime of the object.
511  * DO NOT read the pipe nor close it yourself. Ever.
512  *
513  * @param obj object that would be "killed"
514  * @return a readable pipe descriptor, or -1 on error.
515  */
516 int __vlc_object_waitpipe( vlc_object_t *obj )
517 {
518     int pfd[2] = { -1, -1 };
519     struct vlc_object_internals_t *internals = obj->p_internals;
520     bool killed = false;
521
522     vlc_spin_lock (&internals->spin);
523     if (internals->pipes[0] == -1)
524     {
525         /* This can only ever happen if someone killed us without locking: */
526         assert (internals->pipes[1] == -1);
527         vlc_spin_unlock (&internals->spin);
528
529         if (pipe (pfd))
530             return -1;
531
532         vlc_spin_lock (&internals->spin);
533         if (internals->pipes[0] == -1)
534         {
535             internals->pipes[0] = pfd[0];
536             internals->pipes[1] = pfd[1];
537             pfd[0] = pfd[1] = -1;
538         }
539         killed = obj->b_die;
540     }
541     vlc_spin_unlock (&internals->spin);
542
543     if (killed)
544     {
545         /* Race condition: vlc_object_kill() already invoked! */
546         int fd;
547
548         vlc_spin_lock (&internals->spin);
549         fd = internals->pipes[1];
550         internals->pipes[1] = -1;
551         vlc_spin_unlock (&internals->spin);
552
553         msg_Dbg (obj, "waitpipe: object already dying");
554         if (fd != -1)
555             close (fd);
556     }
557
558     /* Race condition: two threads call pipe() - unlikely */
559     if (pfd[0] != -1)
560         close (pfd[0]);
561     if (pfd[1] != -1)
562         close (pfd[1]);
563
564     return internals->pipes[0];
565 }
566
567
568 /**
569  * Waits for the object to be signaled (using vlc_object_signal()).
570  * If the object already has a signal pending, this function will return
571  * immediately. It is asserted that the caller holds the object lock.
572  *
573  * @return true if the object is dying and should terminate.
574  */
575 bool __vlc_object_wait( vlc_object_t *obj )
576 {
577     vlc_assert_locked( &obj->object_lock );
578     vlc_cond_wait( &obj->object_wait, &obj->object_lock );
579     return obj->b_die;
580 }
581
582
583 /**
584  * Waits for the object to be signaled (using vlc_object_signal()), or for
585  * a timer to expire.
586  * If the object already has a signal pending, this function will return
587  * immediately. It is asserted that the caller holds the object lock.
588  *
589  * @return negative if the object is dying and should terminate,
590  * positive if the the object has been signaled but is not dying,
591  * 0 if timeout has been reached.
592  */
593 int __vlc_object_timedwait( vlc_object_t *obj, mtime_t deadline )
594 {
595     int v;
596
597     vlc_assert_locked( &obj->object_lock );
598     v = vlc_cond_timedwait( &obj->object_wait, &obj->object_lock, deadline );
599     if( v == 0 ) /* signaled */
600         return obj->b_die ? -1 : 1;
601     return 0;
602 }
603
604
605 /**
606  * Checks whether an object has been "killed".
607  * The object lock must be held.
608  *
609  * Typical code for an object thread could be:
610  *
611    vlc_object_lock (self);
612    ...initialization...
613    while (vlc_object_alive (self))
614    {
615        ...preprocessing...
616
617        if (vlc_object_wait (self))
618            continue;
619
620        ...postprocessing...
621    }
622    ...deinitialization...
623    vlc_object_unlock (self);
624  *
625  *
626  * @return true iff the object has not been killed yet
627  */
628 bool __vlc_object_alive( vlc_object_t *obj )
629 {
630     vlc_assert_locked( &obj->object_lock );
631     return !obj->b_die;
632 }
633
634
635 /**
636  * Signals an object for which the lock is held.
637  */
638 void __vlc_object_signal_unlocked( vlc_object_t *obj )
639 {
640     vlc_assert_locked (&obj->object_lock);
641     vlc_cond_signal( &obj->object_wait );
642 }
643
644
645 /**
646  * Requests termination of an object.
647  * If the object is LibVLC, also request to terminate all its children.
648  */
649 void __vlc_object_kill( vlc_object_t *p_this )
650 {
651     struct vlc_object_internals_t *internals = p_this->p_internals;
652     int fd;
653
654     vlc_mutex_lock( &p_this->object_lock );
655     p_this->b_die = true;
656
657     vlc_spin_lock (&internals->spin);
658     fd = internals->pipes[1];
659     internals->pipes[1] = -1;
660     vlc_spin_unlock (&internals->spin);
661
662     if( fd != -1 )
663     {
664         msg_Dbg (p_this, "waitpipe: object killed");
665         close (fd);
666     }
667
668     if( p_this->i_object_type == VLC_OBJECT_LIBVLC )
669         for( int i = 0; i < p_this->i_children ; i++ )
670             vlc_object_kill( p_this->pp_children[i] );
671
672     vlc_object_signal_unlocked( p_this );
673     vlc_mutex_unlock( &p_this->object_lock );
674 }
675
676
677 /**
678  * find an object given its ID
679  *
680  * This function looks for the object whose i_object_id field is i_id. We
681  * use a dichotomy so that lookups are in log2(n).
682  *****************************************************************************/
683 void * vlc_object_get( int i_id )
684 {
685     int i_max, i_middle;
686     vlc_object_t **pp_objects;
687     libvlc_global_data_t *p_libvlc_global = vlc_global();
688
689     vlc_mutex_lock( &structure_lock );
690
691     pp_objects = p_libvlc_global->pp_objects;
692
693     /* Perform our dichotomy */
694     for( i_max = p_libvlc_global->i_objects - 1 ; ; )
695     {
696         i_middle = i_max / 2;
697
698         if( pp_objects[i_middle]->i_object_id > i_id )
699         {
700             i_max = i_middle;
701         }
702         else if( pp_objects[i_middle]->i_object_id < i_id )
703         {
704             if( i_middle )
705             {
706                 pp_objects += i_middle;
707                 i_max -= i_middle;
708             }
709             else
710             {
711                 /* This happens when there are only two remaining objects */
712                 if( pp_objects[i_middle+1]->i_object_id == i_id
713                     && pp_objects[i_middle+1]->p_internals->i_refcount > 0 )
714                 {
715                     vlc_object_yield_locked( pp_objects[i_middle+1] );
716                     vlc_mutex_unlock( &structure_lock );
717                     return pp_objects[i_middle+1];
718                 }
719                 break;
720             }
721         }
722         else if( pp_objects[i_middle]->p_internals->i_refcount > 0 )
723         {
724             vlc_object_yield_locked( pp_objects[i_middle] );
725             vlc_mutex_unlock( &structure_lock );
726             return pp_objects[i_middle];
727         }
728
729         if( i_max == 0 )
730         {
731             /* this means that i_max == i_middle, and since we have already
732              * tested pp_objects[i_middle]), p_found is properly set. */
733             break;
734         }
735     }
736
737     vlc_mutex_unlock( &structure_lock );
738     return NULL;
739 }
740
741 /**
742  ****************************************************************************
743  * find a typed object and increment its refcount
744  *****************************************************************************
745  * This function recursively looks for a given object type. i_mode can be one
746  * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
747  *****************************************************************************/
748 void * __vlc_object_find( vlc_object_t *p_this, int i_type, int i_mode )
749 {
750     vlc_object_t *p_found;
751
752     vlc_mutex_lock( &structure_lock );
753
754     /* If we are of the requested type ourselves, don't look further */
755     if( !(i_mode & FIND_STRICT) && p_this->i_object_type == i_type
756         && p_this->p_internals->i_refcount > 0 )
757     {
758         vlc_object_yield_locked( p_this );
759         vlc_mutex_unlock( &structure_lock );
760         return p_this;
761     }
762
763     /* Otherwise, recursively look for the object */
764     if( (i_mode & 0x000f) == FIND_ANYWHERE )
765     {
766         vlc_object_t *p_root = p_this;
767
768         /* Find the root */
769         while( p_root->p_parent != NULL &&
770                p_root != VLC_OBJECT( p_this->p_libvlc ) )
771         {
772             p_root = p_root->p_parent;
773         }
774
775         p_found = FindObject( p_root, i_type, (i_mode & ~0x000f)|FIND_CHILD );
776         if( p_found == NULL && p_root != VLC_OBJECT( p_this->p_libvlc ) )
777         {
778             p_found = FindObject( VLC_OBJECT( p_this->p_libvlc ),
779                                   i_type, (i_mode & ~0x000f)|FIND_CHILD );
780         }
781     }
782     else
783     {
784         p_found = FindObject( p_this, i_type, i_mode );
785     }
786
787     vlc_mutex_unlock( &structure_lock );
788
789     return p_found;
790 }
791
792 /**
793  ****************************************************************************
794  * find a named object and increment its refcount
795  *****************************************************************************
796  * This function recursively looks for a given object name. i_mode can be one
797  * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
798  *****************************************************************************/
799 void * __vlc_object_find_name( vlc_object_t *p_this, const char *psz_name,
800                                int i_mode )
801 {
802     vlc_object_t *p_found;
803
804     vlc_mutex_lock( &structure_lock );
805
806     /* Avoid obvious freed object uses */
807     assert( p_this->p_internals->i_refcount > 0 );
808
809     /* If have the requested name ourselves, don't look further */
810     if( !(i_mode & FIND_STRICT)
811         && p_this->psz_object_name
812         && !strcmp( p_this->psz_object_name, psz_name )
813         && p_this->p_internals->i_refcount > 0 )
814     {
815         vlc_object_yield_locked( p_this );
816         vlc_mutex_unlock( &structure_lock );
817         return p_this;
818     }
819
820     /* Otherwise, recursively look for the object */
821     if( (i_mode & 0x000f) == FIND_ANYWHERE )
822     {
823         vlc_object_t *p_root = p_this;
824
825         /* Find the root */
826         while( p_root->p_parent != NULL &&
827                p_root != VLC_OBJECT( p_this->p_libvlc ) )
828         {
829             p_root = p_root->p_parent;
830         }
831
832         p_found = FindObjectName( p_root, psz_name,
833                                  (i_mode & ~0x000f)|FIND_CHILD );
834         if( p_found == NULL && p_root != VLC_OBJECT( p_this->p_libvlc ) )
835         {
836             p_found = FindObjectName( VLC_OBJECT( p_this->p_libvlc ),
837                                       psz_name, (i_mode & ~0x000f)|FIND_CHILD );
838         }
839     }
840     else
841     {
842         p_found = FindObjectName( p_this, psz_name, i_mode );
843     }
844
845     vlc_mutex_unlock( &structure_lock );
846
847     return p_found;
848 }
849
850 /**
851  ****************************************************************************
852  * increment an object refcount
853  *****************************************************************************/
854
855 /* When the structure_lock is locked */
856 static void vlc_object_yield_locked( vlc_object_t *p_this )
857 {
858     vlc_assert_locked (&structure_lock);
859
860     /* Avoid obvious freed object uses */
861     assert( p_this->p_internals->i_refcount > 0 );
862
863     /* Increment the counter */
864     p_this->p_internals->i_refcount++;
865 }
866
867 /* Public function */
868 void __vlc_object_yield( vlc_object_t *p_this )
869 {
870     vlc_mutex_lock( &structure_lock );
871     vlc_object_yield_locked( p_this );
872     vlc_mutex_unlock( &structure_lock );
873 }
874
875
876 /*****************************************************************************
877  * decrement an object refcount
878  * And destroy the object if its refcount reach zero.
879  *****************************************************************************/
880 void __vlc_object_release( vlc_object_t *p_this )
881 {
882     bool b_should_destroy;
883
884     vlc_mutex_lock( &structure_lock );
885
886     assert( p_this->p_internals->i_refcount > 0 );
887     p_this->p_internals->i_refcount--;
888     b_should_destroy = (p_this->p_internals->i_refcount == 0);
889
890     if( b_should_destroy )
891     {
892         /* Make sure this object can't be obtained via vlc_find_object now that
893          * it is freed */
894         libvlc_global_data_t *p_libvlc_global = vlc_global();
895         int i_index;
896
897         /* Wooohaa! If *this* fails, we're in serious trouble! Anyway it's
898          * useless to try and recover anything if pp_objects gets smashed. */
899         i_index = FindIndex( p_this, p_libvlc_global->pp_objects,
900                              p_libvlc_global->i_objects );
901         REMOVE_ELEM( p_libvlc_global->pp_objects,
902                      p_libvlc_global->i_objects, i_index );
903     }
904
905     vlc_mutex_unlock( &structure_lock );
906
907     if( b_should_destroy )
908         vlc_object_destroy( p_this );
909 }
910
911 /**
912  ****************************************************************************
913  * attach object to a parent object
914  *****************************************************************************
915  * This function sets p_this as a child of p_parent, and p_parent as a parent
916  * of p_this. This link can be undone using vlc_object_detach.
917  *****************************************************************************/
918 void __vlc_object_attach( vlc_object_t *p_this, vlc_object_t *p_parent )
919 {
920     if( !p_this ) return;
921
922     vlc_mutex_lock( &structure_lock );
923
924     /* Avoid obvious freed object uses */
925     assert( p_this->p_internals->i_refcount > 0 );
926
927     /* Attach the parent to its child */
928     p_this->p_parent = p_parent;
929
930     /* Attach the child to its parent */
931     INSERT_ELEM( p_parent->pp_children, p_parent->i_children,
932                  p_parent->i_children, p_this );
933
934     /* Climb up the tree to see whether we are connected with the root */
935     if( p_parent->p_internals->b_attached )
936     {
937         SetAttachment( p_this, true );
938     }
939
940     vlc_mutex_unlock( &structure_lock );
941 }
942
943 /**
944  ****************************************************************************
945  * detach object from its parent
946  *****************************************************************************
947  * This function removes all links between an object and its parent.
948  *****************************************************************************/
949 void __vlc_object_detach( vlc_object_t *p_this )
950 {
951     if( !p_this ) return;
952
953     vlc_mutex_lock( &structure_lock );
954
955     if( !p_this->p_parent )
956     {
957         msg_Err( p_this, "object is not attached" );
958         vlc_mutex_unlock( &structure_lock );
959         return;
960     }
961
962     /* Climb up the tree to see whether we are connected with the root */
963     if( p_this->p_parent->p_internals->b_attached )
964     {
965         SetAttachment( p_this, false );
966     }
967
968     DetachObject( p_this );
969     vlc_mutex_unlock( &structure_lock );
970     p_this = NULL;
971 }
972
973 /**
974  ****************************************************************************
975  * find a list typed objects and increment their refcount
976  *****************************************************************************
977  * This function recursively looks for a given object type. i_mode can be one
978  * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
979  *****************************************************************************/
980 vlc_list_t * __vlc_list_find( vlc_object_t *p_this, int i_type, int i_mode )
981 {
982     vlc_list_t *p_list;
983     vlc_object_t **pp_current, **pp_end;
984     int i_count = 0, i_index = 0;
985     libvlc_global_data_t *p_libvlc_global = vlc_global();
986
987     vlc_mutex_lock( &structure_lock );
988
989     /* Look for the objects */
990     switch( i_mode & 0x000f )
991     {
992     case FIND_ANYWHERE:
993         pp_current = p_libvlc_global->pp_objects;
994         pp_end = pp_current + p_libvlc_global->i_objects;
995
996         for( ; pp_current < pp_end ; pp_current++ )
997         {
998             if( (*pp_current)->p_internals->b_attached
999                  && (*pp_current)->i_object_type == i_type )
1000             {
1001                 i_count++;
1002             }
1003         }
1004
1005         p_list = NewList( i_count );
1006         pp_current = p_libvlc_global->pp_objects;
1007
1008         for( ; pp_current < pp_end ; pp_current++ )
1009         {
1010             if( (*pp_current)->p_internals->b_attached
1011                  && (*pp_current)->i_object_type == i_type )
1012             {
1013                 ListReplace( p_list, *pp_current, i_index );
1014                 if( i_index < i_count ) i_index++;
1015             }
1016         }
1017     break;
1018
1019     case FIND_CHILD:
1020         i_count = CountChildren( p_this, i_type );
1021         p_list = NewList( i_count );
1022
1023         /* Check allocation was successful */
1024         if( p_list->i_count != i_count )
1025         {
1026             msg_Err( p_this, "list allocation failed!" );
1027             p_list->i_count = 0;
1028             break;
1029         }
1030
1031         p_list->i_count = 0;
1032         ListChildren( p_list, p_this, i_type );
1033         break;
1034
1035     default:
1036         msg_Err( p_this, "unimplemented!" );
1037         p_list = NewList( 0 );
1038         break;
1039     }
1040
1041     vlc_mutex_unlock( &structure_lock );
1042
1043     return p_list;
1044 }
1045
1046 /*****************************************************************************
1047  * DumpCommand: print the current vlc structure
1048  *****************************************************************************
1049  * This function prints either an ASCII tree showing the connections between
1050  * vlc objects, and additional information such as their refcount, thread ID,
1051  * etc. (command "tree"), or the same data as a simple list (command "list").
1052  *****************************************************************************/
1053 static int DumpCommand( vlc_object_t *p_this, char const *psz_cmd,
1054                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
1055 {
1056     libvlc_global_data_t *p_libvlc_global = vlc_global();
1057
1058     (void)oldval; (void)p_data;
1059     if( *psz_cmd == 'l' )
1060     {
1061         vlc_mutex_lock( &structure_lock );
1062
1063         vlc_object_t **pp_current, **pp_end;
1064
1065         pp_current = p_libvlc_global->pp_objects;
1066         pp_end = pp_current + p_libvlc_global->i_objects;
1067
1068         for( ; pp_current < pp_end ; pp_current++ )
1069         {
1070             if( (*pp_current)->p_internals->b_attached )
1071             {
1072                 PrintObject( *pp_current, "" );
1073             }
1074             else
1075             {
1076                 printf( " o %.8i %s (not attached)\n",
1077                         (*pp_current)->i_object_id,
1078                         (*pp_current)->psz_object_type );
1079             }
1080         }
1081
1082         vlc_mutex_unlock( &structure_lock );
1083     }
1084     else
1085     {
1086         vlc_object_t *p_object = NULL;
1087
1088         if( *newval.psz_string )
1089         {
1090             char *end;
1091             int i_id = strtol( newval.psz_string, &end, 0 );
1092             if( end != newval.psz_string )
1093                 p_object = vlc_object_get( i_id );
1094             else
1095             {
1096                 /* try using the object's name to find it */
1097                 vlc_object_t *p_libvlc = vlc_object_get( 1 );
1098                 if( p_libvlc )
1099                 {
1100                     /* Look in p_libvlc's children tree */
1101                     p_object = vlc_object_find_name( p_libvlc,
1102                                                      newval.psz_string,
1103                                                      FIND_CHILD );
1104                     vlc_object_release( p_libvlc );
1105                 }
1106                 if( !p_object )
1107                 {
1108                     /* If it's not in libvlc, look in libvlc_global (== p_this) */
1109                     p_object = vlc_object_find_name( p_this,
1110                                                      newval.psz_string,
1111                                                      FIND_CHILD );
1112                 }
1113             }
1114
1115             if( !p_object )
1116             {
1117                 return VLC_ENOOBJ;
1118             }
1119         }
1120
1121         vlc_mutex_lock( &structure_lock );
1122
1123         if( *psz_cmd == 't' )
1124         {
1125             char psz_foo[2 * MAX_DUMPSTRUCTURE_DEPTH + 1];
1126
1127             if( !p_object )
1128                 p_object = p_this->p_libvlc ? VLC_OBJECT(p_this->p_libvlc) : p_this;
1129
1130             psz_foo[0] = '|';
1131             DumpStructure( p_object, 0, psz_foo );
1132         }
1133         else if( *psz_cmd == 'v' )
1134         {
1135             int i;
1136
1137             if( !p_object )
1138                 p_object = p_this->p_libvlc ? VLC_OBJECT(p_this->p_libvlc) : p_this;
1139
1140             PrintObject( p_object, "" );
1141
1142             if( !p_object->p_internals->i_vars )
1143                 printf( " `-o No variables\n" );
1144             for( i = 0; i < p_object->p_internals->i_vars; i++ )
1145             {
1146                 variable_t *p_var = p_object->p_internals->p_vars + i;
1147
1148                 const char *psz_type = "unknown";
1149                 switch( p_var->i_type & VLC_VAR_TYPE )
1150                 {
1151 #define MYCASE( type, nice )                \
1152                     case VLC_VAR_ ## type:  \
1153                         psz_type = nice;    \
1154                         break;
1155                     MYCASE( VOID, "void" );
1156                     MYCASE( BOOL, "bool" );
1157                     MYCASE( INTEGER, "integer" );
1158                     MYCASE( HOTKEY, "hotkey" );
1159                     MYCASE( STRING, "string" );
1160                     MYCASE( MODULE, "module" );
1161                     MYCASE( FILE, "file" );
1162                     MYCASE( DIRECTORY, "directory" );
1163                     MYCASE( VARIABLE, "variable" );
1164                     MYCASE( FLOAT, "float" );
1165                     MYCASE( TIME, "time" );
1166                     MYCASE( ADDRESS, "address" );
1167                     MYCASE( MUTEX, "mutex" );
1168                     MYCASE( LIST, "list" );
1169 #undef MYCASE
1170                 }
1171                 printf( " %c-o \"%s\" (%s",
1172                         i + 1 == p_object->p_internals->i_vars ? '`' : '|',
1173                         p_var->psz_name, psz_type );
1174                 if( p_var->psz_text )
1175                     printf( ", %s", p_var->psz_text );
1176                 printf( ")" );
1177                 if( p_var->i_type & VLC_VAR_ISCOMMAND )
1178                     printf( ", command" );
1179                 if( p_var->i_entries )
1180                     printf( ", %d callbacks", p_var->i_entries );
1181                 switch( p_var->i_type & 0x00f0 )
1182                 {
1183                     case VLC_VAR_VOID:
1184                     case VLC_VAR_MUTEX:
1185                         break;
1186                     case VLC_VAR_BOOL:
1187                         printf( ": %s", p_var->val.b_bool ? "true" : "false" );
1188                         break;
1189                     case VLC_VAR_INTEGER:
1190                         printf( ": %d", p_var->val.i_int );
1191                         break;
1192                     case VLC_VAR_STRING:
1193                         printf( ": \"%s\"", p_var->val.psz_string );
1194                         break;
1195                     case VLC_VAR_FLOAT:
1196                         printf( ": %f", p_var->val.f_float );
1197                         break;
1198                     case VLC_VAR_TIME:
1199                         printf( ": " I64Fi, (int64_t)p_var->val.i_time );
1200                         break;
1201                     case VLC_VAR_ADDRESS:
1202                         printf( ": %p", p_var->val.p_address );
1203                         break;
1204                     case VLC_VAR_LIST:
1205                         printf( ": TODO" );
1206                         break;
1207                 }
1208                 printf( "\n" );
1209             }
1210         }
1211
1212         vlc_mutex_unlock( &structure_lock );
1213
1214         if( *newval.psz_string )
1215         {
1216             vlc_object_release( p_object );
1217         }
1218     }
1219
1220     return VLC_SUCCESS;
1221 }
1222
1223 /*****************************************************************************
1224  * vlc_list_release: free a list previously allocated by vlc_list_find
1225  *****************************************************************************
1226  * This function decreases the refcount of all objects in the list and
1227  * frees the list.
1228  *****************************************************************************/
1229 void vlc_list_release( vlc_list_t *p_list )
1230 {
1231     int i_index;
1232
1233     for( i_index = 0; i_index < p_list->i_count; i_index++ )
1234     {
1235         vlc_object_release( p_list->p_values[i_index].p_object );
1236     }
1237
1238     free( p_list->p_values );
1239     free( p_list );
1240 }
1241
1242 /*****************************************************************************
1243  * dump an object. (Debug function)
1244  *****************************************************************************/
1245 void __vlc_object_dump( vlc_object_t *p_this )
1246 {
1247     vlc_mutex_lock( &structure_lock );
1248     char psz_foo[2 * MAX_DUMPSTRUCTURE_DEPTH + 1];
1249     psz_foo[0] = '|';
1250     DumpStructure( p_this, 0, psz_foo );
1251     vlc_mutex_unlock( &structure_lock );
1252 }
1253
1254 /* Following functions are local */
1255
1256 /*****************************************************************************
1257  * FindIndex: find the index of an object in an array of objects
1258  *****************************************************************************
1259  * This function assumes that p_this can be found in pp_objects. It will not
1260  * crash if p_this cannot be found, but will return a wrong value. It is your
1261  * duty to check the return value if you are not certain that the object could
1262  * be found for sure.
1263  *****************************************************************************/
1264 static int FindIndex( vlc_object_t *p_this,
1265                       vlc_object_t **pp_objects, int i_count )
1266 {
1267     int i_middle = i_count / 2;
1268
1269     if( i_count == 0 )
1270     {
1271         return 0;
1272     }
1273
1274     if( pp_objects[i_middle] == p_this )
1275     {
1276         return i_middle;
1277     }
1278
1279     if( i_count == 1 )
1280     {
1281         return 0;
1282     }
1283
1284     /* We take advantage of the sorted array */
1285     if( pp_objects[i_middle]->i_object_id < p_this->i_object_id )
1286     {
1287         return i_middle + FindIndex( p_this, pp_objects + i_middle,
1288                                              i_count - i_middle );
1289     }
1290     else
1291     {
1292         return FindIndex( p_this, pp_objects, i_middle );
1293     }
1294 }
1295
1296 static vlc_object_t * FindObject( vlc_object_t *p_this, int i_type, int i_mode )
1297 {
1298     int i;
1299     vlc_object_t *p_tmp;
1300
1301     switch( i_mode & 0x000f )
1302     {
1303     case FIND_PARENT:
1304         p_tmp = p_this->p_parent;
1305         if( p_tmp )
1306         {
1307             if( p_tmp->i_object_type == i_type
1308                 && p_tmp->p_internals->i_refcount > 0 )
1309             {
1310                 vlc_object_yield_locked( p_tmp );
1311                 return p_tmp;
1312             }
1313             else
1314             {
1315                 return FindObject( p_tmp, i_type, i_mode );
1316             }
1317         }
1318         break;
1319
1320     case FIND_CHILD:
1321         for( i = p_this->i_children; i--; )
1322         {
1323             p_tmp = p_this->pp_children[i];
1324             if( p_tmp->i_object_type == i_type
1325                 && p_tmp->p_internals->i_refcount > 0 )
1326             {
1327                 vlc_object_yield_locked( p_tmp );
1328                 return p_tmp;
1329             }
1330             else if( p_tmp->i_children )
1331             {
1332                 p_tmp = FindObject( p_tmp, i_type, i_mode );
1333                 if( p_tmp )
1334                 {
1335                     return p_tmp;
1336                 }
1337             }
1338         }
1339         break;
1340
1341     case FIND_ANYWHERE:
1342         /* Handled in vlc_object_find */
1343         break;
1344     }
1345
1346     return NULL;
1347 }
1348
1349 static vlc_object_t * FindObjectName( vlc_object_t *p_this,
1350                                       const char *psz_name,
1351                                       int i_mode )
1352 {
1353     int i;
1354     vlc_object_t *p_tmp;
1355
1356     switch( i_mode & 0x000f )
1357     {
1358     case FIND_PARENT:
1359         p_tmp = p_this->p_parent;
1360         if( p_tmp )
1361         {
1362             if( p_tmp->psz_object_name
1363                 && !strcmp( p_tmp->psz_object_name, psz_name )
1364                 && p_tmp->p_internals->i_refcount > 0 )
1365             {
1366                 vlc_object_yield_locked( p_tmp );
1367                 return p_tmp;
1368             }
1369             else
1370             {
1371                 return FindObjectName( p_tmp, psz_name, i_mode );
1372             }
1373         }
1374         break;
1375
1376     case FIND_CHILD:
1377         for( i = p_this->i_children; i--; )
1378         {
1379             p_tmp = p_this->pp_children[i];
1380             if( p_tmp->psz_object_name
1381                 && !strcmp( p_tmp->psz_object_name, psz_name )
1382                 && p_tmp->p_internals->i_refcount > 0 )
1383             {
1384                 vlc_object_yield_locked( p_tmp );
1385                 return p_tmp;
1386             }
1387             else if( p_tmp->i_children )
1388             {
1389                 p_tmp = FindObjectName( p_tmp, psz_name, i_mode );
1390                 if( p_tmp )
1391                 {
1392                     return p_tmp;
1393                 }
1394             }
1395         }
1396         break;
1397
1398     case FIND_ANYWHERE:
1399         /* Handled in vlc_object_find */
1400         break;
1401     }
1402
1403     return NULL;
1404 }
1405
1406 static void DetachObject( vlc_object_t *p_this )
1407 {
1408     vlc_object_t *p_parent = p_this->p_parent;
1409     int i_index, i;
1410
1411     /* Remove p_this's parent */
1412     p_this->p_parent = NULL;
1413
1414     /* Remove all of p_parent's children which are p_this */
1415     for( i_index = p_parent->i_children ; i_index-- ; )
1416     {
1417         if( p_parent->pp_children[i_index] == p_this )
1418         {
1419             p_parent->i_children--;
1420             for( i = i_index ; i < p_parent->i_children ; i++ )
1421             {
1422                 p_parent->pp_children[i] = p_parent->pp_children[i+1];
1423             }
1424         }
1425     }
1426
1427     if( p_parent->i_children )
1428     {
1429         p_parent->pp_children = (vlc_object_t **)realloc( p_parent->pp_children,
1430                                p_parent->i_children * sizeof(vlc_object_t *) );
1431     }
1432     else
1433     {
1434         free( p_parent->pp_children );
1435         p_parent->pp_children = NULL;
1436     }
1437 }
1438
1439 /*****************************************************************************
1440  * SetAttachment: recursively set the b_attached flag of a subtree.
1441  *****************************************************************************
1442  * This function is used by the attach and detach functions to propagate
1443  * the b_attached flag in a subtree.
1444  *****************************************************************************/
1445 static void SetAttachment( vlc_object_t *p_this, bool b_attached )
1446 {
1447     int i_index;
1448
1449     for( i_index = p_this->i_children ; i_index-- ; )
1450     {
1451         SetAttachment( p_this->pp_children[i_index], b_attached );
1452     }
1453
1454     p_this->p_internals->b_attached = b_attached;
1455 }
1456
1457 static void PrintObject( vlc_object_t *p_this, const char *psz_prefix )
1458 {
1459     char psz_children[20], psz_refcount[20], psz_thread[30], psz_name[50],
1460          psz_parent[20];
1461
1462     psz_name[0] = '\0';
1463     if( p_this->psz_object_name )
1464     {
1465         snprintf( psz_name, 49, " \"%s\"", p_this->psz_object_name );
1466         if( psz_name[48] )
1467             psz_name[48] = '\"';
1468     }
1469
1470     psz_children[0] = '\0';
1471     switch( p_this->i_children )
1472     {
1473         case 0:
1474             break;
1475         case 1:
1476             strcpy( psz_children, ", 1 child" );
1477             break;
1478         default:
1479             snprintf( psz_children, 19, ", %i children", p_this->i_children );
1480             break;
1481     }
1482
1483     psz_refcount[0] = '\0';
1484     if( p_this->p_internals->i_refcount > 0 )
1485         snprintf( psz_refcount, 19, ", refcount %u",
1486                   p_this->p_internals->i_refcount );
1487
1488     psz_thread[0] = '\0';
1489     if( p_this->p_internals->b_thread )
1490         snprintf( psz_thread, 29, " (thread %u)",
1491 #if defined(WIN32) || defined(UNDER_CE)
1492                   (unsigned)p_this->p_internals->thread_id.id );
1493 #else
1494                   (unsigned)p_this->p_internals->thread_id );
1495 #endif
1496
1497     psz_parent[0] = '\0';
1498     if( p_this->p_parent )
1499         snprintf( psz_parent, 19, ", parent %i", p_this->p_parent->i_object_id );
1500
1501     printf( " %so %.8i %s%s%s%s%s%s\n", psz_prefix,
1502             p_this->i_object_id, p_this->psz_object_type,
1503             psz_name, psz_thread, psz_refcount, psz_children,
1504             psz_parent );
1505 }
1506
1507 static void DumpStructure( vlc_object_t *p_this, int i_level, char *psz_foo )
1508 {
1509     int i;
1510     char i_back = psz_foo[i_level];
1511     psz_foo[i_level] = '\0';
1512
1513     PrintObject( p_this, psz_foo );
1514
1515     psz_foo[i_level] = i_back;
1516
1517     if( i_level / 2 >= MAX_DUMPSTRUCTURE_DEPTH )
1518     {
1519         msg_Warn( p_this, "structure tree is too deep" );
1520         return;
1521     }
1522
1523     for( i = 0 ; i < p_this->i_children ; i++ )
1524     {
1525         if( i_level )
1526         {
1527             psz_foo[i_level-1] = ' ';
1528
1529             if( psz_foo[i_level-2] == '`' )
1530             {
1531                 psz_foo[i_level-2] = ' ';
1532             }
1533         }
1534
1535         if( i == p_this->i_children - 1 )
1536         {
1537             psz_foo[i_level] = '`';
1538         }
1539         else
1540         {
1541             psz_foo[i_level] = '|';
1542         }
1543
1544         psz_foo[i_level+1] = '-';
1545         psz_foo[i_level+2] = '\0';
1546
1547         DumpStructure( p_this->pp_children[i], i_level + 2, psz_foo );
1548     }
1549 }
1550
1551 static vlc_list_t * NewList( int i_count )
1552 {
1553     vlc_list_t * p_list = (vlc_list_t *)malloc( sizeof( vlc_list_t ) );
1554     if( p_list == NULL )
1555     {
1556         return NULL;
1557     }
1558
1559     p_list->i_count = i_count;
1560
1561     if( i_count == 0 )
1562     {
1563         p_list->p_values = NULL;
1564         return p_list;
1565     }
1566
1567     p_list->p_values = malloc( i_count * sizeof( vlc_value_t ) );
1568     if( p_list->p_values == NULL )
1569     {
1570         p_list->i_count = 0;
1571         return p_list;
1572     }
1573
1574     return p_list;
1575 }
1576
1577 static void ListReplace( vlc_list_t *p_list, vlc_object_t *p_object,
1578                          int i_index )
1579 {
1580     if( p_list == NULL || i_index >= p_list->i_count )
1581     {
1582         return;
1583     }
1584
1585     vlc_object_yield_locked( p_object );
1586
1587     p_list->p_values[i_index].p_object = p_object;
1588
1589     return;
1590 }
1591
1592 /*static void ListAppend( vlc_list_t *p_list, vlc_object_t *p_object )
1593 {
1594     if( p_list == NULL )
1595     {
1596         return;
1597     }
1598
1599     p_list->p_values = realloc( p_list->p_values, (p_list->i_count + 1)
1600                                 * sizeof( vlc_value_t ) );
1601     if( p_list->p_values == NULL )
1602     {
1603         p_list->i_count = 0;
1604         return;
1605     }
1606
1607     vlc_object_yield_locked( p_object );
1608
1609     p_list->p_values[p_list->i_count].p_object = p_object;
1610     p_list->i_count++;
1611
1612     return;
1613 }*/
1614
1615 static int CountChildren( vlc_object_t *p_this, int i_type )
1616 {
1617     vlc_object_t *p_tmp;
1618     int i, i_count = 0;
1619
1620     for( i = 0; i < p_this->i_children; i++ )
1621     {
1622         p_tmp = p_this->pp_children[i];
1623
1624         if( p_tmp->i_object_type == i_type )
1625         {
1626             i_count++;
1627         }
1628
1629         if( p_tmp->i_children )
1630         {
1631             i_count += CountChildren( p_tmp, i_type );
1632         }
1633     }
1634
1635     return i_count;
1636 }
1637
1638 static void ListChildren( vlc_list_t *p_list, vlc_object_t *p_this, int i_type )
1639 {
1640     vlc_object_t *p_tmp;
1641     int i;
1642
1643     for( i = 0; i < p_this->i_children; i++ )
1644     {
1645         p_tmp = p_this->pp_children[i];
1646
1647         if( p_tmp->i_object_type == i_type )
1648         {
1649             ListReplace( p_list, p_tmp, p_list->i_count++ );
1650         }
1651
1652         if( p_tmp->i_children )
1653         {
1654             ListChildren( p_list, p_tmp, i_type );
1655         }
1656     }
1657 }