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