Coverage Report

Created: 2026-06-09 09:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/src/media_source/media_source.c
Line
Count
Source
1
/*****************************************************************************
2
 * media_source.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 "media_source.h"
26
27
#include <assert.h>
28
#include <vlc_atomic.h>
29
#include <vlc_playlist.h>
30
#include <vlc_services_discovery.h>
31
#include <vlc_vector.h>
32
#include "../libvlc.h"
33
34
#ifdef TEST_MEDIA_SOURCE
35
#define vlc_module_name "test"
36
#endif /* TEST_MEDIA_SOURCE */
37
38
typedef struct
39
{
40
    vlc_media_source_t public_data;
41
42
    services_discovery_t *sd;
43
    size_t rc;
44
    vlc_media_source_provider_t *owner;
45
    struct vlc_list node;
46
    char name[];
47
} media_source_private_t;
48
49
0
#define ms_priv(ms) container_of(ms, media_source_private_t, public_data)
50
51
struct vlc_media_source_provider_t
52
{
53
    struct vlc_object_t obj;
54
    vlc_mutex_t lock;
55
    struct vlc_list media_sources;
56
};
57
58
/* A new item has been added to a certain services discovery */
59
static void
60
services_discovery_item_added(services_discovery_t *sd,
61
                              input_item_t *parent, input_item_t *media)
62
0
{
63
0
    vlc_media_source_t *ms = sd->owner.sys;
64
0
    vlc_media_tree_t *tree = ms->tree;
65
66
0
    msg_Dbg(sd, "adding: %s", media->psz_name ? media->psz_name : "(null)");
67
68
0
    vlc_media_tree_Lock(tree);
69
70
0
    input_item_node_t *parent_node;
71
0
    if (parent)
72
0
        vlc_media_tree_Find(tree, parent, &parent_node, NULL);
73
0
    else
74
0
        parent_node = &tree->root;
75
76
0
    bool added = vlc_media_tree_Add(tree, parent_node, media) != NULL;
77
0
    if (unlikely(!added))
78
0
        msg_Err(sd, "could not allocate media tree node");
79
80
0
    vlc_media_tree_Unlock(tree);
81
0
}
82
83
static void
84
services_discovery_item_removed(services_discovery_t *sd, input_item_t *media)
85
0
{
86
0
    vlc_media_source_t *ms = sd->owner.sys;
87
0
    vlc_media_tree_t *tree = ms->tree;
88
89
0
    msg_Dbg(sd, "removing: %s", media->psz_name ? media->psz_name : "(null)");
90
91
0
    vlc_media_tree_Lock(tree);
92
0
    bool removed = vlc_media_tree_Remove(tree, media);
93
0
    vlc_media_tree_Unlock(tree);
94
95
0
    if (unlikely(!removed))
96
0
    {
97
0
        msg_Err(sd, "removing item not added"); /* SD plugin bug */
98
0
        return;
99
0
    }
100
0
}
101
102
static const struct services_discovery_callbacks sd_cbs = {
103
    .item_added = services_discovery_item_added,
104
    .item_removed = services_discovery_item_removed,
105
};
106
107
static vlc_media_source_t *
108
vlc_media_source_New(vlc_media_source_provider_t *provider, const char *name)
109
0
{
110
0
    media_source_private_t *priv = malloc(sizeof(*priv) + strlen(name) + 1);
111
0
    if (unlikely(!priv))
112
0
        return NULL;
113
114
0
    priv->rc = 1;
115
116
0
    vlc_media_source_t *ms = &priv->public_data;
117
118
    /* vlc_sd_Create() may call services_discovery_item_added(), which will read
119
     * its tree, so it must be initialized first */
120
0
    ms->tree = vlc_media_tree_New();
121
0
    if (unlikely(!ms->tree))
122
0
    {
123
0
        free(priv);
124
0
        return NULL;
125
0
    }
126
127
0
    strcpy(priv->name, name);
128
129
0
    struct services_discovery_owner_t owner = {
130
0
        .cbs = &sd_cbs,
131
0
        .sys = ms,
132
0
    };
133
134
0
    priv->sd = vlc_sd_Create(provider, name, &owner);
135
0
    if (unlikely(!priv->sd))
136
0
    {
137
0
        vlc_media_tree_Release(ms->tree);
138
0
        free(priv);
139
0
        return NULL;
140
0
    }
141
142
    /* sd->description is set during vlc_sd_Create() */
143
0
    ms->description = priv->sd->description;
144
145
0
    priv->owner = provider;
146
147
0
    return ms;
148
0
}
149
150
151
static void
152
vlc_media_source_Delete(vlc_media_source_t *ms)
153
0
{
154
0
    media_source_private_t *priv = ms_priv(ms);
155
156
0
    vlc_sd_Destroy(priv->sd);
157
0
    vlc_media_tree_Release(ms->tree);
158
0
    free(priv);
159
0
}
160
161
void
162
vlc_media_source_Hold(vlc_media_source_t *ms)
163
0
{
164
0
    media_source_private_t *priv = ms_priv(ms);
165
0
    vlc_media_source_provider_t *provider = priv->owner;
166
0
    vlc_mutex_lock(&provider->lock);
167
0
    vlc_assert(priv->rc != 0);
168
0
    priv->rc++;
169
0
    vlc_mutex_unlock(&provider->lock);
170
0
}
171
172
void
173
vlc_media_source_Release(vlc_media_source_t *ms)
174
0
{
175
0
    media_source_private_t *priv = ms_priv(ms);
176
0
    vlc_media_source_provider_t *provider = priv->owner;
177
178
0
    vlc_mutex_lock(&provider->lock);
179
0
    vlc_assert(priv->rc != 0);
180
0
    priv->rc--;
181
0
    if (priv->rc != 0)
182
0
    {
183
0
        vlc_mutex_unlock(&provider->lock);
184
0
        return;
185
0
    }
186
0
    vlc_list_remove(&priv->node);
187
0
    vlc_mutex_unlock(&provider->lock);
188
189
0
    vlc_media_source_Delete(ms);
190
0
}
191
192
static vlc_media_source_t *
193
vlc_media_source_provider_Find(vlc_media_source_provider_t *provider,
194
                               const char *name)
