/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 | } |