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