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