lib: fix track count in get_track_from_id()
[vlc.git] / src / media_source / media_tree.c
blob5774b294ff897bf16559cffd5fd421e265102f7f
1 /*****************************************************************************
2 * media_tree.c
3 *****************************************************************************
4 * Copyright (C) 2018 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 *****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
25 #include "media_tree.h"
27 #include <assert.h>
28 #include <vlc_common.h>
29 #include <vlc_arrays.h>
30 #include <vlc_atomic.h>
31 #include <vlc_input_item.h>
32 #include <vlc_threads.h>
33 #include "libvlc.h"
35 struct vlc_media_tree_listener_id
37 const struct vlc_media_tree_callbacks *cbs;
38 void *userdata;
39 struct vlc_list node; /**< node of media_tree_private_t.listeners */
42 typedef struct
44 vlc_media_tree_t public_data;
46 struct vlc_list listeners; /**< list of vlc_media_tree_listener_id.node */
47 vlc_mutex_t lock;
48 vlc_atomic_rc_t rc;
49 } media_tree_private_t;
51 #define mt_priv(mt) container_of(mt, media_tree_private_t, public_data)
53 vlc_media_tree_t *
54 vlc_media_tree_New(void)
56 media_tree_private_t *priv = malloc(sizeof(*priv));
57 if (unlikely(!priv))
58 return NULL;
60 vlc_mutex_init(&priv->lock);
61 vlc_atomic_rc_init(&priv->rc);
62 vlc_list_init(&priv->listeners);
64 vlc_media_tree_t *tree = &priv->public_data;
65 input_item_node_t *root = &tree->root;
66 root->p_item = NULL;
67 TAB_INIT(root->i_children, root->pp_children);
69 return tree;
72 static inline void
73 vlc_media_tree_AssertLocked(vlc_media_tree_t *tree)
75 media_tree_private_t *priv = mt_priv(tree);
76 vlc_mutex_assert(&priv->lock);
79 #define vlc_media_tree_listener_foreach(listener, tree) \
80 vlc_list_foreach(listener, &mt_priv(tree)->listeners, node)
82 #define vlc_media_tree_NotifyListener(tree, listener, event, ...) \
83 do { \
84 if (listener->cbs->event) \
85 listener->cbs->event(tree, ##__VA_ARGS__, listener->userdata); \
86 } while(0)
88 #define vlc_media_tree_Notify(tree, event, ...) \
89 do { \
90 vlc_media_tree_AssertLocked(tree); \
91 vlc_media_tree_listener_id *listener; \
92 vlc_media_tree_listener_foreach(listener, tree) \
93 vlc_media_tree_NotifyListener(tree, listener, event, ##__VA_ARGS__); \
94 } while (0)
96 static bool
97 vlc_media_tree_FindNodeByMedia(input_item_node_t *parent,
98 const input_item_t *media,
99 input_item_node_t **result,
100 input_item_node_t **result_parent)
102 for (int i = 0; i < parent->i_children; ++i)
104 input_item_node_t *child = parent->pp_children[i];
105 if (child->p_item == media)
107 *result = child;
108 if (result_parent)
109 *result_parent = parent;
110 return true;
113 if (vlc_media_tree_FindNodeByMedia(child, media, result, result_parent))
114 return true;
117 return false;
120 static input_item_node_t *
121 vlc_media_tree_AddChild(input_item_node_t *parent, input_item_t *media);
123 static void
124 vlc_media_tree_AddSubtree(input_item_node_t *to, input_item_node_t *from)
126 for (int i = 0; i < from->i_children; ++i)
128 input_item_node_t *child = from->pp_children[i];
129 input_item_node_t *node = vlc_media_tree_AddChild(to, child->p_item);
130 if (unlikely(!node))
131 break; /* what could we do? */
133 vlc_media_tree_AddSubtree(node, child);
137 static void
138 vlc_media_tree_ClearChildren(input_item_node_t *root)
140 for (int i = 0; i < root->i_children; ++i)
141 input_item_node_Delete(root->pp_children[i]);
143 free(root->pp_children);
144 root->pp_children = NULL;
145 root->i_children = 0;
148 static void
149 media_subtree_changed(input_item_t *media, input_item_node_t *node,
150 void *userdata)
152 vlc_media_tree_t *tree = userdata;
154 vlc_media_tree_Lock(tree);
155 input_item_node_t *subtree_root;
156 /* TODO retrieve the node without traversing the tree */
157 bool found = vlc_media_tree_FindNodeByMedia(&tree->root, media,
158 &subtree_root, NULL);
159 if (!found) {
160 /* the node probably failed to be allocated */
161 vlc_media_tree_Unlock(tree);
162 return;
165 vlc_media_tree_ClearChildren(subtree_root);
166 vlc_media_tree_AddSubtree(subtree_root, node);
167 vlc_media_tree_Notify(tree, on_children_reset, subtree_root);
168 vlc_media_tree_Unlock(tree);
171 static void
172 media_subtree_preparse_ended(input_item_t *media,
173 enum input_item_preparse_status status,
174 void *user_data)
176 vlc_media_tree_t *tree = user_data;
178 vlc_media_tree_Lock(tree);
179 input_item_node_t *subtree_root;
180 /* TODO retrieve the node without traversing the tree */
181 bool found = vlc_media_tree_FindNodeByMedia(&tree->root, media,
182 &subtree_root, NULL);
183 if (!found) {
184 /* the node probably failed to be allocated */
185 vlc_media_tree_Unlock(tree);
186 return;
188 vlc_media_tree_Notify(tree, on_preparse_end, subtree_root, status);
189 vlc_media_tree_Unlock(tree);
192 static inline void
193 vlc_media_tree_DestroyRootNode(vlc_media_tree_t *tree)
195 vlc_media_tree_ClearChildren(&tree->root);
198 static void
199 vlc_media_tree_Delete(vlc_media_tree_t *tree)
201 media_tree_private_t *priv = mt_priv(tree);
202 vlc_media_tree_listener_id *listener;
203 vlc_list_foreach(listener, &priv->listeners, node)
204 free(listener);
205 vlc_list_init(&priv->listeners); /* reset */
206 vlc_media_tree_DestroyRootNode(tree);
207 free(tree);
210 void
211 vlc_media_tree_Hold(vlc_media_tree_t *tree)
213 media_tree_private_t *priv = mt_priv(tree);
214 vlc_atomic_rc_inc(&priv->rc);
217 void
218 vlc_media_tree_Release(vlc_media_tree_t *tree)
220 media_tree_private_t *priv = mt_priv(tree);
221 if (vlc_atomic_rc_dec(&priv->rc))
222 vlc_media_tree_Delete(tree);
225 void
226 vlc_media_tree_Lock(vlc_media_tree_t *tree)
228 media_tree_private_t *priv = mt_priv(tree);
229 vlc_mutex_lock(&priv->lock);
232 void
233 vlc_media_tree_Unlock(vlc_media_tree_t *tree)
235 media_tree_private_t *priv = mt_priv(tree);
236 vlc_mutex_unlock(&priv->lock);
239 static input_item_node_t *
240 vlc_media_tree_AddChild(input_item_node_t *parent, input_item_t *media)
242 input_item_node_t *node = input_item_node_Create(media);
243 if (unlikely(!node))
244 return NULL;
246 input_item_node_AppendNode(parent, node);
248 return node;
251 static void
252 vlc_media_tree_NotifyCurrentState(vlc_media_tree_t *tree,
253 vlc_media_tree_listener_id *listener)
255 vlc_media_tree_NotifyListener(tree, listener, on_children_reset,
256 &tree->root);
259 vlc_media_tree_listener_id *
260 vlc_media_tree_AddListener(vlc_media_tree_t *tree,
261 const struct vlc_media_tree_callbacks *cbs,
262 void *userdata, bool notify_current_state)
264 vlc_media_tree_listener_id *listener = malloc(sizeof(*listener));
265 if (unlikely(!listener))
266 return NULL;
267 listener->cbs = cbs;
268 listener->userdata = userdata;
270 media_tree_private_t *priv = mt_priv(tree);
271 vlc_media_tree_Lock(tree);
273 vlc_list_append(&listener->node, &priv->listeners);
275 if (notify_current_state)
276 vlc_media_tree_NotifyCurrentState(tree, listener);
278 vlc_media_tree_Unlock(tree);
279 return listener;
282 void
283 vlc_media_tree_RemoveListener(vlc_media_tree_t *tree,
284 vlc_media_tree_listener_id *listener)
286 vlc_media_tree_Lock(tree);
287 vlc_list_remove(&listener->node);
288 vlc_media_tree_Unlock(tree);
290 free(listener);
293 input_item_node_t *
294 vlc_media_tree_Add(vlc_media_tree_t *tree, input_item_node_t *parent,
295 input_item_t *media)
297 vlc_media_tree_AssertLocked(tree);
299 input_item_node_t *node = vlc_media_tree_AddChild(parent, media);
300 if (unlikely(!node))
301 return NULL;
303 vlc_media_tree_Notify(tree, on_children_added, parent, &node, 1);
305 return node;
308 bool
309 vlc_media_tree_Find(vlc_media_tree_t *tree, const input_item_t *media,
310 input_item_node_t **result,
311 input_item_node_t **result_parent)
313 vlc_media_tree_AssertLocked(tree);
315 /* quick & dirty depth-first O(n) implementation, with n the number of nodes
316 * in the tree */
317 return vlc_media_tree_FindNodeByMedia(&tree->root, media, result,
318 result_parent);
321 bool
322 vlc_media_tree_Remove(vlc_media_tree_t *tree, input_item_t *media)
324 vlc_media_tree_AssertLocked(tree);
326 input_item_node_t *node;
327 input_item_node_t *parent;
328 if (!vlc_media_tree_FindNodeByMedia(&tree->root, media, &node, &parent))
329 return false;
331 input_item_node_RemoveNode(parent, node);
332 vlc_media_tree_Notify(tree, on_children_removed, parent, &node, 1);
333 input_item_node_Delete(node);
334 return true;
337 static const input_preparser_callbacks_t input_preparser_callbacks = {
338 .on_subtree_added = media_subtree_changed,
339 .on_preparse_ended = media_subtree_preparse_ended
342 void
343 vlc_media_tree_Preparse(vlc_media_tree_t *tree, libvlc_int_t *libvlc,
344 input_item_t *media, void* id)
346 #ifdef TEST_MEDIA_SOURCE
347 VLC_UNUSED(tree);
348 VLC_UNUSED(libvlc);
349 VLC_UNUSED(media);
350 VLC_UNUSED(id);
351 VLC_UNUSED(input_preparser_callbacks);
352 #else
353 media->i_preparse_depth = 1;
354 vlc_MetadataRequest(libvlc, media, META_REQUEST_OPTION_SCOPE_ANY |
355 META_REQUEST_OPTION_DO_INTERACT,
356 &input_preparser_callbacks, tree, 0, id);
357 #endif
361 void
362 vlc_media_tree_PreparseCancel(libvlc_int_t *libvlc, void* id)
364 #ifdef TEST_MEDIA_SOURCE
365 VLC_UNUSED(libvlc);
366 VLC_UNUSED(id);
367 #else
368 libvlc_MetadataCancel(libvlc, id);
369 #endif