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 media_subtree_changed(input_item_t
*media
, input_item_node_t
*node
,
141 vlc_media_tree_t
*tree
= userdata
;
143 vlc_media_tree_Lock(tree
);
144 input_item_node_t
*subtree_root
;
145 /* TODO retrieve the node without traversing the tree */
146 bool found
= vlc_media_tree_FindNodeByMedia(&tree
->root
, media
,
147 &subtree_root
, NULL
);
149 /* the node probably failed to be allocated */
150 vlc_media_tree_Unlock(tree
);
154 vlc_media_tree_AddSubtree(subtree_root
, node
);
155 vlc_media_tree_Notify(tree
, on_children_reset
, subtree_root
);
156 vlc_media_tree_Unlock(tree
);
160 vlc_media_tree_DestroyRootNode(vlc_media_tree_t
*tree
)
162 input_item_node_t
*root
= &tree
->root
;
163 for (int i
= 0; i
< root
->i_children
; ++i
)
164 input_item_node_Delete(root
->pp_children
[i
]);
166 free(root
->pp_children
);
170 vlc_media_tree_Delete(vlc_media_tree_t
*tree
)
172 media_tree_private_t
*priv
= mt_priv(tree
);
173 vlc_media_tree_listener_id
*listener
;
174 vlc_list_foreach(listener
, &priv
->listeners
, node
)
176 vlc_list_init(&priv
->listeners
); /* reset */
177 vlc_media_tree_DestroyRootNode(tree
);
178 vlc_mutex_destroy(&priv
->lock
);
183 vlc_media_tree_Hold(vlc_media_tree_t
*tree
)
185 media_tree_private_t
*priv
= mt_priv(tree
);
186 vlc_atomic_rc_inc(&priv
->rc
);
190 vlc_media_tree_Release(vlc_media_tree_t
*tree
)
192 media_tree_private_t
*priv
= mt_priv(tree
);
193 if (vlc_atomic_rc_dec(&priv
->rc
))
194 vlc_media_tree_Delete(tree
);
198 vlc_media_tree_Lock(vlc_media_tree_t
*tree
)
200 media_tree_private_t
*priv
= mt_priv(tree
);
201 vlc_mutex_lock(&priv
->lock
);
205 vlc_media_tree_Unlock(vlc_media_tree_t
*tree
)
207 media_tree_private_t
*priv
= mt_priv(tree
);
208 vlc_mutex_unlock(&priv
->lock
);
211 static input_item_node_t
*
212 vlc_media_tree_AddChild(input_item_node_t
*parent
, input_item_t
*media
)
214 input_item_node_t
*node
= input_item_node_Create(media
);
218 input_item_node_AppendNode(parent
, node
);
224 vlc_media_tree_NotifyCurrentState(vlc_media_tree_t
*tree
,
225 vlc_media_tree_listener_id
*listener
)
227 vlc_media_tree_NotifyListener(tree
, listener
, on_children_reset
,
231 vlc_media_tree_listener_id
*
232 vlc_media_tree_AddListener(vlc_media_tree_t
*tree
,
233 const struct vlc_media_tree_callbacks
*cbs
,
234 void *userdata
, bool notify_current_state
)
236 vlc_media_tree_listener_id
*listener
= malloc(sizeof(*listener
));
237 if (unlikely(!listener
))
240 listener
->userdata
= userdata
;
242 media_tree_private_t
*priv
= mt_priv(tree
);
243 vlc_media_tree_Lock(tree
);
245 vlc_list_append(&listener
->node
, &priv
->listeners
);
247 if (notify_current_state
)
248 vlc_media_tree_NotifyCurrentState(tree
, listener
);
250 vlc_media_tree_Unlock(tree
);
255 vlc_media_tree_RemoveListener(vlc_media_tree_t
*tree
,
256 vlc_media_tree_listener_id
*listener
)
258 vlc_media_tree_Lock(tree
);
259 vlc_list_remove(&listener
->node
);
260 vlc_media_tree_Unlock(tree
);
266 vlc_media_tree_Add(vlc_media_tree_t
*tree
, input_item_node_t
*parent
,
269 vlc_media_tree_AssertLocked(tree
);
271 input_item_node_t
*node
= vlc_media_tree_AddChild(parent
, media
);
275 vlc_media_tree_Notify(tree
, on_children_added
, parent
, &node
, 1);
281 vlc_media_tree_Find(vlc_media_tree_t
*tree
, const input_item_t
*media
,
282 input_item_node_t
**result
,
283 input_item_node_t
**result_parent
)
285 vlc_media_tree_AssertLocked(tree
);
287 /* quick & dirty depth-first O(n) implementation, with n the number of nodes
289 return vlc_media_tree_FindNodeByMedia(&tree
->root
, media
, result
,
294 vlc_media_tree_Remove(vlc_media_tree_t
*tree
, input_item_t
*media
)
296 vlc_media_tree_AssertLocked(tree
);
298 input_item_node_t
*node
;
299 input_item_node_t
*parent
;
300 if (!vlc_media_tree_FindNodeByMedia(&tree
->root
, media
, &node
, &parent
))
303 input_item_node_RemoveNode(parent
, node
);
304 vlc_media_tree_Notify(tree
, on_children_removed
, parent
, &node
, 1);
305 input_item_node_Delete(node
);
309 static const input_preparser_callbacks_t input_preparser_callbacks
= {
310 .on_subtree_added
= media_subtree_changed
,
314 vlc_media_tree_Preparse(vlc_media_tree_t
*tree
, libvlc_int_t
*libvlc
,
317 #ifdef TEST_MEDIA_SOURCE
321 VLC_UNUSED(input_preparser_callbacks
);
323 vlc_MetadataRequest(libvlc
, media
, META_REQUEST_OPTION_NONE
,
324 &input_preparser_callbacks
, tree
, -1, NULL
);