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>
27 #include <vlc_executor.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 vlc_executor_t
*executor
;
39 vlc_tick_t default_timeout
;
40 atomic_bool deactivated
;
43 struct vlc_list submitted_tasks
; /**< list of struct task */
48 input_preparser_t
*preparser
;
50 input_item_meta_request_option_t options
;
51 const input_preparser_callbacks_t
*cbs
;
56 input_item_parser_id_t
*parser
;
59 vlc_cond_t cond_ended
;
64 atomic_bool interrupted
;
66 struct vlc_runnable runnable
; /**< to be passed to the executor */
68 struct vlc_list node
; /**< node of input_preparser_t.submitted_tasks */
71 static void RunnableRun(void *);
74 TaskNew(input_preparser_t
*preparser
, input_item_t
*item
,
75 input_item_meta_request_option_t options
,
76 const input_preparser_callbacks_t
*cbs
, void *userdata
,
77 void *id
, vlc_tick_t timeout
)
81 struct task
*task
= malloc(sizeof(*task
));
85 task
->preparser
= preparser
;
87 task
->options
= options
;
89 task
->userdata
= userdata
;
91 task
->timeout
= timeout
;
93 input_item_Hold(item
);
96 vlc_mutex_init(&task
->lock
);
97 vlc_cond_init(&task
->cond_ended
);
98 task
->preparse_ended
= false;
99 task
->preparse_status
= ITEM_PREPARSE_SKIPPED
;
100 task
->fetch_ended
= false;
102 atomic_init(&task
->interrupted
, false);
104 task
->runnable
.run
= RunnableRun
;
105 task
->runnable
.userdata
= task
;
111 TaskDelete(struct task
*task
)
113 input_item_Release(task
->item
);
118 PreparserAddTask(input_preparser_t
*preparser
, struct task
*task
)
120 vlc_mutex_lock(&preparser
->lock
);
121 vlc_list_append(&task
->node
, &preparser
->submitted_tasks
);
122 vlc_mutex_unlock(&preparser
->lock
);
126 PreparserRemoveTask(input_preparser_t
*preparser
, struct task
*task
)
128 vlc_mutex_lock(&preparser
->lock
);
129 vlc_list_remove(&task
->node
);
130 vlc_mutex_unlock(&preparser
->lock
);
134 NotifyPreparseEnded(struct task
*task
)
136 if (task
->cbs
&& task
->cbs
->on_preparse_ended
)
137 task
->cbs
->on_preparse_ended(task
->item
, task
->preparse_status
,
142 OnParserEnded(input_item_t
*item
, int status
, void *task_
)
145 struct task
*task
= task_
;
147 if (atomic_load(&task
->interrupted
))
149 * On interruption, the call to input_item_parser_id_Release() may
150 * trigger this "parser ended" callback. Ignore it.
154 vlc_mutex_lock(&task
->lock
);
155 assert(!task
->preparse_ended
);
156 task
->preparse_status
= status
== VLC_SUCCESS
? ITEM_PREPARSE_DONE
157 : ITEM_PREPARSE_FAILED
;
158 task
->preparse_ended
= true;
159 vlc_mutex_unlock(&task
->lock
);
161 vlc_cond_signal(&task
->cond_ended
);
165 OnParserSubtreeAdded(input_item_t
*item
, input_item_node_t
*subtree
,
169 struct task
*task
= task_
;
171 if (task
->cbs
&& task
->cbs
->on_subtree_added
)
172 task
->cbs
->on_subtree_added(task
->item
, subtree
, task
->userdata
);
176 OnArtFetchEnded(input_item_t
*item
, bool fetched
, void *userdata
)
181 struct task
*task
= userdata
;
183 vlc_mutex_lock(&task
->lock
);
184 assert(!task
->fetch_ended
);
185 task
->fetch_ended
= true;
186 vlc_mutex_unlock(&task
->lock
);
188 vlc_cond_signal(&task
->cond_ended
);
191 static const input_fetcher_callbacks_t input_fetcher_callbacks
= {
192 .on_art_fetch_ended
= OnArtFetchEnded
,
196 Parse(struct task
*task
, vlc_tick_t deadline
)
198 static const input_item_parser_cbs_t cbs
= {
199 .on_ended
= OnParserEnded
,
200 .on_subtree_added
= OnParserSubtreeAdded
,
203 vlc_object_t
*obj
= task
->preparser
->owner
;
204 task
->parser
= input_item_Parse(task
->item
, obj
, &cbs
, task
);
207 task
->preparse_status
= ITEM_PREPARSE_FAILED
;
211 /* Wait until the end of parsing */
212 vlc_mutex_lock(&task
->lock
);
213 if (deadline
== VLC_TICK_INVALID
)
215 while (!task
->preparse_ended
)
216 vlc_cond_wait(&task
->cond_ended
, &task
->lock
);
220 bool timeout
= false;
221 while (!task
->preparse_ended
&& !timeout
)
223 vlc_cond_timedwait(&task
->cond_ended
, &task
->lock
, deadline
);
227 task
->preparse_status
= ITEM_PREPARSE_TIMEOUT
;
228 atomic_store(&task
->interrupted
, true);
231 vlc_mutex_unlock(&task
->lock
);
233 /* This call also interrupts the parsing if it is still running */
234 input_item_parser_id_Release(task
->parser
);
238 Fetch(struct task
*task
)
240 input_fetcher_t
*fetcher
= task
->preparser
->fetcher
;
241 if (!fetcher
|| !(task
->options
& META_REQUEST_OPTION_FETCH_ANY
))
245 input_fetcher_Push(fetcher
, task
->item
,
246 task
->options
& META_REQUEST_OPTION_FETCH_ANY
,
247 &input_fetcher_callbacks
, task
);
248 if (ret
!= VLC_SUCCESS
)
251 /* Wait until the end of fetching (fetching is not interruptible) */
252 vlc_mutex_lock(&task
->lock
);
253 while (!task
->fetch_ended
)
254 vlc_cond_wait(&task
->cond_ended
, &task
->lock
);
255 vlc_mutex_unlock(&task
->lock
);
259 RunnableRun(void *userdata
)
261 struct task
*task
= userdata
;
263 vlc_tick_t deadline
= task
->timeout
? vlc_tick_now() + task
->timeout
266 if (atomic_load(&task
->interrupted
))
269 Parse(task
, deadline
);
271 if (atomic_load(&task
->interrupted
))
276 if (atomic_load(&task
->interrupted
))
279 input_item_SetPreparsed(task
->item
, true);
282 NotifyPreparseEnded(task
);
283 input_preparser_t
*preparser
= task
->preparser
;
284 PreparserRemoveTask(preparser
, task
);
289 Interrupt(struct task
*task
)
291 assert(!atomic_load(&task
->interrupted
));
292 atomic_store(&task
->interrupted
, true);
294 /* Wake up the preparser cond_wait */
295 vlc_mutex_lock(&task
->lock
);
296 task
->preparse_status
= ITEM_PREPARSE_TIMEOUT
;
297 task
->preparse_ended
= true;
298 vlc_mutex_unlock(&task
->lock
);
299 vlc_cond_signal(&task
->cond_ended
);
302 input_preparser_t
* input_preparser_New( vlc_object_t
*parent
)
304 input_preparser_t
* preparser
= malloc( sizeof *preparser
);
308 int max_threads
= var_InheritInteger(parent
, "preparse-threads");
312 preparser
->executor
= vlc_executor_New(max_threads
);
313 if (!preparser
->executor
)
319 preparser
->default_timeout
=
320 VLC_TICK_FROM_MS(var_InheritInteger(parent
, "preparse-timeout"));
321 if (preparser
->default_timeout
< 0)
322 preparser
->default_timeout
= 0;
324 preparser
->owner
= parent
;
325 preparser
->fetcher
= input_fetcher_New( parent
);
326 atomic_init( &preparser
->deactivated
, false );
328 vlc_mutex_init(&preparser
->lock
);
329 vlc_list_init(&preparser
->submitted_tasks
);
331 if( unlikely( !preparser
->fetcher
) )
332 msg_Warn( parent
, "unable to create art fetcher" );
337 void input_preparser_Push( input_preparser_t
*preparser
,
338 input_item_t
*item
, input_item_meta_request_option_t i_options
,
339 const input_preparser_callbacks_t
*cbs
, void *cbs_userdata
,
340 int timeout_ms
, void *id
)
342 if( atomic_load( &preparser
->deactivated
) )
345 vlc_mutex_lock( &item
->lock
);
346 enum input_item_type_e i_type
= item
->i_type
;
347 int b_net
= item
->b_net
;
348 if( i_options
& META_REQUEST_OPTION_DO_INTERACT
)
349 item
->b_preparse_interact
= true;
350 vlc_mutex_unlock( &item
->lock
);
356 case ITEM_TYPE_DIRECTORY
:
357 case ITEM_TYPE_PLAYLIST
:
358 if( !b_net
|| i_options
& META_REQUEST_OPTION_SCOPE_NETWORK
)
362 if (cbs
&& cbs
->on_preparse_ended
)
363 cbs
->on_preparse_ended(item
, ITEM_PREPARSE_SKIPPED
, cbs_userdata
);
367 vlc_tick_t timeout
= timeout_ms
== -1 ? preparser
->default_timeout
368 : VLC_TICK_FROM_MS(timeout_ms
);
370 TaskNew(preparser
, item
, i_options
, cbs
, cbs_userdata
, id
, timeout
);
372 PreparserAddTask(preparser
, task
);
374 vlc_executor_Submit(preparser
->executor
, &task
->runnable
);
377 void input_preparser_fetcher_Push( input_preparser_t
*preparser
,
378 input_item_t
*item
, input_item_meta_request_option_t options
,
379 const input_fetcher_callbacks_t
*cbs
, void *cbs_userdata
)
381 if( preparser
->fetcher
)
382 input_fetcher_Push( preparser
->fetcher
, item
, options
,
386 void input_preparser_Cancel( input_preparser_t
*preparser
, void *id
)
388 vlc_mutex_lock(&preparser
->lock
);
391 vlc_list_foreach(task
, &preparser
->submitted_tasks
, node
)
393 if (!id
|| task
->id
== id
)
396 vlc_executor_Cancel(preparser
->executor
, &task
->runnable
);
399 NotifyPreparseEnded(task
);
400 vlc_list_remove(&task
->node
);
404 /* The task will be finished and destroyed after run() */
409 vlc_mutex_unlock(&preparser
->lock
);
412 void input_preparser_Deactivate( input_preparser_t
* preparser
)
414 atomic_store( &preparser
->deactivated
, true );
415 input_preparser_Cancel(preparser
, NULL
);
418 void input_preparser_Delete( input_preparser_t
*preparser
)
420 /* In case input_preparser_Deactivate() has not been called */
421 input_preparser_Cancel(preparser
, NULL
);
423 vlc_executor_Delete(preparser
->executor
);
425 if( preparser
->fetcher
)
426 input_fetcher_Delete( preparser
->fetcher
);