/src/vlc/src/input/item.c
Line | Count | Source |
1 | | /***************************************************************************** |
2 | | * item.c: input_item management |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 1998-2004 VLC authors and VideoLAN |
5 | | * |
6 | | * Authors: Clément Stenac <zorglub@videolan.org> |
7 | | * |
8 | | * This program is free software; you can redistribute it and/or modify it |
9 | | * under the terms of the GNU Lesser General Public License as published by |
10 | | * the Free Software Foundation; either version 2.1 of the License, or |
11 | | * (at your option) any later version. |
12 | | * |
13 | | * This program is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU Lesser General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU Lesser General Public License |
19 | | * along with this program; if not, write to the Free Software Foundation, |
20 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
21 | | *****************************************************************************/ |
22 | | |
23 | | #ifdef HAVE_CONFIG_H |
24 | | # include "config.h" |
25 | | #endif |
26 | | #include <assert.h> |
27 | | #include <time.h> |
28 | | #include <limits.h> |
29 | | #include <ctype.h> |
30 | | |
31 | | #include <vlc_common.h> |
32 | | #include <vlc_arrays.h> |
33 | | #include <vlc_url.h> |
34 | | #include <vlc_strings.h> |
35 | | |
36 | | #include "item.h" |
37 | | #include "info.h" |
38 | | #include "input_internal.h" |
39 | | |
40 | | #include <vlc_charset.h> |
41 | | |
42 | | struct input_item_opaque |
43 | | { |
44 | | struct input_item_opaque *next; |
45 | | void *value; |
46 | | char name[1]; |
47 | | }; |
48 | | |
49 | | static enum input_item_type_e GuessType( const input_item_t *p_item, bool *p_net ); |
50 | | |
51 | | void input_item_SetPreparsed( input_item_t *p_i ) |
52 | 0 | { |
53 | 0 | vlc_mutex_lock( &p_i->lock ); |
54 | |
|
55 | 0 | int status = vlc_meta_GetStatus(p_i->p_meta); |
56 | 0 | int new_status = status | ITEM_PREPARSED; |
57 | 0 | if( status != new_status ) |
58 | 0 | vlc_meta_SetStatus(p_i->p_meta, new_status); |
59 | |
|
60 | 0 | vlc_mutex_unlock( &p_i->lock ); |
61 | 0 | } |
62 | | |
63 | | void input_item_SetArtNotFound( input_item_t *p_i, bool b_not_found ) |
64 | 0 | { |
65 | 0 | vlc_mutex_lock( &p_i->lock ); |
66 | |
|
67 | 0 | int status = vlc_meta_GetStatus(p_i->p_meta); |
68 | |
|
69 | 0 | if( b_not_found ) |
70 | 0 | status |= ITEM_ART_NOTFOUND; |
71 | 0 | else |
72 | 0 | status &= ~ITEM_ART_NOTFOUND; |
73 | |
|
74 | 0 | vlc_meta_SetStatus(p_i->p_meta, status); |
75 | |
|
76 | 0 | vlc_mutex_unlock( &p_i->lock ); |
77 | 0 | } |
78 | | |
79 | | void input_item_SetArtFetched( input_item_t *p_i, bool b_art_fetched ) |
80 | 0 | { |
81 | 0 | vlc_mutex_lock( &p_i->lock ); |
82 | |
|
83 | 0 | int status = vlc_meta_GetStatus(p_i->p_meta); |
84 | |
|
85 | 0 | if( b_art_fetched ) |
86 | 0 | status |= ITEM_ART_FETCHED; |
87 | 0 | else |
88 | 0 | status &= ~ITEM_ART_FETCHED; |
89 | |
|
90 | 0 | vlc_meta_SetStatus(p_i->p_meta, status); |
91 | |
|
92 | 0 | vlc_mutex_unlock( &p_i->lock ); |
93 | 0 | } |
94 | | |
95 | | void input_item_SetMeta( input_item_t *p_i, vlc_meta_type_t meta_type, const char *psz_val ) |
96 | 0 | { |
97 | 0 | vlc_mutex_lock( &p_i->lock ); |
98 | 0 | vlc_meta_Set( p_i->p_meta, meta_type, psz_val ); |
99 | 0 | vlc_mutex_unlock( &p_i->lock ); |
100 | 0 | } |
101 | | |
102 | | void input_item_SetMetaExtra( input_item_t *p_i, const char *psz_name, const char *psz_value ) |
103 | 0 | { |
104 | 0 | vlc_mutex_lock( &p_i->lock ); |
105 | 0 | vlc_meta_SetExtra( p_i->p_meta, psz_name, psz_value ); |
106 | 0 | vlc_mutex_unlock( &p_i->lock ); |
107 | 0 | } |
108 | | |
109 | | void input_item_CopyOptions( input_item_t *p_child, |
110 | | input_item_t *p_parent ) |
111 | 0 | { |
112 | 0 | char **optv = NULL; |
113 | 0 | uint8_t *flagv = NULL; |
114 | 0 | int optc = 0; |
115 | 0 | char **optv_realloc = NULL; |
116 | 0 | uint8_t *flagv_realloc = NULL; |
117 | |
|
118 | 0 | vlc_mutex_lock( &p_parent->lock ); |
119 | |
|
120 | 0 | if( p_parent->i_options > 0 ) |
121 | 0 | { |
122 | 0 | optv = vlc_alloc( p_parent->i_options, sizeof (*optv) ); |
123 | 0 | if( likely(optv) ) |
124 | 0 | flagv = vlc_alloc( p_parent->i_options, sizeof (*flagv) ); |
125 | |
|
126 | 0 | if( likely(flagv) ) |
127 | 0 | { |
128 | 0 | for( int i = 0; i < p_parent->i_options; i++ ) |
129 | 0 | { |
130 | 0 | char *psz_dup = strdup( p_parent->ppsz_options[i] ); |
131 | 0 | if( likely(psz_dup) ) |
132 | 0 | { |
133 | 0 | flagv[optc] = p_parent->optflagv[i]; |
134 | 0 | optv[optc++] = psz_dup; |
135 | 0 | } |
136 | 0 | } |
137 | 0 | } |
138 | 0 | } |
139 | |
|
140 | 0 | vlc_mutex_unlock( &p_parent->lock ); |
141 | |
|
142 | 0 | if( likely(optv && flagv && optc ) ) |
143 | 0 | { |
144 | 0 | vlc_mutex_lock( &p_child->lock ); |
145 | |
|
146 | 0 | if( INT_MAX - p_child->i_options >= optc && |
147 | 0 | SIZE_MAX / sizeof (*flagv) >= (size_t) (p_child->i_options + optc) ) |
148 | 0 | flagv_realloc = realloc( p_child->optflagv, |
149 | 0 | (p_child->i_options + optc) * sizeof (*flagv) ); |
150 | 0 | if( likely(flagv_realloc) ) |
151 | 0 | { |
152 | 0 | p_child->optflagv = flagv_realloc; |
153 | 0 | if( SIZE_MAX / sizeof (*optv) >= (size_t) (p_child->i_options + optc) ) |
154 | 0 | optv_realloc = realloc( p_child->ppsz_options, |
155 | 0 | (p_child->i_options + optc) * sizeof (*optv) ); |
156 | 0 | if( likely(optv_realloc) ) |
157 | 0 | { |
158 | 0 | p_child->ppsz_options = optv_realloc; |
159 | 0 | memcpy( p_child->ppsz_options + p_child->i_options, optv, |
160 | 0 | optc * sizeof (*optv) ); |
161 | 0 | memcpy( p_child->optflagv + p_child->i_options, flagv, |
162 | 0 | optc * sizeof (*flagv) ); |
163 | 0 | p_child->i_options += optc; |
164 | 0 | p_child->optflagc += optc; |
165 | 0 | } |
166 | 0 | } |
167 | |
|
168 | 0 | vlc_mutex_unlock( &p_child->lock ); |
169 | 0 | } |
170 | |
|
171 | 0 | if( unlikely(!flagv_realloc || !optv_realloc) ) |
172 | 0 | { |
173 | | /* Didn't copy pointers, so need to free the strdup() */ |
174 | 0 | for( int i=0; i<optc; i++ ) |
175 | 0 | free( optv[i] ); |
176 | 0 | } |
177 | |
|
178 | 0 | free( flagv ); |
179 | 0 | free( optv ); |
180 | 0 | } |
181 | | |
182 | | bool input_item_MetaMatch( input_item_t *p_i, |
183 | | vlc_meta_type_t meta_type, const char *psz ) |
184 | 0 | { |
185 | 0 | vlc_mutex_lock( &p_i->lock ); |
186 | |
|
187 | 0 | const char *psz_meta = vlc_meta_Get( p_i->p_meta, meta_type ); |
188 | 0 | bool b_ret = psz_meta && strcasestr( psz_meta, psz ); |
189 | |
|
190 | 0 | vlc_mutex_unlock( &p_i->lock ); |
191 | |
|
192 | 0 | return b_ret; |
193 | 0 | } |
194 | | |
195 | | const char *input_item_GetMetaLocked(input_item_t *item, |
196 | | vlc_meta_type_t meta_type) |
197 | 0 | { |
198 | 0 | vlc_mutex_assert(&item->lock); |
199 | |
|
200 | 0 | return vlc_meta_Get(item->p_meta, meta_type); |
201 | 0 | } |
202 | | |
203 | | char *input_item_GetMeta( input_item_t *p_i, vlc_meta_type_t meta_type ) |
204 | 0 | { |
205 | 0 | vlc_mutex_lock( &p_i->lock ); |
206 | 0 | const char *value = input_item_GetMetaLocked( p_i, meta_type ); |
207 | 0 | char *psz = value ? strdup( value ) : NULL; |
208 | 0 | vlc_mutex_unlock( &p_i->lock ); |
209 | 0 | return psz; |
210 | 0 | } |
211 | | |
212 | | static const char *input_item_GetMetaExtraLocked( input_item_t *item, const char *psz_name ) |
213 | 0 | { |
214 | 0 | vlc_mutex_assert( &item->lock ); |
215 | 0 | return vlc_meta_GetExtra( item->p_meta, psz_name ); |
216 | 0 | } |
217 | | |
218 | | char *input_item_GetMetaExtra( input_item_t *p_i, const char *psz_name ) |
219 | 0 | { |
220 | 0 | vlc_mutex_lock( &p_i->lock ); |
221 | 0 | const char *value = input_item_GetMetaExtraLocked( p_i, psz_name ); |
222 | 0 | char *psz = value ? strdup( value ) : NULL; |
223 | 0 | vlc_mutex_unlock( &p_i->lock ); |
224 | 0 | return psz; |
225 | 0 | } |
226 | | |
227 | | unsigned input_item_GetMetaExtraNames( input_item_t *p_i, char ***pppsz_names ) |
228 | 0 | { |
229 | 0 | vlc_mutex_lock( &p_i->lock ); |
230 | 0 | unsigned count = vlc_meta_GetExtraCount( p_i->p_meta ); |
231 | 0 | *pppsz_names = vlc_meta_CopyExtraNames( p_i->p_meta ); |
232 | 0 | vlc_mutex_unlock( &p_i->lock ); |
233 | 0 | return count; |
234 | 0 | } |
235 | | |
236 | | /* Get the title of a given item or fallback to the name if the title is empty */ |
237 | | char *input_item_GetTitleFbName( input_item_t *p_item ) |
238 | 0 | { |
239 | 0 | char *psz_ret; |
240 | 0 | vlc_mutex_lock( &p_item->lock ); |
241 | |
|
242 | 0 | const char *psz_title = vlc_meta_Get( p_item->p_meta, vlc_meta_Title ); |
243 | 0 | if( !EMPTY_STR( psz_title ) ) |
244 | 0 | psz_ret = strdup( psz_title ); |
245 | 0 | else |
246 | 0 | psz_ret = p_item->psz_name ? strdup( p_item->psz_name ) : NULL; |
247 | |
|
248 | 0 | vlc_mutex_unlock( &p_item->lock ); |
249 | 0 | return psz_ret; |
250 | 0 | } |
251 | | |
252 | | char *input_item_GetName( input_item_t *p_item ) |
253 | 0 | { |
254 | 0 | vlc_mutex_lock( &p_item->lock ); |
255 | |
|
256 | 0 | char *psz_name = p_item->psz_name ? strdup( p_item->psz_name ) : NULL; |
257 | |
|
258 | 0 | vlc_mutex_unlock( &p_item->lock ); |
259 | 0 | return psz_name; |
260 | 0 | } |
261 | | void input_item_SetName( input_item_t *p_item, const char *psz_name ) |
262 | 0 | { |
263 | 0 | vlc_mutex_lock( &p_item->lock ); |
264 | |
|
265 | 0 | free( p_item->psz_name ); |
266 | 0 | p_item->psz_name = strdup( psz_name ); |
267 | |
|
268 | 0 | vlc_mutex_unlock( &p_item->lock ); |
269 | 0 | } |
270 | | |
271 | | char *input_item_GetURI( input_item_t *p_i ) |
272 | 0 | { |
273 | 0 | vlc_mutex_lock( &p_i->lock ); |
274 | |
|
275 | 0 | char *psz_s = p_i->psz_uri ? strdup( p_i->psz_uri ) : NULL; |
276 | |
|
277 | 0 | vlc_mutex_unlock( &p_i->lock ); |
278 | 0 | return psz_s; |
279 | 0 | } |
280 | | |
281 | | void input_item_SetURI( input_item_t *p_i, const char *psz_uri ) |
282 | 0 | { |
283 | 0 | assert( psz_uri ); |
284 | 0 | #ifndef NDEBUG |
285 | 0 | if( !strchr( psz_uri, ':' ) |
286 | 0 | || strchr( psz_uri, ' ' ) || strchr( psz_uri, '"' ) ) |
287 | 0 | fprintf( stderr, "Warning: %s(\"%s\"): file path instead of URL.\n", |
288 | 0 | __func__, psz_uri ); |
289 | 0 | #endif |
290 | 0 | vlc_mutex_lock( &p_i->lock ); |
291 | 0 | free( p_i->psz_uri ); |
292 | 0 | p_i->psz_uri = strdup( psz_uri ); |
293 | |
|
294 | 0 | p_i->i_type = GuessType( p_i, &p_i->b_net ); |
295 | |
|
296 | 0 | if( p_i->psz_name ) |
297 | 0 | ; |
298 | 0 | else |
299 | 0 | if( p_i->i_type == ITEM_TYPE_FILE || p_i->i_type == ITEM_TYPE_DIRECTORY ) |
300 | 0 | { |
301 | 0 | const char *psz_filename = strrchr( p_i->psz_uri, '/' ); |
302 | |
|
303 | 0 | if( psz_filename && *psz_filename == '/' ) |
304 | 0 | psz_filename++; |
305 | 0 | if( psz_filename && *psz_filename ) |
306 | 0 | p_i->psz_name = strdup( psz_filename ); |
307 | | |
308 | | /* Make the name more readable */ |
309 | 0 | if( p_i->psz_name ) |
310 | 0 | { |
311 | 0 | vlc_uri_decode( p_i->psz_name ); |
312 | 0 | EnsureUTF8( p_i->psz_name ); |
313 | 0 | } |
314 | 0 | } |
315 | 0 | else |
316 | 0 | { /* Strip login and password from title */ |
317 | 0 | int r; |
318 | 0 | vlc_url_t url; |
319 | |
|
320 | 0 | vlc_UrlParse( &url, psz_uri ); |
321 | 0 | if( url.psz_protocol ) |
322 | 0 | { |
323 | 0 | if( url.i_port > 0 ) |
324 | 0 | r=asprintf( &p_i->psz_name, "%s://%s:%u%s", url.psz_protocol, |
325 | 0 | url.psz_host, url.i_port, |
326 | 0 | url.psz_path ? url.psz_path : "" ); |
327 | 0 | else |
328 | 0 | r=asprintf( &p_i->psz_name, "%s://%s%s", url.psz_protocol, |
329 | 0 | url.psz_host ? url.psz_host : "", |
330 | 0 | url.psz_path ? url.psz_path : "" ); |
331 | 0 | } |
332 | 0 | else |
333 | 0 | { |
334 | 0 | if( url.i_port > 0 ) |
335 | 0 | r=asprintf( &p_i->psz_name, "%s:%u%s", url.psz_host, url.i_port, |
336 | 0 | url.psz_path ? url.psz_path : "" ); |
337 | 0 | else |
338 | 0 | r=asprintf( &p_i->psz_name, "%s%s", url.psz_host, |
339 | 0 | url.psz_path ? url.psz_path : "" ); |
340 | 0 | } |
341 | 0 | vlc_UrlClean( &url ); |
342 | 0 | if( -1==r ) |
343 | 0 | p_i->psz_name=NULL; /* recover from undefined value */ |
344 | 0 | } |
345 | |
|
346 | 0 | vlc_mutex_unlock( &p_i->lock ); |
347 | 0 | } |
348 | | |
349 | | vlc_tick_t input_item_GetDuration( input_item_t *p_i ) |
350 | 0 | { |
351 | 0 | vlc_mutex_lock( &p_i->lock ); |
352 | |
|
353 | 0 | vlc_tick_t i_duration = p_i->i_duration; |
354 | |
|
355 | 0 | vlc_mutex_unlock( &p_i->lock ); |
356 | 0 | if (i_duration == INPUT_DURATION_INDEFINITE) |
357 | 0 | i_duration = 0; |
358 | 0 | else if (i_duration == INPUT_DURATION_UNSET) |
359 | 0 | i_duration = 0; |
360 | 0 | return i_duration; |
361 | 0 | } |
362 | | |
363 | | void input_item_SetDuration( input_item_t *p_i, vlc_tick_t i_duration ) |
364 | 0 | { |
365 | 0 | vlc_mutex_lock( &p_i->lock ); |
366 | 0 | if( p_i->i_duration != i_duration ) |
367 | 0 | p_i->i_duration = i_duration; |
368 | 0 | vlc_mutex_unlock( &p_i->lock ); |
369 | 0 | } |
370 | | |
371 | | char *input_item_GetNowPlayingFb( input_item_t *p_item ) |
372 | 0 | { |
373 | 0 | char *psz_meta = input_item_GetMeta( p_item, vlc_meta_NowPlaying ); |
374 | 0 | if( !psz_meta || strlen( psz_meta ) == 0 ) |
375 | 0 | { |
376 | 0 | free( psz_meta ); |
377 | 0 | return input_item_GetMeta( p_item, vlc_meta_ESNowPlaying ); |
378 | 0 | } |
379 | | |
380 | 0 | return psz_meta; |
381 | 0 | } |
382 | | |
383 | | bool input_item_IsPreparsed( input_item_t *p_item ) |
384 | 0 | { |
385 | 0 | vlc_mutex_lock( &p_item->lock ); |
386 | 0 | bool b_preparsed = vlc_meta_GetStatus(p_item->p_meta) & ITEM_PREPARSED; |
387 | 0 | vlc_mutex_unlock( &p_item->lock ); |
388 | |
|
389 | 0 | return b_preparsed; |
390 | 0 | } |
391 | | |
392 | | bool input_item_IsArtFetched( input_item_t *p_item ) |
393 | 0 | { |
394 | 0 | vlc_mutex_lock( &p_item->lock ); |
395 | 0 | bool b_fetched = vlc_meta_GetStatus(p_item->p_meta) & ITEM_ART_FETCHED; |
396 | 0 | vlc_mutex_unlock( &p_item->lock ); |
397 | |
|
398 | 0 | return b_fetched; |
399 | 0 | } |
400 | | |
401 | | input_item_t *input_item_Hold( input_item_t *p_item ) |
402 | 0 | { |
403 | 0 | input_item_owner_t *owner = item_owner(p_item); |
404 | |
|
405 | 0 | vlc_atomic_rc_inc( &owner->rc ); |
406 | 0 | return p_item; |
407 | 0 | } |
408 | | |
409 | | void * |
410 | | input_item_GetLibvlcOwner(input_item_t *item) |
411 | 0 | { |
412 | 0 | input_item_owner_t *owner = item_owner(item); |
413 | |
|
414 | 0 | return owner->libvlc_owner; |
415 | 0 | } |
416 | | |
417 | | void |
418 | | input_item_SetLibvlcOwner(input_item_t *item, void *libvlc_owner, |
419 | | void (*release)(void *)) |
420 | 0 | { |
421 | 0 | input_item_owner_t *owner = item_owner(item); |
422 | |
|
423 | 0 | owner->libvlc_owner = libvlc_owner; |
424 | 0 | owner->libvlc_owner_release = release; |
425 | 0 | } |
426 | | |
427 | | void input_item_Release( input_item_t *p_item ) |
428 | 0 | { |
429 | 0 | input_item_owner_t *owner = item_owner(p_item); |
430 | |
|
431 | 0 | if( !vlc_atomic_rc_dec( &owner->rc ) ) |
432 | 0 | return; |
433 | | |
434 | 0 | if( owner->libvlc_owner_release != NULL ) |
435 | 0 | owner->libvlc_owner_release( owner->libvlc_owner ); |
436 | |
|
437 | 0 | free( p_item->psz_name ); |
438 | 0 | free( p_item->psz_uri ); |
439 | 0 | free( p_item->p_stats ); |
440 | 0 | vlc_meta_Delete( p_item->p_meta ); |
441 | |
|
442 | 0 | for( input_item_opaque_t *o = p_item->opaques, *next; o != NULL; o = next ) |
443 | 0 | { |
444 | 0 | next = o->next; |
445 | 0 | free( o ); |
446 | 0 | } |
447 | |
|
448 | 0 | for( int i = 0; i < p_item->i_options; i++ ) |
449 | 0 | free( p_item->ppsz_options[i] ); |
450 | 0 | TAB_CLEAN( p_item->i_options, p_item->ppsz_options ); |
451 | 0 | free( p_item->optflagv ); |
452 | |
|
453 | 0 | for( size_t i = 0; i < p_item->es_vec.size; ++i ) |
454 | 0 | { |
455 | 0 | struct input_item_es *item_es = &p_item->es_vec.data[i]; |
456 | 0 | es_format_Clean( &item_es->es ); |
457 | 0 | free( item_es->id ); |
458 | 0 | } |
459 | 0 | vlc_vector_destroy( &p_item->es_vec ); |
460 | |
|
461 | 0 | for( int i = 0; i < p_item->i_epg; i++ ) |
462 | 0 | vlc_epg_Delete( p_item->pp_epg[i] ); |
463 | 0 | TAB_CLEAN( p_item->i_epg, p_item->pp_epg ); |
464 | |
|
465 | 0 | info_category_t *cat; |
466 | 0 | vlc_list_foreach( cat, &p_item->categories, node ) |
467 | 0 | info_category_Delete( cat ); |
468 | |
|
469 | 0 | for( int i = 0; i < p_item->i_slaves; i++ ) |
470 | 0 | input_item_slave_Delete( p_item->pp_slaves[i] ); |
471 | 0 | TAB_CLEAN( p_item->i_slaves, p_item->pp_slaves ); |
472 | |
|
473 | 0 | free( owner ); |
474 | 0 | } |
475 | | |
476 | | int input_item_AddOption( input_item_t *p_input, const char *psz_option, |
477 | | unsigned flags ) |
478 | 0 | { |
479 | 0 | int err = VLC_SUCCESS; |
480 | |
|
481 | 0 | if( psz_option == NULL ) |
482 | 0 | return VLC_EGENERIC; |
483 | | |
484 | 0 | vlc_mutex_lock( &p_input->lock ); |
485 | 0 | if (flags & VLC_INPUT_OPTION_UNIQUE) |
486 | 0 | { |
487 | 0 | for (int i = 0 ; i < p_input->i_options; i++) |
488 | 0 | if( !strcmp( p_input->ppsz_options[i], psz_option ) ) |
489 | 0 | goto out; |
490 | 0 | } |
491 | | |
492 | 0 | uint8_t *flagv = realloc (p_input->optflagv, p_input->optflagc + 1); |
493 | 0 | if (flagv == NULL) |
494 | 0 | { |
495 | 0 | err = VLC_ENOMEM; |
496 | 0 | goto out; |
497 | 0 | } |
498 | | |
499 | 0 | p_input->optflagv = flagv; |
500 | |
|
501 | 0 | char* psz_option_dup = strdup( psz_option ); |
502 | 0 | if( unlikely( !psz_option_dup ) ) |
503 | 0 | { |
504 | 0 | err = VLC_ENOMEM; |
505 | 0 | goto out; |
506 | 0 | } |
507 | | |
508 | 0 | TAB_APPEND(p_input->i_options, p_input->ppsz_options, psz_option_dup); |
509 | | |
510 | 0 | flagv[p_input->optflagc++] = flags; |
511 | |
|
512 | 0 | out: |
513 | 0 | vlc_mutex_unlock( &p_input->lock ); |
514 | 0 | return err; |
515 | 0 | } |
516 | | |
517 | | int input_item_AddOptions( input_item_t *p_item, int i_options, |
518 | | const char *const *ppsz_options, |
519 | | unsigned i_flags ) |
520 | 0 | { |
521 | 0 | int i_ret = VLC_SUCCESS; |
522 | 0 | for( int i = 0; i < i_options && i_ret == VLC_SUCCESS; i++ ) |
523 | 0 | i_ret = input_item_AddOption( p_item, ppsz_options[i], i_flags ); |
524 | 0 | return i_ret; |
525 | 0 | } |
526 | | |
527 | | int input_item_AddOpaque(input_item_t *item, const char *name, void *value) |
528 | 0 | { |
529 | 0 | assert(name != NULL); |
530 | |
|
531 | 0 | size_t namelen = strlen(name); |
532 | 0 | input_item_opaque_t *entry = malloc(sizeof (*entry) + namelen); |
533 | 0 | if (unlikely(entry == NULL)) |
534 | 0 | return VLC_ENOMEM; |
535 | | |
536 | 0 | memcpy(entry->name, name, namelen + 1); |
537 | 0 | entry->value = value; |
538 | |
|
539 | 0 | vlc_mutex_lock(&item->lock); |
540 | 0 | entry->next = item->opaques; |
541 | 0 | item->opaques = entry; |
542 | 0 | vlc_mutex_unlock(&item->lock); |
543 | 0 | return VLC_SUCCESS; |
544 | 0 | } |
545 | | |
546 | | void input_item_ApplyOptions(vlc_object_t *obj, input_item_t *item) |
547 | 0 | { |
548 | 0 | vlc_mutex_lock(&item->lock); |
549 | 0 | assert(item->optflagc == (unsigned)item->i_options); |
550 | |
|
551 | 0 | for (unsigned i = 0; i < (unsigned)item->i_options; i++) |
552 | 0 | var_OptionParse(obj, item->ppsz_options[i], |
553 | 0 | !!(item->optflagv[i] & VLC_INPUT_OPTION_TRUSTED)); |
554 | |
|
555 | 0 | for (const input_item_opaque_t *o = item->opaques; o != NULL; o = o->next) |
556 | 0 | { |
557 | 0 | var_Create(obj, o->name, VLC_VAR_ADDRESS); |
558 | 0 | var_SetAddress(obj, o->name, o->value); |
559 | 0 | } |
560 | |
|
561 | 0 | vlc_mutex_unlock(&item->lock); |
562 | 0 | } |
563 | | |
564 | | static int bsearch_strcmp_cb(const void *a, const void *b) |
565 | 0 | { |
566 | 0 | const char *const *entry = b; |
567 | 0 | return strcasecmp(a, *entry); |
568 | 0 | } |
569 | | |
570 | | static bool input_item_IsMaster(const char *psz_filename) |
571 | 0 | { |
572 | 0 | static const char *const ppsz_master_exts[] = { MASTER_EXTENSIONS }; |
573 | |
|
574 | 0 | const char *psz_ext = strrchr(psz_filename, '.'); |
575 | 0 | if (psz_ext == NULL || *(++psz_ext) == '\0') |
576 | 0 | return false; |
577 | | |
578 | 0 | return bsearch(psz_ext, ppsz_master_exts, ARRAY_SIZE(ppsz_master_exts), |
579 | 0 | sizeof(const char *), bsearch_strcmp_cb) != NULL; |
580 | 0 | } |
581 | | |
582 | | bool input_item_slave_GetType(const char *psz_filename, |
583 | | enum slave_type *p_slave_type) |
584 | 0 | { |
585 | 0 | static const char *const ppsz_sub_exts[] = { SLAVE_SPU_EXTENSIONS }; |
586 | 0 | static const char *const ppsz_audio_exts[] = { SLAVE_AUDIO_EXTENSIONS }; |
587 | |
|
588 | 0 | static struct { |
589 | 0 | enum slave_type i_type; |
590 | 0 | const char *const *ppsz_exts; |
591 | 0 | size_t nmemb; |
592 | 0 | } p_slave_list[] = { |
593 | 0 | { SLAVE_TYPE_SPU, ppsz_sub_exts, ARRAY_SIZE(ppsz_sub_exts) }, |
594 | 0 | { SLAVE_TYPE_GENERIC, ppsz_audio_exts, ARRAY_SIZE(ppsz_audio_exts) }, |
595 | 0 | }; |
596 | |
|
597 | 0 | const char *psz_ext = strrchr(psz_filename, '.'); |
598 | 0 | if (psz_ext == NULL || *(++psz_ext) == '\0') |
599 | 0 | return false; |
600 | | |
601 | 0 | for (unsigned int i = 0; i < sizeof(p_slave_list) / sizeof(*p_slave_list); ++i) |
602 | 0 | { |
603 | 0 | if (bsearch(psz_ext, p_slave_list[i].ppsz_exts, p_slave_list[i].nmemb, |
604 | 0 | sizeof(const char *), bsearch_strcmp_cb)) |
605 | 0 | { |
606 | 0 | *p_slave_type = p_slave_list[i].i_type; |
607 | 0 | return true; |
608 | 0 | } |
609 | 0 | } |
610 | 0 | return false; |
611 | 0 | } |
612 | | |
613 | | input_item_slave_t *input_item_slave_New(const char *psz_uri, enum slave_type i_type, |
614 | | enum slave_priority i_priority) |
615 | 0 | { |
616 | 0 | if( !psz_uri ) |
617 | 0 | return NULL; |
618 | | |
619 | 0 | input_item_slave_t *p_slave = malloc( sizeof( *p_slave ) + strlen( psz_uri ) + 1 ); |
620 | 0 | if( !p_slave ) |
621 | 0 | return NULL; |
622 | | |
623 | 0 | p_slave->i_type = i_type; |
624 | 0 | p_slave->i_priority = i_priority; |
625 | 0 | p_slave->b_forced = false; |
626 | 0 | strcpy( p_slave->psz_uri, psz_uri ); |
627 | |
|
628 | 0 | return p_slave; |
629 | 0 | } |
630 | | |
631 | | int input_item_AddSlave(input_item_t *p_item, input_item_slave_t *p_slave) |
632 | 0 | { |
633 | 0 | if( p_item == NULL || p_slave == NULL |
634 | 0 | || p_slave->i_priority < SLAVE_PRIORITY_MATCH_NONE ) |
635 | 0 | return VLC_EGENERIC; |
636 | | |
637 | 0 | vlc_mutex_lock( &p_item->lock ); |
638 | |
|
639 | 0 | TAB_APPEND(p_item->i_slaves, p_item->pp_slaves, p_slave); |
640 | | |
641 | 0 | vlc_mutex_unlock( &p_item->lock ); |
642 | 0 | return VLC_SUCCESS; |
643 | 0 | } |
644 | | |
645 | | static info_category_t *InputItemFindCat( input_item_t *p_item, |
646 | | const char *psz_cat ) |
647 | 0 | { |
648 | 0 | vlc_mutex_assert( &p_item->lock ); |
649 | 0 | if( !psz_cat ) |
650 | 0 | return NULL; |
651 | | |
652 | 0 | info_category_t *p_cat; |
653 | 0 | vlc_list_foreach( p_cat, &p_item->categories, node ) |
654 | 0 | { |
655 | 0 | if( !strcmp( p_cat->psz_name, psz_cat ) ) |
656 | 0 | return p_cat; |
657 | 0 | } |
658 | 0 | return NULL; |
659 | 0 | } |
660 | | |
661 | | bool input_item_Playable(const char *psz_filename) |
662 | 0 | { |
663 | 0 | if (psz_filename == NULL) { |
664 | 0 | return false; |
665 | 0 | } |
666 | | |
667 | 0 | enum slave_type unused; |
668 | 0 | return input_item_slave_GetType(psz_filename, &unused) || input_item_IsMaster(psz_filename); |
669 | 0 | } |
670 | | |
671 | | /** |
672 | | * Get a info item from a given category in a given input item. |
673 | | * |
674 | | * \param p_i The input item to get info from |
675 | | * \param psz_cat String representing the category for the info |
676 | | * \param psz_name String representing the name of the desired info |
677 | | * \return A pointer to the string with the given info if found, or an |
678 | | * empty string otherwise. The caller should free the returned |
679 | | * pointer. |
680 | | */ |
681 | | char *input_item_GetInfo( input_item_t *p_i, |
682 | | const char *psz_cat, |
683 | | const char *psz_name ) |
684 | 0 | { |
685 | 0 | vlc_mutex_lock( &p_i->lock ); |
686 | |
|
687 | 0 | char * result = input_item_GetInfoLocked( p_i, psz_cat, psz_name ); |
688 | |
|
689 | 0 | vlc_mutex_unlock( &p_i->lock ); |
690 | |
|
691 | 0 | return result; |
692 | 0 | } |
693 | | |
694 | | char *input_item_GetInfoLocked( input_item_t *p_i, |
695 | | const char *psz_cat, |
696 | | const char *psz_name ) |
697 | 0 | { |
698 | 0 | vlc_mutex_assert( &p_i->lock ); |
699 | |
|
700 | 0 | info_category_t *p_cat = InputItemFindCat( p_i, psz_cat ); |
701 | 0 | if( p_cat ) |
702 | 0 | { |
703 | 0 | info_t *p_info = info_category_FindInfo( p_cat, psz_name ); |
704 | 0 | if( p_info && p_info->psz_value ) |
705 | 0 | { |
706 | 0 | char *psz_ret = strdup( p_info->psz_value ); |
707 | 0 | return psz_ret; |
708 | 0 | } |
709 | 0 | } |
710 | | |
711 | 0 | return strdup( "" ); |
712 | 0 | } |
713 | | |
714 | | static int InputItemVaAddInfo( input_item_t *p_i, |
715 | | const char *psz_cat, |
716 | | const char *psz_name, |
717 | | const char *psz_format, va_list args ) |
718 | 0 | { |
719 | 0 | vlc_mutex_assert( &p_i->lock ); |
720 | |
|
721 | 0 | info_category_t *p_cat = InputItemFindCat( p_i, psz_cat ); |
722 | 0 | if( !p_cat ) |
723 | 0 | { |
724 | 0 | p_cat = info_category_New( psz_cat ); |
725 | 0 | if( !p_cat ) |
726 | 0 | return VLC_ENOMEM; |
727 | 0 | vlc_list_append( &p_cat->node, &p_i->categories ); |
728 | 0 | } |
729 | 0 | info_t *p_info = info_category_VaAddInfo( p_cat, psz_name, psz_format, args ); |
730 | 0 | if( !p_info || !p_info->psz_value ) |
731 | 0 | return VLC_EGENERIC; |
732 | 0 | return VLC_SUCCESS; |
733 | 0 | } |
734 | | |
735 | | int input_item_AddInfo( input_item_t *p_i, |
736 | | const char *psz_cat, |
737 | | const char *psz_name, |
738 | | const char *psz_format, ... ) |
739 | 0 | { |
740 | 0 | va_list args; |
741 | |
|
742 | 0 | vlc_mutex_lock( &p_i->lock ); |
743 | |
|
744 | 0 | va_start( args, psz_format ); |
745 | 0 | const int i_ret = InputItemVaAddInfo( p_i, psz_cat, psz_name, psz_format, args ); |
746 | 0 | va_end( args ); |
747 | |
|
748 | 0 | vlc_mutex_unlock( &p_i->lock ); |
749 | |
|
750 | 0 | return i_ret; |
751 | 0 | } |
752 | | |
753 | | int input_item_DelInfo( input_item_t *p_i, |
754 | | const char *psz_cat, |
755 | | const char *psz_name ) |
756 | 0 | { |
757 | 0 | vlc_mutex_lock( &p_i->lock ); |
758 | 0 | info_category_t *p_cat = InputItemFindCat( p_i, psz_cat ); |
759 | 0 | if( !p_cat ) |
760 | 0 | { |
761 | 0 | vlc_mutex_unlock( &p_i->lock ); |
762 | 0 | return VLC_EGENERIC; |
763 | 0 | } |
764 | | |
765 | 0 | if( psz_name ) |
766 | 0 | { |
767 | | /* Remove a specific info */ |
768 | 0 | int i_ret = info_category_DeleteInfo( p_cat, psz_name ); |
769 | 0 | if( i_ret ) |
770 | 0 | { |
771 | 0 | vlc_mutex_unlock( &p_i->lock ); |
772 | 0 | return VLC_EGENERIC; |
773 | 0 | } |
774 | 0 | } |
775 | 0 | else |
776 | 0 | { |
777 | | /* Remove the complete categorie */ |
778 | 0 | vlc_list_remove( &p_cat->node ); |
779 | 0 | info_category_Delete( p_cat ); |
780 | 0 | } |
781 | 0 | vlc_mutex_unlock( &p_i->lock ); |
782 | |
|
783 | 0 | return VLC_SUCCESS; |
784 | 0 | } |
785 | | void input_item_ReplaceInfos( input_item_t *p_item, info_category_t *p_cat ) |
786 | 0 | { |
787 | 0 | vlc_mutex_lock( &p_item->lock ); |
788 | 0 | info_category_t *p_old = InputItemFindCat( p_item, p_cat->psz_name ); |
789 | 0 | if( p_old ) |
790 | 0 | { |
791 | 0 | vlc_list_add_after( &p_cat->node, &p_old->node ); |
792 | 0 | vlc_list_remove( &p_old->node ); |
793 | 0 | info_category_Delete( p_old ); |
794 | 0 | } |
795 | 0 | else |
796 | 0 | vlc_list_append( &p_cat->node, &p_item->categories ); |
797 | 0 | vlc_mutex_unlock( &p_item->lock ); |
798 | 0 | } |
799 | | |
800 | | void input_item_MergeInfos( input_item_t *p_item, info_category_t *p_cat ) |
801 | 0 | { |
802 | 0 | vlc_mutex_lock( &p_item->lock ); |
803 | 0 | info_category_t *p_old = InputItemFindCat( p_item, p_cat->psz_name ); |
804 | 0 | if( p_old ) |
805 | 0 | { |
806 | 0 | info_t *info; |
807 | |
|
808 | 0 | info_foreach(info, &p_cat->infos) |
809 | 0 | info_category_ReplaceInfo( p_old, info ); |
810 | 0 | vlc_list_init( &p_cat->infos ); |
811 | 0 | info_category_Delete( p_cat ); |
812 | 0 | } |
813 | 0 | else |
814 | 0 | vlc_list_append( &p_cat->node, &p_item->categories ); |
815 | 0 | vlc_mutex_unlock( &p_item->lock ); |
816 | 0 | } |
817 | | |
818 | | void input_item_SetEpgEvent( input_item_t *p_item, const vlc_epg_event_t *p_epg_evt ) |
819 | 0 | { |
820 | 0 | vlc_mutex_lock( &p_item->lock ); |
821 | |
|
822 | 0 | for( int i = 0; i < p_item->i_epg; i++ ) |
823 | 0 | { |
824 | 0 | vlc_epg_t *p_epg = p_item->pp_epg[i]; |
825 | 0 | for( size_t j = 0; j < p_epg->i_event; j++ ) |
826 | 0 | { |
827 | | /* Same event can exist in more than one table */ |
828 | 0 | if( p_epg->pp_event[j]->i_id == p_epg_evt->i_id ) |
829 | 0 | { |
830 | 0 | vlc_epg_event_t *p_dup = vlc_epg_event_Duplicate( p_epg_evt ); |
831 | 0 | if( p_dup ) |
832 | 0 | { |
833 | 0 | if( p_epg->p_current == p_epg->pp_event[j] ) |
834 | 0 | p_epg->p_current = p_dup; |
835 | 0 | vlc_epg_event_Delete( p_epg->pp_event[j] ); |
836 | 0 | p_epg->pp_event[j] = p_dup; |
837 | 0 | } |
838 | 0 | break; |
839 | 0 | } |
840 | 0 | } |
841 | 0 | } |
842 | 0 | vlc_mutex_unlock( &p_item->lock ); |
843 | 0 | } |
844 | | |
845 | | //#define EPG_DEBUG |
846 | | #ifdef EPG_DEBUG |
847 | | static int InputItemAddInfo( input_item_t *p_i, |
848 | | const char *psz_cat, |
849 | | const char *psz_name, |
850 | | const char *psz_format, ... ) |
851 | | { |
852 | | va_list args; |
853 | | |
854 | | va_start( args, psz_format ); |
855 | | const int i_ret = InputItemVaAddInfo( p_i, psz_cat, psz_name, psz_format, args ); |
856 | | va_end( args ); |
857 | | |
858 | | return i_ret; |
859 | | } |
860 | | #endif |
861 | | |
862 | | void input_item_SetEpg( input_item_t *p_item, const vlc_epg_t *p_update, bool b_current_source ) |
863 | 0 | { |
864 | 0 | vlc_epg_t *p_epg = vlc_epg_Duplicate( p_update ); |
865 | 0 | if( !p_epg ) |
866 | 0 | return; |
867 | | |
868 | 0 | vlc_mutex_lock( &p_item->lock ); |
869 | | |
870 | | /* */ |
871 | 0 | vlc_epg_t **pp_epg = NULL; |
872 | 0 | for( int i = 0; i < p_item->i_epg; i++ ) |
873 | 0 | { |
874 | 0 | if( p_item->pp_epg[i]->i_source_id == p_update->i_source_id && |
875 | 0 | p_item->pp_epg[i]->i_id == p_update->i_id ) |
876 | 0 | { |
877 | 0 | pp_epg = &p_item->pp_epg[i]; |
878 | 0 | break; |
879 | 0 | } |
880 | 0 | } |
881 | | |
882 | | /* replace with new version */ |
883 | 0 | if( pp_epg ) |
884 | 0 | { |
885 | 0 | vlc_epg_Delete( *pp_epg ); |
886 | 0 | if( *pp_epg == p_item->p_epg_table ) /* current table can have changed */ |
887 | 0 | p_item->p_epg_table = NULL; |
888 | 0 | *pp_epg = p_epg; |
889 | 0 | } |
890 | 0 | else |
891 | 0 | { |
892 | 0 | TAB_APPEND( p_item->i_epg, p_item->pp_epg, p_epg ); |
893 | 0 | } |
894 | | |
895 | 0 | if( b_current_source && p_epg->b_present ) |
896 | 0 | p_item->p_epg_table = p_epg; |
897 | |
|
898 | 0 | vlc_mutex_unlock( &p_item->lock ); |
899 | |
|
900 | | #ifdef EPG_DEBUG |
901 | | char *psz_epg; |
902 | | if( asprintf( &psz_epg, "EPG %s", p_epg->psz_name ? p_epg->psz_name : "unknown" ) < 0 ) |
903 | | return; |
904 | | |
905 | | input_item_DelInfo( p_item, psz_epg, NULL ); |
906 | | |
907 | | vlc_mutex_lock( &p_item->lock ); |
908 | | for( size_t i = 0; i < p_epg->i_event; i++ ) |
909 | | { |
910 | | const vlc_epg_event_t *p_evt = p_epg->pp_event[i]; |
911 | | time_t t_start = (time_t)p_evt->i_start; |
912 | | struct tm tm_start; |
913 | | char psz_start[128]; |
914 | | |
915 | | localtime_r( &t_start, &tm_start ); |
916 | | |
917 | | snprintf( psz_start, sizeof(psz_start), "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d", |
918 | | 1900 + tm_start.tm_year, 1 + tm_start.tm_mon, tm_start.tm_mday, |
919 | | tm_start.tm_hour, tm_start.tm_min, tm_start.tm_sec ); |
920 | | if( p_evt->psz_short_description || p_evt->psz_description ) |
921 | | InputItemAddInfo( p_item, psz_epg, psz_start, "%s (%2.2d:%2.2d) - %s %s", |
922 | | p_evt->psz_name, |
923 | | p_evt->i_duration/60/60, (p_evt->i_duration/60)%60, |
924 | | p_evt->psz_short_description ? p_evt->psz_short_description : "" , |
925 | | p_evt->psz_description ? p_evt->psz_description : "" ); |
926 | | else |
927 | | InputItemAddInfo( p_item, psz_epg, psz_start, "%s (%2.2d:%2.2d)", |
928 | | p_evt->psz_name, |
929 | | p_evt->i_duration/60/60, (p_evt->i_duration/60)%60 ); |
930 | | } |
931 | | vlc_mutex_unlock( &p_item->lock ); |
932 | | free( psz_epg ); |
933 | | #endif |
934 | 0 | } |
935 | | |
936 | | void input_item_ChangeEPGSource( input_item_t *p_item, int i_source_id ) |
937 | 0 | { |
938 | 0 | vlc_mutex_lock( &p_item->lock ); |
939 | 0 | p_item->p_epg_table = NULL; |
940 | 0 | if( i_source_id > 0 ) |
941 | 0 | { |
942 | | /* Update pointer to current/next table in the full schedule */ |
943 | 0 | for( int i = 0; i < p_item->i_epg; i++ ) |
944 | 0 | { |
945 | 0 | if( p_item->pp_epg[i]->i_source_id == i_source_id && |
946 | 0 | p_item->pp_epg[i]->b_present ) |
947 | 0 | { |
948 | 0 | p_item->p_epg_table = p_item->pp_epg[i]; |
949 | 0 | break; |
950 | 0 | } |
951 | 0 | } |
952 | 0 | } |
953 | 0 | vlc_mutex_unlock( &p_item->lock ); |
954 | 0 | } |
955 | | |
956 | | void input_item_SetEpgTime( input_item_t *p_item, int64_t i_time ) |
957 | 0 | { |
958 | 0 | vlc_mutex_lock( &p_item->lock ); |
959 | 0 | p_item->i_epg_time = i_time; |
960 | 0 | vlc_mutex_unlock( &p_item->lock ); |
961 | 0 | } |
962 | | |
963 | | void input_item_SetEpgOffline( input_item_t *p_item ) |
964 | 0 | { |
965 | 0 | input_item_ChangeEPGSource( p_item, -1 ); |
966 | |
|
967 | | #ifdef EPG_DEBUG |
968 | | vlc_mutex_lock( &p_item->lock ); |
969 | | const int i_epg_info = p_item->i_epg; |
970 | | if( i_epg_info > 0 ) |
971 | | { |
972 | | char *ppsz_epg_info[i_epg_info]; |
973 | | for( int i = 0; i < p_item->i_epg; i++ ) |
974 | | { |
975 | | const vlc_epg_t *p_epg = p_item->pp_epg[i]; |
976 | | if( asprintf( &ppsz_epg_info[i], "EPG %s", p_epg->psz_name ? p_epg->psz_name : "unknown" ) < 0 ) |
977 | | ppsz_epg_info[i] = NULL; |
978 | | } |
979 | | vlc_mutex_unlock( &p_item->lock ); |
980 | | |
981 | | for( int i = 0; i < i_epg_info; i++ ) |
982 | | { |
983 | | if( !ppsz_epg_info[i] ) |
984 | | continue; |
985 | | input_item_DelInfo( p_item, ppsz_epg_info[i], NULL ); |
986 | | free( ppsz_epg_info[i] ); |
987 | | } |
988 | | } |
989 | | else |
990 | | vlc_mutex_unlock( &p_item->lock ); |
991 | | #endif |
992 | 0 | } |
993 | | |
994 | | input_item_t * |
995 | | input_item_NewExt( const char *psz_uri, const char *psz_name, |
996 | | vlc_tick_t duration, enum input_item_type_e type, enum input_item_net_type i_net ) |
997 | 0 | { |
998 | 0 | input_item_owner_t *owner = calloc( 1, sizeof( *owner ) ); |
999 | 0 | if( unlikely(owner == NULL) ) |
1000 | 0 | return NULL; |
1001 | | |
1002 | 0 | vlc_atomic_rc_init( &owner->rc ); |
1003 | 0 | owner->libvlc_owner = NULL; |
1004 | 0 | owner->libvlc_owner_release = NULL; |
1005 | |
|
1006 | 0 | input_item_t *p_input = &owner->item; |
1007 | |
|
1008 | 0 | p_input->p_meta = vlc_meta_New(); |
1009 | 0 | if( unlikely(p_input->p_meta == NULL) ) |
1010 | 0 | { |
1011 | 0 | free(owner); |
1012 | 0 | return NULL; |
1013 | 0 | } |
1014 | | |
1015 | 0 | vlc_mutex_init( &p_input->lock ); |
1016 | |
|
1017 | 0 | p_input->psz_name = NULL; |
1018 | 0 | if( psz_name ) |
1019 | 0 | input_item_SetName( p_input, psz_name ); |
1020 | |
|
1021 | 0 | p_input->psz_uri = NULL; |
1022 | 0 | if( psz_uri ) |
1023 | 0 | input_item_SetURI( p_input, psz_uri ); |
1024 | 0 | else |
1025 | 0 | { |
1026 | 0 | p_input->i_type = ITEM_TYPE_UNKNOWN; |
1027 | 0 | p_input->b_net = false; |
1028 | 0 | } |
1029 | |
|
1030 | 0 | TAB_INIT( p_input->i_options, p_input->ppsz_options ); |
1031 | 0 | p_input->optflagc = 0; |
1032 | 0 | p_input->optflagv = NULL; |
1033 | 0 | p_input->opaques = NULL; |
1034 | |
|
1035 | 0 | p_input->i_duration = duration; |
1036 | 0 | vlc_list_init( &p_input->categories ); |
1037 | 0 | vlc_vector_init( &p_input->es_vec ); |
1038 | 0 | p_input->p_stats = NULL; |
1039 | 0 | TAB_INIT( p_input->i_epg, p_input->pp_epg ); |
1040 | 0 | TAB_INIT( p_input->i_slaves, p_input->pp_slaves ); |
1041 | |
|
1042 | 0 | if( type != ITEM_TYPE_UNKNOWN ) |
1043 | 0 | p_input->i_type = type; |
1044 | |
|
1045 | 0 | if( i_net != ITEM_NET_UNKNOWN ) |
1046 | 0 | p_input->b_net = i_net == ITEM_NET; |
1047 | 0 | return p_input; |
1048 | 0 | } |
1049 | | |
1050 | | input_item_t *input_item_Copy( input_item_t *p_input ) |
1051 | 0 | { |
1052 | 0 | input_item_t *item; |
1053 | |
|
1054 | 0 | vlc_mutex_lock( &p_input->lock ); |
1055 | |
|
1056 | 0 | item = input_item_NewExt( p_input->psz_uri, p_input->psz_name, |
1057 | 0 | p_input->i_duration, p_input->i_type, |
1058 | 0 | ITEM_NET_UNKNOWN ); |
1059 | 0 | if( unlikely(item == NULL) ) |
1060 | 0 | { |
1061 | 0 | vlc_mutex_unlock( &p_input->lock ); |
1062 | 0 | return NULL; |
1063 | 0 | } |
1064 | | |
1065 | 0 | vlc_meta_Merge( item->p_meta, p_input->p_meta ); |
1066 | 0 | item->b_net = p_input->b_net; |
1067 | |
|
1068 | 0 | if( p_input->i_slaves > 0 ) |
1069 | 0 | { |
1070 | 0 | for( int i = 0; i < p_input->i_slaves; i++ ) |
1071 | 0 | { |
1072 | 0 | input_item_slave_t* slave = input_item_slave_New( |
1073 | 0 | p_input->pp_slaves[i]->psz_uri, |
1074 | 0 | p_input->pp_slaves[i]->i_type, |
1075 | 0 | p_input->pp_slaves[i]->i_priority); |
1076 | 0 | if( unlikely(slave != NULL) ) |
1077 | 0 | { |
1078 | 0 | TAB_APPEND(item->i_slaves, item->pp_slaves, slave); |
1079 | 0 | } |
1080 | 0 | } |
1081 | 0 | } |
1082 | | |
1083 | 0 | struct input_item_es item_es; |
1084 | 0 | struct input_item_es cpy_item_es; |
1085 | 0 | vlc_vector_foreach(item_es, &p_input->es_vec) { |
1086 | 0 | cpy_item_es.id_stable = item_es.id_stable; |
1087 | 0 | cpy_item_es.id = strdup(item_es.id); |
1088 | 0 | if (cpy_item_es.id == NULL) { |
1089 | 0 | goto error; |
1090 | 0 | } |
1091 | 0 | es_format_Init(&cpy_item_es.es, UNKNOWN_ES, 0); |
1092 | 0 | if (es_format_Copy(&cpy_item_es.es, &item_es.es) != VLC_SUCCESS) { |
1093 | | /* es_format_Copy didn't stop if an allocation fail so we need to |
1094 | | * clear all memory allocated */ |
1095 | 0 | es_format_Clean(&cpy_item_es.es); |
1096 | 0 | free(cpy_item_es.id); |
1097 | 0 | goto error; |
1098 | 0 | } |
1099 | 0 | vlc_vector_push(&item->es_vec, cpy_item_es); |
1100 | 0 | } |
1101 | | |
1102 | 0 | vlc_mutex_unlock( &p_input->lock ); |
1103 | | |
1104 | | /* No need to lock; no other thread has seen this new item yet. */ |
1105 | 0 | input_item_CopyOptions( item, p_input ); |
1106 | 0 | return item; |
1107 | | |
1108 | 0 | error: |
1109 | 0 | vlc_mutex_unlock(&p_input->lock); |
1110 | 0 | input_item_Release(item); |
1111 | 0 | return NULL; |
1112 | 0 | } |
1113 | | |
1114 | | int input_item_Update(input_item_t *dst, input_item_t *src) |
1115 | 0 | { |
1116 | 0 | assert(dst != NULL); |
1117 | 0 | assert(src != NULL); |
1118 | |
|
1119 | 0 | if (strcmp(dst->psz_uri, src->psz_uri)) { |
1120 | 0 | return VLC_EGENERIC; |
1121 | 0 | } |
1122 | | |
1123 | 0 | input_item_t *tmp = input_item_Copy(src); |
1124 | 0 | if (tmp == NULL) { |
1125 | 0 | return VLC_EGENERIC; |
1126 | 0 | } |
1127 | | |
1128 | 0 | vlc_mutex_lock(&dst->lock); |
1129 | 0 | if (dst->psz_name == NULL) { |
1130 | 0 | dst->psz_name = tmp->psz_name; |
1131 | 0 | tmp->psz_name = NULL; |
1132 | 0 | } |
1133 | 0 | dst->i_duration = tmp->i_duration; |
1134 | |
|
1135 | 0 | struct input_item_es item_es; |
1136 | 0 | vlc_vector_foreach(item_es, &dst->es_vec) { |
1137 | 0 | free(item_es.id); |
1138 | 0 | es_format_Clean(&item_es.es); |
1139 | 0 | } |
1140 | 0 | vlc_vector_clear(&dst->es_vec); |
1141 | 0 | dst->es_vec = tmp->es_vec; |
1142 | 0 | vlc_vector_init(&tmp->es_vec); |
1143 | |
|
1144 | 0 | if (tmp->p_meta != NULL) { |
1145 | 0 | if (dst->p_meta != NULL) { |
1146 | 0 | vlc_meta_Merge(dst->p_meta, tmp->p_meta); |
1147 | 0 | } else { |
1148 | 0 | dst->p_meta = tmp->p_meta; |
1149 | 0 | tmp->p_meta = NULL; |
1150 | 0 | } |
1151 | 0 | } |
1152 | |
|
1153 | 0 | for( int i = 0; i < dst->i_slaves; i++ ) |
1154 | 0 | input_item_slave_Delete( dst->pp_slaves[i] ); |
1155 | 0 | TAB_CLEAN(dst->i_slaves, dst->pp_slaves ); |
1156 | 0 | dst->i_slaves = tmp->i_slaves; |
1157 | 0 | tmp->i_slaves = 0; |
1158 | 0 | dst->pp_slaves = tmp->pp_slaves; |
1159 | 0 | tmp->pp_slaves = NULL; |
1160 | 0 | vlc_mutex_unlock(&dst->lock); |
1161 | |
|
1162 | 0 | input_item_Release(tmp); |
1163 | 0 | return VLC_SUCCESS; |
1164 | 0 | } |
1165 | | |
1166 | | struct item_type_entry |
1167 | | { |
1168 | | const char *psz_scheme; |
1169 | | enum input_item_type_e i_type; |
1170 | | bool b_net; |
1171 | | }; |
1172 | | |
1173 | | static int typecmp( const void *key, const void *entry ) |
1174 | 0 | { |
1175 | 0 | const struct item_type_entry *type = entry; |
1176 | 0 | const char *uri = key, *scheme = type->psz_scheme; |
1177 | |
|
1178 | 0 | return strncmp( uri, scheme, strlen( scheme ) ); |
1179 | 0 | } |
1180 | | |
1181 | | /* Guess the type of the item using the beginning of the mrl */ |
1182 | | static enum input_item_type_e GuessType( const input_item_t *p_item, bool *p_net ) |
1183 | 0 | { |
1184 | 0 | static const struct item_type_entry tab[] = |
1185 | 0 | { /* /!\ Alphabetical order /!\ */ |
1186 | | /* Short match work, not just exact match */ |
1187 | 0 | { "alsa", ITEM_TYPE_CARD, false }, |
1188 | 0 | { "atsc", ITEM_TYPE_CARD, false }, |
1189 | 0 | { "avcapt", ITEM_TYPE_CARD, false }, /* AVCapture */ |
1190 | 0 | { "bd", ITEM_TYPE_DISC, false }, |
1191 | 0 | { "bluray", ITEM_TYPE_DISC, false }, |
1192 | 0 | { "cable", ITEM_TYPE_CARD, false }, |
1193 | 0 | { "cdda", ITEM_TYPE_DISC, false }, |
1194 | 0 | { "cqam", ITEM_TYPE_CARD, false }, |
1195 | 0 | { "dc1394", ITEM_TYPE_CARD, false }, |
1196 | 0 | { "dccp", ITEM_TYPE_STREAM, true }, |
1197 | 0 | { "deckli", ITEM_TYPE_CARD, false }, /* decklink */ |
1198 | 0 | { "dir", ITEM_TYPE_DIRECTORY, false }, |
1199 | 0 | { "dshow", ITEM_TYPE_CARD, false }, |
1200 | 0 | { "dtv", ITEM_TYPE_CARD, false }, |
1201 | 0 | { "dvb", ITEM_TYPE_CARD, false }, |
1202 | 0 | { "dvd", ITEM_TYPE_DISC, false }, |
1203 | 0 | { "fd", ITEM_TYPE_UNKNOWN, false }, |
1204 | 0 | { "file", ITEM_TYPE_FILE, false }, |
1205 | 0 | { "ftp", ITEM_TYPE_FILE, true }, |
1206 | 0 | { "gopher", ITEM_TYPE_STREAM, true }, |
1207 | 0 | { "http", ITEM_TYPE_FILE, true }, |
1208 | 0 | { "icyx", ITEM_TYPE_STREAM, true }, |
1209 | 0 | { "imem", ITEM_TYPE_UNKNOWN, false }, |
1210 | 0 | { "isdb-", ITEM_TYPE_CARD, false }, |
1211 | 0 | { "itpc", ITEM_TYPE_PLAYLIST, true }, |
1212 | 0 | { "jack", ITEM_TYPE_CARD, false }, |
1213 | 0 | { "linsys", ITEM_TYPE_CARD, false }, |
1214 | 0 | { "live", ITEM_TYPE_STREAM, true }, /* livedotcom */ |
1215 | 0 | { "mms", ITEM_TYPE_STREAM, true }, |
1216 | 0 | { "mtp", ITEM_TYPE_DISC, false }, |
1217 | 0 | { "ndi", ITEM_TYPE_STREAM, true}, |
1218 | 0 | { "nfs", ITEM_TYPE_FILE, true }, |
1219 | 0 | { "ofdm", ITEM_TYPE_CARD, false }, |
1220 | 0 | { "oss", ITEM_TYPE_CARD, false }, |
1221 | 0 | { "pnm", ITEM_TYPE_STREAM, true }, |
1222 | 0 | { "pulse", ITEM_TYPE_CARD, false }, |
1223 | 0 | { "qam", ITEM_TYPE_CARD, false }, |
1224 | 0 | { "qpsk", ITEM_TYPE_CARD, false }, |
1225 | 0 | { "qtsound",ITEM_TYPE_CARD, false }, |
1226 | 0 | { "raw139", ITEM_TYPE_CARD, false }, /* raw1394 */ |
1227 | 0 | { "rt", ITEM_TYPE_STREAM, true }, /* rtp, rtsp, rtmp */ |
1228 | 0 | { "satell", ITEM_TYPE_CARD, false }, /* satellite */ |
1229 | 0 | { "satip", ITEM_TYPE_STREAM, true }, /* satellite over ip */ |
1230 | 0 | { "screen", ITEM_TYPE_CARD, false }, |
1231 | 0 | { "sdp", ITEM_TYPE_STREAM, true }, |
1232 | 0 | { "sftp", ITEM_TYPE_FILE, true }, |
1233 | 0 | { "shm", ITEM_TYPE_CARD, false }, |
1234 | 0 | { "smb", ITEM_TYPE_FILE, true }, |
1235 | 0 | { "stream", ITEM_TYPE_STREAM, false }, |
1236 | 0 | { "svcd", ITEM_TYPE_DISC, false }, |
1237 | 0 | { "tcp", ITEM_TYPE_STREAM, true }, |
1238 | 0 | { "terres", ITEM_TYPE_CARD, false }, /* terrestrial */ |
1239 | 0 | { "udp", ITEM_TYPE_STREAM, true }, |
1240 | 0 | { "unsv", ITEM_TYPE_STREAM, true }, |
1241 | 0 | { "upnp", ITEM_TYPE_FILE, true }, |
1242 | 0 | { "v4l", ITEM_TYPE_CARD, false }, |
1243 | 0 | { "vcd", ITEM_TYPE_DISC, false }, |
1244 | 0 | { "vdr", ITEM_TYPE_STREAM, true }, |
1245 | 0 | { "vlctest_",ITEM_TYPE_FILE, false }, |
1246 | 0 | { "wasapi", ITEM_TYPE_CARD, false }, |
1247 | 0 | { "window", ITEM_TYPE_CARD, false }, |
1248 | 0 | }; |
1249 | |
|
1250 | 0 | #ifndef NDEBUG |
1251 | 0 | for( size_t i = 1; i < ARRAY_SIZE( tab ); i++ ) |
1252 | 0 | assert( typecmp( (tab + i)->psz_scheme, tab + i - 1 ) > 0 ); |
1253 | 0 | #endif |
1254 | |
|
1255 | 0 | *p_net = false; |
1256 | |
|
1257 | 0 | if( strchr( p_item->psz_uri, ':' ) == NULL ) |
1258 | 0 | return ITEM_TYPE_UNKNOWN; /* invalid URI */ |
1259 | | |
1260 | 0 | const struct item_type_entry *e = |
1261 | 0 | bsearch( p_item->psz_uri, tab, ARRAY_SIZE( tab ), |
1262 | 0 | sizeof( tab[0] ), typecmp ); |
1263 | 0 | if( e == NULL ) |
1264 | 0 | return ITEM_TYPE_UNKNOWN; |
1265 | | |
1266 | 0 | *p_net = e->b_net; |
1267 | 0 | return e->i_type; |
1268 | 0 | } |
1269 | | |
1270 | | input_item_node_t *input_item_node_Create( input_item_t *p_input ) |
1271 | 0 | { |
1272 | 0 | input_item_node_t* p_node = malloc( sizeof( input_item_node_t ) ); |
1273 | 0 | if( !p_node ) |
1274 | 0 | return NULL; |
1275 | | |
1276 | 0 | assert( p_input ); |
1277 | |
|
1278 | 0 | p_node->p_item = p_input; |
1279 | 0 | input_item_Hold( p_input ); |
1280 | |
|
1281 | 0 | p_node->i_children = 0; |
1282 | 0 | p_node->pp_children = NULL; |
1283 | |
|
1284 | 0 | return p_node; |
1285 | 0 | } |
1286 | | |
1287 | | void input_item_node_Delete( input_item_node_t *p_node ) |
1288 | 0 | { |
1289 | 0 | for( int i = 0; i < p_node->i_children; i++ ) |
1290 | 0 | input_item_node_Delete( p_node->pp_children[i] ); |
1291 | |
|
1292 | 0 | input_item_Release( p_node->p_item ); |
1293 | 0 | free( p_node->pp_children ); |
1294 | 0 | free( p_node ); |
1295 | 0 | } |
1296 | | |
1297 | | input_item_node_t *input_item_node_AppendItem( input_item_node_t *p_node, input_item_t *p_item ) |
1298 | 0 | { |
1299 | 0 | input_item_node_t *p_new_child = input_item_node_Create( p_item ); |
1300 | 0 | if( !p_new_child ) return NULL; |
1301 | | |
1302 | 0 | input_item_node_AppendNode( p_node, p_new_child ); |
1303 | 0 | return p_new_child; |
1304 | 0 | } |
1305 | | |
1306 | | void input_item_node_AppendNode( input_item_node_t *p_parent, |
1307 | | input_item_node_t *p_child ) |
1308 | 0 | { |
1309 | 0 | assert(p_parent != NULL); |
1310 | 0 | assert(p_child != NULL); |
1311 | 0 | TAB_APPEND(p_parent->i_children, p_parent->pp_children, p_child); |
1312 | 0 | } |
1313 | | |
1314 | | void input_item_node_RemoveNode( input_item_node_t *parent, |
1315 | | input_item_node_t *child ) |
1316 | 0 | { |
1317 | 0 | TAB_REMOVE(parent->i_children, parent->pp_children, child); |
1318 | 0 | } |
1319 | | |
1320 | | /* Called by es_out when a new Elementary Stream is added or updated. */ |
1321 | | void input_item_UpdateTracksInfo(input_item_t *item, const es_format_t *fmt, |
1322 | | const char *es_id, bool es_id_stable) |
1323 | 0 | { |
1324 | 0 | assert( es_id != NULL ); |
1325 | 0 | vlc_mutex_lock( &item->lock ); |
1326 | |
|
1327 | 0 | for( size_t i = 0; i < item->es_vec.size; ++i ) |
1328 | 0 | { |
1329 | 0 | struct input_item_es *item_es = &item->es_vec.data[i]; |
1330 | 0 | if (strcmp(item_es->id, es_id) != 0) |
1331 | 0 | continue; |
1332 | | |
1333 | | /* We've found the right ES, replace it */ |
1334 | 0 | es_format_Clean(&item_es->es); |
1335 | 0 | es_format_Copy(&item_es->es, fmt); |
1336 | 0 | vlc_mutex_unlock( &item->lock ); |
1337 | 0 | return; |
1338 | 0 | } |
1339 | | |
1340 | 0 | char *es_id_dup = strdup( es_id ); |
1341 | 0 | if( es_id_dup == NULL ) |
1342 | 0 | { |
1343 | 0 | vlc_mutex_unlock( &item->lock ); |
1344 | 0 | return; |
1345 | 0 | } |
1346 | | |
1347 | | /* ES not found, insert it */ |
1348 | 0 | if (vlc_vector_push_hole( &item->es_vec, 1 )) |
1349 | 0 | { |
1350 | 0 | struct input_item_es *item_es = &item->es_vec.data[item->es_vec.size - 1]; |
1351 | 0 | item_es->id = es_id_dup; |
1352 | 0 | item_es->id_stable = es_id_stable; |
1353 | 0 | es_format_Copy( &item_es->es, fmt ); |
1354 | 0 | } |
1355 | 0 | else |
1356 | 0 | free( es_id_dup ); |
1357 | |
|
1358 | 0 | vlc_mutex_unlock( &item->lock ); |
1359 | 0 | } |
1360 | | |
1361 | | char *input_item_CreateFilename(input_item_t *item, |
1362 | | const char *dir, const char *filenamefmt, |
1363 | | const char *ext) |
1364 | 0 | { |
1365 | 0 | char *path; |
1366 | 0 | char *filename = str_format(NULL, item, filenamefmt); |
1367 | 0 | if (unlikely(filename == NULL)) |
1368 | 0 | return NULL; |
1369 | | |
1370 | 0 | filename_sanitize(filename); |
1371 | |
|
1372 | 0 | if (((ext != NULL) |
1373 | 0 | ? asprintf(&path, "%s"DIR_SEP"%s.%s", dir, filename, ext) |
1374 | 0 | : asprintf(&path, "%s"DIR_SEP"%s", dir, filename)) < 0) |
1375 | 0 | path = NULL; |
1376 | |
|
1377 | 0 | free(filename); |
1378 | 0 | return path; |
1379 | 0 | } |
1380 | | |
1381 | | static int rdh_compar_type(input_item_t *p1, input_item_t *p2) |
1382 | 0 | { |
1383 | 0 | if (p1->i_type != p2->i_type) |
1384 | 0 | { |
1385 | 0 | if (p1->i_type == ITEM_TYPE_DIRECTORY) |
1386 | 0 | return -1; |
1387 | 0 | if (p2->i_type == ITEM_TYPE_DIRECTORY) |
1388 | 0 | return 1; |
1389 | 0 | } |
1390 | 0 | return 0; |
1391 | 0 | } |
1392 | | |
1393 | | static int rdh_compar_filename(const void *a, const void *b) |
1394 | 0 | { |
1395 | 0 | input_item_node_t *const *na = a, *const *nb = b; |
1396 | 0 | input_item_t *ia = (*na)->p_item, *ib = (*nb)->p_item; |
1397 | |
|
1398 | 0 | int i_ret = rdh_compar_type(ia, ib); |
1399 | 0 | if (i_ret != 0) |
1400 | 0 | return i_ret; |
1401 | | |
1402 | 0 | return vlc_filenamecmp(ia->psz_name, ib->psz_name); |
1403 | 0 | } |
1404 | | |
1405 | | static void rdh_sort(input_item_node_t *p_node) |
1406 | 0 | { |
1407 | 0 | if (p_node->i_children <= 0) |
1408 | 0 | return; |
1409 | | |
1410 | | /* Sort current node */ |
1411 | 0 | qsort(p_node->pp_children, p_node->i_children, |
1412 | 0 | sizeof(input_item_node_t *), rdh_compar_filename); |
1413 | | |
1414 | | /* Sort all children */ |
1415 | 0 | for (int i = 0; i < p_node->i_children; i++) |
1416 | 0 | rdh_sort(p_node->pp_children[i]); |
1417 | 0 | } |
1418 | | |
1419 | | /** |
1420 | | * Does the provided file name has one of the extension provided ? |
1421 | | */ |
1422 | | static bool rdh_file_has_ext(const char *psz_filename, |
1423 | | const char *psz_ignored_exts) |
1424 | 0 | { |
1425 | 0 | if (psz_ignored_exts == NULL) |
1426 | 0 | return false; |
1427 | | |
1428 | 0 | const char *ext = strrchr(psz_filename, '.'); |
1429 | 0 | if (ext == NULL) |
1430 | 0 | return false; |
1431 | | |
1432 | 0 | size_t extlen = strlen(++ext); |
1433 | |
|
1434 | 0 | for (const char *type = psz_ignored_exts, *end; type[0]; type = end + 1) |
1435 | 0 | { |
1436 | 0 | end = strchr(type, ','); |
1437 | 0 | if (end == NULL) |
1438 | 0 | end = type + strlen(type); |
1439 | |
|
1440 | 0 | if (type + extlen == end && !strncasecmp(ext, type, extlen)) |
1441 | 0 | return true; |
1442 | | |
1443 | 0 | if (*end == '\0') |
1444 | 0 | break; |
1445 | 0 | } |
1446 | | |
1447 | 0 | return false; |
1448 | 0 | } |
1449 | | |
1450 | | static bool rdh_file_is_ignored(struct vlc_readdir_helper *p_rdh, |
1451 | | const char *psz_filename) |
1452 | 0 | { |
1453 | 0 | return (psz_filename[0] == '\0' |
1454 | 0 | || strcmp(psz_filename, ".") == 0 |
1455 | 0 | || strcmp(psz_filename, "..") == 0 |
1456 | 0 | || (!p_rdh->b_show_hiddenfiles && psz_filename[0] == '.') |
1457 | 0 | || rdh_file_has_ext(psz_filename, p_rdh->psz_ignored_exts)); |
1458 | 0 | } |
1459 | | |
1460 | | struct rdh_slave |
1461 | | { |
1462 | | input_item_slave_t *p_slave; |
1463 | | char *psz_filename; |
1464 | | input_item_node_t *p_node; |
1465 | | }; |
1466 | | |
1467 | | struct rdh_dir |
1468 | | { |
1469 | | input_item_node_t *p_node; |
1470 | | char psz_path[]; |
1471 | | }; |
1472 | | |
1473 | | static char *rdh_name_from_filename(const char *psz_filename) |
1474 | 0 | { |
1475 | | /* remove leading white spaces */ |
1476 | 0 | while (*psz_filename != '\0' && *psz_filename == ' ') |
1477 | 0 | psz_filename++; |
1478 | |
|
1479 | 0 | char *psz_name = strdup(psz_filename); |
1480 | 0 | if (!psz_name) |
1481 | 0 | return NULL; |
1482 | | |
1483 | | /* remove extension */ |
1484 | 0 | char *psz_ptr = strrchr(psz_name, '.'); |
1485 | 0 | if (psz_ptr && psz_ptr != psz_name) |
1486 | 0 | *psz_ptr = '\0'; |
1487 | | |
1488 | | /* remove trailing white spaces */ |
1489 | 0 | int i = strlen(psz_name) - 1; |
1490 | 0 | while (psz_name[i] == ' ' && i >= 0) |
1491 | 0 | psz_name[i--] = '\0'; |
1492 | | |
1493 | | /* convert to lower case */ |
1494 | 0 | psz_ptr = psz_name; |
1495 | 0 | while (*psz_ptr != '\0') |
1496 | 0 | { |
1497 | 0 | *psz_ptr = tolower(*psz_ptr); |
1498 | 0 | psz_ptr++; |
1499 | 0 | } |
1500 | |
|
1501 | 0 | return psz_name; |
1502 | 0 | } |
1503 | | |
1504 | | static uint8_t rdh_get_slave_priority(input_item_t *p_item, |
1505 | | input_item_slave_t *p_slave, |
1506 | | const char *psz_slave_filename) |
1507 | 0 | { |
1508 | 0 | uint8_t i_priority = SLAVE_PRIORITY_MATCH_NONE; |
1509 | 0 | char *psz_item_name = rdh_name_from_filename(p_item->psz_name); |
1510 | 0 | char *psz_slave_name = rdh_name_from_filename(psz_slave_filename); |
1511 | |
|
1512 | 0 | if (!psz_item_name || !psz_slave_name) |
1513 | 0 | goto done; |
1514 | | |
1515 | 0 | size_t i_item_len = strlen(psz_item_name); |
1516 | 0 | size_t i_slave_len = strlen(psz_slave_name); |
1517 | | |
1518 | | /* The slave name len should not be twice longer than the item name len. */ |
1519 | 0 | if (i_item_len > i_slave_len || i_slave_len > 2 * i_item_len) |
1520 | 0 | goto done; |
1521 | | |
1522 | | /* check if the names match exactly */ |
1523 | 0 | if (!strcmp(psz_item_name, psz_slave_name)) |
1524 | 0 | { |
1525 | 0 | i_priority = SLAVE_PRIORITY_MATCH_ALL; |
1526 | 0 | goto done; |
1527 | 0 | } |
1528 | | |
1529 | | /* "cdg" slaves have to be a full match */ |
1530 | 0 | if (p_slave->i_type == SLAVE_TYPE_SPU) |
1531 | 0 | { |
1532 | 0 | char *psz_ext = strrchr(psz_slave_name, '.'); |
1533 | 0 | if (psz_ext != NULL && strcasecmp(++psz_ext, "cdg") == 0) |
1534 | 0 | goto done; |
1535 | 0 | } |
1536 | | |
1537 | | /* check if the item name is a substring of the slave name */ |
1538 | 0 | const char *psz_sub = strstr(psz_slave_name, psz_item_name); |
1539 | |
|
1540 | 0 | if (psz_sub) |
1541 | 0 | { |
1542 | | /* check if the item name was found at the end of the slave name */ |
1543 | 0 | if (strlen(psz_sub + strlen(psz_item_name)) == 0) |
1544 | 0 | { |
1545 | 0 | i_priority = SLAVE_PRIORITY_MATCH_RIGHT; |
1546 | 0 | goto done; |
1547 | 0 | } |
1548 | 0 | else |
1549 | 0 | { |
1550 | 0 | i_priority = SLAVE_PRIORITY_MATCH_LEFT; |
1551 | 0 | goto done; |
1552 | 0 | } |
1553 | 0 | } |
1554 | | |
1555 | 0 | done: |
1556 | 0 | free(psz_item_name); |
1557 | 0 | free(psz_slave_name); |
1558 | 0 | return i_priority; |
1559 | 0 | } |
1560 | | |
1561 | | static int rdh_should_match_idx(struct vlc_readdir_helper *p_rdh, |
1562 | | struct rdh_slave *p_rdh_sub) |
1563 | 0 | { |
1564 | 0 | char *psz_ext = strrchr(p_rdh_sub->psz_filename, '.'); |
1565 | 0 | if (!psz_ext) |
1566 | 0 | return false; |
1567 | 0 | psz_ext++; |
1568 | |
|
1569 | 0 | if (strcasecmp(psz_ext, "sub") != 0) |
1570 | 0 | return false; |
1571 | | |
1572 | 0 | for (size_t i = 0; i < p_rdh->i_slaves; i++) |
1573 | 0 | { |
1574 | 0 | struct rdh_slave *p_rdh_slave = p_rdh->pp_slaves[i]; |
1575 | |
|
1576 | 0 | if (p_rdh_slave == NULL || p_rdh_slave == p_rdh_sub) |
1577 | 0 | continue; |
1578 | | |
1579 | | /* check that priorities match */ |
1580 | 0 | if (p_rdh_slave->p_slave->i_priority != |
1581 | 0 | p_rdh_sub->p_slave->i_priority) |
1582 | 0 | continue; |
1583 | | |
1584 | | /* check that the filenames without extension match */ |
1585 | 0 | if (strncasecmp(p_rdh_sub->psz_filename, p_rdh_slave->psz_filename, |
1586 | 0 | strlen(p_rdh_sub->psz_filename) - 3 ) != 0) |
1587 | 0 | continue; |
1588 | | |
1589 | | /* check that we have an idx file */ |
1590 | 0 | char *psz_ext_idx = strrchr(p_rdh_slave->psz_filename, '.'); |
1591 | 0 | if (psz_ext_idx == NULL) |
1592 | 0 | continue; |
1593 | 0 | psz_ext_idx++; |
1594 | 0 | if (strcasecmp(psz_ext_idx, "idx" ) == 0) |
1595 | 0 | return true; |
1596 | 0 | } |
1597 | 0 | return false; |
1598 | 0 | } |
1599 | | |
1600 | | static void rdh_attach_slaves(struct vlc_readdir_helper *p_rdh, |
1601 | | input_item_node_t *p_parent_node) |
1602 | 0 | { |
1603 | 0 | if (p_rdh->i_sub_autodetect_fuzzy == 0) |
1604 | 0 | return; |
1605 | | |
1606 | | /* Try to match slaves for each items of the node */ |
1607 | 0 | for (int i = 0; i < p_parent_node->i_children; i++) |
1608 | 0 | { |
1609 | 0 | input_item_node_t *p_node = p_parent_node->pp_children[i]; |
1610 | 0 | input_item_t *p_item = p_node->p_item; |
1611 | |
|
1612 | 0 | enum slave_type unused; |
1613 | 0 | if (!input_item_IsMaster(p_item->psz_name) |
1614 | 0 | || input_item_slave_GetType(p_item->psz_name, &unused)) |
1615 | 0 | continue; /* don't match 2 possible slaves between each others */ |
1616 | | |
1617 | 0 | for (size_t j = 0; j < p_rdh->i_slaves; j++) |
1618 | 0 | { |
1619 | 0 | struct rdh_slave *p_rdh_slave = p_rdh->pp_slaves[j]; |
1620 | | |
1621 | | /* Don't try to match slaves with themselves or slaves already |
1622 | | * attached with the higher priority */ |
1623 | 0 | if (p_rdh_slave->p_node == p_node |
1624 | 0 | || p_rdh_slave->p_slave->i_priority == SLAVE_PRIORITY_MATCH_ALL) |
1625 | 0 | continue; |
1626 | | |
1627 | 0 | uint8_t i_priority = |
1628 | 0 | rdh_get_slave_priority(p_item, p_rdh_slave->p_slave, |
1629 | 0 | p_rdh_slave->psz_filename); |
1630 | |
|
1631 | 0 | if (i_priority < p_rdh->i_sub_autodetect_fuzzy) |
1632 | 0 | continue; |
1633 | | |
1634 | | /* Drop the ".sub" slave if a ".idx" slave matches */ |
1635 | 0 | if (p_rdh_slave->p_slave->i_type == SLAVE_TYPE_SPU |
1636 | 0 | && rdh_should_match_idx(p_rdh, p_rdh_slave)) |
1637 | 0 | continue; |
1638 | | |
1639 | 0 | input_item_slave_t *p_slave = |
1640 | 0 | input_item_slave_New(p_rdh_slave->p_slave->psz_uri, |
1641 | 0 | p_rdh_slave->p_slave->i_type, |
1642 | 0 | i_priority); |
1643 | 0 | if (p_slave == NULL) |
1644 | 0 | break; |
1645 | | |
1646 | 0 | if (input_item_AddSlave(p_item, p_slave) != VLC_SUCCESS) |
1647 | 0 | { |
1648 | 0 | input_item_slave_Delete(p_slave); |
1649 | 0 | break; |
1650 | 0 | } |
1651 | | |
1652 | | /* Remove the corresponding node if any: This slave won't be |
1653 | | * added in the parent node */ |
1654 | 0 | if (p_rdh_slave->p_node != NULL) |
1655 | 0 | { |
1656 | 0 | input_item_node_RemoveNode(p_parent_node, p_rdh_slave->p_node); |
1657 | 0 | input_item_node_Delete(p_rdh_slave->p_node); |
1658 | 0 | p_rdh_slave->p_node = NULL; |
1659 | 0 | } |
1660 | |
|
1661 | 0 | p_rdh_slave->p_slave->i_priority = i_priority; |
1662 | 0 | } |
1663 | 0 | } |
1664 | | |
1665 | | /* Attach all children */ |
1666 | 0 | for (int i = 0; i < p_parent_node->i_children; i++) |
1667 | 0 | rdh_attach_slaves(p_rdh, p_parent_node->pp_children[i]); |
1668 | 0 | } |
1669 | | |
1670 | | static int rdh_unflatten(struct vlc_readdir_helper *p_rdh, |
1671 | | input_item_node_t **pp_node, const char *psz_path, |
1672 | | int i_net) |
1673 | 0 | { |
1674 | | /* Create an input input for each sub folders that is contained in the full |
1675 | | * path. Update pp_node to point to the direct parent of the future item to |
1676 | | * add. */ |
1677 | |
|
1678 | 0 | assert(psz_path != NULL); |
1679 | 0 | const char *psz_subpaths = psz_path; |
1680 | |
|
1681 | 0 | while ((psz_subpaths = strchr(psz_subpaths, '/'))) |
1682 | 0 | { |
1683 | 0 | input_item_node_t *p_subnode = NULL; |
1684 | | |
1685 | | /* Check if this sub folder item was already added */ |
1686 | 0 | for (size_t i = 0; i < p_rdh->i_dirs && p_subnode == NULL; i++) |
1687 | 0 | { |
1688 | 0 | struct rdh_dir *rdh_dir = p_rdh->pp_dirs[i]; |
1689 | 0 | if (!strncmp(rdh_dir->psz_path, psz_path, psz_subpaths - psz_path)) |
1690 | 0 | p_subnode = rdh_dir->p_node; |
1691 | 0 | } |
1692 | | |
1693 | | /* The sub folder item doesn't exist, so create it */ |
1694 | 0 | if (p_subnode == NULL) |
1695 | 0 | { |
1696 | 0 | size_t i_sub_path_len = psz_subpaths - psz_path; |
1697 | 0 | struct rdh_dir *p_rdh_dir = |
1698 | 0 | malloc(sizeof(struct rdh_dir) + 1 + i_sub_path_len); |
1699 | 0 | if (p_rdh_dir == NULL) |
1700 | 0 | return VLC_ENOMEM; |
1701 | 0 | strncpy(p_rdh_dir->psz_path, psz_path, i_sub_path_len); |
1702 | 0 | p_rdh_dir->psz_path[i_sub_path_len] = 0; |
1703 | |
|
1704 | 0 | const char *psz_subpathname = strrchr(p_rdh_dir->psz_path, '/'); |
1705 | 0 | if (psz_subpathname != NULL) |
1706 | 0 | ++psz_subpathname; |
1707 | 0 | else |
1708 | 0 | psz_subpathname = p_rdh_dir->psz_path; |
1709 | |
|
1710 | 0 | input_item_t *p_item = |
1711 | 0 | input_item_NewExt(INPUT_ITEM_URI_NOP, psz_subpathname, INPUT_DURATION_UNSET, |
1712 | 0 | ITEM_TYPE_DIRECTORY, i_net); |
1713 | 0 | if (p_item == NULL) |
1714 | 0 | { |
1715 | 0 | free(p_rdh_dir); |
1716 | 0 | return VLC_ENOMEM; |
1717 | 0 | } |
1718 | 0 | input_item_CopyOptions(p_item, (*pp_node)->p_item); |
1719 | 0 | *pp_node = input_item_node_AppendItem(*pp_node, p_item); |
1720 | 0 | input_item_Release(p_item); |
1721 | 0 | if (*pp_node == NULL) |
1722 | 0 | { |
1723 | 0 | free(p_rdh_dir); |
1724 | 0 | return VLC_ENOMEM; |
1725 | 0 | } |
1726 | 0 | p_rdh_dir->p_node = *pp_node; |
1727 | 0 | TAB_APPEND(p_rdh->i_dirs, p_rdh->pp_dirs, p_rdh_dir); |
1728 | 0 | } |
1729 | 0 | else |
1730 | 0 | *pp_node = p_subnode; |
1731 | 0 | psz_subpaths++; |
1732 | 0 | } |
1733 | 0 | return VLC_SUCCESS; |
1734 | 0 | } |
1735 | | |
1736 | | #undef vlc_readdir_helper_init |
1737 | | void vlc_readdir_helper_init(struct vlc_readdir_helper *p_rdh, |
1738 | | vlc_object_t *p_obj, input_item_node_t *p_node) |
1739 | 0 | { |
1740 | | /* Read options from the parent item. This allows vlc_stream_ReadDir() |
1741 | | * users to specify options without affecting any existing vlc_object_t. |
1742 | | * Apply options on a temporary object in order to not apply options (which |
1743 | | * can be insecure) to the current object. */ |
1744 | 0 | vlc_object_t *p_var_obj = vlc_object_create(p_obj, sizeof(vlc_object_t)); |
1745 | 0 | if (p_var_obj != NULL) |
1746 | 0 | { |
1747 | 0 | input_item_ApplyOptions(p_var_obj, p_node->p_item); |
1748 | 0 | p_obj = p_var_obj; |
1749 | 0 | } |
1750 | |
|
1751 | 0 | p_rdh->p_node = p_node; |
1752 | 0 | p_rdh->b_show_hiddenfiles = var_InheritBool(p_obj, "show-hiddenfiles"); |
1753 | 0 | p_rdh->psz_ignored_exts = var_InheritString(p_obj, "ignore-filetypes"); |
1754 | 0 | bool b_autodetect = var_InheritBool(p_obj, "sub-autodetect-file"); |
1755 | 0 | p_rdh->i_sub_autodetect_fuzzy = !b_autodetect ? 0 : |
1756 | 0 | var_InheritInteger(p_obj, "sub-autodetect-fuzzy"); |
1757 | 0 | p_rdh->b_flatten = var_InheritBool(p_obj, "extractor-flatten"); |
1758 | 0 | TAB_INIT(p_rdh->i_slaves, p_rdh->pp_slaves); |
1759 | 0 | TAB_INIT(p_rdh->i_dirs, p_rdh->pp_dirs); |
1760 | |
|
1761 | 0 | if (p_var_obj != NULL) |
1762 | 0 | vlc_object_delete(p_var_obj); |
1763 | 0 | } |
1764 | | |
1765 | | void vlc_readdir_helper_finish(struct vlc_readdir_helper *p_rdh, bool b_success) |
1766 | 0 | { |
1767 | 0 | if (b_success) |
1768 | 0 | { |
1769 | 0 | rdh_sort(p_rdh->p_node); |
1770 | 0 | rdh_attach_slaves(p_rdh, p_rdh->p_node); |
1771 | 0 | } |
1772 | 0 | free(p_rdh->psz_ignored_exts); |
1773 | | |
1774 | | /* Remove unmatched slaves */ |
1775 | 0 | for (size_t i = 0; i < p_rdh->i_slaves; i++) |
1776 | 0 | { |
1777 | 0 | struct rdh_slave *p_rdh_slave = p_rdh->pp_slaves[i]; |
1778 | 0 | if (p_rdh_slave != NULL) |
1779 | 0 | { |
1780 | 0 | input_item_slave_Delete(p_rdh_slave->p_slave); |
1781 | 0 | free(p_rdh_slave->psz_filename); |
1782 | 0 | free(p_rdh_slave); |
1783 | 0 | } |
1784 | 0 | } |
1785 | 0 | TAB_CLEAN(p_rdh->i_slaves, p_rdh->pp_slaves); |
1786 | |
|
1787 | 0 | for (size_t i = 0; i < p_rdh->i_dirs; i++) |
1788 | 0 | free(p_rdh->pp_dirs[i]); |
1789 | 0 | TAB_CLEAN(p_rdh->i_dirs, p_rdh->pp_dirs); |
1790 | 0 | } |
1791 | | |
1792 | | int vlc_readdir_helper_additem(struct vlc_readdir_helper *p_rdh, |
1793 | | const char *psz_uri, const char *psz_flatpath, |
1794 | | const char *psz_filename, int i_type, int i_net, |
1795 | | input_item_t **restrict created_item) |
1796 | 0 | { |
1797 | 0 | enum slave_type i_slave_type; |
1798 | 0 | struct rdh_slave *p_rdh_slave = NULL; |
1799 | 0 | assert(psz_flatpath || psz_filename); |
1800 | |
|
1801 | 0 | if (!p_rdh->b_flatten) |
1802 | 0 | { |
1803 | 0 | if (psz_filename == NULL) |
1804 | 0 | { |
1805 | 0 | psz_filename = strrchr(psz_flatpath, '/'); |
1806 | 0 | if (psz_filename != NULL) |
1807 | 0 | ++psz_filename; |
1808 | 0 | else |
1809 | 0 | psz_filename = psz_flatpath; |
1810 | 0 | } |
1811 | 0 | } |
1812 | 0 | else |
1813 | 0 | { |
1814 | 0 | if (psz_filename == NULL) |
1815 | 0 | psz_filename = psz_flatpath; |
1816 | 0 | psz_flatpath = NULL; |
1817 | 0 | } |
1818 | |
|
1819 | 0 | if (p_rdh->i_sub_autodetect_fuzzy != 0 |
1820 | 0 | && input_item_slave_GetType(psz_filename, &i_slave_type)) |
1821 | 0 | { |
1822 | 0 | p_rdh_slave = malloc(sizeof(*p_rdh_slave)); |
1823 | 0 | if (!p_rdh_slave) |
1824 | 0 | return VLC_ENOMEM; |
1825 | | |
1826 | 0 | p_rdh_slave->p_node = NULL; |
1827 | 0 | p_rdh_slave->psz_filename = strdup(psz_filename); |
1828 | 0 | p_rdh_slave->p_slave = input_item_slave_New(psz_uri, i_slave_type, |
1829 | 0 | SLAVE_PRIORITY_MATCH_NONE); |
1830 | 0 | if (!p_rdh_slave->p_slave || !p_rdh_slave->psz_filename) |
1831 | 0 | { |
1832 | 0 | free(p_rdh_slave->psz_filename); |
1833 | 0 | free(p_rdh_slave); |
1834 | 0 | return VLC_ENOMEM; |
1835 | 0 | } |
1836 | | |
1837 | 0 | TAB_APPEND(p_rdh->i_slaves, p_rdh->pp_slaves, p_rdh_slave); |
1838 | 0 | } |
1839 | | |
1840 | 0 | if (rdh_file_is_ignored(p_rdh, psz_filename)) |
1841 | 0 | { |
1842 | 0 | if (created_item != NULL) |
1843 | 0 | *created_item = NULL; |
1844 | 0 | return VLC_SUCCESS; |
1845 | 0 | } |
1846 | | |
1847 | 0 | input_item_node_t *p_node = p_rdh->p_node; |
1848 | |
|
1849 | 0 | if (psz_flatpath != NULL) |
1850 | 0 | { |
1851 | 0 | int i_ret = rdh_unflatten(p_rdh, &p_node, psz_flatpath, i_net); |
1852 | 0 | if (i_ret != VLC_SUCCESS) |
1853 | 0 | return i_ret; |
1854 | 0 | } |
1855 | | |
1856 | 0 | input_item_t *p_item = input_item_NewExt(psz_uri, psz_filename, INPUT_DURATION_UNSET, i_type, |
1857 | 0 | i_net); |
1858 | 0 | if (p_item == NULL) |
1859 | 0 | return VLC_ENOMEM; |
1860 | | |
1861 | 0 | input_item_CopyOptions(p_item, p_node->p_item); |
1862 | 0 | p_node = input_item_node_AppendItem(p_node, p_item); |
1863 | 0 | input_item_Release(p_item); |
1864 | 0 | if (p_node == NULL) |
1865 | 0 | return VLC_ENOMEM; |
1866 | | |
1867 | | /* A slave can also be an item. If there is a match, this item will be |
1868 | | * removed from the parent node. This is not a common case, since most |
1869 | | * slaves will be ignored by rdh_file_is_ignored() */ |
1870 | 0 | if (p_rdh_slave != NULL) |
1871 | 0 | p_rdh_slave->p_node = p_node; |
1872 | |
|
1873 | 0 | if (created_item != NULL) |
1874 | 0 | *created_item = p_item; |
1875 | 0 | return VLC_SUCCESS; |
1876 | 0 | } |