objects.c 30.3 KB
Newer Older
1
2
3
/*****************************************************************************
 * objects.c: vlc_object_t handling
 *****************************************************************************
zorglub's avatar
zorglub committed
4
 * Copyright (C) 2004 VideoLAN
5
 * $Id$
6
7
8
9
10
11
12
 *
 * Authors: Samuel Hocevar <sam@zoy.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
13
 *
14
15
16
17
18
19
20
21
22
23
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

zorglub's avatar
zorglub committed
24
25
26
27
28
29
/**
 * \file
 * This file contains the functions to handle the vlc_object_t type
 */


30
31
32
/*****************************************************************************
 * Preamble
 *****************************************************************************/
33
34
35
36
37
38
39
40
41
42
#include <vlc/vlc.h>

#ifdef HAVE_STDLIB_H
#   include <stdlib.h>                                          /* realloc() */
#endif

#include "stream_control.h"
#include "input_ext-intf.h"
#include "input_ext-dec.h"

43
#include "vlc_video.h"
44
45
46
#include "video_output.h"

#include "audio_output.h"
47
#include "aout_internal.h"
48
#include "stream_output.h"
49

50
#include "vlc_playlist.h"
51
#include "vlc_interface.h"
gbazin's avatar
   
gbazin committed
52
#include "vlc_codec.h"
53

54
#include "vlc_httpd.h"
55
#include "vlc_vlm.h"
56
57
58
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
59
60
static int  DumpCommand( vlc_object_t *, char const *,
                         vlc_value_t, vlc_value_t, void * );
61

62
63
64
65
66
67
68
static vlc_object_t * FindObject    ( vlc_object_t *, int, int );
static void           DetachObject  ( vlc_object_t * );
static void           PrintObject   ( vlc_object_t *, const char * );
static void           DumpStructure ( vlc_object_t *, int, char * );
static int            FindIndex     ( vlc_object_t *, vlc_object_t **, int );
static void           SetAttachment ( vlc_object_t *, vlc_bool_t );

69
static vlc_list_t   * NewList       ( int );
gbazin's avatar
   
gbazin committed
70
71
static void           ListReplace   ( vlc_list_t *, vlc_object_t *, int );
static void           ListAppend    ( vlc_list_t *, vlc_object_t * );
gbazin's avatar
   
gbazin committed
72
73
static int            CountChildren ( vlc_object_t *, int );
static void           ListChildren  ( vlc_list_t *, vlc_object_t *, int );
74

75
76
77
78
79
/*****************************************************************************
 * Local structure lock
 *****************************************************************************/
static vlc_mutex_t    structure_lock;

80
81
82
83
84
85
86
/*****************************************************************************
 * vlc_object_create: initialize a vlc object
 *****************************************************************************
 * This function allocates memory for a vlc object and initializes it. If
 * i_type is not a known value such as VLC_OBJECT_ROOT, VLC_OBJECT_VOUT and
 * so on, vlc_object_create will use its value for the object size.
 *****************************************************************************/
zorglub's avatar
zorglub committed
87

zorglub's avatar
zorglub committed
88
/**
zorglub's avatar
zorglub committed
89
90
91
92
93
94
 * Initialize a vlc object
 *
 * This function allocates memory for a vlc object and initializes it. If
 * i_type is not a known value such as VLC_OBJECT_ROOT, VLC_OBJECT_VOUT and
 * so on, vlc_object_create will use its value for the object size.
 */
