Add Media Plugin
[src/app-framework-binder.git] / plugins / media / media-rygel.c
1 /*
2  * Copyright (C) 2016 "IoT.bzh"
3  * Author "Manuel Bachmann"
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include "media-api.h"
20
21 /* -------------- MEDIA RYGEL IMPLEMENTATION ---------------- */
22
23 /* --- PUBLIC FUNCTIONS --- */
24
25 PUBLIC unsigned char _rygel_init (mediaCtxHandleT *ctx) {
26
27     GMainContext *loop;
28     GUPnPContext *context;
29     GUPnPControlPoint *control_point;
30     struct timeval tv_start, tv_now;
31
32     context = gupnp_context_new (NULL, NULL, 0, NULL);
33
34     control_point = gupnp_control_point_new (context, URN_MEDIA_SERVER);
35
36     g_signal_connect (control_point, "device-proxy-available",
37                       G_CALLBACK (_rygel_device_cb), ctx);
38
39     /* start searching for servers */
40     gssdp_resource_browser_set_active (GSSDP_RESOURCE_BROWSER (control_point), TRUE);
41
42     loop = g_main_context_default ();
43
44     /* 5 seconds should be sufficient to find Rygel */
45     gettimeofday (&tv_start, NULL);
46     gettimeofday (&tv_now, NULL);
47     while (tv_now.tv_sec - tv_start.tv_sec <= 5) {
48
49         g_main_context_iteration (loop, FALSE);
50
51         if (ctx->media_server)
52             break;
53     }
54     /* fail if we found no server */
55     if (!ctx->media_server)
56       return -1;
57
58     dev_ctx[client_count]->loop = loop;
59     dev_ctx[client_count]->context = context;
60
61     client_count++;
62
63     return 0;
64 }
65
66 PUBLIC void _rygel_free (mediaCtxHandleT *ctx) {
67
68     dev_ctx_T *dev_ctx_c = (dev_ctx_T*)ctx->media_server;
69
70     client_count--;
71
72     g_main_context_unref (dev_ctx_c->loop);
73     dev_ctx_c->loop = NULL;
74     dev_ctx_c->context = NULL;
75     dev_ctx_c->device_info = NULL;
76     dev_ctx_c->content_dir = NULL;
77 }
78
79 PUBLIC char* _rygel_list (mediaCtxHandleT *ctx) {
80
81     dev_ctx_T *dev_ctx_c = (dev_ctx_T*)ctx->media_server;
82     GUPnPServiceProxy *content_dir_proxy;
83
84     dev_ctx_c->content_res = NULL;
85     content_dir_proxy = GUPNP_SERVICE_PROXY (dev_ctx_c->content_dir);
86
87     gupnp_service_proxy_begin_action (content_dir_proxy, "Browse", _rygel_content_cb, dev_ctx_c,
88                                       "ObjectID", G_TYPE_STRING, "Filesystem",
89                                       "BrowseFlag", G_TYPE_STRING, "BrowseDirectChildren",
90                                       "Filter", G_TYPE_STRING, "@childCount",
91                                       "StartingIndex", G_TYPE_UINT, 0,
92                                       "RequestedCount", G_TYPE_UINT, 64,
93                                       "SortCriteria", G_TYPE_STRING, "",
94                                        NULL);
95
96     while (!dev_ctx_c->content_res)
97       g_main_context_iteration (dev_ctx_c->loop, FALSE);
98
99     return dev_ctx_c->content_res;
100 }
101
102  /* ---- LOCAL CALLBACK FUNCTIONS ---- */
103
104 STATIC void _rygel_device_cb (GUPnPControlPoint *point, GUPnPDeviceProxy *proxy,
105                                      gpointer data) {
106
107     mediaCtxHandleT *ctx = (mediaCtxHandleT*)data;
108     GUPnPDeviceInfo *device_info;
109     GUPnPServiceInfo *content_dir;
110     const char *device_name;
111
112     device_info = GUPNP_DEVICE_INFO (proxy);
113     device_name = gupnp_device_info_get_model_name (device_info);
114     content_dir = gupnp_device_info_get_service (device_info, URN_CONTENT_DIR);
115
116     if (strcmp (device_name, "Rygel") != 0)
117         return;
118     if (!content_dir)
119         return;
120
121     /* allocate the global array if it has not been not done */
122     if (!dev_ctx)
123         dev_ctx = (dev_ctx_T**) malloc (sizeof(dev_ctx_T));
124     else
125         dev_ctx = (dev_ctx_T**) realloc (dev_ctx, (client_count+1)*sizeof(dev_ctx_T));
126
127     /* create an element for the client in the global array */
128     dev_ctx[client_count] = malloc (sizeof(dev_ctx_T));
129     dev_ctx[client_count]->device_info = device_info;
130     dev_ctx[client_count]->content_dir = content_dir;
131
132     /* make the client context aware of it */
133     ctx->media_server = (void*)dev_ctx[client_count];
134 }
135
136 STATIC void _rygel_content_cb (GUPnPServiceProxy *content_dir, GUPnPServiceProxyAction *action,
137                                       gpointer data) {
138
139     dev_ctx_T *dev_ctx_c = (dev_ctx_T*)data;
140     GUPnPServiceProxy *content_dir_proxy = GUPNP_SERVICE_PROXY (content_dir);
141     GError *error;
142     char *result;
143     guint32 number_returned;
144     guint32 total_matches;
145     char *found;
146     char subid[33];
147
148     gupnp_service_proxy_end_action (content_dir, action, &error,
149                                     "Result", G_TYPE_STRING, &result,
150                                     "NumberReturned", G_TYPE_UINT, &number_returned,
151                                     "TotalMatches", G_TYPE_UINT, &total_matches,
152                                      NULL);
153
154     if (number_returned == 0)
155         return;
156
157     if (number_returned == 1) {
158         found = strstr (result, "id=\"");       
159         found += 4;
160         strncpy (subid, found, 32); subid[32] = '\0';
161
162         gupnp_service_proxy_begin_action (content_dir_proxy, "Browse", _rygel_content_cb, NULL,
163                                           "ObjectID", G_TYPE_STRING, subid,
164                                           "BrowseFlag", G_TYPE_STRING, "BrowseDirectChildren",
165                                           "Filter", G_TYPE_STRING, "@childCount",
166                                           "StartingIndex", G_TYPE_UINT, 0,
167                                           "RequestedCount", G_TYPE_UINT, 64,
168                                           "SortCriteria", G_TYPE_STRING, "",
169                                            NULL);
170         return;
171     }
172
173     if (number_returned > 1)
174         dev_ctx_c->content_res = strdup (result);
175 }