Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-3.0-or-later
2 : : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
3 : :
4 : : #define G_LOG_DOMAIN "valent-media"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gio/gio.h>
9 : : #include <libpeas.h>
10 : : #include <libvalent-core.h>
11 : :
12 : : #include "valent-media.h"
13 : : #include "valent-media-adapter.h"
14 : : #include "valent-media-player.h"
15 : :
16 : :
17 : : /**
18 : : * ValentMedia:
19 : : *
20 : : * A class for monitoring and controlling media players.
21 : : *
22 : : * `ValentMedia` is an aggregator of media players, intended for use by
23 : : * [class@Valent.DevicePlugin] implementations.
24 : : *
25 : : * Plugins can implement [class@Valent.MediaAdapter] to provide an
26 : : * interface to manage instances of [class@Valent.MediaPlayer].
27 : : *
28 : : * Since: 1.0
29 : : */
30 : :
31 : : struct _ValentMedia
32 : : {
33 : : ValentComponent parent_instance;
34 : :
35 : : GPtrArray *adapters;
36 : : GListModel *exports;
37 : : GPtrArray *players;
38 : : GPtrArray *paused;
39 : : };
40 : :
41 : : static void g_list_model_iface_init (GListModelInterface *iface);
42 : :
43 [ + + + - ]: 342 : G_DEFINE_FINAL_TYPE_WITH_CODE (ValentMedia, valent_media, VALENT_TYPE_COMPONENT,
44 : : G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, g_list_model_iface_init))
45 : :
46 : : static ValentMedia *default_media = NULL;
47 : :
48 : :
49 : : static void
50 : 76 : on_items_changed (GListModel *list,
51 : : unsigned int position,
52 : : unsigned int removed,
53 : : unsigned int added,
54 : : ValentMedia *self)
55 : : {
56 : 76 : unsigned int real_position = 0;
57 : :
58 : 76 : VALENT_ENTRY;
59 : :
60 [ + - ]: 76 : g_assert (G_IS_LIST_MODEL (list));
61 [ + - ]: 76 : g_assert (VALENT_IS_MEDIA (self));
62 : :
63 : : /* Translate the adapter position */
64 [ + - ]: 127 : for (unsigned int i = 0; i < self->adapters->len; i++)
65 : : {
66 : 127 : GListModel *adapter = g_ptr_array_index (self->adapters, i);
67 : :
68 [ + + ]: 127 : if (adapter == list)
69 : : break;
70 : :
71 : 51 : real_position += g_list_model_get_n_items (adapter);
72 : : }
73 : :
74 : 76 : real_position += position;
75 : :
76 : : /* Propagate the changes */
77 [ + + ]: 96 : for (unsigned int i = 0; i < removed; i++)
78 : : {
79 : 20 : g_autoptr (ValentMediaPlayer) player = NULL;
80 : :
81 : 20 : player = g_ptr_array_steal_index (self->players, real_position);
82 : 20 : g_ptr_array_remove (self->paused, player);
83 : :
84 [ + - ]: 20 : VALENT_NOTE ("removed %s (%s)",
85 : : G_OBJECT_TYPE_NAME (player),
86 : : valent_media_player_get_name (player));
87 : : }
88 : :
89 [ + + ]: 96 : for (unsigned int i = 0; i < added; i++)
90 : : {
91 : 20 : ValentMediaPlayer *player = NULL;
92 : :
93 : 20 : player = g_list_model_get_item (list, position + i);
94 : 20 : g_ptr_array_insert (self->players, real_position + i, player);
95 : :
96 : 20 : VALENT_NOTE ("added %s (%s)",
97 : : G_OBJECT_TYPE_NAME (player),
98 : : valent_media_player_get_name (player));
99 : : }
100 : :
101 : 76 : g_list_model_items_changed (G_LIST_MODEL (self), real_position, removed, added);
102 : :
103 : 76 : VALENT_EXIT;
104 : : }
105 : :
106 : : /*
107 : : * GListModel
108 : : */
109 : : static gpointer
110 : 25 : valent_media_get_item (GListModel *list,
111 : : unsigned int position)
112 : : {
113 : 25 : ValentMedia *self = VALENT_MEDIA (list);
114 : :
115 [ + - ]: 25 : g_assert (VALENT_IS_MEDIA (self));
116 : :
117 [ + - ]: 25 : if G_UNLIKELY (position >= self->players->len)
118 : : return NULL;
119 : :
120 : 25 : return g_object_ref (g_ptr_array_index (self->players, position));
121 : : }
122 : :
123 : : static GType
124 : 0 : valent_media_get_item_type (GListModel *list)
125 : : {
126 : 0 : return VALENT_TYPE_MEDIA_PLAYER;
127 : : }
128 : :
129 : : static unsigned int
130 : 28 : valent_media_get_n_items (GListModel *list)
131 : : {
132 : 28 : ValentMedia *self = VALENT_MEDIA (list);
133 : :
134 [ + - ]: 28 : g_assert (VALENT_IS_MEDIA (self));
135 : :
136 : 28 : return self->players->len;
137 : : }
138 : :
139 : : static void
140 : 5 : g_list_model_iface_init (GListModelInterface *iface)
141 : : {
142 : 5 : iface->get_item = valent_media_get_item;
143 : 5 : iface->get_item_type = valent_media_get_item_type;
144 : 5 : iface->get_n_items = valent_media_get_n_items;
145 : 5 : }
146 : :
147 : : /*
148 : : * ValentComponent
149 : : */
150 : : static void
151 : 18 : valent_media_bind_extension (ValentComponent *component,
152 : : GObject *extension)
153 : : {
154 : 18 : ValentMedia *self = VALENT_MEDIA (component);
155 : 18 : GListModel *list = G_LIST_MODEL (extension);
156 : 18 : unsigned int n_exports = 0;
157 : :
158 : 18 : VALENT_ENTRY;
159 : :
160 [ + - ]: 18 : g_assert (VALENT_IS_MEDIA (self));
161 [ - + ]: 18 : g_assert (VALENT_IS_MEDIA_ADAPTER (extension));
162 : :
163 : 18 : g_ptr_array_add (self->adapters, g_object_ref (extension));
164 : 18 : on_items_changed (list, 0, 0, g_list_model_get_n_items (list), self);
165 : 18 : g_signal_connect_object (list,
166 : : "items-changed",
167 : : G_CALLBACK (on_items_changed),
168 : : self,
169 : : 0);
170 : :
171 : 18 : n_exports = g_list_model_get_n_items (G_LIST_MODEL (self->exports));
172 : :
173 [ - + ]: 18 : for (unsigned int i = 0; i < n_exports; i++)
174 : : {
175 : 0 : g_autoptr (ValentMediaPlayer) player = NULL;
176 : :
177 : 0 : player = g_list_model_get_item (G_LIST_MODEL (self->exports), i);
178 [ # # ]: 0 : valent_media_adapter_export_player (VALENT_MEDIA_ADAPTER (extension),
179 : : player);
180 : : }
181 : :
182 : 18 : VALENT_EXIT;
183 : : }
184 : :
185 : : static void
186 : 16 : valent_media_unbind_extension (ValentComponent *component,
187 : : GObject *extension)
188 : : {
189 : 16 : ValentMedia *self = VALENT_MEDIA (component);
190 : 16 : GListModel *list = G_LIST_MODEL (extension);
191 : :
192 : 16 : VALENT_ENTRY;
193 : :
194 [ + - ]: 16 : g_assert (VALENT_IS_MEDIA (self));
195 [ - + ]: 16 : g_assert (VALENT_IS_MEDIA_ADAPTER (extension));
196 : :
197 [ - + ]: 16 : if (!g_ptr_array_find (self->adapters, extension, NULL))
198 : : {
199 : 0 : g_warning ("No such adapter \"%s\" found in \"%s\"",
200 : : G_OBJECT_TYPE_NAME (extension),
201 : : G_OBJECT_TYPE_NAME (component));
202 : 0 : return;
203 : : }
204 : :
205 : 16 : g_signal_handlers_disconnect_by_func (list, on_items_changed, self);
206 : 16 : on_items_changed (list, 0, g_list_model_get_n_items (list), 0, self);
207 : 16 : g_ptr_array_remove (self->adapters, extension);
208 : :
209 : 16 : VALENT_EXIT;
210 : : }
211 : :
212 : : /*
213 : : * ValentObject
214 : : */
215 : : static void
216 : 14 : valent_media_destroy (ValentObject *object)
217 : : {
218 : 14 : ValentMedia *self = VALENT_MEDIA (object);
219 : :
220 : 14 : g_list_store_remove_all (G_LIST_STORE (self->exports));
221 : :
222 : 14 : VALENT_OBJECT_CLASS (valent_media_parent_class)->destroy (object);
223 : 14 : }
224 : :
225 : : /*
226 : : * GObject
227 : : */
228 : : static void
229 : 14 : valent_media_finalize (GObject *object)
230 : : {
231 : 14 : ValentMedia *self = VALENT_MEDIA (object);
232 : :
233 [ + - ]: 14 : g_clear_object (&self->exports);
234 [ + - ]: 14 : g_clear_pointer (&self->adapters, g_ptr_array_unref);
235 [ + - ]: 14 : g_clear_pointer (&self->players, g_ptr_array_unref);
236 [ + - ]: 14 : g_clear_pointer (&self->paused, g_ptr_array_unref);
237 : :
238 : 14 : G_OBJECT_CLASS (valent_media_parent_class)->finalize (object);
239 : 14 : }
240 : :
241 : : static void
242 : 5 : valent_media_class_init (ValentMediaClass *klass)
243 : : {
244 : 5 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
245 : 5 : ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
246 : 5 : ValentComponentClass *component_class = VALENT_COMPONENT_CLASS (klass);
247 : :
248 : 5 : object_class->finalize = valent_media_finalize;
249 : :
250 : 5 : vobject_class->destroy = valent_media_destroy;
251 : :
252 : 5 : component_class->bind_extension = valent_media_bind_extension;
253 : 5 : component_class->unbind_extension = valent_media_unbind_extension;
254 : : }
255 : :
256 : : static void
257 : 15 : valent_media_init (ValentMedia *self)
258 : : {
259 : 15 : self->adapters = g_ptr_array_new_with_free_func (g_object_unref);
260 : 15 : self->exports = G_LIST_MODEL (g_list_store_new (VALENT_TYPE_MEDIA_PLAYER));
261 : 15 : self->players = g_ptr_array_new_with_free_func (g_object_unref);
262 : 15 : self->paused = g_ptr_array_new ();
263 : :
264 : 15 : g_ptr_array_add (self->adapters, g_object_ref (self->exports));
265 : 15 : g_signal_connect_object (self->exports,
266 : : "items-changed",
267 : : G_CALLBACK (on_items_changed),
268 : : self, 0);
269 : 15 : }
270 : :
271 : : /**
272 : : * valent_media_get_default:
273 : : *
274 : : * Get the default [class@Valent.Media].
275 : : *
276 : : * Returns: (transfer none) (not nullable): a `ValentMedia`
277 : : *
278 : : * Since: 1.0
279 : : */
280 : : ValentMedia *
281 : 46 : valent_media_get_default (void)
282 : : {
283 [ + + ]: 46 : if (default_media == NULL)
284 : : {
285 : 15 : default_media = g_object_new (VALENT_TYPE_MEDIA,
286 : : "plugin-domain", "media",
287 : : "plugin-type", VALENT_TYPE_MEDIA_ADAPTER,
288 : : NULL);
289 : :
290 : 15 : g_object_add_weak_pointer (G_OBJECT (default_media),
291 : : (gpointer)&default_media);
292 : : }
293 : :
294 : 46 : return default_media;
295 : : }
296 : :
297 : : /**
298 : : * valent_media_export_player:
299 : : * @media: a `ValentMedia`
300 : : * @player: a `ValentMediaPlayer`
301 : : *
302 : : * Export @player on all adapters that support it.
303 : : *
304 : : * Since: 1.0
305 : : */
306 : : void
307 : 13 : valent_media_export_player (ValentMedia *media,
308 : : ValentMediaPlayer *player)
309 : : {
310 : 13 : VALENT_ENTRY;
311 : :
312 [ + - ]: 13 : g_return_if_fail (VALENT_IS_MEDIA (media));
313 [ - + ]: 13 : g_return_if_fail (VALENT_IS_MEDIA_PLAYER (player));
314 : :
315 [ - + ]: 13 : if (g_ptr_array_find (media->players, player, NULL))
316 : : {
317 : 0 : g_critical ("%s(): known player %s (%s)",
318 : : G_STRFUNC,
319 : : G_OBJECT_TYPE_NAME (player),
320 : : valent_media_player_get_name (player));
321 : 0 : VALENT_EXIT;
322 : : }
323 : :
324 : : // Starting at index `1` skips the exports GListModel
325 [ + + ]: 27 : for (unsigned int i = 1; i < media->adapters->len; i++)
326 : : {
327 : 14 : ValentMediaAdapter *adapter = NULL;
328 : :
329 : 14 : adapter = g_ptr_array_index (media->adapters, i);
330 : 14 : valent_media_adapter_export_player (adapter, player);
331 : : }
332 : :
333 : 13 : g_list_store_append (G_LIST_STORE (media->exports), player);
334 : :
335 : 13 : VALENT_EXIT;
336 : : }
337 : :
338 : : /**
339 : : * valent_media_unexport_player:
340 : : * @media: a `ValentMedia`
341 : : * @player: a `ValentMediaPlayer`
342 : : *
343 : : * Unexport @player from all adapters that support it.
344 : : *
345 : : * Since: 1.0
346 : : */
347 : : void
348 : 1 : valent_media_unexport_player (ValentMedia *media,
349 : : ValentMediaPlayer *player)
350 : : {
351 : 1 : unsigned int position = 0;
352 : :
353 : 1 : VALENT_ENTRY;
354 : :
355 [ + - ]: 1 : g_return_if_fail (VALENT_IS_MEDIA (media));
356 [ - + ]: 1 : g_return_if_fail (VALENT_IS_MEDIA_PLAYER (player));
357 : :
358 [ - + ]: 1 : if (!g_list_store_find (G_LIST_STORE (media->exports), player, &position))
359 : : {
360 : 0 : g_critical ("%s(): unknown player %s (%s)",
361 : : G_STRFUNC,
362 : : G_OBJECT_TYPE_NAME (player),
363 : : valent_media_player_get_name (player));
364 : 0 : VALENT_EXIT;
365 : : }
366 : :
367 : : // Starting at index `1` skips the exports GListModel
368 [ + + ]: 3 : for (unsigned int i = 1; i < media->adapters->len; i++)
369 : : {
370 : 2 : ValentMediaAdapter *adapter = NULL;
371 : :
372 : 2 : adapter = g_ptr_array_index (media->adapters, i);
373 : 2 : valent_media_adapter_unexport_player (adapter, player);
374 : : }
375 : :
376 : 1 : g_list_store_remove (G_LIST_STORE (media->exports), position);
377 : :
378 : 1 : VALENT_EXIT;
379 : : }
380 : :
|