Fix Media Plugin content list API
[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     handler_cb = 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         gettimeofday (&tv_now, NULL);
54     }
55     /* fail if we found no server */
56     if (!ctx->media_server)
57       return -1;
58
59     dev_ctx[client_count]->loop = loop;
60     dev_ctx[client_count]->context = context;
61
62     client_count++;
63
64     return 0;
65 }
66
67 PUBLIC void _rygel_free (mediaCtxHandleT *ctx) {
68
69     dev_ctx_T *dev_ctx_c = (dev_ctx_T*)ctx->media_server;
70
71     client_count--;
72
73     g_main_context_unref (dev_ctx_c->loop);
74     dev_ctx_c->loop = NULL;
75     dev_ctx_c->context = NULL;
76     dev_ctx_c->device_info = NULL;
77     dev_ctx_c->content_dir = NULL;
78     if (dev_ctx_c->content_res)
79       free (dev_ctx_c->content_res);
80     dev_ctx_c->content_res = NULL;
81 }
82
83 PUBLIC char* _rygel_list (mediaCtxHandleT *ctx) {
84
85     dev_ctx_T *dev_ctx_c = (dev_ctx_T*)ctx->media_server;
86     GUPnPServiceProxy *content_dir_proxy;
87     struct timeval tv_start, tv_now;
88
89     if (dev_ctx_c->content_res)
90       free (dev_ctx_c->content_res);
91     dev_ctx_c->content_res = NULL;
92
93     content_dir_proxy = GUPNP_SERVICE_PROXY (dev_ctx_c->content_dir);
94
95     gupnp_service_proxy_begin_action (content_dir_proxy, "Browse", _rygel_content_cb, dev_ctx_c,
96                                       "ObjectID", G_TYPE_STRING, "Filesystem",
97                                       "BrowseFlag", G_TYPE_STRING, "BrowseDirectChildren",
98                                       "Filter", G_TYPE_STRING, "@childCount",
99                                       "StartingIndex", G_TYPE_UINT, 0,
100                                       "RequestedCount", G_TYPE_UINT, 64,
101                                       "SortCriteria", G_TYPE_STRING, "",
102                                        NULL);
103
104     gettimeofday (&tv_start, NULL);
105     gettimeofday (&tv_now, NULL);
106     while (tv_now.tv_sec - tv_start.tv_sec <= 5) {
107
108         g_main_context_iteration (dev_ctx_c->loop, FALSE);
109
110         if (dev_ctx_c->content_res)
111             break;
112         gettimeofday (&tv_now, NULL);
113     }
114
115     return dev_ctx_c->content_res;
116 }
117
118  /* ---- LOCAL CALLBACK FUNCTIONS ---- */
119
120 STATIC void _rygel_device_cb (GUPnPControlPoint *point, GUPnPDeviceProxy *proxy,
121                                      gpointer data) {
122
123     mediaCtxHandleT *ctx = (mediaCtxHandleT*)data;
124     GUPnPDeviceInfo *device_info;
125     GUPnPServiceInfo *content_dir;
126     const char *device_name;
127
128     device_info = GUPNP_DEVICE_INFO (proxy);
129     device_name = gupnp_device_info_get_model_name (device_info);
130     content_dir = gupnp_device_info_get_service (device_info, URN_CONTENT_DIR);
131
132     if (strcmp (device_name, "Rygel") != 0)
133         return;
134     if (!content_dir)
135         return;
136
137     /* we have found Rygel ; stop looking for it... */
138     g_signal_handler_disconnect (point, handler_cb);
139
140     /* allocate the global array if it has not been not done */
141     if (!dev_ctx)
142         dev_ctx = (dev_ctx_T**) malloc (sizeof(dev_ctx_T));
143     else
144         dev_ctx = (dev_ctx_T**) realloc (dev_ctx, (client_count+1)*sizeof(dev_ctx_T));
145
146     /* create an element for the client in the global array */
147     dev_ctx[client_count] = (dev_ctx_T*) malloc (sizeof(dev_ctx_T));
148     dev_ctx[client_count]->device_info = device_info;
149     dev_ctx[client_count]->content_dir = content_dir;
150
151     /* make the client context aware of it */
152     ctx->media_server = (void*)dev_ctx[client_count];
153 }
154
155 STATIC void _rygel_content_cb (GUPnPServiceProxy *content_dir, GUPnPServiceProxyAction *action,
156                                       gpointer data) {
157
158     dev_ctx_T *dev_ctx_c = (dev_ctx_T*)data;
159     GUPnPServiceProxy *content_dir_proxy = GUPNP_SERVICE_PROXY (content_dir);
160     GError *error;
161     char *result;
162     guint32 number_returned;
163     guint32 total_matches;
164     char *found;
165     char subid[33];
166
167     gupnp_service_proxy_end_action (content_dir, action, &error,
168                                     "Result", G_TYPE_STRING, &result,
169                                     "NumberReturned", G_TYPE_UINT, &number_returned,
170                                     "TotalMatches", G_TYPE_UINT, &total_matches,
171                                      NULL);
172
173     if (number_returned == 0)
174         return;
175
176     if (number_returned == 1) {
177         found = strstr (result, "id=\"");       
178         found += 4;
179         strncpy (subid, found, 32); subid[32] = '\0';
180
181         gupnp_service_proxy_begin_action (content_dir_proxy, "Browse", _rygel_content_cb, dev_ctx_c,
182                                           "ObjectID", G_TYPE_STRING, subid,
183                                           "BrowseFlag", G_TYPE_STRING, "BrowseDirectChildren",
184                                           "Filter", G_TYPE_STRING, "@childCount",
185                                           "StartingIndex", G_TYPE_UINT, 0,
186                                           "RequestedCount", G_TYPE_UINT, 64,
187                                           "SortCriteria", G_TYPE_STRING, "",
188                                            NULL);
189         return;
190     }
191
192     if (number_returned > 1)
193         dev_ctx_c->content_res = strdup (result);
194 }