Coverage Report

Created: 2023-11-19 07:07

/src/vlc/src/media_source/media_tree.c
Line
Count
Source (jump to first uncovered line)
1
/*****************************************************************************
2
 * media_tree.c
3
 *****************************************************************************
4
 * Copyright (C) 2018 VLC authors and VideoLAN
5
 *
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.
10
 *
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.
15
 *
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
 *****************************************************************************/
20
21
#ifdef HAVE_CONFIG_H
22
# include "config.h"
23
#endif
24
25
#include <vlc_media_source.h>
26
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"
34
35
struct vlc_media_tree_listener_id
36
{
37
    const struct vlc_media_tree_callbacks *cbs;
38
    void *userdata;
39
    struct vlc_list node; /**< node of media_tree_private_t.listeners */
40
};
41
42
typedef struct
43
{
44
    vlc_media_tree_t public_data;
45
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;
50
51
0
#define mt_priv(mt) container_of(mt, media_tree_private_t, public_data)
52
53
vlc_media_tree_t *
54
vlc_media_tree_New(void)
55
0
{
56
0
    media_tree_private_t *priv = malloc(sizeof(*priv));
57
0
    if (unlikely(!priv))
58
0
        return NULL;
59
60
0
    vlc_mutex_init(&priv->lock);
61
0
    vlc_atomic_rc_init(&priv->rc);
62
0
    vlc_list_init(&priv->listeners);
63
64
0
    vlc_media_tree_t *tree = &priv->public_data;
65
0
    input_item_node_t *root = &tree->root;
66
0
    root->p_item = NULL;
67
0
    TAB_INIT(root->i_children, root->pp_children);
68
69
0
    return tree;
70
0
}
71
72
static inline void
73
vlc_media_tree_AssertLocked(vlc_media_tree_t *tree)
74
0
{
75
0
    media_tree_private_t *priv = mt_priv(tree);
76
0
    vlc_mutex_assert(&priv->lock);
77
0
}
78
79
#define vlc_media_tree_listener_foreach(listener, tree) \
80
0
    vlc_list_foreach(listener, &mt_priv(tree)->listeners, node)
81
82
0
#define vlc_media_tree_NotifyListener(tree, listener, event, ...) \
83
0
do { \
84
0
    if (listener->cbs->event) \
85
0
        listener->cbs->event(tree, ##__VA_ARGS__, listener->userdata); \
86
0
} while(0)
87
88
0
#define vlc_media_tree_Notify(tree, event, ...) \
89
0
do { \
90
0
    vlc_media_tree_AssertLocked(tree); \
91
0
    vlc_media_tree_listener_id *listener; \
92
0
    vlc_media_tree_listener_foreach(listener, tree) \
93
0
        vlc_media_tree_NotifyListener(tree, listener, event, ##__VA_ARGS__); \
94
0
} while (0)
95
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)
101
0
{
102
0
    for (int i = 0; i < parent->i_children; ++i)
103
0
    {
104
0
        input_item_node_t *child = parent->pp_children[i];
105
0
        if (child->p_item == media)
106
0
        {
107
0
            *result = child;
108
0
            if (result_parent)
109
0
                *result_parent = parent;
110
0
            return true;
111
0
        }
112
113
0
        if (vlc_media_tree_FindNodeByMedia(child, media, result, result_parent))
114
0
            return true;
115
0
    }
116
117
0
    return false;
118
0
}
119
120
static input_item_node_t *
121
vlc_media_tree_AddChild(input_item_node_t *parent, input_item_t *media);
122
123
static void
124
vlc_media_tree_AddSubtree(input_item_node_t *to, input_item_node_t *from)
125
0
{
126
0
    for (int i = 0; i < from->i_children; ++i)
127
0
    {
128
0
        input_item_node_t *child = from->pp_children[i];
129
0
        input_item_node_t *node = vlc_media_tree_AddChild(to, child->p_item);
130
0
        if (unlikely(!node))
131
0
            break; /* what could we do? */
132
133
0
        vlc_media_tree_AddSubtree(node, child);
134
0
    }
135
0
}
136
137
static void
138
vlc_media_tree_ClearChildren(input_item_node_t *root)
139
0
{
140
0
    for (int i = 0; i < root->i_children; ++i)
141
0
        input_item_node_Delete(root->pp_children[i]);
142
143
0
    free(root->pp_children);
144
0
    root->pp_children = NULL;
145
0
    root->i_children = 0;
146
0
}
147
148
static void
149
media_subtree_changed(input_item_t *media, input_item_node_t *node,
150
                      void *userdata)
151
0
{
152
0
    vlc_media_tree_t *tree = userdata;
153
154
0
    vlc_media_tree_Lock(tree);
155
0
    input_item_node_t *subtree_root;
156
    /* TODO retrieve the node without traversing the tree */
157
0
    bool found = vlc_media_tree_FindNodeByMedia(&tree->root, media,
158
0
                                                &subtree_root, NULL);
159
0
    if (!found) {
160
        /* the node probably failed to be allocated */
161
0
        vlc_media_tree_Unlock(tree);
162
0
        return;
163
0
    }
164
165
0
    vlc_media_tree_ClearChildren(subtree_root);
166
0
    vlc_media_tree_AddSubtree(subtree_root, node);
167
0
    vlc_media_tree_Notify(tree, on_children_reset, subtree_root);
168
0
    vlc_media_tree_Unlock(tree);
169
0
}
170
171
static void
172
media_subtree_preparse_ended(input_item_t *media,
173
                             enum input_item_preparse_status status,
174
                             void *user_data)
175
0
{
176
0
    vlc_media_tree_t *tree = user_data;
177
178
0
    vlc_media_tree_Lock(tree);
179
0
    input_item_node_t *subtree_root;
180
    /* TODO retrieve the node without traversing the tree */
181
0
    bool found = vlc_media_tree_FindNodeByMedia(&tree->root, media,
182
0
                                                &subtree_root, NULL);
183
0
    if (!found) {
184
        /* the node probably failed to be allocated */
185
0
        vlc_media_tree_Unlock(tree);
186
0
        return;
187
0
    }
188
0
    vlc_media_tree_Notify(tree, on_preparse_end, subtree_root, status);
189
0
    vlc_media_tree_Unlock(tree);
190
0
}
191
192
static inline void
193
vlc_media_tree_DestroyRootNode(vlc_media_tree_t *tree)
194
0
{
195
0
    vlc_media_tree_ClearChildren(&tree->root);
196
0
}
197
198
static void
199
vlc_media_tree_Delete(vlc_media_tree_t *tree)
200
0
{
201
0
    media_tree_private_t *priv = mt_priv(tree);
202
0
    vlc_media_tree_listener_id *listener;
203
0
    vlc_list_foreach(listener, &priv->listeners, node)
204
0
        free(listener);
205
0
    vlc_list_init(&priv->listeners); /* reset */
206
0
    vlc_media_tree_DestroyRootNode(tree);
207
0
    free(tree);
208
0
}
209
210
void
211
vlc_media_tree_Hold(vlc_media_tree_t *tree)
212
0
{
213
0
    media_tree_private_t *priv = mt_priv(tree);
214
0
    vlc_atomic_rc_inc(&priv->rc);
215
0
}
216
217
void
218
vlc_media_tree_Release(vlc_media_tree_t *tree)
219
0
{
220
0
    media_tree_private_t *priv = mt_priv(tree);
221
0
    if (vlc_atomic_rc_dec(&priv->rc))
222
0
        vlc_media_tree_Delete(tree);
223
0
}
224
225
void
226
vlc_media_tree_Lock(vlc_media_tree_t *tree)
227
0
{
228
0
    media_tree_private_t *priv = mt_priv(tree);
229
0
    vlc_mutex_lock(&priv->lock);
230
0
}
231
232
void
233
vlc_media_tree_Unlock(vlc_media_tree_t *tree)
234
0
{
235
0
    media_tree_private_t *priv = mt_priv(tree);
236
0
    vlc_mutex_unlock(&priv->lock);
237
0
}
238
239
static input_item_node_t *
240
vlc_media_tree_AddChild(input_item_node_t *parent, input_item_t *media)
241
0
{
242
0
    input_item_node_t *node = input_item_node_Create(media);
243
0
    if (unlikely(!node))
244
0
        return NULL;
245
246
0
    input_item_node_AppendNode(parent, node);
247
248
0
    return node;
249
0
}
250
251
static void
252
vlc_media_tree_NotifyCurrentState(vlc_media_tree_t *tree,
253
                                  vlc_media_tree_listener_id *listener)
254
0
{
255
0
    vlc_media_tree_NotifyListener(tree, listener, on_children_reset,
256
0
                                  &tree->root);
257
0
}
258
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)
263
0
{
264
0
    vlc_media_tree_listener_id *listener = malloc(sizeof(*listener));
265
0
    if (unlikely(!listener))
266
0
        return NULL;
267
0
    listener->cbs = cbs;
268
0
    listener->userdata = userdata;
269
270
0
    media_tree_private_t *priv = mt_priv(tree);
271
0
    vlc_media_tree_Lock(tree);
272
273
0
    vlc_list_append(&listener->node, &priv->listeners);
274
275
0
    if (notify_current_state)
276
0
        vlc_media_tree_NotifyCurrentState(tree, listener);
277
278
0
    vlc_media_tree_Unlock(tree);
279
0
    return listener;
280
0
}
281
282
void
283
vlc_media_tree_RemoveListener(vlc_media_tree_t *tree,
284
                              vlc_media_tree_listener_id *listener)
285
0
{
286
0
    vlc_media_tree_Lock(tree);
287
0
    vlc_list_remove(&listener->node);
288
0
    vlc_media_tree_Unlock(tree);
289
290
0
    free(listener);
291
0
}
292
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)
296
0
{
297
0
    vlc_media_tree_AssertLocked(tree);
298
299
0
    input_item_node_t *node = vlc_media_tree_AddChild(parent, media);
300
0
    if (unlikely(!node))
301
0
        return NULL;
302
303
0
    vlc_media_tree_Notify(tree, on_children_added, parent, &node, 1);
304
305
0
    return node;
306
0
}
307
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)
312
0
{
313
0
    vlc_media_tree_AssertLocked(tree);
314
315
    /* quick & dirty depth-first O(n) implementation, with n the number of nodes
316
     * in the tree */
317
0
    return vlc_media_tree_FindNodeByMedia(&tree->root, media, result,
318
0
                                          result_parent);
319
0
}
320
321
bool
322
vlc_media_tree_Remove(vlc_media_tree_t *tree, input_item_t *media)
323
0
{
324
0
    vlc_media_tree_AssertLocked(tree);
325
326
0
    input_item_node_t *node;
327
0
    input_item_node_t *parent;
328
0
    if (!vlc_media_tree_FindNodeByMedia(&tree->root, media, &node, &parent))
329
0
        return false;
330
331
0
    input_item_node_RemoveNode(parent, node);
332
0
    vlc_media_tree_Notify(tree, on_children_removed, parent, &node, 1);
333
0
    input_item_node_Delete(node);
334
0
    return true;
335
0
}
336
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
340
};
341
342
void
343
vlc_media_tree_Preparse(vlc_media_tree_t *tree, libvlc_int_t *libvlc,
344
                        input_item_t *media, void* id)
345
0
{
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
0
    media->i_preparse_depth = 1;
354
0
    vlc_MetadataRequest(libvlc, media, META_REQUEST_OPTION_SCOPE_ANY |
355
0
                        META_REQUEST_OPTION_DO_INTERACT,
356
0
                        &input_preparser_callbacks, tree, 0, id);
357
0
#endif
358
0
}
359
360
361
void
362
vlc_media_tree_PreparseCancel(libvlc_int_t *libvlc, void* id)
363
0
{
364
#ifdef TEST_MEDIA_SOURCE
365
    VLC_UNUSED(libvlc);
366
    VLC_UNUSED(id);
367
#else
368
0
    libvlc_MetadataCancel(libvlc, id);
369
0
#endif
370
0
}