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>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #include "gmyth_upnp.h"
34 #include <gmyth/gmyth.h>
35 #include <upnp/upnp.h>
39 #define UPNP_SEARCH_TIMEOUT 5
40 #define UPNP_SERVICE_FILTER "urn:schemas-mythtv-org:service:MythTv:1"
41 #define SERVER_ID "MythTV AV Media Server"
43 typedef struct _GMythUPnPPrivate GMythUPnPPrivate;
44 typedef struct _GMythUPnPIdleData GMythUPnPIdleData;
46 #define GMYTH_UPNP_GET_PRIVATE(obj) \
47 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GMYTH_UPNP_TYPE, GMythUPnPPrivate))
56 struct _GMythUPnPIdleData
59 GMythBackendInfo *server;
62 struct _GMythUPnPPrivate {
64 GMythUPnPDeviceStatus last_status;
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 _idle_emit_device_found_signal (gpointer data)
218 GMythUPnPPrivate *priv;
219 GMythUPnPIdleData *idle_data;
221 idle_data = (GMythUPnPIdleData *) data;
222 priv = GMYTH_UPNP_GET_PRIVATE (idle_data->parent);
224 g_signal_emit (idle_data->parent, signals[DEVICE_FOUND], 0, idle_data->server);
226 g_object_unref (idle_data->server);
234 _idle_emit_device_lost_signal (gpointer data)
236 GMythUPnPPrivate *priv;
237 GMythUPnPIdleData *idle_data;
239 idle_data = (GMythUPnPIdleData *) data;
240 priv = GMYTH_UPNP_GET_PRIVATE (idle_data->parent);
242 g_signal_emit (idle_data->parent, signals[DEVICE_LOST], 0, idle_data->server);
244 g_object_unref (idle_data->server);
252 _xml_get_first_document_item (IXML_Document * doc,
255 IXML_NodeList *node_list = NULL;
256 IXML_Node *text_node = NULL;
257 IXML_Node *tmp_node = NULL;
261 node_list = ixmlDocument_getElementsByTagName (doc,
266 if ((tmp_node = ixmlNodeList_item (node_list, 0)))
268 text_node = ixmlNode_getFirstChild (tmp_node);
270 ret = strdup (ixmlNode_getNodeValue (text_node));
275 ixmlNodeList_free (node_list);
282 _append_mythtv_server_from_loation (GMythUPnP *self,
284 const gchar *location)
286 GMythUPnPPrivate *priv;
290 priv = GMYTH_UPNP_GET_PRIVATE (self);
292 base_url = g_strdup (location);
293 end = g_strrstr (base_url, "/");
297 IXML_Document *desc_doc;
301 info_url = g_strconcat (base_url,
302 "Myth/GetConnectionInfo",
306 ret = UpnpDownloadXmlDoc (info_url, &desc_doc);
307 if (ret != UPNP_E_SUCCESS)
309 g_warning ("Error obtaining device desc: %d", ret);
313 GMythBackendInfo *info;
314 GMythUPnPIdleData *idle_data;
316 info = gmyth_backend_info_new_full (
317 _xml_get_first_document_item (desc_doc, "Host"),
318 _xml_get_first_document_item (desc_doc, "UserName"),
319 _xml_get_first_document_item (desc_doc, "Password"),
320 _xml_get_first_document_item (desc_doc, "Name"),
321 // Current mythtv version not export port number
325 ixmlDocument_free (desc_doc);
327 g_mutex_lock (priv->mutex);
328 g_hash_table_insert (priv->servers,
330 g_object_ref (info));
331 g_mutex_unlock (priv->mutex);
332 g_debug ("info url: %s", info_url);
335 idle_data = g_new0 (GMythUPnPIdleData, 1);
336 idle_data->parent = self;
337 idle_data->server = g_object_ref (info);
340 g_idle_add (_idle_emit_device_found_signal, idle_data);
346 _remove_mythtv_server (GMythUPnP *self,
349 GMythUPnPPrivate *priv;
350 GMythBackendInfo *info;
352 priv = GMYTH_UPNP_GET_PRIVATE (self);
354 g_mutex_lock (priv->mutex);
355 info = g_hash_table_lookup (priv->servers, uuid);
358 GMythUPnPIdleData *idle_data;
360 idle_data = g_new0 (GMythUPnPIdleData, 1);
361 idle_data->parent = self;
362 idle_data->server = g_object_ref (info);
364 g_hash_table_remove (priv->servers, uuid);
367 g_idle_add (_idle_emit_device_lost_signal, idle_data);
369 g_mutex_unlock (priv->mutex);
373 static GMythBackendInfo*
374 _find_service_by_uuid (GMythUPnP *self,
377 GMythUPnPPrivate *priv;
378 GMythBackendInfo *info;
380 priv = GMYTH_UPNP_GET_PRIVATE (self);
383 g_mutex_lock (priv->mutex);
384 info = g_hash_table_lookup (priv->servers, uuid);
385 g_mutex_unlock (priv->mutex);
391 _upnp_event_handler (Upnp_EventType e_type,
395 g_return_val_if_fail (singleton != NULL, 0);
399 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
400 case UPNP_DISCOVERY_SEARCH_RESULT:
402 struct Upnp_Discovery *d_event;
404 d_event = (struct Upnp_Discovery *) e;
406 g_debug ("TYPE: %s", d_event->ServiceType);
408 if (strcmp (d_event->ServiceType, UPNP_SERVICE_FILTER) != 0)
410 g_debug ("invalid device : %s", d_event->DeviceId);
415 if (d_event->ErrCode != UPNP_E_SUCCESS)
417 g_warning ("Error in Discovery: %d", d_event->ErrCode);
421 if (_find_service_by_uuid (GMYTH_UPNP (singleton), d_event->DeviceId) == NULL)
422 _append_mythtv_server_from_loation (singleton,
429 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
431 GMythUPnPPrivate *priv;
432 struct Upnp_Discovery *d_event;
434 d_event = (struct Upnp_Discovery *) e;
435 if (d_event->ErrCode != UPNP_E_SUCCESS)
437 g_warning ("Error in Discovery: %d", d_event->ErrCode);
441 priv = GMYTH_UPNP_GET_PRIVATE (singleton);
442 _remove_mythtv_server (singleton,
449 g_debug ("No handle event: %d", e_type);