No issues found
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Implementatin of DAAP (iTunes Music Sharing) GStreamer source
4 *
5 * Copyright (C) 2005 Charles Schmidt <cschmidt2@emich.edu>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * The Rhythmbox authors hereby grant permission for non-GPL compatible
13 * GStreamer plugins to be used and distributed together with GStreamer
14 * and Rhythmbox. This permission is above and beyond the permissions granted
15 * by the GPL license by which Rhythmbox is covered. If you modify this code
16 * you may extend this exception to your version of the code, but you are not
17 * obligated to do so. If you do not wish to do so, delete this exception
18 * statement from your version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
28 *
29 */
30
31 #include "config.h"
32
33 #include <gst/gst.h>
34 #include <string.h>
35
36 #include "rb-daap-plugin.h"
37 #include "rb-daap-src.h"
38
39 #define RB_TYPE_DAAP_SRC (rb_daap_src_get_type())
40 #define RB_DAAP_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),RB_TYPE_DAAP_SRC,RBDAAPSrc))
41 #define RB_DAAP_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RB_TYPE_DAAP_SRC,RBDAAPSrcClass))
42 #define RB_IS_DAAP_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),RB_TYPE_DAAP_SRC))
43 #define RB_IS_DAAP_SRC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),RB_TYPE_DAAP_SRC))
44
45 typedef struct _RBDAAPSrc RBDAAPSrc;
46 typedef struct _RBDAAPSrcClass RBDAAPSrcClass;
47
48 struct _RBDAAPSrc
49 {
50 GstBin parent;
51
52 /* uri */
53 gchar *daap_uri;
54
55 GstElement *souphttpsrc;
56 GstPad *ghostpad;
57 };
58
59 struct _RBDAAPSrcClass
60 {
61 GstBinClass parent_class;
62 };
63
64 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
65 GST_PAD_SRC,
66 GST_PAD_ALWAYS,
67 GST_STATIC_CAPS_ANY);
68
69 GST_DEBUG_CATEGORY_STATIC (rb_daap_src_debug);
70 #define GST_CAT_DEFAULT rb_daap_src_debug
71
72 static GstElementDetails rb_daap_src_details =
73 GST_ELEMENT_DETAILS ("RBDAAP Source",
74 "Source/File",
75 "Read a DAAP (music share) file",
76 "Charles Schmidt <cschmidt2@emich.edu");
77
78 static RBDaapPlugin *daap_plugin = NULL;
79
80 static void rb_daap_src_uri_handler_init (gpointer g_iface, gpointer iface_data);
81
82 static void
83 _do_init (GType daap_src_type)
84 {
85 static const GInterfaceInfo urihandler_info = {
86 rb_daap_src_uri_handler_init,
87 NULL,
88 NULL
89 };
90 GST_DEBUG_CATEGORY_INIT (rb_daap_src_debug,
91 "daapsrc", GST_DEBUG_FG_WHITE,
92 "Rhythmbox built in DAAP source element");
93
94 g_type_add_interface_static (daap_src_type, GST_TYPE_URI_HANDLER,
95 &urihandler_info);
96 }
97
98 GST_BOILERPLATE_FULL (RBDAAPSrc, rb_daap_src, GstBin, GST_TYPE_BIN, _do_init);
99
100 static void rb_daap_src_dispose (GObject *object);
101 static void rb_daap_src_set_property (GObject *object,
102 guint prop_id,
103 const GValue *value,
104 GParamSpec *pspec);
105 static void rb_daap_src_get_property (GObject *object,
106 guint prop_id,
107 GValue *value,
108 GParamSpec *pspec);
109
110 static GstStateChangeReturn rb_daap_src_change_state (GstElement *element, GstStateChange transition);
111
112 void
113 rb_daap_src_set_plugin (GObject *plugin)
114 {
115 g_assert (RB_IS_DAAP_PLUGIN (plugin));
116 daap_plugin = RB_DAAP_PLUGIN (plugin);
117 }
118
119 enum
120 {
121 PROP_0,
122 PROP_LOCATION,
123 };
124
125 static void
126 rb_daap_src_base_init (gpointer g_class)
127 {
128 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
129 gst_element_class_add_pad_template (element_class,
130 gst_static_pad_template_get (&srctemplate));
131 gst_element_class_set_details (element_class, &rb_daap_src_details);
132 }
133
134 static void
135 rb_daap_src_class_init (RBDAAPSrcClass *klass)
136 {
137 GObjectClass *gobject_class;
138 GstElementClass *element_class;
139
140 gobject_class = G_OBJECT_CLASS (klass);
141 gobject_class->dispose = rb_daap_src_dispose;
142 gobject_class->set_property = rb_daap_src_set_property;
143 gobject_class->get_property = rb_daap_src_get_property;
144
145 element_class = GST_ELEMENT_CLASS (klass);
146 element_class->change_state = rb_daap_src_change_state;
147
148 g_object_class_install_property (gobject_class, PROP_LOCATION,
149 g_param_spec_string ("location",
150 "file location",
151 "location of the file to read",
152 NULL,
153 G_PARAM_READWRITE));
154 }
155
156 static void
157 rb_daap_src_init (RBDAAPSrc *src, RBDAAPSrcClass *klass)
158 {
159 GstPad *pad;
160
161 /* create actual source */
162 src->souphttpsrc = gst_element_factory_make ("souphttpsrc", NULL);
163 if (src->souphttpsrc == NULL) {
164 g_warning ("couldn't create souphttpsrc element");
165 return;
166 }
167
168 gst_bin_add (GST_BIN (src), src->souphttpsrc);
169 gst_object_ref (src->souphttpsrc);
170
171 /* create ghost pad */
172 pad = gst_element_get_pad (src->souphttpsrc, "src");
173 src->ghostpad = gst_ghost_pad_new ("src", pad);
174 gst_element_add_pad (GST_ELEMENT (src), src->ghostpad);
175 gst_object_ref (src->ghostpad);
176 gst_object_unref (pad);
177
178 src->daap_uri = NULL;
179 }
180
181 static void
182 rb_daap_src_dispose (GObject *object)
183 {
184 RBDAAPSrc *src;
185 src = RB_DAAP_SRC (object);
186
187 if (src->ghostpad) {
188 gst_object_unref (src->ghostpad);
189 src->ghostpad = NULL;
190 }
191
192 if (src->souphttpsrc) {
193 gst_object_unref (src->souphttpsrc);
194 src->souphttpsrc = NULL;
195 }
196
197 g_free (src->daap_uri);
198 src->daap_uri = NULL;
199
200 G_OBJECT_CLASS (parent_class)->dispose (object);
201 }
202
203 static void
204 rb_daap_src_set_property (GObject *object,
205 guint prop_id,
206 const GValue *value,
207 GParamSpec *pspec)
208 {
209 RBDAAPSrc *src = RB_DAAP_SRC (object);
210
211 switch (prop_id) {
212 case PROP_LOCATION:
213 /* XXX check stuff */
214 if (src->daap_uri) {
215 g_free (src->daap_uri);
216 src->daap_uri = NULL;
217 }
218 src->daap_uri = g_strdup (g_value_get_string (value));
219 break;
220 default:
221 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
222 break;
223 }
224 }
225
226 static void
227 rb_daap_src_get_property (GObject *object,
228 guint prop_id,
229 GValue *value,
230 GParamSpec *pspec)
231 {
232 RBDAAPSrc *src = RB_DAAP_SRC (object);
233
234 switch (prop_id) {
235 case PROP_LOCATION:
236 g_value_set_string (value, src->daap_uri);
237 break;
238 default:
239 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
240 break;
241 }
242 }
243
244 static void
245 rb_daap_src_set_header (const char *name, const char *value, gpointer headers)
246 {
247 gst_structure_set (headers, name, G_TYPE_STRING, value, NULL);
248 }
249
250 static GstStructure *
251 rb_daap_src_soup_message_headers_to_gst_structure (SoupMessageHeaders *headers)
252 {
253 GstStructure *gst_headers = gst_structure_new ("extra-headers", NULL);
254
255 if (gst_headers == NULL)
256 return gst_headers;
257
258 soup_message_headers_foreach (headers,
259 rb_daap_src_set_header,
260 gst_headers);
261
262 return gst_headers;
263 }
264
265 GstStateChangeReturn
266 rb_daap_src_change_state (GstElement *element, GstStateChange transition)
267 {
268 RBDAAPSrc *src = RB_DAAP_SRC (element);
269
270 switch (transition) {
271 case GST_STATE_CHANGE_NULL_TO_READY:
272 {
273 const char *http = "http";
274 char *httpuri;
275 SoupMessageHeaders *headers;
276 GstStructure *gst_headers;
277 RBDAAPSource *source;
278
279 /* Retrieve extra headers for the HTTP connection. */
280 source = rb_daap_plugin_find_source_for_uri (daap_plugin, src->daap_uri);
281 if (source == NULL) {
282 g_warning ("Unable to lookup source for URI: %s", src->daap_uri);
283 return GST_STATE_CHANGE_FAILURE;
284 }
285
286 /* The following can fail if the source is no longer connected */
287 headers = rb_daap_source_get_headers (source, src->daap_uri);
288 if (headers == NULL) {
289 return GST_STATE_CHANGE_FAILURE;
290 }
291
292 gst_headers = rb_daap_src_soup_message_headers_to_gst_structure
293 (headers);
294 if (gst_headers == NULL) {
295 return GST_STATE_CHANGE_FAILURE;
296 }
297 soup_message_headers_free (headers);
298
299 g_object_set (src->souphttpsrc, "extra-headers", gst_headers, NULL);
300 gst_structure_free (gst_headers);
301
302 /* Set daap://... URI as http:// on souphttpsrc to ready connection. */
303 httpuri = g_strdup (src->daap_uri);
304 strncpy (httpuri, http, 4);
305
306 g_object_set (src->souphttpsrc, "location", httpuri, NULL);
307 g_free (httpuri);
308 break;
309 }
310
311 case GST_STATE_CHANGE_READY_TO_PAUSED:
312 break;
313
314 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
315 break;
316
317 default:
318 break;
319 }
320
321 return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
322 }
323
324 static gboolean
325 plugin_init (GstPlugin *plugin)
326 {
327 gboolean ret = gst_element_register (plugin, "rbdaapsrc", GST_RANK_PRIMARY, RB_TYPE_DAAP_SRC);
328 return ret;
329 }
330
331 GST_PLUGIN_DEFINE_STATIC (GST_VERSION_MAJOR,
332 GST_VERSION_MINOR,
333 "rbdaap",
334 "element to access DAAP music share files",
335 plugin_init,
336 VERSION,
337 "GPL",
338 PACKAGE,
339 "");
340
341 /*** GSTURIHANDLER INTERFACE *************************************************/
342
343 static guint
344 rb_daap_src_uri_get_type (void)
345 {
346 return GST_URI_SRC;
347 }
348
349 static gchar **
350 rb_daap_src_uri_get_protocols (void)
351 {
352 static gchar *protocols[] = {"daap", NULL};
353
354 return protocols;
355 }
356
357 static const gchar *
358 rb_daap_src_uri_get_uri (GstURIHandler *handler)
359 {
360 RBDAAPSrc *src = RB_DAAP_SRC (handler);
361
362 return src->daap_uri;
363 }
364
365 static gboolean
366 rb_daap_src_uri_set_uri (GstURIHandler *handler,
367 const gchar *uri)
368 {
369 RBDAAPSrc *src = RB_DAAP_SRC (handler);
370
371 if (GST_STATE (src) == GST_STATE_PLAYING || GST_STATE (src) == GST_STATE_PAUSED) {
372 return FALSE;
373 }
374
375 g_object_set (G_OBJECT (src), "location", uri, NULL);
376
377 return TRUE;
378 }
379
380 static void
381 rb_daap_src_uri_handler_init (gpointer g_iface,
382 gpointer iface_data)
383 {
384 GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
385
386 iface->get_type = rb_daap_src_uri_get_type;
387 iface->get_protocols = rb_daap_src_uri_get_protocols;
388 iface->get_uri = rb_daap_src_uri_get_uri;
389 iface->set_uri = rb_daap_src_uri_set_uri;
390 }