adaptive: extract variant info
[platform/upstream/gstreamer.git] / gst-libs / gst / uridownloader / gsturidownloader.c
1 /* GStreamer
2  * Copyright (C) 2011 Andoni Morales Alastruey <ylatuya@gmail.com>
3  *
4  * gstfragment.c:
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #include <glib.h>
23 #include "gstfragment.h"
24 #include "gsturidownloader.h"
25 #include "gsturidownloader_debug.h"
26
27 #define GST_CAT_DEFAULT uridownloader_debug
28 GST_DEBUG_CATEGORY (uridownloader_debug);
29
30 #define GST_URI_DOWNLOADER_GET_PRIVATE(obj)  \
31    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
32     GST_TYPE_URI_DOWNLOADER, GstUriDownloaderPrivate))
33
34 struct _GstUriDownloaderPrivate
35 {
36   /* Fragments fetcher */
37   GstElement *urisrc;
38   GstBus *bus;
39   GstPad *pad;
40   GTimeVal *timeout;
41   GstFragment *download;
42   gboolean got_buffer;
43   GMutex download_lock;         /* used to restrict to one download only */
44
45   GWeakRef parent;
46
47   GError *err;
48
49   GCond cond;
50   gboolean cancelled;
51 };
52
53 static void gst_uri_downloader_finalize (GObject * object);
54 static void gst_uri_downloader_dispose (GObject * object);
55
56 static GstFlowReturn gst_uri_downloader_chain (GstPad * pad, GstObject * parent,
57     GstBuffer * buf);
58 static gboolean gst_uri_downloader_sink_event (GstPad * pad, GstObject * parent,
59     GstEvent * event);
60 static GstBusSyncReply gst_uri_downloader_bus_handler (GstBus * bus,
61     GstMessage * message, gpointer data);
62
63 static gboolean gst_uri_downloader_ensure_src (GstUriDownloader * downloader,
64     const gchar * uri);
65 static void gst_uri_downloader_destroy_src (GstUriDownloader * downloader);
66
67 static GstStaticPadTemplate sinkpadtemplate = GST_STATIC_PAD_TEMPLATE ("sink",
68     GST_PAD_SINK,
69     GST_PAD_ALWAYS,
70     GST_STATIC_CAPS_ANY);
71
72 #define _do_init \
73 { \
74   GST_DEBUG_CATEGORY_INIT (uridownloader_debug, "uridownloader", 0, "URI downloader"); \
75 }
76
77 G_DEFINE_TYPE_WITH_CODE (GstUriDownloader, gst_uri_downloader, GST_TYPE_OBJECT,
78     _do_init);
79
80 static void
81 gst_uri_downloader_class_init (GstUriDownloaderClass * klass)
82 {
83   GObjectClass *gobject_class;
84
85   gobject_class = (GObjectClass *) klass;
86
87   g_type_class_add_private (klass, sizeof (GstUriDownloaderPrivate));
88
89   gobject_class->dispose = gst_uri_downloader_dispose;
90   gobject_class->finalize = gst_uri_downloader_finalize;
91 }
92
93 static void
94 gst_uri_downloader_init (GstUriDownloader * downloader)
95 {
96   downloader->priv = GST_URI_DOWNLOADER_GET_PRIVATE (downloader);
97
98   /* Initialize the sink pad. This pad will be connected to the src pad of the
99    * element created with gst_element_make_from_uri and will handle the download */
100   downloader->priv->pad =
101       gst_pad_new_from_static_template (&sinkpadtemplate, "sink");
102   gst_pad_set_chain_function (downloader->priv->pad,
103       GST_DEBUG_FUNCPTR (gst_uri_downloader_chain));
104   gst_pad_set_event_function (downloader->priv->pad,
105       GST_DEBUG_FUNCPTR (gst_uri_downloader_sink_event));
106   gst_pad_set_element_private (downloader->priv->pad, downloader);
107   gst_pad_set_active (downloader->priv->pad, TRUE);
108
109   /* Create a bus to handle error and warning message from the source element */
110   downloader->priv->bus = gst_bus_new ();
111
112   g_mutex_init (&downloader->priv->download_lock);
113   g_cond_init (&downloader->priv->cond);
114 }
115
116 static void
117 gst_uri_downloader_dispose (GObject * object)
118 {
119   GstUriDownloader *downloader = GST_URI_DOWNLOADER (object);
120
121   gst_uri_downloader_destroy_src (downloader);
122
123   if (downloader->priv->bus != NULL) {
124     gst_object_unref (downloader->priv->bus);
125     downloader->priv->bus = NULL;
126   }
127
128   if (downloader->priv->pad) {
129     gst_object_unref (downloader->priv->pad);
130     downloader->priv->pad = NULL;
131   }
132
133   if (downloader->priv->download) {
134     g_object_unref (downloader->priv->download);
135     downloader->priv->download = NULL;
136   }
137
138   g_weak_ref_clear (&downloader->priv->parent);
139
140   G_OBJECT_CLASS (gst_uri_downloader_parent_class)->dispose (object);
141 }
142
143 static void
144 gst_uri_downloader_finalize (GObject * object)
145 {
146   GstUriDownloader *downloader = GST_URI_DOWNLOADER (object);
147
148   g_mutex_clear (&downloader->priv->download_lock);
149   g_cond_clear (&downloader->priv->cond);
150
151   G_OBJECT_CLASS (gst_uri_downloader_parent_class)->finalize (object);
152 }
153
154 GstUriDownloader *
155 gst_uri_downloader_new (void)
156 {
157   return g_object_new (GST_TYPE_URI_DOWNLOADER, NULL);
158 }
159
160 /**
161  * gst_uri_downloader_set_parent:
162  * @param downloader: the #GstUriDownloader
163  * @param parent: the parent #GstElement
164  *
165  * Sets an element as parent of this #GstUriDownloader so that context
166  * requests from the underlying source are proxied to the main pipeline
167  * and set back if a context was provided.
168  */
169 void
170 gst_uri_downloader_set_parent (GstUriDownloader * downloader,
171     GstElement * parent)
172 {
173   g_weak_ref_set (&downloader->priv->parent, parent);
174 }
175
176 static gboolean
177 gst_uri_downloader_sink_event (GstPad * pad, GstObject * parent,
178     GstEvent * event)
179 {
180   gboolean ret = FALSE;
181   GstUriDownloader *downloader;
182
183   downloader = GST_URI_DOWNLOADER (gst_pad_get_element_private (pad));
184
185   switch (event->type) {
186     case GST_EVENT_EOS:{
187       GST_OBJECT_LOCK (downloader);
188       GST_DEBUG_OBJECT (downloader, "Got EOS on the fetcher pad");
189       if (downloader->priv->download != NULL) {
190         /* signal we have fetched the URI */
191         downloader->priv->download->completed = TRUE;
192         downloader->priv->download->download_stop_time =
193             gst_util_get_timestamp ();
194         GST_DEBUG_OBJECT (downloader, "Signaling chain funtion");
195         g_cond_signal (&downloader->priv->cond);
196       }
197       GST_OBJECT_UNLOCK (downloader);
198       gst_event_unref (event);
199       break;
200     }
201     case GST_EVENT_CUSTOM_DOWNSTREAM_STICKY:{
202       const GstStructure *str;
203       str = gst_event_get_structure (event);
204       if (gst_structure_has_name (str, "http-headers")) {
205         GST_OBJECT_LOCK (downloader);
206         if (downloader->priv->download != NULL) {
207           if (downloader->priv->download->headers)
208             gst_structure_free (downloader->priv->download->headers);
209           downloader->priv->download->headers = gst_structure_copy (str);
210         }
211         GST_OBJECT_UNLOCK (downloader);
212       }
213     }
214       /* falls through */
215     default:
216       ret = gst_pad_event_default (pad, parent, event);
217       break;
218   }
219
220   return ret;
221 }
222
223 static GstBusSyncReply
224 gst_uri_downloader_bus_handler (GstBus * bus,
225     GstMessage * message, gpointer data)
226 {
227   GstUriDownloader *downloader = (GstUriDownloader *) (data);
228
229   if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR) {
230     GError *err = NULL;
231     gchar *dbg_info = NULL;
232     gchar *new_error = NULL;
233
234     gst_message_parse_error (message, &err, &dbg_info);
235     GST_WARNING_OBJECT (downloader,
236         "Received error: %s from %s, the download will be cancelled",
237         err->message, GST_OBJECT_NAME (message->src));
238     GST_DEBUG ("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");
239
240     if (dbg_info)
241       new_error = g_strdup_printf ("%s: %s\n", err->message, dbg_info);
242     if (new_error) {
243       g_free (err->message);
244       err->message = new_error;
245     }
246
247     if (!downloader->priv->err)
248       downloader->priv->err = err;
249     else
250       g_error_free (err);
251
252     g_free (dbg_info);
253
254     /* remove the sync handler to avoid duplicated messages */
255     gst_bus_set_sync_handler (downloader->priv->bus, NULL, NULL, NULL);
256
257     /* stop the download */
258     GST_OBJECT_LOCK (downloader);
259     if (downloader->priv->download != NULL) {
260       GST_DEBUG_OBJECT (downloader, "Stopping download");
261       g_object_unref (downloader->priv->download);
262       downloader->priv->download = NULL;
263       downloader->priv->cancelled = TRUE;
264       g_cond_signal (&downloader->priv->cond);
265     }
266     GST_OBJECT_UNLOCK (downloader);
267   } else if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_WARNING) {
268     GError *err = NULL;
269     gchar *dbg_info = NULL;
270
271     gst_message_parse_warning (message, &err, &dbg_info);
272     GST_WARNING_OBJECT (downloader,
273         "Received warning: %s from %s",
274         GST_OBJECT_NAME (message->src), err->message);
275     GST_DEBUG ("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");
276     g_error_free (err);
277     g_free (dbg_info);
278   } else if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_NEED_CONTEXT) {
279     GstElement *parent = g_weak_ref_get (&downloader->priv->parent);
280
281     /* post the same need-context as if it was from the parent and then
282      * get it to our internal element that requested it */
283     if (parent && GST_IS_ELEMENT (GST_MESSAGE_SRC (message))) {
284       const gchar *context_type;
285       GstContext *context;
286       GstElement *msg_src = GST_ELEMENT_CAST (GST_MESSAGE_SRC (message));
287
288       gst_message_parse_context_type (message, &context_type);
289       context = gst_element_get_context (parent, context_type);
290
291       /* No context, request one */
292       if (!context) {
293         GstMessage *need_context_msg =
294             gst_message_new_need_context (GST_OBJECT_CAST (parent),
295             context_type);
296         gst_element_post_message (parent, need_context_msg);
297         context = gst_element_get_context (parent, context_type);
298       }
299
300       if (context) {
301         gst_element_set_context (msg_src, context);
302         gst_context_unref (context);
303       }
304     }
305     if (parent)
306       gst_object_unref (parent);
307   }
308
309   gst_message_unref (message);
310   return GST_BUS_DROP;
311 }
312
313 static GstFlowReturn
314 gst_uri_downloader_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
315 {
316   GstUriDownloader *downloader;
317
318   downloader = GST_URI_DOWNLOADER (gst_pad_get_element_private (pad));
319
320   /* HTML errors (404, 500, etc...) are also pushed through this pad as
321    * response but the source element will also post a warning or error message
322    * in the bus, which is handled synchronously cancelling the download.
323    */
324   GST_OBJECT_LOCK (downloader);
325   if (downloader->priv->download == NULL) {
326     /* Download cancelled, quit */
327     gst_buffer_unref (buf);
328     GST_OBJECT_UNLOCK (downloader);
329     goto done;
330   }
331
332   GST_LOG_OBJECT (downloader, "The uri fetcher received a new buffer "
333       "of size %" G_GSIZE_FORMAT, gst_buffer_get_size (buf));
334   downloader->priv->got_buffer = TRUE;
335   if (!gst_fragment_add_buffer (downloader->priv->download, buf)) {
336     GST_WARNING_OBJECT (downloader, "Could not add buffer to fragment");
337     gst_buffer_unref (buf);
338   }
339   GST_OBJECT_UNLOCK (downloader);
340
341 done:
342   {
343     return GST_FLOW_OK;
344   }
345 }
346
347 void
348 gst_uri_downloader_reset (GstUriDownloader * downloader)
349 {
350   g_return_if_fail (downloader != NULL);
351
352   GST_OBJECT_LOCK (downloader);
353   downloader->priv->cancelled = FALSE;
354   GST_OBJECT_UNLOCK (downloader);
355 }
356
357 void
358 gst_uri_downloader_cancel (GstUriDownloader * downloader)
359 {
360   GST_OBJECT_LOCK (downloader);
361   if (downloader->priv->download != NULL) {
362     GST_DEBUG_OBJECT (downloader, "Cancelling download");
363     g_object_unref (downloader->priv->download);
364     downloader->priv->download = NULL;
365     downloader->priv->cancelled = TRUE;
366     GST_DEBUG_OBJECT (downloader, "Signaling chain funtion");
367     g_cond_signal (&downloader->priv->cond);
368   } else {
369     gboolean cancelled;
370
371     cancelled = downloader->priv->cancelled;
372     downloader->priv->cancelled = TRUE;
373     if (cancelled)
374       GST_DEBUG_OBJECT (downloader,
375           "Trying to cancel a download that was alredy cancelled");
376   }
377   GST_OBJECT_UNLOCK (downloader);
378 }
379
380 static gboolean
381 gst_uri_downloader_set_range (GstUriDownloader * downloader,
382     gint64 range_start, gint64 range_end)
383 {
384   g_return_val_if_fail (range_start >= 0, FALSE);
385   g_return_val_if_fail (range_end >= -1, FALSE);
386
387   if (range_start || (range_end >= 0)) {
388     GstEvent *seek;
389
390     seek = gst_event_new_seek (1.0, GST_FORMAT_BYTES, GST_SEEK_FLAG_FLUSH,
391         GST_SEEK_TYPE_SET, range_start, GST_SEEK_TYPE_SET, range_end);
392
393     return gst_element_send_event (downloader->priv->urisrc, seek);
394   }
395   return TRUE;
396 }
397
398 static gboolean
399 gst_uri_downloader_ensure_src (GstUriDownloader * downloader, const gchar * uri)
400 {
401   if (downloader->priv->urisrc) {
402     gchar *old_protocol, *new_protocol;
403     gchar *old_uri;
404
405     old_uri =
406         gst_uri_handler_get_uri (GST_URI_HANDLER (downloader->priv->urisrc));
407     old_protocol = gst_uri_get_protocol (old_uri);
408     new_protocol = gst_uri_get_protocol (uri);
409
410     if (!g_str_equal (old_protocol, new_protocol)) {
411       gst_uri_downloader_destroy_src (downloader);
412       GST_DEBUG_OBJECT (downloader, "Can't re-use old source element");
413     } else {
414       GError *err = NULL;
415
416       GST_DEBUG_OBJECT (downloader, "Re-using old source element");
417       if (!gst_uri_handler_set_uri
418           (GST_URI_HANDLER (downloader->priv->urisrc), uri, &err)) {
419         GST_DEBUG_OBJECT (downloader,
420             "Failed to re-use old source element: %s", err->message);
421         g_clear_error (&err);
422         gst_uri_downloader_destroy_src (downloader);
423       }
424     }
425     g_free (old_uri);
426     g_free (old_protocol);
427     g_free (new_protocol);
428   }
429
430   if (!downloader->priv->urisrc) {
431     GST_DEBUG_OBJECT (downloader, "Creating source element for the URI:%s",
432         uri);
433     downloader->priv->urisrc =
434         gst_element_make_from_uri (GST_URI_SRC, uri, NULL, NULL);
435     if (downloader->priv->urisrc) {
436       /* gst_element_make_from_uri returns a floating reference
437        * and we are not going to transfer the ownership, so we
438        * should take it.
439        */
440       gst_object_ref_sink (downloader->priv->urisrc);
441     }
442   }
443
444   return downloader->priv->urisrc != NULL;
445 }
446
447 static void
448 gst_uri_downloader_destroy_src (GstUriDownloader * downloader)
449 {
450   if (!downloader->priv->urisrc)
451     return;
452
453   gst_element_set_state (downloader->priv->urisrc, GST_STATE_NULL);
454   gst_object_unref (downloader->priv->urisrc);
455   downloader->priv->urisrc = NULL;
456 }
457
458 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
459 static gboolean
460 gst_uri_downloader_set_uri (GstUriDownloader * downloader, const gchar * uri,
461     const gchar * referer, gchar* user_agent, gchar** cookies, gint max_retry , gint timeout , gboolean compress, gboolean refresh,
462     gboolean allow_cache)
463 #else
464 static gboolean
465 gst_uri_downloader_set_uri (GstUriDownloader * downloader, const gchar * uri,
466     const gchar * referer, gboolean compress,
467     gboolean refresh, gboolean allow_cache)
468 #endif
469 {
470   GstPad *pad;
471   GObjectClass *gobject_class;
472
473   if (!gst_uri_is_valid (uri))
474     return FALSE;
475
476   if (!gst_uri_downloader_ensure_src (downloader, uri))
477     return FALSE;
478
479   gobject_class = G_OBJECT_GET_CLASS (downloader->priv->urisrc);
480   if (g_object_class_find_property (gobject_class, "compress"))
481     g_object_set (downloader->priv->urisrc, "compress", compress, NULL);
482   if (g_object_class_find_property (gobject_class, "keep-alive"))
483     g_object_set (downloader->priv->urisrc, "keep-alive", TRUE, NULL);
484 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
485   if (user_agent && g_object_class_find_property (gobject_class, "user-agent"))
486     g_object_set (downloader->priv->urisrc, "user-agent", user_agent, NULL);
487   if (cookies && g_object_class_find_property (gobject_class, "cookies"))
488     g_object_set (downloader->priv->urisrc, "cookies", cookies, NULL);
489
490   if ((max_retry != -1)&&(g_object_class_find_property (gobject_class, "retries")))
491     g_object_set (downloader->priv->urisrc, "retries", max_retry, NULL);
492   if ((timeout != -1)&&(g_object_class_find_property (gobject_class, "timeout")))
493     g_object_set (downloader->priv->urisrc, "timeout", timeout, NULL);
494 #endif
495   if (g_object_class_find_property (gobject_class, "extra-headers")) {
496     if (referer || refresh || !allow_cache) {
497       GstStructure *extra_headers = gst_structure_new_empty ("headers");
498
499       if (referer)
500         gst_structure_set (extra_headers, "Referer", G_TYPE_STRING, referer,
501             NULL);
502
503       if (!allow_cache)
504         gst_structure_set (extra_headers, "Cache-Control", G_TYPE_STRING,
505             "no-cache", NULL);
506       else if (refresh)
507         gst_structure_set (extra_headers, "Cache-Control", G_TYPE_STRING,
508             "max-age=0", NULL);
509
510       g_object_set (downloader->priv->urisrc, "extra-headers", extra_headers,
511           NULL);
512
513       gst_structure_free (extra_headers);
514     } else {
515       g_object_set (downloader->priv->urisrc, "extra-headers", NULL, NULL);
516     }
517   }
518
519   /* add a sync handler for the bus messages to detect errors in the download */
520   gst_element_set_bus (GST_ELEMENT (downloader->priv->urisrc),
521       downloader->priv->bus);
522   gst_bus_set_sync_handler (downloader->priv->bus,
523       gst_uri_downloader_bus_handler, downloader, NULL);
524
525   pad = gst_element_get_static_pad (downloader->priv->urisrc, "src");
526   if (!pad)
527     return FALSE;
528   gst_pad_link (pad, downloader->priv->pad);
529   gst_object_unref (pad);
530   return TRUE;
531 }
532
533 static gboolean
534 gst_uri_downloader_set_method (GstUriDownloader * downloader,
535     const gchar * method)
536 {
537   GObjectClass *gobject_class;
538
539   if (!downloader->priv->urisrc)
540     return FALSE;
541
542   gobject_class = G_OBJECT_GET_CLASS (downloader->priv->urisrc);
543   if (g_object_class_find_property (gobject_class, "method")) {
544     g_object_set (downloader->priv->urisrc, "method", method, NULL);
545     return TRUE;
546   }
547   return FALSE;
548 }
549 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
550 GstFragment *
551 gst_uri_downloader_fetch_uri (GstUriDownloader * downloader,
552     const gchar * uri, const gchar * referer, gchar* user_agent, gchar** cookies, gint max_retry , gint timeout , gboolean compress,
553     gboolean refresh, gboolean allow_cache, GError ** err)
554 {
555   return gst_uri_downloader_fetch_uri_with_range (downloader, uri,
556       referer, user_agent, cookies, max_retry, timeout, compress, refresh, allow_cache, 0, -1, err);
557 }
558 #else
559 GstFragment *
560 gst_uri_downloader_fetch_uri (GstUriDownloader * downloader,
561     const gchar * uri, const gchar * referer, gboolean compress,
562     gboolean refresh, gboolean allow_cache, GError ** err)
563 {
564   return gst_uri_downloader_fetch_uri_with_range (downloader, uri,
565       referer, compress, refresh, allow_cache, 0, -1, err);
566 }
567 #endif
568 /**
569  * gst_uri_downloader_fetch_uri_with_range:
570  * @downloader: the #GstUriDownloader
571  * @uri: the uri
572  * @range_start: the starting byte index
573  * @range_end: the final byte index, use -1 for unspecified
574  *
575  * Returns the downloaded #GstFragment
576  */
577 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
578 GstFragment *
579 gst_uri_downloader_fetch_uri_with_range (GstUriDownloader *
580     downloader, const gchar * uri, const gchar * referer, gchar* user_agent, gchar** cookies, gint max_retry , gint timeout , gboolean compress,
581     gboolean refresh, gboolean allow_cache,
582     gint64 range_start, gint64 range_end, GError ** err)
583 #else
584 GstFragment *
585 gst_uri_downloader_fetch_uri_with_range (GstUriDownloader *
586     downloader, const gchar * uri, const gchar * referer, gboolean compress,
587     gboolean refresh, gboolean allow_cache,
588     gint64 range_start, gint64 range_end, GError ** err)
589 #endif
590 {
591   GstStateChangeReturn ret;
592   GstFragment *download = NULL;
593
594   GST_DEBUG_OBJECT (downloader, "Fetching URI %s", uri);
595
596   g_mutex_lock (&downloader->priv->download_lock);
597   downloader->priv->err = NULL;
598   downloader->priv->got_buffer = FALSE;
599
600   GST_OBJECT_LOCK (downloader);
601   if (downloader->priv->cancelled) {
602     GST_DEBUG_OBJECT (downloader, "Cancelled, aborting fetch");
603     goto quit;
604   }
605 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
606   if (!gst_uri_downloader_set_uri (downloader, uri, referer, user_agent, cookies, max_retry, timeout ,compress, refresh,
607           allow_cache)) {
608     GST_WARNING_OBJECT (downloader, "Failed to set URI");
609     goto quit;
610   }
611 #else
612   if (!gst_uri_downloader_set_uri (downloader, uri, referer, compress, refresh,
613           allow_cache)) {
614     GST_WARNING_OBJECT (downloader, "Failed to set URI");
615     goto quit;
616   }
617 #endif
618   gst_bus_set_flushing (downloader->priv->bus, FALSE);
619   if (downloader->priv->download)
620     g_object_unref (downloader->priv->download);
621   downloader->priv->download = gst_fragment_new ();
622   downloader->priv->download->range_start = range_start;
623   downloader->priv->download->range_end = range_end;
624   GST_OBJECT_UNLOCK (downloader);
625   ret = gst_element_set_state (downloader->priv->urisrc, GST_STATE_READY);
626   GST_OBJECT_LOCK (downloader);
627   if (ret == GST_STATE_CHANGE_FAILURE || downloader->priv->download == NULL) {
628     GST_WARNING_OBJECT (downloader, "Failed to set src to READY");
629     goto quit;
630   }
631
632   /* might have been cancelled because of failures in state change */
633   if (downloader->priv->cancelled) {
634     goto quit;
635   }
636
637   if (range_start < 0 && range_end < 0) {
638     if (!gst_uri_downloader_set_method (downloader, "HEAD")) {
639       GST_WARNING_OBJECT (downloader, "Failed to set HTTP method");
640       goto quit;
641     }
642   } else {
643     if (!gst_uri_downloader_set_range (downloader, range_start, range_end)) {
644       GST_WARNING_OBJECT (downloader, "Failed to set range");
645       goto quit;
646     }
647   }
648
649   GST_OBJECT_UNLOCK (downloader);
650   ret = gst_element_set_state (downloader->priv->urisrc, GST_STATE_PLAYING);
651   GST_OBJECT_LOCK (downloader);
652   if (ret == GST_STATE_CHANGE_FAILURE) {
653     if (downloader->priv->download) {
654       g_object_unref (downloader->priv->download);
655       downloader->priv->download = NULL;
656     }
657     goto quit;
658   }
659
660   /* might have been cancelled because of failures in state change */
661   if (downloader->priv->cancelled) {
662     goto quit;
663   }
664
665   /* wait until:
666    *   - the download succeed (EOS in the src pad)
667    *   - the download failed (Error message on the fetcher bus)
668    *   - the download was canceled
669    */
670   GST_DEBUG_OBJECT (downloader, "Waiting to fetch the URI %s", uri);
671   while (!downloader->priv->cancelled && !downloader->priv->download->completed)
672     g_cond_wait (&downloader->priv->cond, GST_OBJECT_GET_LOCK (downloader));
673
674   if (downloader->priv->cancelled) {
675     if (downloader->priv->download) {
676       g_object_unref (downloader->priv->download);
677       downloader->priv->download = NULL;
678     }
679     goto quit;
680   }
681
682   download = downloader->priv->download;
683   downloader->priv->download = NULL;
684   if (!downloader->priv->got_buffer) {
685     if (download->range_start < 0 && download->range_end < 0) {
686       /* HEAD request, so we don't expect a response */
687     } else {
688       g_object_unref (download);
689       download = NULL;
690       GST_ERROR_OBJECT (downloader, "Didn't retrieve a buffer before EOS");
691     }
692   }
693
694   if (download != NULL)
695     GST_INFO_OBJECT (downloader, "URI fetched successfully");
696   else
697     GST_INFO_OBJECT (downloader, "Error fetching URI");
698
699 quit:
700   {
701     if (downloader->priv->urisrc) {
702       GstPad *pad;
703       GstElement *urisrc;
704
705       urisrc = downloader->priv->urisrc;
706
707       GST_DEBUG_OBJECT (downloader, "Stopping source element %s",
708           GST_ELEMENT_NAME (urisrc));
709
710       /* remove the bus' sync handler */
711       gst_bus_set_sync_handler (downloader->priv->bus, NULL, NULL, NULL);
712       gst_bus_set_flushing (downloader->priv->bus, TRUE);
713
714       /* set the element state to NULL */
715       GST_OBJECT_UNLOCK (downloader);
716       if (download == NULL) {
717         gst_element_set_state (urisrc, GST_STATE_NULL);
718       } else {
719         GstQuery *query;
720
721         /* Download successfull, let's query the URI */
722         query = gst_query_new_uri ();
723         if (gst_element_query (urisrc, query)) {
724           gst_query_parse_uri (query, &download->uri);
725           gst_query_parse_uri_redirection (query, &download->redirect_uri);
726           gst_query_parse_uri_redirection_permanent (query,
727               &download->redirect_permanent);
728         }
729         gst_query_unref (query);
730         gst_element_set_state (urisrc, GST_STATE_READY);
731       }
732       GST_OBJECT_LOCK (downloader);
733       gst_element_set_bus (urisrc, NULL);
734
735       /* unlink the source element from the internal pad */
736       pad = gst_pad_get_peer (downloader->priv->pad);
737       if (pad) {
738         gst_pad_unlink (pad, downloader->priv->pad);
739         gst_object_unref (pad);
740       }
741     }
742     GST_OBJECT_UNLOCK (downloader);
743
744     if (download == NULL) {
745       if (!downloader->priv->err) {
746         g_set_error (err, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_OPEN_READ,
747             "Failed to download '%s'", uri);
748       } else {
749         g_propagate_error (err, downloader->priv->err);
750         downloader->priv->err = NULL;
751       }
752     }
753
754     downloader->priv->cancelled = FALSE;
755
756     g_mutex_unlock (&downloader->priv->download_lock);
757     return download;
758   }
759 }