media.c (7108B)
1 /* 2 This file is part of GNUnet. 3 Copyright (C) 2024 GNUnet e.V. 4 5 GNUnet is free software: you can redistribute it and/or modify it 6 under the terms of the GNU Affero General Public License as published 7 by the Free Software Foundation, either version 3 of the License, 8 or (at your option) any later version. 9 10 GNUnet is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Affero General Public License for more details. 14 15 You should have received a copy of the GNU Affero General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 SPDX-License-Identifier: AGPL3.0-or-later 19 */ 20 /* 21 * @author Tobias Frisch 22 * @file media.c 23 */ 24 25 #include "media.h" 26 27 #include <glib-2.0/glib.h> 28 #include <pipewire/impl.h> 29 30 #include "application.h" 31 32 static void 33 on_core_done(void *data, 34 UNUSED uint32_t id, 35 int seq) 36 { 37 g_assert(data); 38 39 MESSENGER_MediaInfo *media = (MESSENGER_MediaInfo*) data; 40 MESSENGER_Application *app = media->app; 41 42 if ((seq == media->pw.pending) && (app->pw.main_loop)) 43 pw_main_loop_quit(app->pw.main_loop); 44 } 45 46 static void 47 on_core_error(void *data, 48 UNUSED uint32_t id, 49 UNUSED int seq, 50 int res, 51 const char *message) 52 { 53 g_assert((data) && (message)); 54 55 MESSENGER_MediaInfo *media = (MESSENGER_MediaInfo*) data; 56 MESSENGER_Application *app = media->app; 57 58 g_printerr("ERROR: %s\n", message); 59 60 if ((id == PW_ID_CORE) && (res == -EPIPE) && (app->pw.main_loop)) 61 pw_main_loop_quit(app->pw.main_loop); 62 } 63 64 static const struct pw_core_events remote_core_events = { 65 PW_VERSION_CORE_EVENTS, 66 .done = on_core_done, 67 .error = on_core_error, 68 }; 69 70 static void 71 registry_event_global(void *data, 72 uint32_t id, 73 uint32_t permissions, 74 const char *type, 75 uint32_t version, 76 const struct spa_dict *props) 77 { 78 g_assert(data); 79 80 MESSENGER_MediaInfo *media = (MESSENGER_MediaInfo*) data; 81 82 if (!props) 83 return; 84 85 struct pw_properties *properties = pw_properties_new_dict(props); 86 if (!properties) 87 return; 88 89 size_t size = pw_map_get_size(&(media->pw.globals)); 90 while (id > size) 91 pw_map_insert_at(&(media->pw.globals), size++, NULL); 92 93 pw_map_insert_at(&(media->pw.globals), id, properties); 94 95 media->pw.pending = media->pw.core? pw_core_sync(media->pw.core, 0, 0) : 0; 96 } 97 98 static void 99 registry_event_global_remove(void *data, 100 uint32_t id) 101 { 102 g_assert(data); 103 104 MESSENGER_MediaInfo *media = (MESSENGER_MediaInfo*) data; 105 106 struct pw_properties *properties = pw_map_lookup(&(media->pw.globals), id); 107 if (!properties) 108 return; 109 110 pw_map_insert_at(&(media->pw.globals), id, NULL); 111 pw_properties_free(properties); 112 } 113 114 static const struct pw_registry_events registry_events = { 115 PW_VERSION_REGISTRY_EVENTS, 116 .global = registry_event_global, 117 .global_remove = registry_event_global_remove, 118 }; 119 120 void 121 media_pw_init(MESSENGER_MediaInfo *media, 122 MESSENGER_Application *app, 123 int fd) 124 { 125 g_assert((media) && (app)); 126 127 media_pw_cleanup(media); 128 media->app = app; 129 130 if (app->pw.context) 131 { 132 if (-1 != fd) 133 media->pw.core = pw_context_connect_fd( 134 app->pw.context, 135 fd, 136 NULL, 137 0 138 ); 139 else 140 media->pw.core = pw_context_connect(app->pw.context, NULL, 0); 141 } 142 else 143 media->pw.core = NULL; 144 145 media->pw.registry = media->pw.core? 146 pw_core_get_registry(media->pw.core, PW_VERSION_REGISTRY, 0) : NULL; 147 148 pw_map_init(&(media->pw.globals), 64, 16); 149 150 if (media->pw.core) 151 pw_core_add_listener( 152 media->pw.core, 153 &(media->pw.core_listener), 154 &remote_core_events, 155 media 156 ); 157 158 if (media->pw.registry) 159 pw_registry_add_listener( 160 media->pw.registry, 161 &(media->pw.registry_listener), 162 ®istry_events, 163 media 164 ); 165 } 166 167 void 168 media_init_camera_capturing(MESSENGER_MediaInfo *media, 169 MESSENGER_Application *app) 170 { 171 g_assert((media) && (app)); 172 int fd = -1; 173 174 #ifndef MESSENGER_APPLICATION_NO_PORTAL 175 if ((app->portal) && (xdp_portal_is_camera_present(app->portal))) 176 fd = xdp_portal_open_pipewire_remote_for_camera(app->portal); 177 #endif 178 179 media_pw_init(media, app, fd); 180 } 181 182 void 183 media_init_screen_sharing(MESSENGER_MediaInfo *media, 184 MESSENGER_Application *app) 185 { 186 g_assert((media) && (app)); 187 int fd = -1; 188 189 #ifndef MESSENGER_APPLICATION_NO_PORTAL 190 fd = application_get_active_session_remote(app); 191 #endif 192 193 media_pw_init(media, app, fd); 194 } 195 196 static int 197 destroy_global(void *obj, 198 UNUSED void *data) 199 { 200 struct pw_properties *properties = (struct pw_properties*) obj; 201 202 if (!properties) 203 return 0; 204 205 pw_properties_free(properties); 206 return 0; 207 } 208 209 void 210 media_pw_cleanup(MESSENGER_MediaInfo *media) 211 { 212 g_assert(media); 213 214 if (media->pw.registry) 215 pw_proxy_destroy((struct pw_proxy*) media->pw.registry); 216 217 media->pw.registry = NULL; 218 219 pw_map_for_each(&(media->pw.globals), destroy_global, NULL); 220 pw_map_clear(&(media->pw.globals)); 221 222 if (media->pw.core) 223 pw_core_disconnect(media->pw.core); 224 225 media->pw.core = NULL; 226 media->app = NULL; 227 } 228 229 void 230 media_pw_main_loop_run(MESSENGER_MediaInfo *media) 231 { 232 g_assert(media); 233 234 if (!(media->pw.core)) 235 return; 236 237 media->pw.pending = media->pw.core? pw_core_sync(media->pw.core, 0, 0) : 0; 238 239 application_pw_main_loop_run(media->app); 240 } 241 242 typedef struct IterateGlobalClosure 243 { 244 MESSENGER_MediaNodeIterator iterator; 245 void *cls; 246 } IterateGlobalClosure; 247 248 static int 249 iterate_global(void *obj, 250 void *data) 251 { 252 g_assert(data); 253 254 IterateGlobalClosure *closure = (IterateGlobalClosure*) data; 255 struct pw_properties *properties = (struct pw_properties*) obj; 256 257 if (!properties) 258 return 0; 259 260 struct spa_dict *props = &(properties->dict); 261 262 if ((!props) || (!props->n_items)) 263 return 0; 264 265 const char *name = NULL; 266 const char *description = NULL; 267 const char *media_role = NULL; 268 const char *media_class = NULL; 269 270 const struct spa_dict_item *item; 271 spa_dict_for_each(item, props) 272 { 273 if (0 == g_strcmp0(item->key, "node.name")) 274 name = item->value; 275 276 if (0 == g_strcmp0(item->key, "node.description")) 277 description = item->value; 278 279 if (0 == g_strcmp0(item->key, "media.class")) 280 media_class = item->value; 281 282 if (0 == g_strcmp0(item->key, "media.role")) 283 media_role = item->value; 284 } 285 286 if ((!name) || (!media_class)) 287 return 0; 288 289 closure->iterator( 290 closure->cls, 291 name, 292 description, 293 media_class, 294 media_role 295 ); 296 297 return 0; 298 } 299 300 void 301 media_pw_iterate_nodes(MESSENGER_MediaInfo *media, 302 MESSENGER_MediaNodeIterator it, 303 void *cls) 304 { 305 g_assert(media); 306 307 if (!it) 308 return; 309 310 IterateGlobalClosure closure; 311 closure.iterator = it; 312 closure.cls = cls; 313 314 pw_map_for_each(&(media->pw.globals), iterate_global, &closure); 315 }