95
96
97
98
99
100
101
102
103
void * __vlc_object_create( vlc_object_t *p_this, int i_type )
{
    vlc_object_t * p_new;
    char *         psz_type;
    size_t         i_size;

    switch( i_type )
    {
        case VLC_OBJECT_ROOT:
104
            i_size = sizeof(libvlc_t);
105
106
            psz_type = "root";
            break;
107
108
109
110
        case VLC_OBJECT_VLC:
            i_size = sizeof(vlc_t);
            psz_type = "vlc";
            break;
111
112
113
114
115
116
117
118
        case VLC_OBJECT_MODULE:
            i_size = sizeof(module_t);
            psz_type = "module";
            break;
        case VLC_OBJECT_INTF:
            i_size = sizeof(intf_thread_t);
            psz_type = "interface";
            break;
gbazin's avatar
   
gbazin committed
119
120
121
122
        case VLC_OBJECT_DIALOGS:
            i_size = sizeof(intf_thread_t);
            psz_type = "dialogs provider";
            break;
123
124
125
126
127
128
129
130
        case VLC_OBJECT_PLAYLIST:
            i_size = sizeof(playlist_t);
            psz_type = "playlist";
            break;
        case VLC_OBJECT_INPUT:
            i_size = sizeof(input_thread_t);
            psz_type = "input";
            break;
131
132
133
134
        case VLC_OBJECT_DEMUX:
            i_size = sizeof(demux_t);
            psz_type = "demux";
            break;
135
136
137
138
        case VLC_OBJECT_ACCESS:
            i_size = sizeof(access_t);
            psz_type = "access";
            break;
139
        case VLC_OBJECT_DECODER:
gbazin's avatar
   
gbazin committed
140
141
142
            i_size = sizeof(decoder_t);
            psz_type = "decoder";
            break;
gbazin's avatar
   
gbazin committed
143
144
145
146
147
148
149
150
        case VLC_OBJECT_PACKETIZER:
            i_size = sizeof(decoder_t);
            psz_type = "packetizer";
            break;
        case VLC_OBJECT_ENCODER:
            i_size = sizeof(encoder_t);
            psz_type = "encoder";
            break;
151
152
153
154
155
        case VLC_OBJECT_VOUT:
            i_size = sizeof(vout_thread_t);
            psz_type = "video output";
            break;
        case VLC_OBJECT_AOUT:
156
            i_size = sizeof(aout_instance_t);
157
158
            psz_type = "audio output";
            break;
159
160
161
162
        case VLC_OBJECT_SOUT:
            i_size = sizeof(sout_instance_t);
            psz_type = "stream output";
            break;
163
164
165
166
        case VLC_OBJECT_HTTPD:
            i_size = sizeof( httpd_t );
            psz_type = "http daemon";
            break;
167
168
169
170
        case VLC_OBJECT_VLM:
            i_size = sizeof( vlm_t );
            psz_type = "vlm dameon";
            break;
zorglub's avatar
zorglub committed
171
172
173
174
        case VLC_OBJECT_ANNOUNCE:
            i_size = sizeof( announce_handler_t );
            psz_type = "announce handler";
            break;
175
        default:
176
            i_size = i_type > 0
177
178
179
                      ? i_type > (int)sizeof(vlc_object_t)
                         ? i_type : (int)sizeof(vlc_object_t)
                      : (int)sizeof(vlc_object_t);
180
181
            i_type = VLC_OBJECT_GENERIC;
            psz_type = "generic";
182
183
184
            break;
    }

185
    if( i_type == VLC_OBJECT_ROOT )
186
    {
187
        p_new = p_this;
188
    }
189
190
191
    else
    {
        p_new = malloc( i_size );
192

193
194
195
196
197
198
199
        if( !p_new )
        {
            return NULL;
        }

        memset( p_new, 0, i_size );
    }
200
201
202
203
204
205

    p_new->i_object_type = i_type;
    p_new->psz_object_type = psz_type;

    p_new->psz_object_name = NULL;

206
207
208
209
    p_new->b_die = VLC_FALSE;
    p_new->b_error = VLC_FALSE;
    p_new->b_dead = VLC_FALSE;
    p_new->b_attached = VLC_FALSE;
210

Sam Hocevar's avatar
Sam Hocevar committed
211
212
213
214
215
216
217
218
219
    p_new->i_vars = 0;
    p_new->p_vars = (variable_t *)malloc( 16 * sizeof( variable_t ) );

    if( !p_new->p_vars )
    {
        free( p_new );
        return NULL;
    }

220
221
    if( i_type == VLC_OBJECT_ROOT )
    {
222
        /* If i_type is root, then p_new is actually p_libvlc */
223
224
        p_new->p_libvlc = (libvlc_t*)p_new;
        p_new->p_vlc = NULL;
225

226
        p_new->p_libvlc->i_counter = 0;
227
        p_new->i_object_id = 0;
228

229
230
231
        p_new->p_libvlc->i_objects = 1;
        p_new->p_libvlc->pp_objects = malloc( sizeof(vlc_object_t *) );
        p_new->p_libvlc->pp_objects[0] = p_new;
232
        p_new->b_attached = VLC_TRUE;
233
234
235
    }
    else
    {
236
237
238
        p_new->p_libvlc = p_this->p_libvlc;
        p_new->p_vlc = ( i_type == VLC_OBJECT_VLC ) ? (vlc_t*)p_new
                                                    : p_this->p_vlc;
239

240
        vlc_mutex_lock( &structure_lock );
241

242
243
        p_new->p_libvlc->i_counter++;
        p_new->i_object_id = p_new->p_libvlc->i_counter;
244
245
246

        /* Wooohaa! If *this* fails, we're in serious trouble! Anyway it's
         * useless to try and recover anything if pp_objects gets smashed. */
247
248
249
250
        INSERT_ELEM( p_new->p_libvlc->pp_objects,
                     p_new->p_libvlc->i_objects,
                     p_new->p_libvlc->i_objects,
                     p_new );
251

252
        vlc_mutex_unlock( &structure_lock );
253
254
    }

Sam Hocevar's avatar
Sam Hocevar committed
255
    p_new->i_refcount = 0;
256
    p_new->p_parent = NULL;
257
258
259
    p_new->pp_children = NULL;
    p_new->i_children = 0;

260
261
    p_new->p_private = NULL;

262
    /* Initialize mutexes and condvars */
Sam Hocevar's avatar
Sam Hocevar committed
263
    vlc_mutex_init( p_new, &p_new->object_lock );
264
    vlc_cond_init( p_new, &p_new->object_wait );
Sam Hocevar's avatar
Sam Hocevar committed
265
    vlc_mutex_init( p_new, &p_new->var_lock );
Sam Hocevar's avatar
Sam Hocevar committed
266

267
268
269
    if( i_type == VLC_OBJECT_ROOT )
    {
        vlc_mutex_init( p_new, &structure_lock );
270

gbazin's avatar
   
gbazin committed
271
272
273
274
        var_Create( p_new, "list", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
        var_AddCallback( p_new, "list", DumpCommand, NULL );
        var_Create( p_new, "tree", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
        var_AddCallback( p_new, "tree", DumpCommand, NULL );
275
276
    }

277
278
279
    return p_new;
}

zorglub's avatar
zorglub committed
280
281
282
/**
 ****************************************************************************
 * Destroy a vlc object
zorglub's avatar
zorglub committed
283
 *
284
285
286
287
 * This function destroys an object that has been previously allocated with
 * vlc_object_create. The object's refcount must be zero and it must not be
 * attached to other objects in any way.
 *****************************************************************************/
288
289
void __vlc_object_destroy( vlc_object_t *p_this )
{
290
    int i_delay = 0;
291
292
293

    if( p_this->i_children )
    {
gbazin's avatar
   
gbazin committed
294
295
        msg_Err( p_this, "cannot delete object (%i, %s) with children" ,
                 p_this->i_object_id, p_this->psz_object_name );
296
        return;
297
298
    }

299
    if( p_this->p_parent )
300
    {
gbazin's avatar
   
gbazin committed
301
302
        msg_Err( p_this, "cannot delete object (%i, %s) with a parent",
                 p_this->i_object_id, p_this->psz_object_name );
303
304
305
306
307
        return;
    }

    while( p_this->i_refcount )
    {
308
309
310
311
        i_delay++;

        /* Don't warn immediately ... 100ms seems OK */
        if( i_delay == 2 )
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
        {
            msg_Warn( p_this, "refcount is %i, delaying before deletion",
                              p_this->i_refcount );
        }
        else if( i_delay == 12 )
        {
            msg_Err( p_this, "refcount is %i, I have a bad feeling about this",
                             p_this->i_refcount );
        }
        else if( i_delay == 42 )
        {
            msg_Err( p_this, "we waited too long, cancelling destruction" );
            return;
        }

        msleep( 100000 );
328
329
    }

Sam Hocevar's avatar
Sam Hocevar committed
330
331
332
333
334
335
336
337
338
339
    /* Destroy the associated variables, starting from the end so that
     * no memmove calls have to be done. */
    while( p_this->i_vars )
    {
        var_Destroy( p_this, p_this->p_vars[p_this->i_vars - 1].psz_name );
    }

    free( p_this->p_vars );
    vlc_mutex_destroy( &p_this->var_lock );

340
341
342
343
344
    if( p_this->i_object_type == VLC_OBJECT_ROOT )
    {
        /* We are the root object ... no need to lock. */
        free( p_this->p_libvlc->pp_objects );
        p_this->p_libvlc->pp_objects = NULL;
345
        p_this->p_libvlc->i_objects--;
346
347
348
349

        vlc_mutex_destroy( &structure_lock );
    }
    else
350
    {
gbazin's avatar
   
gbazin committed
351
352
        int i_index;

353
        vlc_mutex_lock( &structure_lock );
gbazin's avatar
   
gbazin committed
354

355
356
        /* Wooohaa! If *this* fails, we're in serious trouble! Anyway it's
         * useless to try and recover anything if pp_objects gets smashed. */
357
358
        i_index = FindIndex( p_this, p_this->p_libvlc->pp_objects,
                             p_this->p_libvlc->i_objects );
359
360
        REMOVE_ELEM( p_this->p_libvlc->pp_objects,
                     p_this->p_libvlc->i_objects, i_index );
gbazin's avatar
   
gbazin committed
361

362
        vlc_mutex_unlock( &structure_lock );
363
364
    }

Sam Hocevar's avatar
Sam Hocevar committed
365
366
367
    vlc_mutex_destroy( &p_this->object_lock );
    vlc_cond_destroy( &p_this->object_wait );

368
369
370
    free( p_this );
}

zorglub's avatar
zorglub committed
371
372
373
/**
 * find an object given its ID
 *
Sam Hocevar's avatar
Sam Hocevar committed
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
 * This function looks for the object whose i_object_id field is i_id. We
 * use a dichotomy so that lookups are in log2(n).
 *****************************************************************************/
void * __vlc_object_get( vlc_object_t *p_this, int i_id )
{
    int i_max, i_middle;
    vlc_object_t **pp_objects;

    vlc_mutex_lock( &structure_lock );

    pp_objects = p_this->p_libvlc->pp_objects;

    /* Perform our dichotomy */
    for( i_max = p_this->p_libvlc->i_objects - 1 ; ; )
    {
        i_middle = i_max / 2;

        if( pp_objects[i_middle]->i_object_id > i_id )
        {
            i_max = i_middle;
        }
        else if( pp_objects[i_middle]->i_object_id < i_id )
        {
            if( i_middle )
            {
                pp_objects += i_middle;
                i_max -= i_middle;
            }
            else
            {
                /* This happens when there are only two remaining objects */
                if( pp_objects[i_middle+1]->i_object_id == i_id )
                {
                    vlc_mutex_unlock( &structure_lock );
gbazin's avatar
   
gbazin committed
408
                    pp_objects[i_middle+1]->i_refcount++;
Sam Hocevar's avatar
Sam Hocevar committed
409
410
411
412
413
414
415
416
                    return pp_objects[i_middle+1];
                }
                break;
            }
        }
        else
        {
            vlc_mutex_unlock( &structure_lock );
gbazin's avatar
   
gbazin committed
417
            pp_objects[i_middle]->i_refcount++;
Sam Hocevar's avatar
Sam Hocevar committed
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
            return pp_objects[i_middle];
        }

        if( i_max == 0 )
        {
            /* this means that i_max == i_middle, and since we have already
             * tested pp_objects[i_middle]), p_found is properly set. */
            break;
        }
    }

    vlc_mutex_unlock( &structure_lock );
    return NULL;
}

zorglub's avatar
zorglub committed
433
434
435
/**
 ****************************************************************************
 * find a typed object and increment its refcount
436
437
438
439
 *****************************************************************************
 * This function recursively looks for a given object type. i_mode can be one
 * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
 *****************************************************************************/
440
441
442
443
void * __vlc_object_find( vlc_object_t *p_this, int i_type, int i_mode )
{
    vlc_object_t *p_found;

444
    vlc_mutex_lock( &structure_lock );
445
446
447
448
449

    /* If we are of the requested type ourselves, don't look further */
    if( !(i_mode & FIND_STRICT) && p_this->i_object_type == i_type )
    {
        p_this->i_refcount++;
450
        vlc_mutex_unlock( &structure_lock );
451
452
453
454
        return p_this;
    }

    /* Otherwise, recursively look for the object */
455
456
    if( (i_mode & 0x000f) == FIND_ANYWHERE )
    {
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
        vlc_object_t *p_root = p_this;

        /* Find the root */
        while( p_root->p_parent != NULL &&
               p_root != VLC_OBJECT( p_this->p_vlc ) )
        {
            p_root = p_root->p_parent;
        }

        p_found = FindObject( p_root, i_type, (i_mode & ~0x000f)|FIND_CHILD );
        if( p_found == NULL && p_root != VLC_OBJECT( p_this->p_vlc ) )
        {
            p_found = FindObject( VLC_OBJECT( p_this->p_vlc ),
                                  i_type, (i_mode & ~0x000f)|FIND_CHILD );
        }
472
473
474
    }
    else
    {
475
        p_found = FindObject( p_this, i_type, i_mode );
476
    }
477

478
    vlc_mutex_unlock( &structure_lock );
479
480
481
482

    return p_found;
}

zorglub's avatar
zorglub committed
483
484
485
/**
 ****************************************************************************
 * increment an object refcount
486
 *****************************************************************************/
487
488
void __vlc_object_yield( vlc_object_t *p_this )
{
489
    vlc_mutex_lock( &structure_lock );
490
    p_this->i_refcount++;
491
    vlc_mutex_unlock( &structure_lock );
492
493
}

zorglub's avatar
zorglub committed
494
495
496
/**
 ****************************************************************************
 * decrement an object refcount
497
 *****************************************************************************/
498
499
void __vlc_object_release( vlc_object_t *p_this )
{
500
    vlc_mutex_lock( &structure_lock );
501
    p_this->i_refcount--;
502
    vlc_mutex_unlock( &structure_lock );
503
504
}

zorglub's avatar
zorglub committed
505
506
507
/**
 ****************************************************************************
 * attach object to a parent object
508
509
510
511
512
513
 *****************************************************************************
 * This function sets p_this as a child of p_parent, and p_parent as a parent
 * of p_this. This link can be undone using vlc_object_detach.
 *****************************************************************************/
void __vlc_object_attach( vlc_object_t *p_this, vlc_object_t *p_parent )
{
514
    vlc_mutex_lock( &structure_lock );
515

516
517
    /* Attach the parent to its child */
    p_this->p_parent = p_parent;
518

519
    /* Attach the child to its parent */
520
521
    INSERT_ELEM( p_parent->pp_children, p_parent->i_children,
                 p_parent->i_children, p_this );
522

523
524
525
    /* Climb up the tree to see whether we are connected with the root */
    if( p_parent->b_attached )
    {
526
        SetAttachment( p_this, VLC_TRUE );
527
    }
528

529
    vlc_mutex_unlock( &structure_lock );
530
531
}

zorglub's avatar
zorglub committed
532
533
534
/**
 ****************************************************************************
 * detach object from its parent
535
 *****************************************************************************
536
 * This function removes all links between an object and its parent.
537
 *****************************************************************************/
538
void __vlc_object_detach( vlc_object_t *p_this )
539
{
540
    vlc_mutex_lock( &structure_lock );
541
    if( !p_this->p_parent )
542
    {
543
        msg_Err( p_this, "object is not attached" );
544
        vlc_mutex_unlock( &structure_lock );
545
        return;
546
547
    }

548
549
550
    /* Climb up the tree to see whether we are connected with the root */
    if( p_this->p_parent->b_attached )
    {
551
552
553
554
        SetAttachment( p_this, VLC_FALSE );
    }

    DetachObject( p_this );
555
    vlc_mutex_unlock( &structure_lock );
556
557
}

zorglub's avatar
zorglub committed
558
559
560
/**
 ****************************************************************************
 * find a list typed objects and increment their refcount
561
562
563
564
 *****************************************************************************
 * This function recursively looks for a given object type. i_mode can be one
 * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
 *****************************************************************************/
565
vlc_list_t * __vlc_list_find( vlc_object_t *p_this, int i_type, int i_mode )
566
{
567
    vlc_list_t *p_list;
gbazin's avatar
   
gbazin committed
568
569
    vlc_object_t **pp_current, **pp_end;
    int i_count = 0, i_index = 0;
570

571
    vlc_mutex_lock( &structure_lock );
572
573

    /* Look for the objects */
gbazin's avatar
   
gbazin committed
574
    switch( i_mode & 0x000f )
575
    {
gbazin's avatar
   
gbazin committed
576
    case FIND_ANYWHERE:
577
578
        pp_current = p_this->p_libvlc->pp_objects;
        pp_end = pp_current + p_this->p_libvlc->i_objects;
579
580
581
582
583
584

        for( ; pp_current < pp_end ; pp_current++ )
        {
            if( (*pp_current)->b_attached
                 && (*pp_current)->i_object_type == i_type )
            {
gbazin's avatar
   
gbazin committed
585
586
587
588
                i_count++;
            }
        }

589
        p_list = NewList( i_count );
gbazin's avatar
   
gbazin committed
590
591
592
593
594
595
596
        pp_current = p_this->p_libvlc->pp_objects;

        for( ; pp_current < pp_end ; pp_current++ )
        {
            if( (*pp_current)->b_attached
                 && (*pp_current)->i_object_type == i_type )
            {
597
                ListReplace( p_list, *pp_current, i_index );
gbazin's avatar
   
gbazin committed
598
                if( i_index < i_count ) i_index++;
599
600
            }
        }
gbazin's avatar
   
gbazin committed
601
602
603
604
    break;

    case FIND_CHILD:
        i_count = CountChildren( p_this, i_type );
605
        p_list = NewList( i_count );
gbazin's avatar
   
gbazin committed
606
607

        /* Check allocation was successful */
608
        if( p_list->i_count != i_count )
gbazin's avatar
   
gbazin committed
609
610
        {
            msg_Err( p_this, "list allocation failed!" );
611
            p_list->i_count = 0;
gbazin's avatar
   
gbazin committed
612
613
614
            break;
        }

615
616
        p_list->i_count = 0;
        ListChildren( p_list, p_this, i_type );
gbazin's avatar
   
gbazin committed
617
618
619
        break;

    default:
620
        msg_Err( p_this, "unimplemented!" );
621
        p_list = NewList( 0 );
gbazin's avatar
   
gbazin committed
622
        break;
623
624
    }

625
    vlc_mutex_unlock( &structure_lock );
626

627
    return p_list;
628
629
630
}

/*****************************************************************************
631
 * DumpCommand: print the current vlc structure
632
 *****************************************************************************
633
634
635
 * This function prints either an ASCII tree showing the connections between
 * vlc objects, and additional information such as their refcount, thread ID,
 * etc. (command "tree"), or the same data as a simple list (command "list").
636
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
637
638
static int DumpCommand( vlc_object_t *p_this, char const *psz_cmd,
                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
639
{
640
    if( *psz_cmd == 't' )
641
    {
642
643
644
        char psz_foo[2 * MAX_DUMPSTRUCTURE_DEPTH + 1];
        vlc_object_t *p_object;

gbazin's avatar
   
gbazin committed
645
        if( *newval.psz_string )
646
        {
gbazin's avatar
   
gbazin committed
647
            p_object = vlc_object_get( p_this, atoi(newval.psz_string) );
648
649
650
651
652

            if( !p_object )
            {
                return VLC_ENOOBJ;
            }
653
654
655
        }
        else
        {
656
            p_object = p_this->p_vlc ? VLC_OBJECT(p_this->p_vlc) : p_this;
657
        }
658

659
660
        vlc_mutex_lock( &structure_lock );

661
662
        psz_foo[0] = '|';
        DumpStructure( p_object, 0, psz_foo );
663
664

        vlc_mutex_unlock( &structure_lock );
gbazin's avatar
   
gbazin committed
665
666
667
668

        if( *newval.psz_string )
        {
            vlc_object_release( p_this );
669
        }
670
    }
671
672
673
    else if( *psz_cmd == 'l' )
    {
        vlc_object_t **pp_current, **pp_end;
674

675
676
        vlc_mutex_lock( &structure_lock );

677
678
        pp_current = p_this->p_libvlc->pp_objects;
        pp_end = pp_current + p_this->p_libvlc->i_objects;
679

680
681
682
683
684
685
686
687
688
689
690
691
692
        for( ; pp_current < pp_end ; pp_current++ )
        {
            if( (*pp_current)->b_attached )
            {
                PrintObject( *pp_current, "" );
            }
            else
            {
                printf( " o %.8i %s (not attached)\n",
                        (*pp_current)->i_object_id,
                        (*pp_current)->psz_object_type );
            }
        }
693

694
695
        vlc_mutex_unlock( &structure_lock );
    }
696
697

    return VLC_SUCCESS;
698
699
700
701
702
703
704
705
}

/*****************************************************************************
 * vlc_list_release: free a list previously allocated by vlc_list_find
 *****************************************************************************
 * This function decreases the refcount of all objects in the list and
 * frees the list.
 *****************************************************************************/
706
void vlc_list_release( vlc_list_t *p_list )
707
{
gbazin's avatar
   
gbazin committed
708
    int i_index;
709

gbazin's avatar
   
gbazin committed
710
711
    for( i_index = 0; i_index < p_list->i_count; i_index++ )
    {
712
        vlc_mutex_lock( &structure_lock );
713

gbazin's avatar
   
gbazin committed
714
        p_list->p_values[i_index].p_object->i_refcount--;
715

716
        vlc_mutex_unlock( &structure_lock );
717
    }
718

gbazin's avatar
   
gbazin committed
719
    free( p_list->p_values );
720
    free( p_list );
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
}

/* Following functions are local */

/*****************************************************************************
 * FindIndex: find the index of an object in an array of objects
 *****************************************************************************
 * This function assumes that p_this can be found in pp_objects. It will not
 * crash if p_this cannot be found, but will return a wrong value. It is your
 * duty to check the return value if you are not certain that the object could
 * be found for sure.
 *****************************************************************************/
static int FindIndex( vlc_object_t *p_this,
                      vlc_object_t **pp_objects, int i_count )
{
    int i_middle = i_count / 2;

    if( i_count == 0 )
    {
        return 0;
    }

    if( pp_objects[i_middle] == p_this )
    {
        return i_middle;
    }

    if( i_count == 1 )
    {
        return 0;
    }

    /* We take advantage of the sorted array */
    if( pp_objects[i_middle]->i_object_id < p_this->i_object_id )
    {
        return i_middle + FindIndex( p_this, pp_objects + i_middle,
                                             i_count - i_middle );
    }
    else
    {
        return FindIndex( p_this, pp_objects, i_middle );
    }
}

static vlc_object_t * FindObject( vlc_object_t *p_this, int i_type, int i_mode )
{
    int i;
    vlc_object_t *p_tmp;

    switch( i_mode & 0x000f )
    {
    case FIND_PARENT:
        p_tmp = p_this->p_parent;
        if( p_tmp )
        {
            if( p_tmp->i_object_type == i_type )
            {
                p_tmp->i_refcount++;
                return p_tmp;
            }
            else
            {
                return FindObject( p_tmp, i_type, i_mode );
            }
        }
        break;

    case FIND_CHILD:
        for( i = p_this->i_children; i--; )
        {
            p_tmp = p_this->pp_children[i];
            if( p_tmp->i_object_type == i_type )
            {
                p_tmp->i_refcount++;
                return p_tmp;
            }
            else if( p_tmp->i_children )
            {
                p_tmp = FindObject( p_tmp, i_type, i_mode );
                if( p_tmp )
                {
                    return p_tmp;
                }
            }
        }
        break;

    case FIND_ANYWHERE:
        /* Handled in vlc_object_find */
        break;
    }

    return NULL;
}

static void DetachObject( vlc_object_t *p_this )
817
{
818
    vlc_object_t *p_parent = p_this->p_parent;
819
820
    int i_index, i;

821
822
    /* Remove p_this's parent */
    p_this->p_parent = NULL;
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848

    /* Remove all of p_parent's children which are p_this */
    for( i_index = p_parent->i_children ; i_index-- ; )
    {
        if( p_parent->pp_children[i_index] == p_this )
        {
            p_parent->i_children--;
            for( i = i_index ; i < p_parent->i_children ; i++ )
            {
                p_parent->pp_children[i] = p_parent->pp_children[i+1];
            }
        }
    }

    if( p_parent->i_children )
    {
        p_parent->pp_children = (vlc_object_t **)realloc( p_parent->pp_children,
                               p_parent->i_children * sizeof(vlc_object_t *) );
    }
    else
    {
        free( p_parent->pp_children );
        p_parent->pp_children = NULL;
    }
}

849
/*****************************************************************************
850
 * SetAttachment: recursively set the b_attached flag of a subtree.
851
852
853
854
 *****************************************************************************
 * This function is used by the attach and detach functions to propagate
 * the b_attached flag in a subtree.
 *****************************************************************************/
855
static void SetAttachment( vlc_object_t *p_this, vlc_bool_t b_attached )
856
857
858
859
860
{
    int i_index;

    for( i_index = p_this->i_children ; i_index-- ; )
    {
861
        SetAttachment( p_this->pp_children[i_index], b_attached );
862
863
864
865
866
    }

    p_this->b_attached = b_attached;
}

867
static void PrintObject( vlc_object_t *p_this, const char *psz_prefix )
868
{
869
    char psz_children[20], psz_refcount[20], psz_thread[20], psz_name[50];
870

871
872
873
874
875
876
877
    psz_name[0] = '\0';
    if( p_this->psz_object_name )
    {
        snprintf( psz_name, 50, " \"%s\"", p_this->psz_object_name );
        psz_name[48] = '\"';
        psz_name[49] = '\0';
    }
878

879
880
881
882
883
884
885
886
887
    psz_children[0] = '\0';
    switch( p_this->i_children )
    {
        case 0:
            break;
        case 1:
            strcpy( psz_children, ", 1 child" );
            break;
        default:
888
889
            snprintf( psz_children, 20,
                      ", %i children", p_this->i_children );
890
891
892
            psz_children[19] = '\0';
            break;
    }
893

894
895
896
897
898
899
900
901
902
903
    psz_refcount[0] = '\0';
    if( p_this->i_refcount )
    {
        snprintf( psz_refcount, 20, ", refcount %i", p_this->i_refcount );
        psz_refcount[19] = '\0';
    }

    psz_thread[0] = '\0';
    if( p_this->b_thread )
    {
gbazin's avatar
   
gbazin committed
904
        snprintf( psz_thread, 20, " (thread %d)", (int)p_this->thread_id );
905
906
907
        psz_thread[19] = '\0';
    }

908
    printf( " %so %.8i %s%s%s%s%s\n", psz_prefix,
909
910
            p_this->i_object_id, p_this->psz_object_type,
            psz_name, psz_thread, psz_refcount, psz_children );
911
912
}

913
static void DumpStructure( vlc_object_t *p_this, int i_level, char *psz_foo )
914
915
916
{
    int i;
    char i_back = psz_foo[i_level];
917
    psz_foo[i_level] = '\0';
918

919
    PrintObject( p_this, psz_foo );
920

921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
    psz_foo[i_level] = i_back;

    if( i_level / 2 >= MAX_DUMPSTRUCTURE_DEPTH )
    {
        msg_Warn( p_this, "structure tree is too deep" );
        return;
    }

    for( i = 0 ; i < p_this->i_children ; i++ )
    {
        if( i_level )
        {
            psz_foo[i_level-1] = ' ';

            if( psz_foo[i_level-2] == '`' )
            {
                psz_foo[i_level-2] = ' ';
            }
        }

        if( i == p_this->i_children - 1 )
        {
            psz_foo[i_level] = '`';
        }
        else
        {
            psz_foo[i_level] = '|';
        }

        psz_foo[i_level+1] = '-';
        psz_foo[i_level+2] = '\0';

953
954
955
956
        DumpStructure( p_this->pp_children[i], i_level + 2, psz_foo );
    }
}

957
static vlc_list_t * NewList( int i_count )
958
{
959
960
961
962
963
    vlc_list_t * p_list = (vlc_list_t *)malloc( sizeof( vlc_list_t ) );
    if( p_list == NULL )
    {
        return NULL;
    }
964

965
    p_list->i_count = i_count;
gbazin's avatar
   
gbazin committed
966
967

    if( i_count == 0 )
968
    {
969
970
        p_list->p_values = NULL;
        return p_list;
971
972
    }

973
974
    p_list->p_values = malloc( i_count * sizeof( vlc_value_t ) );
    if( p_list->p_values == NULL )
gbazin's avatar
   
gbazin committed
975
    {
976
977
        p_list->i_count = 0;
        return p_list;
gbazin's avatar
   
gbazin committed
978
979
    }

980
    return p_list;
gbazin's avatar
   
gbazin committed
981
982
983
984
985
986
987
988
989
}

static void ListReplace( vlc_list_t *p_list, vlc_object_t *p_object,
                         int i_index )
{
    if( p_list == NULL || i_index >= p_list->i_count )
    {
        return;
    }
990

gbazin's avatar
   
gbazin committed
991
992
993
    p_object->i_refcount++;

    p_list->p_values[i_index].p_object = p_object;
994

gbazin's avatar
   
gbazin committed
995
    return;
996
997
}

gbazin's avatar
   
gbazin committed
998
static void ListAppend( vlc_list_t *p_list, vlc_object_t *p_object )
999
1000
1001
{
    if( p_list == NULL )
    {
gbazin's avatar
   
gbazin committed
1002
        return;
1003
    }
1004

gbazin's avatar
   
gbazin committed
1005
1006
1007
    p_list->p_values = realloc( p_list->p_values, (p_list->i_count + 1)
                                * sizeof( vlc_value_t ) );
    if( p_list->p_values == NULL )
1008
    {
gbazin's avatar
   
gbazin committed
1009
1010
        p_list->i_count = 0;
        return;
1011
1012
1013
1014
    }

    p_object->i_refcount++;

gbazin's avatar
   
gbazin committed
1015
    p_list->p_values[p_list->i_count].p_object = p_object;
1016
1017
    p_list->i_count++;

gbazin's avatar
   
gbazin committed
1018
    return;
1019
}
gbazin's avatar
   
gbazin committed
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063

static int CountChildren( vlc_object_t *p_this, int i_type )
{
    vlc_object_t *p_tmp;
    int i, i_count = 0;

    for( i = 0; i < p_this->i_children; i++ )
    {
        p_tmp = p_this->pp_children[i];

        if( p_tmp->i_object_type == i_type )
        {
            i_count++;
        }

        if( p_tmp->i_children )
        {
            i_count += CountChildren( p_tmp, i_type );
        }
    }

    return i_count;
}

static void ListChildren( vlc_list_t *p_list, vlc_object_t *p_this, int i_type )
{
    vlc_object_t *p_tmp;
    int i;

    for( i = 0; i < p_this->i_children; i++ )
    {
        p_tmp = p_this->pp_children[i];

        if( p_tmp->i_object_type == i_type )
        {
            ListReplace( p_list, p_tmp, p_list->i_count++ );
        }

        if( p_tmp->i_children )
        {
            ListChildren( p_list, p_tmp, i_type );
        }
    }
}