messenger-gtk

Gtk+3 graphical user interfaces for GNUnet Messenger
Log | Files | Refs | Submodules | README | LICENSE

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       &registry_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 }