Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

daap.c

00001 /*****************************************************************************
00002  * daap.c :  Apple DAAP discovery module
00003  *****************************************************************************
00004  * Copyright (C) 2004 the VideoLAN team
00005  * $Id: sap.c 9569 2004-12-15 22:17:52Z zorglub $
00006  *
00007  * Authors: Clément Stenac <[email protected]>
00008  *
00009  * This program is free software; you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation; either version 2 of the License, or
00012  * (at your option) any later version.
00013  *
00014  * This program is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  * GNU General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00022  *****************************************************************************/
00023 
00024 /*****************************************************************************
00025  * Includes
00026  *****************************************************************************/
00027 #include <stdlib.h>                                      /* malloc(), free() */
00028 
00029 #include <vlc/vlc.h>
00030 #include <vlc/intf.h>
00031 
00032 #include "network.h"
00033 
00034 #include <vlc/input.h>
00035 
00036 #include <daap/client.h>
00037 
00038 /************************************************************************
00039  * Macros and definitions
00040  ************************************************************************/
00041 
00042 /*****************************************************************************
00043  * Module descriptor
00044  *****************************************************************************/
00045 
00046 /* Callbacks */
00047     static int  Open ( vlc_object_t * );
00048     static void Close( vlc_object_t * );
00049     static int  OpenAccess ( vlc_object_t * );
00050     static void CloseAccess( vlc_object_t * );
00051 
00052 vlc_module_begin();
00053     set_description( _("DAAP shares") );
00054     set_category( CAT_PLAYLIST );
00055     set_subcategory( SUBCAT_PLAYLIST_SD );
00056 
00057     set_capability( "services_discovery", 0 );
00058     set_callbacks( Open, Close );
00059 
00060     add_submodule();
00061         set_description( _( "DAAP access") );
00062         set_capability( "access2", 0 );
00063         set_callbacks( OpenAccess, CloseAccess );
00064 vlc_module_end();
00065 
00066 
00067 /*****************************************************************************
00068  * Local structures
00069  *****************************************************************************/
00070 
00071 typedef struct dhost_s {
00072     char *psz_name;
00073     int i_id;
00074 
00075     DAAP_SClientHost *p_host;
00076     vlc_bool_t b_updated;
00077     vlc_bool_t b_new;
00078     int i_database_id;
00079 
00080     playlist_item_t *p_node;
00081 
00082     DAAP_ClientHost_DatabaseItem *p_songs;
00083     int i_songs;
00084 } dhost_t;
00085 
00086 typedef struct daap_db_s {
00087     dhost_t **pp_hosts;
00088     int       i_hosts;
00089 
00090     int i_last_id;
00091 
00092     vlc_mutex_t search_lock;
00093 } daap_db_t;
00094 
00095 struct services_discovery_sys_t {
00096     playlist_item_t *p_node;
00097 
00098     DAAP_SClient *p_client;
00099     DAAP_SClientHost *p_host;
00100 
00101     daap_db_t *p_db;
00102 };
00103 
00104 struct access_sys_t {
00105     vlc_url_t url;
00106 
00107     dhost_t *p_host;
00108     int i_host;
00109     int i_song;
00110 
00111     daap_db_t *p_db;
00112 
00113     DAAP_ClientHost_Song song;
00114     DAAP_ClientHost_DatabaseItem songdata;
00115     int i_orig_size;
00116     void *p_orig_buffer;
00117 };
00118 
00119 /*****************************************************************************
00120  * Local prototypes
00121  *****************************************************************************/
00122 
00123 /* Main functions */
00124     static void Run    ( services_discovery_t *p_sd );
00125     static void Callback( DAAP_SClient *p_client, DAAP_Status status,
00126                           int i_pos, void *p_context );
00127     static int EnumerateCallback( DAAP_SClient *p_client,
00128                                   DAAP_SClientHost *p_host,
00129                                   void *p_context );
00130     static void OnHostsUpdate( services_discovery_t *p_sd );
00131     static void ProcessHost( services_discovery_t *p_sd, dhost_t *p_host );
00132     static void FreeHost( services_discovery_t *p_sd, dhost_t *p_host );
00133 
00134     static int Control( access_t *p_access, int i_query, va_list args );
00135     static int Read( access_t *, uint8_t *, int );
00136     static int Seek( access_t *, int64_t );
00137 
00138 /*****************************************************************************
00139  * Open: initialize and create stuff
00140  *****************************************************************************/
00141 static int Open( vlc_object_t *p_this )
00142 {
00143     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
00144     services_discovery_sys_t *p_sys  = (services_discovery_sys_t *)
00145                                 malloc( sizeof( services_discovery_sys_t ) );
00146 
00147     playlist_t          *p_playlist;
00148     playlist_view_t     *p_view;
00149     vlc_value_t         val;
00150 
00151     p_sd->pf_run = Run;
00152     p_sd->p_sys  = p_sys;
00153 
00154     p_sys->p_db = (daap_db_t *)malloc( sizeof( daap_db_t ) );
00155     if( !p_sys->p_db )
00156     {
00157         return VLC_EGENERIC;
00158     }
00159     p_sys->p_db->pp_hosts = NULL;
00160     p_sys->p_db->i_hosts = 0;
00161 
00162     var_Create( p_sd->p_vlc, "daap-db", VLC_VAR_ADDRESS );
00163     val.p_address = p_sys->p_db;
00164     var_Set( p_sd->p_vlc, "daap-db", val );
00165 
00166     vlc_mutex_init( p_sd, &p_sys->p_db->search_lock );
00167 
00168     /* Init DAAP */
00169     p_sys->p_client = DAAP_Client_Create( Callback, p_sd );
00170     p_sys->p_db->i_last_id = 0;
00171 
00172     /* TODO: Set debugging correctly */
00173 //    DAAP_Client_SetDebug( p_sys->p_client, "+trace" );
00174 
00175 
00176     /* Create our playlist node */
00177     p_playlist = (playlist_t *)vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
00178                                                 FIND_ANYWHERE );
00179     if( !p_playlist )
00180     {
00181         msg_Warn( p_sd, "unable to find playlist, cancelling DAAP" );
00182         return VLC_EGENERIC;
00183     }
00184 
00185     p_view = playlist_ViewFind( p_playlist, VIEW_CATEGORY );
00186     p_sys->p_node = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
00187                                          _("DAAP shares"), p_view->p_root );
00188     p_sys->p_node->i_flags |= PLAYLIST_RO_FLAG;
00189 
00190     val.b_bool = VLC_TRUE;
00191     var_Set( p_playlist, "intf-change", val );
00192     vlc_object_release( p_playlist );
00193 
00194     return VLC_SUCCESS;
00195 }
00196 
00197 static int OpenAccess( vlc_object_t *p_this )
00198 {
00199     access_t     *p_access = (access_t*)p_this;
00200     access_sys_t *p_sys;
00201     vlc_value_t val;
00202     vlc_bool_t b_found = VLC_FALSE;
00203     int i, i_ret;
00204 
00205     p_access->pf_read = Read;
00206     p_access->pf_block = NULL;
00207     p_access->pf_control = Control;
00208     p_access->pf_seek = Seek;
00209     p_access->info.i_update = 0;
00210     p_access->info.i_size = 0;
00211     p_access->info.i_pos = 0;
00212     p_access->info.b_eof = VLC_FALSE;
00213     p_access->info.i_title = 0;
00214     p_access->info.i_seekpoint = 0;
00215     p_access->p_sys = p_sys = malloc( sizeof( access_sys_t ) );
00216     memset( p_sys, 0, sizeof( access_sys_t ) );
00217 
00218     i_ret = var_Get( p_access->p_vlc , "daap-db", &val );
00219     p_sys->p_db = val.p_address;
00220 
00221     if( p_sys->p_db == NULL || i_ret )
00222     {
00223         msg_Err( p_access, "The DAAP services_discovery module must be enabled" );
00224         return VLC_EGENERIC;
00225     }
00226 
00227     vlc_UrlParse( &p_sys->url, p_access->psz_path, 0 );
00228 
00229     p_sys->p_host = NULL;
00230     p_sys->i_host = atoi( p_sys->url.psz_host ) ;
00231     p_sys->i_song = p_sys->url.i_port;
00232 
00233     if( !p_sys->i_host || !p_sys->i_song )
00234     {
00235         msg_Err( p_access, "invalid host or song" );
00236         return VLC_EGENERIC;
00237     }
00238 
00239     /* Search the host */
00240     vlc_mutex_lock( &p_sys->p_db->search_lock );
00241     for( i = 0 ; i < p_sys->p_db->i_hosts ; i++ )
00242     {
00243         if( p_sys->p_db->pp_hosts[i]->i_id == p_sys->i_host )
00244         {
00245             p_sys->p_host = p_sys->p_db->pp_hosts[i];
00246             break;
00247         }
00248     }
00249     if( p_sys->p_host )
00250     {
00251        for( i = 0 ; i < p_sys->p_host->i_songs ; i++ )
00252        {
00253            if( p_sys->p_host->p_songs[i].id == p_sys->i_song )
00254            {
00255                p_sys->songdata = p_sys->p_host->p_songs[i];
00256                b_found = VLC_TRUE;
00257                break;
00258            }
00259        }
00260        if( !b_found )
00261        {
00262            msg_Err( p_access, "invalid song (not found in %i)",
00263                              p_sys->p_host->i_songs );
00264        }
00265     }
00266     else
00267     {
00268         msg_Warn( p_access, "invalid host (not found in %i)",
00269                              p_sys->p_db->i_hosts );
00270     }
00271     vlc_mutex_unlock( &p_sys->p_db->search_lock );
00272 
00273     if( !p_sys->p_host || !b_found )
00274     {
00275         return VLC_EGENERIC;
00276     }
00277 
00278 
00279     msg_Dbg( p_access, "Downloading %s song %i (db %i)",
00280                            p_sys->songdata.songformat,
00281                            p_sys->i_song, p_sys->p_host->i_database_id );
00282 
00283     /* FIXME: wait for better method by upstream */
00284     i_ret = DAAP_ClientHost_GetAudioFile( p_sys->p_host->p_host,
00285                                           p_sys->p_host->i_database_id,
00286                                           p_sys->i_song,
00287                                           p_sys->songdata.songformat,
00288                                           &(p_sys->song) );
00289 
00290     msg_Dbg( p_access, "Finished downloading, read %i bytes (ret %i)",
00291                                           p_sys->song.size, i_ret );
00292 
00293     p_access->info.i_size = p_sys->song.size;
00294 
00295     if( i_ret != 0 )
00296         return VLC_EGENERIC;
00297 
00298     return VLC_SUCCESS;
00299 }
00300 
00301 /*****************************************************************************
00302  * Close:
00303  *****************************************************************************/
00304 static void Close( vlc_object_t *p_this )
00305 {
00306     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
00307     services_discovery_sys_t    *p_sys  = p_sd->p_sys;
00308 
00309     playlist_t *p_playlist;
00310     int i;
00311 
00312     p_playlist = (playlist_t *) vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
00313                                                  FIND_ANYWHERE );
00314 
00315     for( i = 0 ; i< p_sys->p_db->i_hosts ; i++ )
00316     {
00317         FreeHost( p_sd, p_sys->p_db->pp_hosts[i] );
00318     }
00319 
00320     var_Destroy( p_sd->p_vlc, "daap-db" );
00321 
00322     if( p_playlist )
00323     {
00324         playlist_NodeDelete( p_playlist, p_sys->p_node, VLC_TRUE, VLC_TRUE );
00325         vlc_object_release( p_playlist );
00326     }
00327 
00328     free( p_sys );
00329 }
00330 
00331 static void CloseAccess( vlc_object_t *p_this )
00332 {
00333     access_t *p_access = (access_t*) p_this;
00334     access_sys_t *p_sys = p_access->p_sys;
00335 
00336     if( p_sys )
00337     {
00338         if( p_sys->p_host )
00339         {
00340             p_sys->song.data = p_sys->p_orig_buffer;
00341             p_sys->song.size = p_sys->i_orig_size;
00342             DAAP_ClientHost_FreeAudioFile( p_sys->p_host->p_host, &p_sys->song );
00343         }
00344         free( p_sys );
00345     }
00346 }
00347 
00348 /*****************************************************************************
00349  * Run: main DAAP thread
00350  *****************************************************************************/
00351 static void Run( services_discovery_t *p_sd )
00352 {
00353     while( !p_sd->b_die )
00354     {
00355         msleep( 100000 );
00356     }
00357 }
00358 
00359 /*****************************************************************************
00360  * Access functions
00361  *****************************************************************************/
00362 static int Control( access_t *p_access, int i_query, va_list args )
00363 {
00364     vlc_bool_t *pb_bool;
00365     int64_t *pi_64;
00366     switch( i_query )
00367     {
00368         case ACCESS_CAN_SEEK:
00369         case ACCESS_CAN_FASTSEEK:
00370             pb_bool = (vlc_bool_t *)va_arg( args, vlc_bool_t *);
00371             *pb_bool = VLC_TRUE;
00372             break;
00373         case ACCESS_CAN_PAUSE:
00374         case ACCESS_CAN_CONTROL_PACE:
00375             pb_bool = (vlc_bool_t *)va_arg( args, vlc_bool_t *);
00376             *pb_bool = VLC_TRUE;
00377             break;
00378 
00379         case ACCESS_GET_PTS_DELAY:
00380             pi_64 = (int64_t *)va_arg( args, int64_t *);
00381             *pi_64 = (int64_t)300000;
00382             break;
00383 
00384         case ACCESS_SET_PAUSE_STATE:
00385             break;
00386 
00387         case ACCESS_GET_TITLE_INFO:
00388         case ACCESS_SET_TITLE:
00389         case ACCESS_SET_SEEKPOINT:
00390         case ACCESS_SET_PRIVATE_ID_STATE:
00391             return VLC_EGENERIC;
00392 
00393         default:
00394             msg_Warn( p_access, "unimplemented query control %i", i_query );
00395             return VLC_EGENERIC;
00396     }
00397     return VLC_SUCCESS;
00398 }
00399 
00400 static int Read( access_t *p_access, uint8_t *p_buffer, int i_size )
00401 {
00402     access_sys_t *p_sys = (access_sys_t *)p_access->p_sys;
00403     int i_send;
00404 
00405     if( i_size < p_sys->song.size && p_sys->song.size > 0 )
00406     {
00407         i_send = i_size;
00408     }
00409     else if( p_sys->song.size == 0 )
00410     {
00411         return 0;
00412     }
00413     else
00414     {
00415         i_send = p_sys->song.size;
00416     }
00417 
00418     memcpy( p_buffer, p_sys->song.data, i_send );
00419     p_sys->song.size -= i_send;
00420     p_sys->song.data += i_send;
00421 
00422     return i_send;
00423 }
00424 
00425 static int Seek( access_t *p_access, int64_t i_pos )
00426 {
00427     if( i_pos > p_access->p_sys->i_orig_size )
00428     {
00429         return VLC_EGENERIC;
00430     }
00431     p_access->p_sys->song.size = p_access->p_sys->i_orig_size - i_pos;
00432     p_access->p_sys->song.data = p_access->p_sys->p_orig_buffer + i_pos;
00433     return VLC_SUCCESS;
00434 }
00435 
00436 /**************************************************************
00437  * Local functions
00438  **************************************************************/
00439 static void Callback( DAAP_SClient *p_client, DAAP_Status status,
00440                       int i_pos, void *p_context )
00441 {
00442     services_discovery_t *p_sd = (services_discovery_t *)p_context;
00443 
00444     if( status == DAAP_STATUS_hostschanged )
00445     {
00446         OnHostsUpdate( p_sd );
00447     }
00448     else if( status == DAAP_STATUS_downloading )
00449     {
00450     }
00451 }
00452 
00453 static void OnHostsUpdate( services_discovery_t *p_sd )
00454 {
00455     int i;
00456 
00457     for( i = 0 ; i< p_sd->p_sys->p_db->i_hosts ; i ++ )
00458     {
00459         p_sd->p_sys->p_db->pp_hosts[i]->b_updated = VLC_FALSE;
00460         p_sd->p_sys->p_db->pp_hosts[i]->b_new     = VLC_FALSE;
00461     }
00462 
00463     vlc_mutex_lock( &p_sd->p_sys->p_db->search_lock );
00464     DAAP_Client_EnumerateHosts( p_sd->p_sys->p_client, EnumerateCallback, p_sd);
00465 
00466     for( i = 0 ; i< p_sd->p_sys->p_db->i_hosts ; i ++ )
00467     {
00468         if( p_sd->p_sys->p_db->pp_hosts[i]->b_updated == VLC_FALSE )
00469         {
00470             dhost_t *p_host = p_sd->p_sys->p_db->pp_hosts[i];
00471             FreeHost( p_sd, p_host );
00472             REMOVE_ELEM( p_sd->p_sys->p_db->pp_hosts,
00473                          p_sd->p_sys->p_db->i_hosts, i );
00474         }
00475     }
00476     vlc_mutex_unlock( &p_sd->p_sys->p_db->search_lock );
00477 
00478     for( i = 0 ; i< p_sd->p_sys->p_db->i_hosts ; i ++ )
00479     {
00480         if( p_sd->p_sys->p_db->pp_hosts[i]->b_new )
00481             ProcessHost( p_sd, p_sd->p_sys->p_db->pp_hosts[i] );
00482     }
00483 }
00484 
00485 static int EnumerateCallback( DAAP_SClient *p_client,
00486                               DAAP_SClientHost *p_host,
00487                               void *p_context )
00488 {
00489     int i;
00490     int i_size = DAAP_ClientHost_GetSharename( p_host, NULL, 0 );
00491     vlc_bool_t b_found = VLC_FALSE;
00492     char *psz_buffer = (char *)malloc( i_size );
00493     DAAP_ClientHost_GetSharename( p_host, psz_buffer, i_size );
00494 
00495     services_discovery_t *p_sd = (services_discovery_t *)p_context;
00496     services_discovery_sys_t *p_sys = p_sd->p_sys;
00497 
00498     for( i = 0 ; i< p_sys->p_db->i_hosts; i++ )
00499     {
00500         if( !strcmp( p_sys->p_db->pp_hosts[i]->psz_name, psz_buffer ) )
00501         {
00502             p_sys->p_db->pp_hosts[i]->b_updated = VLC_TRUE;
00503             b_found = VLC_TRUE;
00504             break;
00505         }
00506     }
00507 
00508     if( !b_found )
00509     {
00510         dhost_t *p_vlchost = (dhost_t *)malloc( sizeof( dhost_t ) );
00511         p_vlchost->p_node = NULL;
00512         p_vlchost->p_host = p_host;
00513         p_vlchost->psz_name = psz_buffer;
00514         p_vlchost->b_new = VLC_TRUE;
00515         p_vlchost->b_updated = VLC_TRUE;
00516         INSERT_ELEM( p_sys->p_db->pp_hosts, p_sys->p_db->i_hosts,
00517                      p_sys->p_db->i_hosts, p_vlchost );
00518     }
00519 
00520     return VLC_SUCCESS;
00521 }
00522 
00523 static void ProcessHost( services_discovery_t *p_sd, dhost_t *p_host )
00524 {
00525     int i_dbsize, i_db, i, i_songsize, i_ret;
00526     int i_size = DAAP_ClientHost_GetSharename( p_host->p_host, NULL, 0 );
00527 
00528     playlist_t *p_playlist;
00529 
00530     p_playlist = (playlist_t *) vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
00531                                                        FIND_ANYWHERE );
00532 
00533     if( !p_playlist )
00534     {
00535         return;
00536     }
00537 
00538     /* Connect to host */
00539     if( p_host->b_new )
00540     {
00541         p_host->psz_name = (char *)malloc( i_size );
00542         p_host->b_new = VLC_FALSE;
00543         DAAP_ClientHost_GetSharename( p_host->p_host, p_host->psz_name ,
00544                                       i_size );
00545 
00546         msg_Dbg( p_sd, "new share %s", p_host->psz_name );
00547         DAAP_ClientHost_AddRef( p_host->p_host );
00548         i_ret = DAAP_ClientHost_Connect( p_host->p_host );
00549         if( i_ret )
00550         {
00551             msg_Warn( p_sd, "unable to connect to DAAP host %s",
00552                              p_host->psz_name );
00553 //            DAAP_ClientHost_Release( p_host->p_host );
00554             vlc_object_release( p_playlist );
00555             return;
00556         }
00557 
00558         p_host->p_node = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
00559                                               p_host->psz_name,
00560                                               p_sd->p_sys->p_node );
00561         p_host->i_id = ++p_sd->p_sys->p_db->i_last_id;
00562     }
00563 
00564     /* Get DB */
00565     i_dbsize = DAAP_ClientHost_GetDatabases( p_host->p_host, NULL, NULL, 0 );
00566 
00567     DAAP_ClientHost_Database *p_database = malloc( i_dbsize );
00568     DAAP_ClientHost_GetDatabases( p_host->p_host, p_database, &i_db, i_dbsize );
00569 
00570 
00571     if( !i_db || !p_database )
00572     {
00573         msg_Warn( p_sd, "no database on DAAP host %s", p_host->psz_name );
00574         vlc_object_release( p_playlist );
00575         return;
00576     }
00577 
00578     /* We only use the first database */
00579     p_host->i_database_id = p_database[0].id;
00580 
00581     /* Get songs */
00582     i_songsize = DAAP_ClientHost_GetDatabaseItems( p_host->p_host,
00583                                                    p_host->i_database_id,
00584                                                    NULL, NULL, 0 );
00585     if( !i_songsize )
00586     {
00587         vlc_object_release( p_playlist );
00588         return;
00589     }
00590     p_host->p_songs = malloc( i_songsize );
00591 
00592     DAAP_ClientHost_GetDatabaseItems( p_host->p_host ,
00593                                       p_host->i_database_id,
00594                                       p_host->p_songs,
00595                                       &p_host->i_songs, i_songsize );
00596 
00597     for( i = 0; i< p_host->i_songs; i++ )
00598     {
00599         playlist_item_t *p_item;
00600         int i_len = 7 + 10 + 1 + 10 ;    /* "daap://" + host + ":" + song */
00601         char *psz_buff = (char *)malloc( i_len );
00602 
00603         snprintf( psz_buff, i_len, "daap://%i:%i", p_host->i_id,
00604                                                    p_host->p_songs[i].id );
00605         p_item = playlist_ItemNew( p_sd, psz_buff,
00606                                          p_host->p_songs[i].itemname );
00607         vlc_input_item_AddInfo( &p_item->input, _("Meta-information"),
00608                                 _("Artist"), p_host->p_songs[i].songartist );
00609         vlc_input_item_AddInfo( &p_item->input, _("Meta-information"),
00610                                 _("Album"), p_host->p_songs[i].songalbum );
00611 
00612         playlist_NodeAddItem( p_playlist, p_item, VIEW_CATEGORY,
00613                               p_host->p_node, PLAYLIST_APPEND, PLAYLIST_END );
00614 
00615     }
00616 
00617     DAAP_ClientHost_AsyncWaitUpdate( p_host->p_host );
00618 
00619     vlc_object_release( p_playlist );
00620 }
00621 
00622 static void FreeHost( services_discovery_t *p_sd, dhost_t *p_host )
00623 {
00624     playlist_t *p_playlist;
00625 
00626     if( p_host->p_host )
00627     {
00628         DAAP_ClientHost_Disconnect( p_host->p_host );
00629         DAAP_ClientHost_Release( p_host->p_host );
00630     }
00631 
00632     p_playlist = (playlist_t *) vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
00633                                                  FIND_ANYWHERE );
00634     if( p_playlist )
00635     {
00636         if( p_host->p_node )
00637             playlist_NodeDelete( p_playlist, p_host->p_node, VLC_TRUE ,
00638                                                              VLC_TRUE);
00639         vlc_object_release( p_playlist );
00640     }
00641 
00642     if( p_host->p_songs ) free( p_host->p_songs );
00643 }

Generated on Tue Dec 20 10:14:49 2005 for vlc-0.8.4a by  doxygen 1.4.2