1 /*****************************************************************************
2  * preparser.c
3  *****************************************************************************
4  * Copyright © 2017-2017 VLC authors and VideoLAN
5  * $Id: 661b2122e14a10340eefbb054dabfebd4fc7cc9b $
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published by
9  * the Free Software Foundation; either version 2.1 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this program; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
20  *****************************************************************************/
21 
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25 
26 #include <vlc_common.h>
27 
28 #include "misc/background_worker.h"
29 #include "input/input_interface.h"
30 #include "input/input_internal.h"
31 #include "preparser.h"
32 #include "fetcher.h"
33 
34 struct playlist_preparser_t
35 {
36     vlc_object_t* owner;
37     playlist_fetcher_t* fetcher;
38     struct background_worker* worker;
39     atomic_bool deactivated;
40 };
41 
InputEvent(vlc_object_t * obj,const char * varname,vlc_value_t old,vlc_value_t cur,void * worker)42 static int InputEvent( vlc_object_t* obj, const char* varname,
43     vlc_value_t old, vlc_value_t cur, void* worker )
44 {
45     VLC_UNUSED( obj ); VLC_UNUSED( varname ); VLC_UNUSED( old );
46 
47     if( cur.i_int == INPUT_EVENT_DEAD )
48         background_worker_RequestProbe( worker );
49 
50     return VLC_SUCCESS;
51 }
52 
PreparserOpenInput(void * preparser_,void * item_,void ** out)53 static int PreparserOpenInput( void* preparser_, void* item_, void** out )
54 {
55     playlist_preparser_t* preparser = preparser_;
56 
57     input_thread_t* input = input_CreatePreparser( preparser->owner, item_ );
58     if( !input )
59     {
60         input_item_SignalPreparseEnded( item_, ITEM_PREPARSE_FAILED );
61         return VLC_EGENERIC;
62     }
63 
64     var_AddCallback( input, "intf-event", InputEvent, preparser->worker );
65     if( input_Start( input ) )
66     {
67         var_DelCallback( input, "intf-event", InputEvent, preparser->worker );
68         input_Close( input );
69         input_item_SignalPreparseEnded( item_, ITEM_PREPARSE_FAILED );
70         return VLC_EGENERIC;
71     }
72 
73     *out = input;
74     return VLC_SUCCESS;
75 }
76 
PreparserProbeInput(void * preparser_,void * input_)77 static int PreparserProbeInput( void* preparser_, void* input_ )
78 {
79     int state = input_GetState( input_ );
80     return state == END_S || state == ERROR_S;
81     VLC_UNUSED( preparser_ );
82 }
83 
PreparserCloseInput(void * preparser_,void * input_)84 static void PreparserCloseInput( void* preparser_, void* input_ )
85 {
86     playlist_preparser_t* preparser = preparser_;
87     input_thread_t* input = input_;
88     input_item_t* item = input_priv(input)->p_item;
89 
90     var_DelCallback( input, "intf-event", InputEvent, preparser->worker );
91 
92     int status;
93     switch( input_GetState( input ) )
94     {
95         case END_S:
96             status = ITEM_PREPARSE_DONE;
97             break;
98         case ERROR_S:
99             status = ITEM_PREPARSE_FAILED;
100             break;
101         default:
102             status = ITEM_PREPARSE_TIMEOUT;
103     }
104 
105     input_Stop( input );
106     input_Close( input );
107 
108     if( preparser->fetcher )
109     {
110         if( !playlist_fetcher_Push( preparser->fetcher, item, 0, status ) )
111             return;
112     }
113 
114     input_item_SetPreparsed( item, true );
115     input_item_SignalPreparseEnded( item, status );
116 }
117 
InputItemRelease(void * item)118 static void InputItemRelease( void* item ) { input_item_Release( item ); }
InputItemHold(void * item)119 static void InputItemHold( void* item ) { input_item_Hold( item ); }
120 
playlist_preparser_New(vlc_object_t * parent)121 playlist_preparser_t* playlist_preparser_New( vlc_object_t *parent )
122 {
123     playlist_preparser_t* preparser = malloc( sizeof *preparser );
124 
125     struct background_worker_config conf = {
126         .default_timeout = var_InheritInteger( parent, "preparse-timeout" ),
127         .pf_start = PreparserOpenInput,
128         .pf_probe = PreparserProbeInput,
129         .pf_stop = PreparserCloseInput,
130         .pf_release = InputItemRelease,
131         .pf_hold = InputItemHold };
132 
133 
134     if( likely( preparser ) )
135         preparser->worker = background_worker_New( preparser, &conf );
136 
137     if( unlikely( !preparser || !preparser->worker ) )
138     {
139         free( preparser );
140         return NULL;
141     }
142 
143     preparser->owner = parent;
144     preparser->fetcher = playlist_fetcher_New( parent );
145     atomic_init( &preparser->deactivated, false );
146 
147     if( unlikely( !preparser->fetcher ) )
148         msg_Warn( parent, "unable to create art fetcher" );
149 
150     return preparser;
151 }
152 
playlist_preparser_Push(playlist_preparser_t * preparser,input_item_t * item,input_item_meta_request_option_t i_options,int timeout,void * id)153 void playlist_preparser_Push( playlist_preparser_t *preparser,
154     input_item_t *item, input_item_meta_request_option_t i_options,
155     int timeout, void *id )
156 {
157     if( atomic_load( &preparser->deactivated ) )
158         return;
159 
160     vlc_mutex_lock( &item->lock );
161     int i_type = item->i_type;
162     int b_net = item->b_net;
163     vlc_mutex_unlock( &item->lock );
164 
165     switch( i_type )
166     {
167         case ITEM_TYPE_NODE:
168         case ITEM_TYPE_FILE:
169         case ITEM_TYPE_DIRECTORY:
170         case ITEM_TYPE_PLAYLIST:
171             if( !b_net || i_options & META_REQUEST_OPTION_SCOPE_NETWORK )
172                 break;
173         default:
174             input_item_SignalPreparseEnded( item, ITEM_PREPARSE_SKIPPED );
175             return;
176     }
177 
178     if( background_worker_Push( preparser->worker, item, id, timeout ) )
179         input_item_SignalPreparseEnded( item, ITEM_PREPARSE_FAILED );
180 }
181 
playlist_preparser_fetcher_Push(playlist_preparser_t * preparser,input_item_t * item,input_item_meta_request_option_t options)182 void playlist_preparser_fetcher_Push( playlist_preparser_t *preparser,
183     input_item_t *item, input_item_meta_request_option_t options )
184 {
185     if( preparser->fetcher )
186         playlist_fetcher_Push( preparser->fetcher, item, options, -1 );
187 }
188 
playlist_preparser_Cancel(playlist_preparser_t * preparser,void * id)189 void playlist_preparser_Cancel( playlist_preparser_t *preparser, void *id )
190 {
191     background_worker_Cancel( preparser->worker, id );
192 }
193 
playlist_preparser_Deactivate(playlist_preparser_t * preparser)194 void playlist_preparser_Deactivate( playlist_preparser_t* preparser )
195 {
196     atomic_store( &preparser->deactivated, true );
197     background_worker_Cancel( preparser->worker, NULL );
198 }
199 
playlist_preparser_Delete(playlist_preparser_t * preparser)200 void playlist_preparser_Delete( playlist_preparser_t *preparser )
201 {
202     background_worker_Delete( preparser->worker );
203 
204     if( preparser->fetcher )
205         playlist_fetcher_Delete( preparser->fetcher );
206 
207     free( preparser );
208 }
209