4 * @file gmyth/gmyth_upnp.c
6 * @brief <p> GMythUPnP allows that a MythTV frontend discovers a
7 * MythTV backend, using the UPnP architecture.
9 * Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia.
10 * @author Rosfran Lins Borges <rosfran.borges@indt.org.br>
11 * @authon Renato Araujo Oliveira Filho <renato.filho@indt.org.br>
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Library General Public
15 * License as published by the Free Software Foundation; either
16 * version 2 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Library General Public License for more details.
23 * You should have received a copy of the GNU Library General Public
24 * License along with this library; if not, write to the
25 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26 * Boston, MA 02111-1307, USA.
33 #include "gmyth_upnp.h"
35 #include <gmyth/gmyth.h>
36 #include <upnp/upnp.h>
40 #define UPNP_SEARCH_TIMEOUT 5
41 #define UPNP_SERVICE_FILTER "urn:schemas-mythtv-org:service:MythTv:1"
42 #define SERVER_ID "MythTV AV Media Server"
44 typedef struct _GMythUPnPPrivate GMythUPnPPrivate;
45 typedef struct _GMythUPnPIdleData GMythUPnPIdleData;
47 #define GMYTH_UPNP_GET_PRIVATE(obj) \
48 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GMYTH_UPNP_TYPE, GMythUPnPPrivate))
57 struct _GMythUPnPIdleData
60 GMythBackendInfo *server;
63 struct _GMythUPnPPrivate {
65 gboolean upnp_dev_found;
71 UpnpClient_Handle client_id;
74 static void gmyth_upnp_class_init (GMythUPnPClass* klass);
75 static void gmyth_upnp_init (GMythUPnP* object);
76 static void gmyth_upnp_dispose (GObject* object);
77 static void gmyth_upnp_finalize (GObject* object);
78 static GObject* gmyth_upnp_constructor (GType type,
79 guint n_construct_params,
80 GObjectConstructParam *construct_params);
83 static int _upnp_event_handler (Upnp_EventType e_type,
88 static int signals[LAST_SIGNAL] = {0};
90 static GMythUPnP *singleton = NULL;
92 G_DEFINE_TYPE(GMythUPnP, gmyth_upnp, G_TYPE_OBJECT);
95 gmyth_upnp_class_init(GMythUPnPClass * klass)
97 GObjectClass *gobject_class;
98 GMythUPnPClass *gupnp_class;
100 gobject_class = (GObjectClass *) klass;
101 gupnp_class = (GMythUPnPClass *) gobject_class;
103 gobject_class->dispose = gmyth_upnp_dispose;
104 gobject_class->finalize = gmyth_upnp_finalize;
105 gobject_class->constructor = gmyth_upnp_constructor;
107 g_type_class_add_private (gobject_class, sizeof(GMythUPnPPrivate));
111 signals[DEVICE_FOUND] = g_signal_new("device-found",
112 G_TYPE_FROM_CLASS(gupnp_class),
115 g_cclosure_marshal_VOID__OBJECT,
117 GMYTH_BACKEND_INFO_TYPE);
119 signals[DEVICE_LOST] = g_signal_new("device-lost",
120 G_TYPE_FROM_CLASS(gupnp_class),
123 g_cclosure_marshal_VOID__OBJECT,
125 GMYTH_BACKEND_INFO_TYPE);
130 gmyth_upnp_init(GMythUPnP* self)
133 GMythUPnPPrivate *priv;
135 priv = GMYTH_UPNP_GET_PRIVATE (self);
137 priv->mutex = g_mutex_new ();
138 priv->servers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
140 /* initalize upnp client */
141 ret = UpnpInit (NULL, 0);
142 if (ret != UPNP_E_SUCCESS)
143 g_warning ("Fail to inilialize upnp SDK: %d", ret);
146 ret = UpnpRegisterClient (_upnp_event_handler,
147 &priv->client_id, &priv->client_id);
149 if (ret != UPNP_E_SUCCESS)
150 g_warning ("Fail to start upnp client: %d", ret);
155 gmyth_upnp_constructor (GType type,
156 guint n_construct_params,
157 GObjectConstructParam *construct_params)
163 object = G_OBJECT_CLASS (gmyth_upnp_parent_class)->constructor (type,
167 singleton = GMYTH_UPNP (object);
170 object = g_object_ref (G_OBJECT (singleton));
176 gmyth_upnp_dispose(GObject * object)
178 /* finalize upnp client */
180 G_OBJECT_CLASS(gmyth_upnp_parent_class)->dispose(object);
184 gmyth_upnp_finalize(GObject * object)
186 G_OBJECT_CLASS(gmyth_upnp_parent_class)->finalize(object);
192 gmyth_upnp_get_instance (void)
194 return GMYTH_UPNP(g_object_new(GMYTH_UPNP_TYPE, NULL));
199 gmyth_upnp_search (GMythUPnP *self)
202 GMythUPnPPrivate *priv;
204 priv = GMYTH_UPNP_GET_PRIVATE (self);
206 ret = UpnpSearchAsync (priv->client_id,
211 if (ret != UPNP_E_SUCCESS)
212 g_warning ("Fail to start upnp listener: %d", ret);
216 _fill_servers_cb (gpointer key,
222 lst = (GList **) user_data;
224 *lst = g_list_append (*lst, g_object_ref (value));
228 gmyth_upnp_get_devices (GMythUPnP *self)
230 GMythUPnPPrivate *priv;
233 priv = GMYTH_UPNP_GET_PRIVATE (self);
235 g_hash_table_foreach (priv->servers, (GHFunc) _fill_servers_cb, &lst);
241 _idle_emit_device_found_signal (gpointer data)
243 GMythUPnPPrivate *priv;
244 GMythUPnPIdleData *idle_data;
246 idle_data = (GMythUPnPIdleData *) data;
247 priv = GMYTH_UPNP_GET_PRIVATE (idle_data->parent);
249 g_signal_emit (idle_data->parent, signals[DEVICE_FOUND], 0, idle_data->server);
251 g_object_unref (idle_data->server);
259 _idle_emit_device_lost_signal (gpointer data)
261 GMythUPnPPrivate *priv;
262 GMythUPnPIdleData *idle_data;
264 idle_data = (GMythUPnPIdleData *) data;
265 priv = GMYTH_UPNP_GET_PRIVATE (idle_data->parent);
267 g_signal_emit (idle_data->parent, signals[DEVICE_LOST], 0, idle_data->server);
269 g_object_unref (idle_data->server);
277 _xml_get_first_document_item (IXML_Document * doc,
280 IXML_NodeList *node_list = NULL;
281 IXML_Node *text_node = NULL;
282 IXML_Node *tmp_node = NULL;
286 node_list = ixmlDocument_getElementsByTagName (doc,
291 if ((tmp_node = ixmlNodeList_item (node_list, 0)))
293 text_node = ixmlNode_getFirstChild (tmp_node);
295 ret = strdup (ixmlNode_getNodeValue (text_node));
300 ixmlNodeList_free (node_list);
307 _append_mythtv_server_from_loation (GMythUPnP *self,
309 const gchar *location)
311 GMythUPnPPrivate *priv;
315 priv = GMYTH_UPNP_GET_PRIVATE (self);
317 base_url = g_strdup (location);
318 end = g_strrstr (base_url, "/");
322 IXML_Document *desc_doc;
326 info_url = g_strconcat (base_url,
327 "Myth/GetConnectionInfo",
331 ret = UpnpDownloadXmlDoc (info_url, &desc_doc);
332 if (ret != UPNP_E_SUCCESS)
334 g_warning ("Error obtaining device desc: %d", ret);
338 GMythBackendInfo *info;
339 GMythUPnPIdleData *idle_data;
341 info = gmyth_backend_info_new_full (
342 _xml_get_first_document_item (desc_doc, "Host"),
343 _xml_get_first_document_item (desc_doc, "UserName"),
344 _xml_get_first_document_item (desc_doc, "Password"),
345 _xml_get_first_document_item (desc_doc, "Name"),
346 // Current mythtv version not export port number
350 ixmlDocument_free (desc_doc);
352 g_mutex_lock (priv->mutex);
353 g_hash_table_insert (priv->servers,
355 g_object_ref (info));
356 g_mutex_unlock (priv->mutex);
359 idle_data = g_new0 (GMythUPnPIdleData, 1);
360 idle_data->parent = self;
361 idle_data->server = g_object_ref (info);
364 g_idle_add (_idle_emit_device_found_signal, idle_data);
370 _remove_mythtv_server (GMythUPnP *self,
373 GMythUPnPPrivate *priv;
374 GMythBackendInfo *info;
376 priv = GMYTH_UPNP_GET_PRIVATE (self);
378 g_mutex_lock (priv->mutex);
379 info = g_hash_table_lookup (priv->servers, uuid);
382 GMythUPnPIdleData *idle_data;
384 idle_data = g_new0 (GMythUPnPIdleData, 1);
385 idle_data->parent = self;
386 idle_data->server = g_object_ref (info);
388 g_hash_table_remove (priv->servers, uuid);
391 g_idle_add (_idle_emit_device_lost_signal, idle_data);
393 g_mutex_unlock (priv->mutex);
397 static GMythBackendInfo*
398 _find_service_by_uuid (GMythUPnP *self,
401 GMythUPnPPrivate *priv;
402 GMythBackendInfo *info;
404 priv = GMYTH_UPNP_GET_PRIVATE (self);
407 g_mutex_lock (priv->mutex);
408 info = g_hash_table_lookup (priv->servers, uuid);
409 g_mutex_unlock (priv->mutex);
415 _upnp_event_handler (Upnp_EventType e_type,
419 g_return_val_if_fail (singleton != NULL, 0);
423 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
424 case UPNP_DISCOVERY_SEARCH_RESULT:
426 struct Upnp_Discovery *d_event;
428 d_event = (struct Upnp_Discovery *) e;
430 if (strcmp (d_event->ServiceType, UPNP_SERVICE_FILTER) != 0)
432 g_warning ("invalid device : %s", d_event->DeviceId);
437 if (d_event->ErrCode != UPNP_E_SUCCESS)
439 g_warning ("Error in Discovery: %d", d_event->ErrCode);
443 if (_find_service_by_uuid (GMYTH_UPNP (singleton), d_event->DeviceId) == NULL)
444 _append_mythtv_server_from_loation (singleton,
451 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
453 GMythUPnPPrivate *priv;
454 struct Upnp_Discovery *d_event;
456 d_event = (struct Upnp_Discovery *) e;
457 if (d_event->ErrCode != UPNP_E_SUCCESS)
459 g_warning ("Error in Discovery: %d", d_event->ErrCode);
463 priv = GMYTH_UPNP_GET_PRIVATE (singleton);
464 _remove_mythtv_server (singleton,