ext/soup/gstsouphttpsrc.c: Add transferMode.dnla.org header to HTTP requests as this...
[platform/upstream/gstreamer.git] / ext / soup / gstsouphttpsrc.c
1 /* GStreamer
2  * Copyright (C) 2007-2008 Wouter Cloetens <wouter@mind.be>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more
13  */
14
15 /**
16  * SECTION:element-souphttpsrc
17  * @short_description: Read from an HTTP/HTTPS/WebDAV/Icecast/Shoutcast
18  * location.
19  *
20  * <refsect2>
21  * <para>
22  * This plugin reads data from a remote location specified by a URI.
23  * Supported protocols are 'http', 'https'.
24  * </para>
25  * <para>
26  * An HTTP proxy must be specified by its URL.
27  * If the "http_proxy" environment variable is set, its value is used.
28  * The element-souphttpsrc::proxy property can be used to override the
29  * default.
30  * </para>
31  * <para>
32  * In case the element-souphttpsrc::iradio-mode property is set and the
33  * location is an HTTP resource, souphttpsrc will send special Icecast HTTP
34  * headers to the server to request additional Icecast meta-information. If
35  * the server is not an Icecast server, it will behave as if the
36  * element-souphttpsrc::iradio-mode property were not set. If it is,
37  * souphttpsrc will output data with a media type of application/x-icy,
38  * in which case you will need to use the #ICYDemux element as follow-up
39  * element to extract the Icecast metadata and to determine the underlying
40  * media type.
41  * </para>
42  * <para>
43  * Example pipeline:
44  * <programlisting>
45  * gst-launch -v souphttpsrc location=https://some.server.org/index.html
46  *     ! filesink location=/home/joe/server.html
47  * </programlisting>
48  * The above pipeline reads a web page from a server using the HTTPS protocol
49  * and writes it to a local file.
50  * </para>
51  * <para>
52  * Another example pipeline:
53  * <programlisting>
54  * gst-launch -v souphttpsrc user-agent="FooPlayer 0.99 beta"
55  *     automatic-redirect=false proxy=http://proxy.intranet.local:8080
56  *     location=http://music.foobar.com/demo.mp3 ! mad ! audioconvert
57  *     ! audioresample ! alsasink
58  * </programlisting>
59  * The above pipeline will read and decode and play an mp3 file from a
60  * web server using the HTTP protocol. If the server sends redirects,
61  * the request fails instead of following the redirect. The specified
62  * HTTP proxy server is used. The User-Agent HTTP request header
63  * is set to a custom string instead of "GStreamer souphttpsrc."
64  * </para>
65  * <para>
66  * Yet another example pipeline:
67  * <programlisting>
68  * gst-launch -v souphttpsrc location=http://10.11.12.13/mjpeg
69  *     do-timestamp=true ! multipartdemux
70  *     ! image/jpeg,width=640,height=480 ! matroskamux
71  *     ! filesink location=mjpeg.mkv
72  * </programlisting>
73  * The above pipeline reads a motion JPEG stream from an IP camera
74  * using the HTTP protocol, encoded as mime/multipart image/jpeg
75  * parts, and writes a Matroska motion JPEG file. The width and
76  * height properties are set in the caps to provide the Matroska
77  * multiplexer with the information to set this in the header.
78  * Timestamps are set on the buffers as they arrive from the camera.
79  * These are used by the mime/multipart demultiplexer to emit timestamps
80  * on the JPEG-encoded video frame buffers. This allows the Matroska
81  * multiplexer to timestamp the frames in the resulting file.
82  * </para>
83  * </refsect2>
84  *
85  */
86
87 #ifdef HAVE_CONFIG_H
88 #include "config.h"
89 #endif
90
91 #include <string.h>
92 #ifdef HAVE_STDLIB_H
93 #include <stdlib.h>             /* atoi() */
94 #endif
95 #include <gst/gstelement.h>
96 #include <gst/gst-i18n-plugin.h>
97 #include <libsoup/soup.h>
98 #include "gstsouphttpsrc.h"
99
100 #include <gst/tag/tag.h>
101
102 GST_DEBUG_CATEGORY_STATIC (souphttpsrc_debug);
103 #define GST_CAT_DEFAULT souphttpsrc_debug
104
105 static const GstElementDetails gst_soup_http_src_details =
106 GST_ELEMENT_DETAILS ("HTTP client source",
107     "Source/Network",
108     "Receive data as a client over the network via HTTP using SOUP",
109     "Wouter Cloetens <wouter@mind.be>");
110
111 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
112     GST_PAD_SRC,
113     GST_PAD_ALWAYS,
114     GST_STATIC_CAPS_ANY);
115
116 enum
117 {
118   PROP_0,
119   PROP_LOCATION,
120   PROP_IS_LIVE,
121   PROP_USER_AGENT,
122   PROP_AUTOMATIC_REDIRECT,
123   PROP_PROXY,
124   PROP_COOKIES,
125   PROP_IRADIO_MODE,
126   PROP_IRADIO_NAME,
127   PROP_IRADIO_GENRE,
128   PROP_IRADIO_URL,
129   PROP_IRADIO_TITLE
130 };
131
132 #define DEFAULT_USER_AGENT           "GStreamer souphttpsrc "
133
134 static void gst_soup_http_src_uri_handler_init (gpointer g_iface,
135     gpointer iface_data);
136 static void gst_soup_http_src_init (GstSoupHTTPSrc * src,
137     GstSoupHTTPSrcClass * g_class);
138 static void gst_soup_http_src_dispose (GObject * gobject);
139
140 static void gst_soup_http_src_set_property (GObject * object, guint prop_id,
141     const GValue * value, GParamSpec * pspec);
142 static void gst_soup_http_src_get_property (GObject * object, guint prop_id,
143     GValue * value, GParamSpec * pspec);
144
145 static GstFlowReturn gst_soup_http_src_create (GstPushSrc * psrc,
146     GstBuffer ** outbuf);
147 static gboolean gst_soup_http_src_start (GstBaseSrc * bsrc);
148
149 static gboolean gst_soup_http_src_stop (GstBaseSrc * bsrc);
150
151 static gboolean gst_soup_http_src_get_size (GstBaseSrc * bsrc, guint64 * size);
152
153 static gboolean gst_soup_http_src_is_seekable (GstBaseSrc * bsrc);
154
155 static gboolean gst_soup_http_src_do_seek (GstBaseSrc * bsrc,
156     GstSegment * segment);
157 static gboolean gst_soup_http_src_unlock (GstBaseSrc * bsrc);
158
159 static gboolean gst_soup_http_src_unlock_stop (GstBaseSrc * bsrc);
160
161 static gboolean gst_soup_http_src_set_location (GstSoupHTTPSrc * src,
162     const gchar * uri);
163 static gboolean gst_soup_http_src_set_proxy (GstSoupHTTPSrc * src,
164     const gchar * uri);
165
166 static char *gst_soup_http_src_unicodify (const char *str);
167
168 static gboolean gst_soup_http_src_build_message (GstSoupHTTPSrc * src);
169
170 static void gst_soup_http_src_cancel_message (GstSoupHTTPSrc * src);
171
172 static void gst_soup_http_src_queue_message (GstSoupHTTPSrc * src);
173
174 static gboolean gst_soup_http_src_add_range_header (GstSoupHTTPSrc * src,
175     guint64 offset);
176 static void gst_soup_http_src_session_unpause_message (GstSoupHTTPSrc * src);
177
178 static void gst_soup_http_src_session_pause_message (GstSoupHTTPSrc * src);
179
180 static void gst_soup_http_src_session_close (GstSoupHTTPSrc * src);
181
182 static void gst_soup_http_src_parse_status (SoupMessage * msg,
183     GstSoupHTTPSrc * src);
184 static void gst_soup_http_src_chunk_free (gpointer gstbuf);
185
186 static SoupBuffer *gst_soup_http_src_chunk_allocator (SoupMessage * msg,
187     gsize max_len, gpointer user_data);
188 static void gst_soup_http_src_got_chunk_cb (SoupMessage * msg,
189     SoupBuffer * chunk, GstSoupHTTPSrc * src);
190 static void gst_soup_http_src_response_cb (SoupSession * session,
191     SoupMessage * msg, GstSoupHTTPSrc * src);
192 static void gst_soup_http_src_got_headers_cb (SoupMessage * msg,
193     GstSoupHTTPSrc * src);
194 static void gst_soup_http_src_got_body_cb (SoupMessage * msg,
195     GstSoupHTTPSrc * src);
196 static void gst_soup_http_src_finished_cb (SoupMessage * msg,
197     GstSoupHTTPSrc * src);
198
199
200 static void
201 _do_init (GType type)
202 {
203   static const GInterfaceInfo urihandler_info = {
204     gst_soup_http_src_uri_handler_init,
205     NULL,
206     NULL
207   };
208
209   g_type_add_interface_static (type, GST_TYPE_URI_HANDLER, &urihandler_info);
210
211   GST_DEBUG_CATEGORY_INIT (souphttpsrc_debug, "souphttpsrc", 0,
212       "SOUP HTTP src");
213 }
214
215 GST_BOILERPLATE_FULL (GstSoupHTTPSrc, gst_soup_http_src, GstPushSrc,
216     GST_TYPE_PUSH_SRC, _do_init);
217
218 static void
219 gst_soup_http_src_base_init (gpointer g_class)
220 {
221   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
222
223   gst_element_class_add_pad_template (element_class,
224       gst_static_pad_template_get (&srctemplate));
225
226   gst_element_class_set_details (element_class, &gst_soup_http_src_details);
227 }
228
229 static void
230 gst_soup_http_src_class_init (GstSoupHTTPSrcClass * klass)
231 {
232   GObjectClass *gobject_class;
233
234   GstBaseSrcClass *gstbasesrc_class;
235
236   GstPushSrcClass *gstpushsrc_class;
237
238   gobject_class = (GObjectClass *) klass;
239   gstbasesrc_class = (GstBaseSrcClass *) klass;
240   gstpushsrc_class = (GstPushSrcClass *) klass;
241
242   gobject_class->set_property = gst_soup_http_src_set_property;
243   gobject_class->get_property = gst_soup_http_src_get_property;
244   gobject_class->dispose = gst_soup_http_src_dispose;
245
246   g_object_class_install_property (gobject_class,
247       PROP_LOCATION,
248       g_param_spec_string ("location", "Location",
249           "Location to read from", "", G_PARAM_READWRITE));
250   g_object_class_install_property (gobject_class,
251       PROP_USER_AGENT,
252       g_param_spec_string ("user-agent", "User-Agent",
253           "Value of the User-Agent HTTP request header field",
254           DEFAULT_USER_AGENT, G_PARAM_READWRITE));
255   g_object_class_install_property (gobject_class,
256       PROP_AUTOMATIC_REDIRECT,
257       g_param_spec_boolean ("automatic-redirect", "automatic-redirect",
258           "Automatically follow HTTP redirects (HTTP Status Code 3xx)",
259           TRUE, G_PARAM_READWRITE));
260   g_object_class_install_property (gobject_class,
261       PROP_PROXY,
262       g_param_spec_string ("proxy", "Proxy",
263           "HTTP proxy server URI", "", G_PARAM_READWRITE));
264   g_object_class_install_property (gobject_class,
265       PROP_COOKIES, g_param_spec_boxed ("cookies", "Cookies",
266           "HTTP request cookies", G_TYPE_STRV, G_PARAM_READWRITE));
267   g_object_class_install_property (gobject_class,
268       PROP_IS_LIVE,
269       g_param_spec_boolean ("is-live", "is-live",
270           "Act like a live source", FALSE, G_PARAM_READWRITE));
271
272   /* icecast stuff */
273   g_object_class_install_property (gobject_class,
274       PROP_IRADIO_MODE,
275       g_param_spec_boolean ("iradio-mode",
276           "iradio-mode",
277           "Enable internet radio mode (extraction of shoutcast/icecast metadata)",
278           FALSE, G_PARAM_READWRITE));
279   g_object_class_install_property (gobject_class,
280       PROP_IRADIO_NAME,
281       g_param_spec_string ("iradio-name",
282           "iradio-name", "Name of the stream", NULL, G_PARAM_READABLE));
283   g_object_class_install_property (gobject_class,
284       PROP_IRADIO_GENRE,
285       g_param_spec_string ("iradio-genre",
286           "iradio-genre", "Genre of the stream", NULL, G_PARAM_READABLE));
287   g_object_class_install_property (gobject_class,
288       PROP_IRADIO_URL,
289       g_param_spec_string ("iradio-url",
290           "iradio-url",
291           "Homepage URL for radio stream", NULL, G_PARAM_READABLE));
292   g_object_class_install_property (gobject_class,
293       PROP_IRADIO_TITLE,
294       g_param_spec_string ("iradio-title",
295           "iradio-title",
296           "Name of currently playing song", NULL, G_PARAM_READABLE));
297
298   gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_soup_http_src_start);
299   gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_soup_http_src_stop);
300   gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_soup_http_src_unlock);
301   gstbasesrc_class->unlock_stop =
302       GST_DEBUG_FUNCPTR (gst_soup_http_src_unlock_stop);
303   gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_soup_http_src_get_size);
304   gstbasesrc_class->is_seekable =
305       GST_DEBUG_FUNCPTR (gst_soup_http_src_is_seekable);
306   gstbasesrc_class->do_seek = GST_DEBUG_FUNCPTR (gst_soup_http_src_do_seek);
307
308   gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_soup_http_src_create);
309
310   GST_DEBUG_CATEGORY_INIT (souphttpsrc_debug, "souphttpsrc", 0,
311       "SOUP HTTP Client Source");
312 }
313
314 static void
315 gst_soup_http_src_init (GstSoupHTTPSrc * src, GstSoupHTTPSrcClass * g_class)
316 {
317   const gchar *proxy;
318
319   src->location = NULL;
320   src->automatic_redirect = TRUE;
321   src->user_agent = g_strdup (DEFAULT_USER_AGENT);
322   src->cookies = NULL;
323   src->icy_caps = NULL;
324   src->iradio_mode = FALSE;
325   src->iradio_name = NULL;
326   src->iradio_genre = NULL;
327   src->iradio_url = NULL;
328   src->iradio_title = NULL;
329   src->loop = NULL;
330   src->context = NULL;
331   src->session = NULL;
332   src->msg = NULL;
333   src->interrupted = FALSE;
334   src->retry = FALSE;
335   src->have_size = FALSE;
336   src->seekable = TRUE;
337   src->read_position = 0;
338   src->request_position = 0;
339   proxy = g_getenv ("http_proxy");
340   if (proxy && !gst_soup_http_src_set_proxy (src, proxy)) {
341     GST_WARNING_OBJECT (src,
342         "The proxy in the http_proxy env var (\"%s\") cannot be parsed.",
343         proxy);
344   }
345 }
346
347 static void
348 gst_soup_http_src_dispose (GObject * gobject)
349 {
350   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (gobject);
351
352   GST_DEBUG_OBJECT (src, "dispose");
353   g_free (src->location);
354   src->location = NULL;
355   g_free (src->user_agent);
356   src->user_agent = NULL;
357   if (src->proxy != NULL) {
358     soup_uri_free (src->proxy);
359     src->proxy = NULL;
360   }
361   g_strfreev (src->cookies);
362   g_free (src->iradio_name);
363   src->iradio_name = NULL;
364   g_free (src->iradio_genre);
365   src->iradio_genre = NULL;
366   g_free (src->iradio_url);
367   src->iradio_url = NULL;
368   g_free (src->iradio_title);
369   src->iradio_title = NULL;
370   if (src->icy_caps) {
371     gst_caps_unref (src->icy_caps);
372     src->icy_caps = NULL;
373   }
374
375   G_OBJECT_CLASS (parent_class)->dispose (gobject);
376 }
377
378 static void
379 gst_soup_http_src_set_property (GObject * object, guint prop_id,
380     const GValue * value, GParamSpec * pspec)
381 {
382   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (object);
383
384   switch (prop_id) {
385     case PROP_LOCATION:
386     {
387       const gchar *location;
388
389       location = g_value_get_string (value);
390
391       if (location == NULL) {
392         GST_WARNING ("location property cannot be NULL");
393         goto done;
394       }
395       if (!gst_soup_http_src_set_location (src, location)) {
396         GST_WARNING ("badly formatted location");
397         goto done;
398       }
399       break;
400     }
401     case PROP_USER_AGENT:
402       if (src->user_agent)
403         g_free (src->user_agent);
404       src->user_agent = g_value_dup_string (value);
405       break;
406     case PROP_IRADIO_MODE:
407       src->iradio_mode = g_value_get_boolean (value);
408       break;
409     default:
410       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
411       break;
412     case PROP_AUTOMATIC_REDIRECT:
413       src->automatic_redirect = g_value_get_boolean (value);
414       break;
415     case PROP_PROXY:
416     {
417       const gchar *proxy;
418
419       proxy = g_value_get_string (value);
420
421       if (proxy == NULL) {
422         GST_WARNING ("proxy property cannot be NULL");
423         goto done;
424       }
425       if (!gst_soup_http_src_set_proxy (src, proxy)) {
426         GST_WARNING ("badly formatted proxy URI");
427         goto done;
428       }
429       break;
430     }
431     case PROP_COOKIES:
432       g_strfreev (src->cookies);
433       src->cookies = g_strdupv (g_value_get_boxed (value));
434       break;
435     case PROP_IS_LIVE:
436       gst_base_src_set_live (GST_BASE_SRC (src), g_value_get_boolean (value));
437       break;
438   }
439 done:
440   return;
441 }
442
443 static void
444 gst_soup_http_src_get_property (GObject * object, guint prop_id,
445     GValue * value, GParamSpec * pspec)
446 {
447   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (object);
448
449   switch (prop_id) {
450     case PROP_LOCATION:
451       g_value_set_string (value, src->location);
452       break;
453     case PROP_USER_AGENT:
454       g_value_set_string (value, src->user_agent);
455       break;
456     case PROP_AUTOMATIC_REDIRECT:
457       g_value_set_boolean (value, src->automatic_redirect);
458       break;
459     case PROP_PROXY:
460       if (src->proxy == NULL)
461         g_value_set_string (value, "");
462       else {
463         char *proxy = soup_uri_to_string (src->proxy, FALSE);
464
465         g_value_set_string (value, proxy);
466         g_free (proxy);
467       }
468       break;
469     case PROP_COOKIES:
470       g_value_set_boxed (value, g_strdupv (src->cookies));
471       break;
472     case PROP_IS_LIVE:
473       g_value_set_boolean (value, gst_base_src_is_live (GST_BASE_SRC (src)));
474       break;
475     case PROP_IRADIO_MODE:
476       g_value_set_boolean (value, src->iradio_mode);
477       break;
478     case PROP_IRADIO_NAME:
479       g_value_set_string (value, src->iradio_name);
480       break;
481     case PROP_IRADIO_GENRE:
482       g_value_set_string (value, src->iradio_genre);
483       break;
484     case PROP_IRADIO_URL:
485       g_value_set_string (value, src->iradio_url);
486       break;
487     case PROP_IRADIO_TITLE:
488       g_value_set_string (value, src->iradio_title);
489       break;
490     default:
491       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
492       break;
493   }
494 }
495
496 static gchar *
497 gst_soup_http_src_unicodify (const gchar * str)
498 {
499   const gchar *env_vars[] = { "GST_ICY_TAG_ENCODING",
500     "GST_TAG_ENCODING", NULL
501   };
502
503   return gst_tag_freeform_string_to_utf8 (str, -1, env_vars);
504 }
505
506 static void
507 gst_soup_http_src_cancel_message (GstSoupHTTPSrc * src)
508 {
509   if (src->msg != NULL) {
510     src->session_io_status = GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_CANCELLED;
511     soup_session_cancel_message (src->session, src->msg, SOUP_STATUS_CANCELLED);
512   }
513   src->session_io_status = GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_IDLE;
514   src->msg = NULL;
515 }
516
517 static void
518 gst_soup_http_src_queue_message (GstSoupHTTPSrc * src)
519 {
520   soup_session_queue_message (src->session, src->msg,
521       (SoupSessionCallback) gst_soup_http_src_response_cb, src);
522   src->session_io_status = GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_QUEUED;
523 }
524
525 static gboolean
526 gst_soup_http_src_add_range_header (GstSoupHTTPSrc * src, guint64 offset)
527 {
528   gchar buf[64];
529
530   gint rc;
531
532   soup_message_headers_remove (src->msg->request_headers, "Range");
533   if (offset) {
534     rc = g_snprintf (buf, sizeof (buf), "bytes=%" G_GUINT64_FORMAT "-", offset);
535     if (rc > sizeof (buf) || rc < 0)
536       return FALSE;
537     soup_message_headers_append (src->msg->request_headers, "Range", buf);
538   }
539   src->read_position = offset;
540   return TRUE;
541 }
542
543 static void
544 gst_soup_http_src_session_unpause_message (GstSoupHTTPSrc * src)
545 {
546   soup_session_unpause_message (src->session, src->msg);
547 }
548
549 static void
550 gst_soup_http_src_session_pause_message (GstSoupHTTPSrc * src)
551 {
552   soup_session_pause_message (src->session, src->msg);
553 }
554
555 static void
556 gst_soup_http_src_session_close (GstSoupHTTPSrc * src)
557 {
558   if (src->session) {
559     soup_session_abort (src->session);  /* This unrefs the message. */
560     g_object_unref (src->session);
561     src->session = NULL;
562     src->msg = NULL;
563   }
564 }
565
566 static void
567 gst_soup_http_src_got_headers_cb (SoupMessage * msg, GstSoupHTTPSrc * src)
568 {
569   const char *value;
570
571   GstTagList *tag_list;
572
573   GstBaseSrc *basesrc;
574
575   guint64 newsize;
576
577   GST_DEBUG_OBJECT (src, "got headers");
578
579   if (src->automatic_redirect && SOUP_STATUS_IS_REDIRECTION (msg->status_code)) {
580     GST_DEBUG_OBJECT (src, "%u redirect to \"%s\"", msg->status_code,
581         soup_message_headers_get (msg->response_headers, "Location"));
582     return;
583   }
584
585   if (msg->status_code == SOUP_STATUS_UNAUTHORIZED)
586     return;
587
588   src->session_io_status = GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING;
589
590   /* Parse Content-Length. */
591   if (soup_message_headers_get_encoding (msg->response_headers) ==
592       SOUP_ENCODING_CONTENT_LENGTH) {
593     newsize = src->request_position +
594         soup_message_headers_get_content_length (msg->response_headers);
595     if (!src->have_size || (src->content_size != newsize)) {
596       src->content_size = newsize;
597       src->have_size = TRUE;
598       GST_DEBUG_OBJECT (src, "size = %" G_GUINT64_FORMAT, src->content_size);
599
600       basesrc = GST_BASE_SRC_CAST (src);
601       gst_segment_set_duration (&basesrc->segment, GST_FORMAT_BYTES,
602           src->content_size);
603       gst_element_post_message (GST_ELEMENT (src),
604           gst_message_new_duration (GST_OBJECT (src), GST_FORMAT_BYTES,
605               src->content_size));
606     }
607   }
608
609   /* Icecast stuff */
610   tag_list = gst_tag_list_new ();
611
612   if ((value =
613           soup_message_headers_get (msg->response_headers,
614               "icy-metaint")) != NULL) {
615     gint icy_metaint = atoi (value);
616
617     GST_DEBUG_OBJECT (src, "icy-metaint: %s (parsed: %d)", value, icy_metaint);
618     if (icy_metaint > 0) {
619       if (src->icy_caps)
620         gst_caps_unref (src->icy_caps);
621
622       src->icy_caps = gst_caps_new_simple ("application/x-icy",
623           "metadata-interval", G_TYPE_INT, icy_metaint, NULL);
624     }
625   }
626
627   if ((value =
628           soup_message_headers_get (msg->response_headers,
629               "icy-name")) != NULL) {
630     g_free (src->iradio_name);
631     src->iradio_name = gst_soup_http_src_unicodify (value);
632     if (src->iradio_name) {
633       g_object_notify (G_OBJECT (src), "iradio-name");
634       gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE, GST_TAG_ORGANIZATION,
635           src->iradio_name, NULL);
636     }
637   }
638   if ((value =
639           soup_message_headers_get (msg->response_headers,
640               "icy-genre")) != NULL) {
641     g_free (src->iradio_genre);
642     src->iradio_genre = gst_soup_http_src_unicodify (value);
643     if (src->iradio_genre) {
644       g_object_notify (G_OBJECT (src), "iradio-genre");
645       gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE, GST_TAG_GENRE,
646           src->iradio_genre, NULL);
647     }
648   }
649   if ((value = soup_message_headers_get (msg->response_headers, "icy-url"))
650       != NULL) {
651     g_free (src->iradio_url);
652     src->iradio_url = gst_soup_http_src_unicodify (value);
653     if (src->iradio_url) {
654       g_object_notify (G_OBJECT (src), "iradio-url");
655       gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE, GST_TAG_LOCATION,
656           src->iradio_url, NULL);
657     }
658   }
659   if (!gst_tag_list_is_empty (tag_list)) {
660     GST_DEBUG_OBJECT (src,
661         "calling gst_element_found_tags with %" GST_PTR_FORMAT, tag_list);
662     gst_element_found_tags (GST_ELEMENT_CAST (src), tag_list);
663   } else {
664     gst_tag_list_free (tag_list);
665   }
666
667   /* Handle HTTP errors. */
668   gst_soup_http_src_parse_status (msg, src);
669
670   /* Check if Range header was respected. */
671   if (src->ret == GST_FLOW_CUSTOM_ERROR &&
672       src->read_position && msg->status_code != SOUP_STATUS_PARTIAL_CONTENT) {
673     src->seekable = FALSE;
674     GST_ELEMENT_ERROR (src, RESOURCE, READ,
675         ("\"%s\": failed to seek; server does not accept Range HTTP header",
676             src->location), (NULL));
677     src->ret = GST_FLOW_ERROR;
678   }
679 }
680
681 /* Have body. Signal EOS. */
682 static void
683 gst_soup_http_src_got_body_cb (SoupMessage * msg, GstSoupHTTPSrc * src)
684 {
685   if (G_UNLIKELY (msg != src->msg)) {
686     GST_DEBUG_OBJECT (src, "got body, but not for current message");
687     return;
688   }
689   if (G_UNLIKELY (src->session_io_status !=
690           GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING)) {
691     /* Probably a redirect. */
692     return;
693   }
694   GST_DEBUG_OBJECT (src, "got body");
695   src->ret = GST_FLOW_UNEXPECTED;
696   if (src->loop)
697     g_main_loop_quit (src->loop);
698   gst_soup_http_src_session_pause_message (src);
699 }
700
701 /* Finished. Signal EOS. */
702 static void
703 gst_soup_http_src_finished_cb (SoupMessage * msg, GstSoupHTTPSrc * src)
704 {
705   if (G_UNLIKELY (msg != src->msg)) {
706     GST_DEBUG_OBJECT (src, "finished, but not for current message");
707     return;
708   }
709   GST_DEBUG_OBJECT (src, "finished");
710   src->ret = GST_FLOW_UNEXPECTED;
711   if (src->session_io_status == GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_CANCELLED) {
712     /* gst_soup_http_src_cancel_message() triggered this; probably a seek
713      * that occurred in the QUEUEING state; i.e. before the connection setup
714      * was complete. Do nothing */
715   } else if (src->session_io_status ==
716       GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING && src->read_position > 0) {
717     /* The server disconnected while streaming. Reconnect and seeking to the
718      * last location. */
719     src->retry = TRUE;
720     src->ret = GST_FLOW_CUSTOM_ERROR;
721   } else if (G_UNLIKELY (src->session_io_status !=
722           GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING)) {
723     GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND,
724         ("%s", msg->reason_phrase),
725         ("libsoup status code %d", msg->status_code));
726   }
727   if (src->loop)
728     g_main_loop_quit (src->loop);
729 }
730
731 /* Buffer lifecycle management.
732  *
733  * gst_soup_http_src_create() runs the GMainLoop for this element, to let
734  * Soup take control.
735  * A GstBuffer is allocated in gst_soup_http_src_chunk_allocator() and
736  * associated with a SoupBuffer.
737  * Soup reads HTTP data in the GstBuffer's data buffer.
738  * The gst_soup_http_src_got_chunk_cb() is then called with the SoupBuffer.
739  * That sets gst_soup_http_src_create()'s return argument to the GstBuffer,
740  * increments its refcount (to 2), pauses the flow of data from the HTTP
741  * source to prevent gst_soup_http_src_got_chunk_cb() from being called
742  * again and breaks out of the GMainLoop.
743  * Because the SOUP_MESSAGE_OVERWRITE_CHUNKS flag is set, Soup frees the
744  * SoupBuffer and calls gst_soup_http_src_chunk_free(), which decrements the
745  * refcount (to 1).
746  * gst_soup_http_src_create() returns the GstBuffer. It will be freed by a
747  * downstream element.
748  * If Soup fails to read HTTP data, it does not call
749  * gst_soup_http_src_got_chunk_cb(), but still frees the SoupBuffer and
750  * calls gst_soup_http_src_chunk_free(), which decrements the GstBuffer's
751  * refcount to 0, freeing it.
752  */
753
754 static void
755 gst_soup_http_src_chunk_free (gpointer gstbuf)
756 {
757   gst_buffer_unref (GST_BUFFER_CAST (gstbuf));
758 }
759
760 static SoupBuffer *
761 gst_soup_http_src_chunk_allocator (SoupMessage * msg, gsize max_len,
762     gpointer user_data)
763 {
764   GstSoupHTTPSrc *src = (GstSoupHTTPSrc *) user_data;
765
766   GstBaseSrc *basesrc = GST_BASE_SRC_CAST (src);
767
768   GstBuffer *gstbuf;
769
770   SoupBuffer *soupbuf;
771
772   gsize length;
773
774   GstFlowReturn rc;
775
776   if (max_len)
777     length = MIN (basesrc->blocksize, max_len);
778   else
779     length = basesrc->blocksize;
780   GST_DEBUG_OBJECT (src, "alloc %" G_GSIZE_FORMAT " bytes <= %" G_GSIZE_FORMAT,
781       length, max_len);
782
783
784   rc = gst_pad_alloc_buffer (GST_BASE_SRC_PAD (basesrc),
785       GST_BUFFER_OFFSET_NONE, length,
786       src->icy_caps ? src->icy_caps :
787       GST_PAD_CAPS (GST_BASE_SRC_PAD (basesrc)), &gstbuf);
788   if (G_UNLIKELY (rc != GST_FLOW_OK)) {
789     /* Failed to allocate buffer. Stall SoupSession and return error code
790      * to create(). */
791     src->ret = rc;
792     g_main_loop_quit (src->loop);
793     return NULL;
794   }
795
796   soupbuf = soup_buffer_new_with_owner (GST_BUFFER_DATA (gstbuf), length,
797       gstbuf, gst_soup_http_src_chunk_free);
798
799   return soupbuf;
800 }
801
802 static void
803 gst_soup_http_src_got_chunk_cb (SoupMessage * msg, SoupBuffer * chunk,
804     GstSoupHTTPSrc * src)
805 {
806   GstBaseSrc *basesrc;
807
808   guint64 new_position;
809
810   if (G_UNLIKELY (msg != src->msg)) {
811     GST_DEBUG_OBJECT (src, "got chunk, but not for current message");
812     return;
813   }
814   if (G_UNLIKELY (src->session_io_status !=
815           GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING)) {
816     /* Probably a redirect. */
817     return;
818   }
819   basesrc = GST_BASE_SRC_CAST (src);
820   GST_DEBUG_OBJECT (src, "got chunk of %" G_GSIZE_FORMAT " bytes",
821       chunk->length);
822
823   /* Extract the GstBuffer from the SoupBuffer and set its fields. */
824   *src->outbuf = GST_BUFFER_CAST (soup_buffer_get_owner (chunk));
825   gst_buffer_ref (*src->outbuf);
826   GST_BUFFER_SIZE (*src->outbuf) = chunk->length;
827   GST_BUFFER_OFFSET (*src->outbuf) = basesrc->segment.last_stop;
828
829   gst_buffer_set_caps (*src->outbuf,
830       (src->icy_caps) ? src->icy_caps :
831       GST_PAD_CAPS (GST_BASE_SRC_PAD (basesrc)));
832
833   new_position = src->read_position + chunk->length;
834   if (G_LIKELY (src->request_position == src->read_position))
835     src->request_position = new_position;
836   src->read_position = new_position;
837
838   src->ret = GST_FLOW_OK;
839   g_main_loop_quit (src->loop);
840   gst_soup_http_src_session_pause_message (src);
841 }
842
843 static void
844 gst_soup_http_src_response_cb (SoupSession * session, SoupMessage * msg,
845     GstSoupHTTPSrc * src)
846 {
847   if (G_UNLIKELY (msg != src->msg)) {
848     GST_DEBUG_OBJECT (src, "got response %d: %s, but not for current message",
849         msg->status_code, msg->reason_phrase);
850     return;
851   }
852   if (G_UNLIKELY (src->session_io_status !=
853           GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING)
854       && SOUP_STATUS_IS_REDIRECTION (msg->status_code)) {
855     /* Ignore redirections. */
856     return;
857   }
858   GST_DEBUG_OBJECT (src, "got response %d: %s", msg->status_code,
859       msg->reason_phrase);
860   if (src->session_io_status == GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING &&
861       src->read_position > 0) {
862     /* The server disconnected while streaming. Reconnect and seeking to the
863      * last location. */
864     src->retry = TRUE;
865   } else
866     gst_soup_http_src_parse_status (msg, src);
867   /* The session's SoupMessage object expires after this callback returns. */
868   src->msg = NULL;
869   g_main_loop_quit (src->loop);
870 }
871
872 static void
873 gst_soup_http_src_parse_status (SoupMessage * msg, GstSoupHTTPSrc * src)
874 {
875   if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) {
876     switch (msg->status_code) {
877       case SOUP_STATUS_CANT_RESOLVE:
878         GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND,
879             ("\"%s\": %s", src->location, msg->reason_phrase),
880             ("libsoup status code %d", msg->status_code));
881         src->ret = GST_FLOW_ERROR;
882         break;
883       case SOUP_STATUS_CANT_RESOLVE_PROXY:
884         GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND,
885             ("%s", msg->reason_phrase),
886             ("libsoup status code %d", msg->status_code));
887         src->ret = GST_FLOW_ERROR;
888         break;
889       case SOUP_STATUS_CANT_CONNECT:
890       case SOUP_STATUS_CANT_CONNECT_PROXY:
891       case SOUP_STATUS_SSL_FAILED:
892         GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
893             ("\"%s\": %s", src->location, msg->reason_phrase),
894             ("libsoup status code %d", msg->status_code));
895         src->ret = GST_FLOW_ERROR;
896         break;
897       case SOUP_STATUS_IO_ERROR:
898       case SOUP_STATUS_MALFORMED:
899         GST_ELEMENT_ERROR (src, RESOURCE, READ,
900             ("\"%s\": %s", src->location, msg->reason_phrase),
901             ("libsoup status code %d", msg->status_code));
902         src->ret = GST_FLOW_ERROR;
903         break;
904       case SOUP_STATUS_CANCELLED:
905         /* No error message when interrupted by program. */
906         break;
907     }
908   } else if (SOUP_STATUS_IS_CLIENT_ERROR (msg->status_code) ||
909       SOUP_STATUS_IS_REDIRECTION (msg->status_code) ||
910       SOUP_STATUS_IS_SERVER_ERROR (msg->status_code)) {
911     /* Report HTTP error. */
912     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
913         ("\"%s\": %s", src->location, msg->reason_phrase),
914         ("%d %s", msg->status_code, msg->reason_phrase));
915     src->ret = GST_FLOW_ERROR;
916   }
917 }
918
919 static gboolean
920 gst_soup_http_src_build_message (GstSoupHTTPSrc * src)
921 {
922   src->msg = soup_message_new (SOUP_METHOD_GET, src->location);
923   if (!src->msg) {
924     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
925         (NULL), ("Error parsing URL \"%s\"", src->location));
926     return FALSE;
927   }
928   src->session_io_status = GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_IDLE;
929   soup_message_headers_append (src->msg->request_headers, "Connection",
930       "close");
931   if (src->iradio_mode) {
932     soup_message_headers_append (src->msg->request_headers, "icy-metadata",
933         "1");
934   }
935   if (src->cookies) {
936     gchar **cookie;
937
938     for (cookie = src->cookies; *cookie != NULL; cookie++) {
939       soup_message_headers_append (src->msg->request_headers, "Cookie",
940           *cookie);
941     }
942   }
943   soup_message_headers_append (src->msg->request_headers,
944       "transferMode.dlna.org", "Streaming");
945   src->retry = FALSE;
946
947   g_signal_connect (src->msg, "got_headers",
948       G_CALLBACK (gst_soup_http_src_got_headers_cb), src);
949   g_signal_connect (src->msg, "got_body",
950       G_CALLBACK (gst_soup_http_src_got_body_cb), src);
951   g_signal_connect (src->msg, "finished",
952       G_CALLBACK (gst_soup_http_src_finished_cb), src);
953   g_signal_connect (src->msg, "got_chunk",
954       G_CALLBACK (gst_soup_http_src_got_chunk_cb), src);
955   soup_message_set_flags (src->msg, SOUP_MESSAGE_OVERWRITE_CHUNKS |
956       (src->automatic_redirect ? 0 : SOUP_MESSAGE_NO_REDIRECT));
957   soup_message_set_chunk_allocator (src->msg,
958       gst_soup_http_src_chunk_allocator, src, NULL);
959   gst_soup_http_src_add_range_header (src, src->request_position);
960
961   return TRUE;
962 }
963
964 static GstFlowReturn
965 gst_soup_http_src_create (GstPushSrc * psrc, GstBuffer ** outbuf)
966 {
967   GstSoupHTTPSrc *src;
968
969   src = GST_SOUP_HTTP_SRC (psrc);
970
971   if (src->msg && (src->request_position != src->read_position)) {
972     if (src->session_io_status == GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_IDLE) {
973       gst_soup_http_src_add_range_header (src, src->request_position);
974     } else {
975       GST_DEBUG_OBJECT (src, "Seek from position %" G_GUINT64_FORMAT
976           " to %" G_GUINT64_FORMAT ": requeueing connection request",
977           src->read_position, src->request_position);
978       gst_soup_http_src_cancel_message (src);
979     }
980   }
981   if (!src->msg)
982     if (!gst_soup_http_src_build_message (src))
983       return GST_FLOW_ERROR;
984
985   src->ret = GST_FLOW_CUSTOM_ERROR;
986   src->outbuf = outbuf;
987   do {
988     if (src->interrupted) {
989       GST_DEBUG_OBJECT (src, "interrupted");
990       break;
991     }
992     if (src->retry) {
993       GST_DEBUG_OBJECT (src, "Reconnecting");
994       if (!gst_soup_http_src_build_message (src))
995         return GST_FLOW_ERROR;
996       src->retry = FALSE;
997       continue;
998     }
999     if (!src->msg) {
1000       GST_DEBUG_OBJECT (src, "EOS reached");
1001       break;
1002     }
1003
1004     switch (src->session_io_status) {
1005       case GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_IDLE:
1006         GST_DEBUG_OBJECT (src, "Queueing connection request");
1007         gst_soup_http_src_queue_message (src);
1008         break;
1009       case GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_QUEUED:
1010         break;
1011       case GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING:
1012         gst_soup_http_src_session_unpause_message (src);
1013         break;
1014       case GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_CANCELLED:
1015         /* Impossible. */
1016         break;
1017     }
1018
1019     if (src->ret == GST_FLOW_CUSTOM_ERROR)
1020       g_main_loop_run (src->loop);
1021   } while (src->ret == GST_FLOW_CUSTOM_ERROR);
1022
1023   if (src->ret == GST_FLOW_CUSTOM_ERROR)
1024     src->ret = GST_FLOW_UNEXPECTED;
1025   return src->ret;
1026 }
1027
1028 static gboolean
1029 gst_soup_http_src_start (GstBaseSrc * bsrc)
1030 {
1031   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (bsrc);
1032
1033   GST_DEBUG_OBJECT (src, "start(\"%s\")", src->location);
1034
1035   if (!src->location) {
1036     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
1037         (NULL), ("Missing location property"));
1038     return FALSE;
1039   }
1040
1041   src->context = g_main_context_new ();
1042
1043   src->loop = g_main_loop_new (src->context, TRUE);
1044   if (!src->loop) {
1045     GST_ELEMENT_ERROR (src, LIBRARY, INIT,
1046         (NULL), ("Failed to start GMainLoop"));
1047     g_main_context_unref (src->context);
1048     return FALSE;
1049   }
1050
1051   if (src->proxy == NULL)
1052     src->session =
1053         soup_session_async_new_with_options (SOUP_SESSION_ASYNC_CONTEXT,
1054         src->context, SOUP_SESSION_USER_AGENT, src->user_agent, NULL);
1055   else
1056     src->session =
1057         soup_session_async_new_with_options (SOUP_SESSION_ASYNC_CONTEXT,
1058         src->context, SOUP_SESSION_PROXY_URI, src->proxy,
1059         SOUP_SESSION_USER_AGENT, src->user_agent, NULL);
1060   if (!src->session) {
1061     GST_ELEMENT_ERROR (src, LIBRARY, INIT,
1062         (NULL), ("Failed to create async session"));
1063     return FALSE;
1064   }
1065
1066   return TRUE;
1067 }
1068
1069 static gboolean
1070 gst_soup_http_src_stop (GstBaseSrc * bsrc)
1071 {
1072   GstSoupHTTPSrc *src;
1073
1074   src = GST_SOUP_HTTP_SRC (bsrc);
1075   GST_DEBUG_OBJECT (src, "stop()");
1076   gst_soup_http_src_session_close (src);
1077   if (src->loop) {
1078     g_main_loop_unref (src->loop);
1079     g_main_context_unref (src->context);
1080     src->loop = NULL;
1081     src->context = NULL;
1082   }
1083
1084   return TRUE;
1085 }
1086
1087 /* Interrupt a blocking request. */
1088 static gboolean
1089 gst_soup_http_src_unlock (GstBaseSrc * bsrc)
1090 {
1091   GstSoupHTTPSrc *src;
1092
1093   src = GST_SOUP_HTTP_SRC (bsrc);
1094   GST_DEBUG_OBJECT (src, "unlock()");
1095
1096   src->interrupted = TRUE;
1097   if (src->loop)
1098     g_main_loop_quit (src->loop);
1099   return TRUE;
1100 }
1101
1102 /* Interrupt interrupt. */
1103 static gboolean
1104 gst_soup_http_src_unlock_stop (GstBaseSrc * bsrc)
1105 {
1106   GstSoupHTTPSrc *src;
1107
1108   src = GST_SOUP_HTTP_SRC (bsrc);
1109   GST_DEBUG_OBJECT (src, "unlock_stop()");
1110
1111   src->interrupted = FALSE;
1112   return TRUE;
1113 }
1114
1115 static gboolean
1116 gst_soup_http_src_get_size (GstBaseSrc * bsrc, guint64 * size)
1117 {
1118   GstSoupHTTPSrc *src;
1119
1120   src = GST_SOUP_HTTP_SRC (bsrc);
1121
1122   if (src->have_size) {
1123     GST_DEBUG_OBJECT (src, "get_size() = %" G_GUINT64_FORMAT,
1124         src->content_size);
1125     *size = src->content_size;
1126     return TRUE;
1127   }
1128   GST_DEBUG_OBJECT (src, "get_size() = FALSE");
1129   return FALSE;
1130 }
1131
1132 static gboolean
1133 gst_soup_http_src_is_seekable (GstBaseSrc * bsrc)
1134 {
1135   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (bsrc);
1136
1137   return src->seekable;
1138 }
1139
1140 static gboolean
1141 gst_soup_http_src_do_seek (GstBaseSrc * bsrc, GstSegment * segment)
1142 {
1143   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (bsrc);
1144
1145   GST_DEBUG_OBJECT (src, "do_seek(%" G_GUINT64_FORMAT ")", segment->start);
1146
1147   if (src->read_position == segment->start)
1148     return TRUE;
1149
1150   if (!src->seekable)
1151     return FALSE;
1152
1153   /* Wait for create() to handle the jump in offset. */
1154   src->request_position = segment->start;
1155   return TRUE;
1156 }
1157
1158 static gboolean
1159 gst_soup_http_src_set_location (GstSoupHTTPSrc * src, const gchar * uri)
1160 {
1161   if (src->location) {
1162     g_free (src->location);
1163     src->location = NULL;
1164   }
1165   src->location = g_strdup (uri);
1166
1167   return TRUE;
1168 }
1169
1170 static gboolean
1171 gst_soup_http_src_set_proxy (GstSoupHTTPSrc * src, const gchar * uri)
1172 {
1173   if (src->proxy) {
1174     soup_uri_free (src->proxy);
1175     src->proxy = NULL;
1176   }
1177   if (g_str_has_prefix (uri, "http://")) {
1178     src->proxy = soup_uri_new (uri);
1179   } else {
1180     gchar *new_uri = g_strconcat ("http://", uri, NULL);
1181
1182     src->proxy = soup_uri_new (new_uri);
1183     g_free (new_uri);
1184   }
1185
1186   return TRUE;
1187 }
1188
1189 static guint
1190 gst_soup_http_src_uri_get_type (void)
1191 {
1192   return GST_URI_SRC;
1193 }
1194
1195 static gchar **
1196 gst_soup_http_src_uri_get_protocols (void)
1197 {
1198   static gchar *protocols[] = { "http", "https", NULL };
1199   return protocols;
1200 }
1201
1202 static const gchar *
1203 gst_soup_http_src_uri_get_uri (GstURIHandler * handler)
1204 {
1205   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (handler);
1206
1207   return src->location;
1208 }
1209
1210 static gboolean
1211 gst_soup_http_src_uri_set_uri (GstURIHandler * handler, const gchar * uri)
1212 {
1213   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (handler);
1214
1215   return gst_soup_http_src_set_location (src, uri);
1216 }
1217
1218 static void
1219 gst_soup_http_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
1220 {
1221   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
1222
1223   iface->get_type = gst_soup_http_src_uri_get_type;
1224   iface->get_protocols = gst_soup_http_src_uri_get_protocols;
1225   iface->get_uri = gst_soup_http_src_uri_get_uri;
1226   iface->set_uri = gst_soup_http_src_uri_set_uri;
1227 }
1228
1229 static gboolean
1230 plugin_init (GstPlugin * plugin)
1231 {
1232   return gst_element_register (plugin, "souphttpsrc", GST_RANK_PRIMARY,
1233       GST_TYPE_SOUP_HTTP_SRC);
1234 }
1235
1236 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1237     GST_VERSION_MINOR,
1238     "soup",
1239     "libsoup HTTP client src",
1240     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)