1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright © 2017-2017 VLC authors and VideoLAN
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
25 #include <vlc_common.h>
26 #include <vlc_atomic.h>
28 #include "misc/background_worker.h"
29 #include "input/input_interface.h"
30 #include "input/input_internal.h"
31 #include "preparser.h"
34 struct input_preparser_t
37 input_fetcher_t
* fetcher
;
38 struct background_worker
* worker
;
39 atomic_bool deactivated
;
42 typedef struct input_preparser_req_t
45 input_item_meta_request_option_t options
;
46 const input_preparser_callbacks_t
*cbs
;
49 } input_preparser_req_t
;
51 typedef struct input_preparser_task_t
53 input_preparser_req_t
*req
;
54 input_preparser_t
* preparser
;
56 input_item_parser_id_t
*parser
;
59 } input_preparser_task_t
;
61 static input_preparser_req_t
*ReqCreate(input_item_t
*item
,
62 input_item_meta_request_option_t options
,
63 const input_preparser_callbacks_t
*cbs
,
66 input_preparser_req_t
*req
= malloc(sizeof(*req
));
71 req
->options
= options
;
73 req
->userdata
= userdata
;
74 vlc_atomic_rc_init(&req
->rc
);
76 input_item_Hold(item
);
81 static void ReqHold(input_preparser_req_t
*req
)
83 vlc_atomic_rc_inc(&req
->rc
);
86 static void ReqRelease(input_preparser_req_t
*req
)
88 if (vlc_atomic_rc_dec(&req
->rc
))
90 input_item_Release(req
->item
);
95 static void OnParserEnded(input_item_t
*item
, int status
, void *task_
)
98 input_preparser_task_t
* task
= task_
;
100 atomic_store( &task
->state
, status
);
101 atomic_store( &task
->done
, true );
102 background_worker_RequestProbe( task
->preparser
->worker
);
105 static void OnParserSubtreeAdded(input_item_t
*item
, input_item_node_t
*subtree
,
109 input_preparser_task_t
* task
= task_
;
110 input_preparser_req_t
*req
= task
->req
;
112 if (req
->cbs
&& req
->cbs
->on_subtree_added
)
113 req
->cbs
->on_subtree_added(req
->item
, subtree
, req
->userdata
);
116 static int PreparserOpenInput( void* preparser_
, void* req_
, void** out
)
118 input_preparser_t
* preparser
= preparser_
;
119 input_preparser_req_t
*req
= req_
;
120 input_preparser_task_t
* task
= malloc( sizeof *task
);
122 if( unlikely( !task
) )
125 static const input_item_parser_cbs_t cbs
= {
126 .on_ended
= OnParserEnded
,
127 .on_subtree_added
= OnParserSubtreeAdded
,
130 atomic_init( &task
->state
, VLC_ETIMEOUT
);
131 atomic_init( &task
->done
, false );
133 task
->preparser
= preparser_
;
135 task
->preparse_status
= -1;
136 task
->parser
= input_item_Parse( req
->item
, preparser
->owner
, &cbs
,
147 if (req
->cbs
&& req
->cbs
->on_preparse_ended
)
148 req
->cbs
->on_preparse_ended(req
->item
, ITEM_PREPARSE_FAILED
, req
->userdata
);
152 static int PreparserProbeInput( void* preparser_
, void* task_
)
154 input_preparser_task_t
* task
= task_
;
155 return atomic_load( &task
->done
);
156 VLC_UNUSED( preparser_
);
159 static void on_art_fetch_ended(input_item_t
*item
, bool fetched
, void *userdata
)
163 input_preparser_task_t
*task
= userdata
;
164 input_preparser_req_t
*req
= task
->req
;
166 input_item_SetPreparsed(req
->item
, true);
168 if (req
->cbs
&& req
->cbs
->on_preparse_ended
)
169 req
->cbs
->on_preparse_ended(req
->item
, task
->preparse_status
, req
->userdata
);
175 static const input_fetcher_callbacks_t input_fetcher_callbacks
= {
176 .on_art_fetch_ended
= on_art_fetch_ended
,
179 static void PreparserCloseInput( void* preparser_
, void* task_
)
181 input_preparser_task_t
* task
= task_
;
182 input_preparser_req_t
*req
= task
->req
;
184 input_preparser_t
* preparser
= preparser_
;
185 input_item_t
* item
= req
->item
;
188 switch( atomic_load( &task
->state
) )
191 status
= ITEM_PREPARSE_DONE
;
194 status
= ITEM_PREPARSE_TIMEOUT
;
197 status
= ITEM_PREPARSE_FAILED
;
201 input_item_parser_id_Release( task
->parser
);
203 if( preparser
->fetcher
&& (req
->options
& META_REQUEST_OPTION_FETCH_ANY
) )
205 task
->preparse_status
= status
;
207 if (!input_fetcher_Push(preparser
->fetcher
, item
,
208 req
->options
& META_REQUEST_OPTION_FETCH_ANY
,
209 &input_fetcher_callbacks
, task
))
213 ReqRelease(task
->req
);
218 input_item_SetPreparsed( item
, true );
219 if (req
->cbs
&& req
->cbs
->on_preparse_ended
)
220 req
->cbs
->on_preparse_ended(req
->item
, status
, req
->userdata
);
223 static void ReqHoldVoid(void *item
) { ReqHold(item
); }
224 static void ReqReleaseVoid(void *item
) { ReqRelease(item
); }
226 input_preparser_t
* input_preparser_New( vlc_object_t
*parent
)
228 input_preparser_t
* preparser
= malloc( sizeof *preparser
);
230 struct background_worker_config conf
= {
231 .default_timeout
= VLC_TICK_FROM_MS(var_InheritInteger( parent
, "preparse-timeout" )),
232 .max_threads
= var_InheritInteger( parent
, "preparse-threads" ),
233 .pf_start
= PreparserOpenInput
,
234 .pf_probe
= PreparserProbeInput
,
235 .pf_stop
= PreparserCloseInput
,
236 .pf_release
= ReqReleaseVoid
,
237 .pf_hold
= ReqHoldVoid
241 if( likely( preparser
) )
242 preparser
->worker
= background_worker_New( preparser
, &conf
);
244 if( unlikely( !preparser
|| !preparser
->worker
) )
250 preparser
->owner
= parent
;
251 preparser
->fetcher
= input_fetcher_New( parent
);
252 atomic_init( &preparser
->deactivated
, false );
254 if( unlikely( !preparser
->fetcher
) )
255 msg_Warn( parent
, "unable to create art fetcher" );
260 void input_preparser_Push( input_preparser_t
*preparser
,
261 input_item_t
*item
, input_item_meta_request_option_t i_options
,
262 const input_preparser_callbacks_t
*cbs
, void *cbs_userdata
,
263 int timeout
, void *id
)
265 if( atomic_load( &preparser
->deactivated
) )
268 vlc_mutex_lock( &item
->lock
);
269 enum input_item_type_e i_type
= item
->i_type
;
270 int b_net
= item
->b_net
;
271 if( i_options
& META_REQUEST_OPTION_DO_INTERACT
)
272 item
->b_preparse_interact
= true;
273 vlc_mutex_unlock( &item
->lock
);
279 case ITEM_TYPE_DIRECTORY
:
280 case ITEM_TYPE_PLAYLIST
:
281 if( !b_net
|| i_options
& META_REQUEST_OPTION_SCOPE_NETWORK
)
285 if (cbs
&& cbs
->on_preparse_ended
)
286 cbs
->on_preparse_ended(item
, ITEM_PREPARSE_SKIPPED
, cbs_userdata
);
290 struct input_preparser_req_t
*req
= ReqCreate(item
, i_options
,
293 if (background_worker_Push(preparser
->worker
, req
, id
, timeout
))
294 if (req
->cbs
&& cbs
->on_preparse_ended
)
295 cbs
->on_preparse_ended(item
, ITEM_PREPARSE_FAILED
, cbs_userdata
);
300 void input_preparser_fetcher_Push( input_preparser_t
*preparser
,
301 input_item_t
*item
, input_item_meta_request_option_t options
,
302 const input_fetcher_callbacks_t
*cbs
, void *cbs_userdata
)
304 if( preparser
->fetcher
)
305 input_fetcher_Push( preparser
->fetcher
, item
, options
,
309 void input_preparser_Cancel( input_preparser_t
*preparser
, void *id
)
311 background_worker_Cancel( preparser
->worker
, id
);
314 void input_preparser_Deactivate( input_preparser_t
* preparser
)
316 atomic_store( &preparser
->deactivated
, true );
317 background_worker_Cancel( preparser
->worker
, NULL
);
320 void input_preparser_Delete( input_preparser_t
*preparser
)
322 background_worker_Delete( preparser
->worker
);
324 if( preparser
->fetcher
)
325 input_fetcher_Delete( preparser
->fetcher
);