195
0
{
196
0
    vlc_mutex_assert(&provider->lock);
197
0
    media_source_private_t *entry;
198
0
    vlc_list_foreach(entry, &provider->media_sources, node)
199
0
        if (!strcmp(name, entry->name))
200
0
            return &entry->public_data;
201
0
    return NULL;
202
0
}
203
204
vlc_media_source_provider_t *
205
vlc_media_source_provider_Get(libvlc_int_t *libvlc)
206
0
{
207
0
    return libvlc_priv(libvlc)->media_source_provider;
208
0
}
209
210
static void *
211
CreateObject(vlc_object_t *parent, size_t length, const char *typename)
212
84
{
213
#ifdef TEST_MEDIA_SOURCE
214
    VLC_UNUSED(parent);
215
    VLC_UNUSED(typename);
216
    return malloc(length);
217
#else
218
84
    return vlc_custom_create(parent, length, typename);
219
84
#endif
220
84
}
221
222
static void
223
ReleaseObject(void *obj)
224
0
{
225
#ifdef TEST_MEDIA_SOURCE
226
    free(obj);
227
#else
228
0
    vlc_object_delete((vlc_media_source_provider_t *)obj);
229
0
#endif
230
0
}
231
232
#undef vlc_media_source_provider_New
233
vlc_media_source_provider_t *
234
vlc_media_source_provider_New(vlc_object_t *parent)
235
84
{
236
84
    vlc_media_source_provider_t *provider =
237
84
            CreateObject(parent, sizeof(*provider), "media-source-provider");
238
84
    if (unlikely(!provider))
239
0
        return NULL;
240
241
84
    vlc_mutex_init(&provider->lock);
242
84
    vlc_list_init(&provider->media_sources);
243
84
    return provider;
244
84
}
245
246
void
247
vlc_media_source_provider_Delete(vlc_media_source_provider_t *provider)
248
0
{
249
0
    ReleaseObject(provider);
250
0
}
251
252
static vlc_media_source_t *
253
vlc_media_source_provider_Add(vlc_media_source_provider_t *provider,
254
                              const char *name)
