1 /*****************************************************************************
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 *****************************************************************************/
25 #include "media_tree.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>
35 struct vlc_media_tree_listener_id
37 const struct vlc_media_tree_callbacks
*cbs
;
39 struct vlc_list node
; /**< node of media_tree_private_t.listeners */
44 vlc_media_tree_t public_data
;
46 struct vlc_list listeners
; /**< list of vlc_media_tree_listener_id.node */
49 } media_tree_private_t
;
51 #define mt_priv(mt) container_of(mt, media_tree_private_t, public_data)
54 vlc_media_tree_New(void)
56 media_tree_private_t
*priv
= malloc(sizeof(*priv
));
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
;
67 TAB_INIT(root
->i_children
, root
->pp_children
);
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, ...) \
84 if (listener->cbs->event) \
85 listener->cbs->event(tree, ##__VA_ARGS__, listener->userdata); \
88 #define vlc_media_tree_Notify(tree, event, ...) \
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__); \
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
)
109 *result_parent
= parent
;
113 if (vlc_media_tree_FindNodeByMedia(child
, media
, result
, result_parent
))
120 static input_item_node_t
*
121 vlc_media_tree_AddChild(input_item_node_t
*parent
, input_item_t
*media
);
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
);
131 break; /* what could we do? */
133 vlc_media_tree_AddSubtree(node
, child
);
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;
149 media_subtree_changed(input_item_t
*media
, input_item_node_t
*node
,
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
);
160 /* the node probably failed to be allocated */
161 vlc_media_tree_Unlock(tree
);
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
);
172 media_subtree_preparse_ended(input_item_t
*media
,
173 enum input_item_preparse_status status
,
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
);
184 /* the node probably failed to be allocated */
185 vlc_media_tree_Unlock(tree
);
188 vlc_media_tree_Notify(tree
, on_preparse_end
, subtree_root
, status
);
189 vlc_media_tree_Unlock(tree
);
193 vlc_media_tree_DestroyRootNode(vlc_media_tree_t
*tree
)
195 vlc_media_tree_ClearChildren(&tree
->root
);
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
)
205 vlc_list_init(&priv
->listeners
); /* reset */
206 vlc_media_tree_DestroyRootNode(tree
);
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
);
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
);
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
);
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
);
246 input_item_node_AppendNode(parent
, node
);
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
,
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
))
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
);
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
);
294 vlc_media_tree_Add(vlc_media_tree_t
*tree
, input_item_node_t
*parent
,
297 vlc_media_tree_AssertLocked(tree
);
299 input_item_node_t
*node
= vlc_media_tree_AddChild(parent
, media
);
303 vlc_media_tree_Notify(tree
, on_children_added
, parent
, &node
, 1);
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
317 return vlc_media_tree_FindNodeByMedia(&tree
->root
, media
, result
,
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
))
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
);
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
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
351 VLC_UNUSED(input_preparser_callbacks
);
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
);
362 vlc_media_tree_PreparseCancel(libvlc_int_t
*libvlc
, void* id
)
364 #ifdef TEST_MEDIA_SOURCE
368 libvlc_MetadataCancel(libvlc
, id
);