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 GMythUPnPDeviceStatus last_status;
66 gboolean upnp_dev_found;
72 UpnpClient_Handle client_id;
75 static void gmyth_upnp_class_init (GMythUPnPClass* klass);
76 static void gmyth_upnp_init (GMythUPnP* object);
77 static void gmyth_upnp_dispose (GObject* object);
78 static void gmyth_upnp_finalize (GObject* object);
79 static GObject* gmyth_upnp_constructor (GType type,
80 guint n_construct_params,
81 GObjectConstructParam *construct_params);
84 static int _upnp_event_handler (Upnp_EventType e_type,
89 static int signals[LAST_SIGNAL] = {0};
91 static GMythUPnP *singleton = NULL;
93 G_DEFINE_TYPE(GMythUPnP, gmyth_upnp, G_TYPE_OBJECT);
96 gmyth_upnp_class_init(GMythUPnPClass * klass)
98 GObjectClass *gobject_class;
99 GMythUPnPClass *gupnp_class;
101 gobject_class = (GObjectClass *) klass;
102 gupnp_class = (GMythUPnPClass *) gobject_class;
104 gobject_class->dispose = gmyth_upnp_dispose;
105 gobject_class->finalize = gmyth_upnp_finalize;
106 gobject_class->constructor = gmyth_upnp_constructor;
108 g_type_class_add_private (gobject_class, sizeof(GMythUPnPPrivate));
112 signals[DEVICE_FOUND] = g_signal_new("device-found",
113 G_TYPE_FROM_CLASS(gupnp_class),
116 g_cclosure_marshal_VOID__OBJECT,
118 GMYTH_BACKEND_INFO_TYPE);
120 signals[DEVICE_LOST] = g_signal_new("device-lost",
121 G_TYPE_FROM_CLASS(gupnp_class),
124 g_cclosure_marshal_VOID__OBJECT,
126 GMYTH_BACKEND_INFO_TYPE);
131 gmyth_upnp_init(GMythUPnP* self)
134 GMythUPnPPrivate *priv;
136 priv = GMYTH_UPNP_GET_PRIVATE (self);
138 priv->mutex = g_mutex_new ();
139 priv->servers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
141 /* initalize upnp client */
142 ret = UpnpInit (NULL, 0);
143 if (ret != UPNP_E_SUCCESS)
144 g_warning ("Fail to inilialize upnp SDK: %d", ret);
147 ret = UpnpRegisterClient (_upnp_event_handler,
148 &priv->client_id, &priv->client_id);
150 if (ret != UPNP_E_SUCCESS)
151 g_warning ("Fail to start upnp client: %d", ret);
156 gmyth_upnp_constructor (GType type,
157 guint n_construct_params,
158 GObjectConstructParam *construct_params)
164 object = G_OBJECT_CLASS (gmyth_upnp_parent_class)->constructor (type,
168 singleton = GMYTH_UPNP (object);
171 object = g_object_ref (G_OBJECT (singleton));
177 gmyth_upnp_dispose(GObject * object)
179 /* finalize upnp client */
181 G_OBJECT_CLASS(gmyth_upnp_parent_class)->dispose(object);
185 gmyth_upnp_finalize(GObject * object)
187 G_OBJECT_CLASS(gmyth_upnp_parent_class)->finalize(object);
193 gmyth_upnp_get_instance (void)
195 return GMYTH_UPNP(g_object_new(GMYTH_UPNP_TYPE, NULL));
200 gmyth_upnp_search (GMythUPnP *self)
203 GMythUPnPPrivate *priv;
205 priv = GMYTH_UPNP_GET_PRIVATE (self);
207 ret = UpnpSearchAsync (priv->client_id,
212 if (ret != UPNP_E_SUCCESS)
213 g_warning ("Fail to start upnp listener: %d", ret);
217 _fill_servers_cb (gpointer key,
223 lst = (GList **) user_data;
225 *lst = g_list_append (*lst, g_object_ref (value));
229 gmyth_upnp_get_devices (GMythUPnP *self)
231 GMythUPnPPrivate *priv;
234 priv = GMYTH_UPNP_GET_PRIVATE (self);
236 g_hash_table_foreach (priv->servers, (GHFunc) _fill_servers_cb, &lst);
242 _idle_emit_device_found_signal (gpointer data)
244 GMythUPnPPrivate *priv;
245 GMythUPnPIdleData *idle_data;
247 idle_data = (GMythUPnPIdleData *) data;
248 priv = GMYTH_UPNP_GET_PRIVATE (idle_data->parent);
250 g_signal_emit (idle_data->parent, signals[DEVICE_FOUND], 0, idle_data->server);
252 g_object_unref (idle_data->server);
260 _idle_emit_device_lost_signal (gpointer data)
262 GMythUPnPPrivate *priv;
263 GMythUPnPIdleData *idle_data;
265 idle_data = (GMythUPnPIdleData *) data;
266 priv = GMYTH_UPNP_GET_PRIVATE (idle_data->parent);
268 g_signal_emit (idle_data->parent, signals[DEVICE_LOST], 0, idle_data->server);
270 g_object_unref (idle_data->server);
278 _xml_get_first_document_item (IXML_Document * doc,
281 IXML_NodeList *node_list = NULL;
282 IXML_Node *text_node = NULL;
283 IXML_Node *tmp_node = NULL;
287 node_list = ixmlDocument_getElementsByTagName (doc,
292 if ((tmp_node = ixmlNodeList_item (node_list, 0)))
294 text_node = ixmlNode_getFirstChild (tmp_node);
296 ret = strdup (ixmlNode_getNodeValue (text_node));
301 ixmlNodeList_free (node_list);
308 _append_mythtv_server_from_loation (GMythUPnP *self,
310 const gchar *location)
312 GMythUPnPPrivate *priv;
316 priv = GMYTH_UPNP_GET_PRIVATE (self);
318 base_url = g_strdup (location);
319 end = g_strrstr (base_url, "/");
323 IXML_Document *desc_doc;
327 info_url = g_strconcat (base_url,
328 "Myth/GetConnectionInfo",
332 ret = UpnpDownloadXmlDoc (info_url, &desc_doc);
333 if (ret != UPNP_E_SUCCESS)
335 g_warning ("Error obtaining device desc: %d", ret);
339 GMythBackendInfo *info;
340 GMythUPnPIdleData *idle_data;
342 info = gmyth_backend_info_new_full (
343 _xml_get_first_document_item (desc_doc, "Host"),
344 _xml_get_first_document_item (desc_doc, "UserName"),
345 _xml_get_first_document_item (desc_doc, "Password"),
346 _xml_get_first_document_item (desc_doc, "Name"),
347 // Current mythtv version not export port number
351 ixmlDocument_free (desc_doc);
353 g_mutex_lock (priv->mutex);
354 g_hash_table_insert (priv->servers,
356 g_object_ref (info));
357 g_mutex_unlock (priv->mutex);
360 idle_data = g_new0 (GMythUPnPIdleData, 1);
361 idle_data->parent = self;
362 idle_data->server = g_object_ref (info);
365 g_idle_add (_idle_emit_device_found_signal, idle_data);
371 _remove_mythtv_server (GMythUPnP *self,
374 GMythUPnPPrivate *priv;
375 GMythBackendInfo *info;
377 priv = GMYTH_UPNP_GET_PRIVATE (self);
379 g_mutex_lock (priv->mutex);
380 info = g_hash_table_lookup (priv->servers, uuid);
383 GMythUPnPIdleData *idle_data;
385 idle_data = g_new0 (GMythUPnPIdleData, 1);
386 idle_data->parent = self;
387 idle_data->server = g_object_ref (info);
389 g_hash_table_remove (priv->servers, uuid);
392 g_idle_add (_idle_emit_device_lost_signal, idle_data);
394 g_mutex_unlock (priv->mutex);
398 static GMythBackendInfo*
399 _find_service_by_uuid (GMythUPnP *self,
402 GMythUPnPPrivate *priv;
403 GMythBackendInfo *info;
405 priv = GMYTH_UPNP_GET_PRIVATE (self);
408 g_mutex_lock (priv->mutex);
409 info = g_hash_table_lookup (priv->servers, uuid);
410 g_mutex_unlock (priv->mutex);
416 _upnp_event_handler (Upnp_EventType e_type,
420 g_return_val_if_fail (singleton != NULL, 0);
424 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
425 case UPNP_DISCOVERY_SEARCH_RESULT:
427 struct Upnp_Discovery *d_event;
429 d_event = (struct Upnp_Discovery *) e;
431 if (strcmp (d_event->ServiceType, UPNP_SERVICE_FILTER) != 0)
433 g_warning ("invalid device : %s", d_event->DeviceId);
438 if (d_event->ErrCode != UPNP_E_SUCCESS)
440 g_warning ("Error in Discovery: %d", d_event->ErrCode);
444 if (_find_service_by_uuid (GMYTH_UPNP (singleton), d_event->DeviceId) == NULL)
445 _append_mythtv_server_from_loation (singleton,
452 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
454 GMythUPnPPrivate *priv;
455 struct Upnp_Discovery *d_event;
457 d_event = (struct Upnp_Discovery *) e;
458 if (d_event->ErrCode != UPNP_E_SUCCESS)
460 g_warning ("Error in Discovery: %d", d_event->ErrCode);
464 priv = GMYTH_UPNP_GET_PRIVATE (singleton);
465 _remove_mythtv_server (singleton,