255
0
{
256
0
    vlc_mutex_assert(&provider->lock);
257
258
0
    vlc_media_source_t *ms = vlc_media_source_New(provider, name);
259
0
    if (unlikely(!ms))
260
0
        return NULL;
261
262
0
    vlc_list_append(&ms_priv(ms)->node, &provider->media_sources);
263
0
    return ms;
264
0
}
265
266
vlc_media_source_t *
267
vlc_media_source_provider_GetMediaSource(vlc_media_source_provider_t *provider,
268
                                         const char *name)
269
0
{
270
0
    vlc_mutex_lock(&provider->lock);
271
0
    vlc_media_source_t *ms = vlc_media_source_provider_Find(provider, name);
272
0
    if (ms)
273
0
    {
274
0
        media_source_private_t *priv = ms_priv(ms);
275
0
        vlc_assert(priv->rc != 0);
276
0
        priv->rc++;
277
0
    }
278
0
    else
279
0
        ms = vlc_media_source_provider_Add(provider, name);
280
0
    vlc_mutex_unlock(&provider->lock);
281
282
0
    return ms;
283
0
}
284
285
struct vlc_media_source_meta_list
286
{
287
    struct VLC_VECTOR(struct vlc_media_source_meta) vec;
288
};
289
290
struct vlc_media_source_meta_list *
291
vlc_media_source_provider_List(vlc_media_source_provider_t *provider,
292
                               enum services_discovery_category_e category)
293
0
{
294
0
    char **longnames;
295
0
    int *categories;
296
0
    char **names = vlc_sd_GetNames(provider, &longnames, &categories);
297
0
    if (!names)
298
        /* vlc_sd_GetNames() returns NULL both on error or no result */
299
0
        return NULL;
300
301
0
    struct vlc_media_source_meta_list *list = malloc(sizeof(*list));
302
0
    if (unlikely(!list))
303
0
        return NULL;
304
305
0
    vlc_vector_init(&list->vec);
306
0
    for (size_t i = 0; names[i]; ++i)
307
0
    {
308
0
        if (category && categories[i] != (int) category)
309
0
        {
310
0
            free(names[i]);
311
0
            free(longnames[i]);
312
            /* only list items for the requested category */
313
0
            continue;
314
0
        }
315
316
0
        struct vlc_media_source_meta meta = {
317
0
            .name = names[i],
318
0
            .longname = longnames[i],
319
0
            .category = categories[i],
320
0
        };
321
0
        bool ok = vlc_vector_push(&list->vec, meta);
322
0
        if (unlikely(!ok)) {
323
            /* failure, clean up */
324
0
            for (char **p = names; *p; ++p)
325
0
                free(*p);
326
0
            for (char **p = longnames; *p; ++p)
327
0
                free(*p);
328
0
            vlc_vector_destroy(&list->vec);
329
0
            free(list);
330
0
            list = NULL;
331
0
            break;
332
0
        }
333
0
    }
334
335
0
    free(names);
336
0
    free(longnames);
337
0
    free(categories);
338
339
0
    return list;
340
0
}
341
342
size_t
343
vlc_media_source_meta_list_Count(vlc_media_source_meta_list_t *list)
344
0
{
345
0
    return list->vec.size;
346
0
}
347
348
struct vlc_media_source_meta *
349
vlc_media_source_meta_list_Get(vlc_media_source_meta_list_t *list, size_t index)
350
0
{
351
0
    return &list->vec.data[index];
352
0
}
353
354
void
355
0
vlc_media_source_meta_list_Delete(vlc_media_source_meta_list_t *list) {
356
0
    for (size_t i = 0; i < list->vec.size; ++i)
357
0
    {
358
0
        free(list->vec.data[i].name);
359
0
        free(list->vec.data[i].longname);
360
0
    }
361
0
    vlc_vector_destroy(&list->vec);
362
0
    free(list);
363
0
}