2 * Copyright (C) 2011 Andoni Morales Alastruey <ylatuya@gmail.com>
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.
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.
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.
23 #include "gstfragment.h"
24 #include "gsturidownloader.h"
25 #include "gsturidownloader_debug.h"
27 #define GST_CAT_DEFAULT uridownloader_debug
28 GST_DEBUG_CATEGORY (uridownloader_debug);
30 #define GST_URI_DOWNLOADER_GET_PRIVATE(obj) \
31 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
32 GST_TYPE_URI_DOWNLOADER, GstUriDownloaderPrivate))
34 struct _GstUriDownloaderPrivate
36 /* Fragments fetcher */
41 GstFragment *download;
43 GMutex download_lock; /* used to restrict to one download only */
53 static void gst_uri_downloader_finalize (GObject * object);
54 static void gst_uri_downloader_dispose (GObject * object);
56 static GstFlowReturn gst_uri_downloader_chain (GstPad * pad, GstObject * parent,
58 static gboolean gst_uri_downloader_sink_event (GstPad * pad, GstObject * parent,
60 static GstBusSyncReply gst_uri_downloader_bus_handler (GstBus * bus,
61 GstMessage * message, gpointer data);
63 static gboolean gst_uri_downloader_ensure_src (GstUriDownloader * downloader,
65 static void gst_uri_downloader_destroy_src (GstUriDownloader * downloader);
67 static GstStaticPadTemplate sinkpadtemplate = GST_STATIC_PAD_TEMPLATE ("sink",
74 GST_DEBUG_CATEGORY_INIT (uridownloader_debug, "uridownloader", 0, "URI downloader"); \
77 G_DEFINE_TYPE_WITH_CODE (GstUriDownloader, gst_uri_downloader, GST_TYPE_OBJECT,
81 gst_uri_downloader_class_init (GstUriDownloaderClass * klass)
83 GObjectClass *gobject_class;
85 gobject_class = (GObjectClass *) klass;
87 g_type_class_add_private (klass, sizeof (GstUriDownloaderPrivate));
89 gobject_class->dispose = gst_uri_downloader_dispose;
90 gobject_class->finalize = gst_uri_downloader_finalize;
94 gst_uri_downloader_init (GstUriDownloader * downloader)
96 downloader->priv = GST_URI_DOWNLOADER_GET_PRIVATE (downloader);
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);
109 /* Create a bus to handle error and warning message from the source element */
110 downloader->priv->bus = gst_bus_new ();
112 g_mutex_init (&downloader->priv->download_lock);
113 g_cond_init (&downloader->priv->cond);
117 gst_uri_downloader_dispose (GObject * object)
119 GstUriDownloader *downloader = GST_URI_DOWNLOADER (object);
121 gst_uri_downloader_destroy_src (downloader);
123 if (downloader->priv->bus != NULL) {
124 gst_object_unref (downloader->priv->bus);
125 downloader->priv->bus = NULL;
128 if (downloader->priv->pad) {
129 gst_object_unref (downloader->priv->pad);
130 downloader->priv->pad = NULL;
133 if (downloader->priv->download) {
134 g_object_unref (downloader->priv->download);
135 downloader->priv->download = NULL;
138 g_weak_ref_clear (&downloader->priv->parent);
140 G_OBJECT_CLASS (gst_uri_downloader_parent_class)->dispose (object);
144 gst_uri_downloader_finalize (GObject * object)
146 GstUriDownloader *downloader = GST_URI_DOWNLOADER (object);
148 g_mutex_clear (&downloader->priv->download_lock);
149 g_cond_clear (&downloader->priv->cond);
151 G_OBJECT_CLASS (gst_uri_downloader_parent_class)->finalize (object);
155 gst_uri_downloader_new (void)
157 return g_object_new (GST_TYPE_URI_DOWNLOADER, NULL);
161 * gst_uri_downloader_set_parent:
162 * @param downloader: the #GstUriDownloader
163 * @param parent: the parent #GstElement
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.
170 gst_uri_downloader_set_parent (GstUriDownloader * downloader,
173 g_weak_ref_set (&downloader->priv->parent, parent);
177 gst_uri_downloader_sink_event (GstPad * pad, GstObject * parent,
180 gboolean ret = FALSE;
181 GstUriDownloader *downloader;
183 downloader = GST_URI_DOWNLOADER (gst_pad_get_element_private (pad));
185 switch (event->type) {
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);
197 GST_OBJECT_UNLOCK (downloader);
198 gst_event_unref (event);
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);
211 GST_OBJECT_UNLOCK (downloader);
216 ret = gst_pad_event_default (pad, parent, event);
223 static GstBusSyncReply
224 gst_uri_downloader_bus_handler (GstBus * bus,
225 GstMessage * message, gpointer data)
227 GstUriDownloader *downloader = (GstUriDownloader *) (data);
229 if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR) {
231 gchar *dbg_info = NULL;
232 gchar *new_error = NULL;
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");
241 new_error = g_strdup_printf ("%s: %s\n", err->message, dbg_info);
243 g_free (err->message);
244 err->message = new_error;
247 if (!downloader->priv->err)
248 downloader->priv->err = err;
254 /* remove the sync handler to avoid duplicated messages */
255 gst_bus_set_sync_handler (downloader->priv->bus, NULL, NULL, NULL);
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);
266 GST_OBJECT_UNLOCK (downloader);
267 } else if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_WARNING) {
269 gchar *dbg_info = NULL;
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");
278 } else if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_NEED_CONTEXT) {
279 GstElement *parent = g_weak_ref_get (&downloader->priv->parent);
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;
286 GstElement *msg_src = GST_ELEMENT_CAST (GST_MESSAGE_SRC (message));
288 gst_message_parse_context_type (message, &context_type);
289 context = gst_element_get_context (parent, context_type);
291 /* No context, request one */
293 GstMessage *need_context_msg =
294 gst_message_new_need_context (GST_OBJECT_CAST (parent),
296 gst_element_post_message (parent, need_context_msg);
297 context = gst_element_get_context (parent, context_type);
301 gst_element_set_context (msg_src, context);
302 gst_context_unref (context);
306 gst_object_unref (parent);
309 gst_message_unref (message);
314 gst_uri_downloader_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
316 GstUriDownloader *downloader;
318 downloader = GST_URI_DOWNLOADER (gst_pad_get_element_private (pad));
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.
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);
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);
339 GST_OBJECT_UNLOCK (downloader);
348 gst_uri_downloader_reset (GstUriDownloader * downloader)
350 g_return_if_fail (downloader != NULL);
352 GST_OBJECT_LOCK (downloader);
353 downloader->priv->cancelled = FALSE;
354 GST_OBJECT_UNLOCK (downloader);
358 gst_uri_downloader_cancel (GstUriDownloader * downloader)
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);
371 cancelled = downloader->priv->cancelled;
372 downloader->priv->cancelled = TRUE;
374 GST_DEBUG_OBJECT (downloader,
375 "Trying to cancel a download that was alredy cancelled");
377 GST_OBJECT_UNLOCK (downloader);
381 gst_uri_downloader_set_range (GstUriDownloader * downloader,
382 gint64 range_start, gint64 range_end)
384 g_return_val_if_fail (range_start >= 0, FALSE);
385 g_return_val_if_fail (range_end >= -1, FALSE);
387 if (range_start || (range_end >= 0)) {
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);
393 return gst_element_send_event (downloader->priv->urisrc, seek);
399 gst_uri_downloader_ensure_src (GstUriDownloader * downloader, const gchar * uri)
401 if (downloader->priv->urisrc) {
402 gchar *old_protocol, *new_protocol;
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);
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");
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);
426 g_free (old_protocol);
427 g_free (new_protocol);
430 if (!downloader->priv->urisrc) {
431 GST_DEBUG_OBJECT (downloader, "Creating source element for the URI:%s",
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
440 gst_object_ref_sink (downloader->priv->urisrc);
444 return downloader->priv->urisrc != NULL;
448 gst_uri_downloader_destroy_src (GstUriDownloader * downloader)
450 if (!downloader->priv->urisrc)
453 gst_element_set_state (downloader->priv->urisrc, GST_STATE_NULL);
454 gst_object_unref (downloader->priv->urisrc);
455 downloader->priv->urisrc = NULL;
458 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
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)
465 gst_uri_downloader_set_uri (GstUriDownloader * downloader, const gchar * uri,
466 const gchar * referer, gboolean compress,
467 gboolean refresh, gboolean allow_cache)
471 GObjectClass *gobject_class;
473 if (!gst_uri_is_valid (uri))
476 if (!gst_uri_downloader_ensure_src (downloader, uri))
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);
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);
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");
500 gst_structure_set (extra_headers, "Referer", G_TYPE_STRING, referer,
504 gst_structure_set (extra_headers, "Cache-Control", G_TYPE_STRING,
507 gst_structure_set (extra_headers, "Cache-Control", G_TYPE_STRING,
510 g_object_set (downloader->priv->urisrc, "extra-headers", extra_headers,
513 gst_structure_free (extra_headers);
515 g_object_set (downloader->priv->urisrc, "extra-headers", NULL, NULL);
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);
525 pad = gst_element_get_static_pad (downloader->priv->urisrc, "src");
528 gst_pad_link (pad, downloader->priv->pad);
529 gst_object_unref (pad);
534 gst_uri_downloader_set_method (GstUriDownloader * downloader,
535 const gchar * method)
537 GObjectClass *gobject_class;
539 if (!downloader->priv->urisrc)
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);
549 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
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)
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);
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)
564 return gst_uri_downloader_fetch_uri_with_range (downloader, uri,
565 referer, compress, refresh, allow_cache, 0, -1, err);
569 * gst_uri_downloader_fetch_uri_with_range:
570 * @downloader: the #GstUriDownloader
572 * @range_start: the starting byte index
573 * @range_end: the final byte index, use -1 for unspecified
575 * Returns the downloaded #GstFragment
577 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
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)
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)
591 GstStateChangeReturn ret;
592 GstFragment *download = NULL;
594 GST_DEBUG_OBJECT (downloader, "Fetching URI %s", uri);
596 g_mutex_lock (&downloader->priv->download_lock);
597 downloader->priv->err = NULL;
598 downloader->priv->got_buffer = FALSE;
600 GST_OBJECT_LOCK (downloader);
601 if (downloader->priv->cancelled) {
602 GST_DEBUG_OBJECT (downloader, "Cancelled, aborting fetch");
605 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
606 if (!gst_uri_downloader_set_uri (downloader, uri, referer, user_agent, cookies, max_retry, timeout ,compress, refresh,
608 GST_WARNING_OBJECT (downloader, "Failed to set URI");
612 if (!gst_uri_downloader_set_uri (downloader, uri, referer, compress, refresh,
614 GST_WARNING_OBJECT (downloader, "Failed to set URI");
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");
632 /* might have been cancelled because of failures in state change */
633 if (downloader->priv->cancelled) {
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");
643 if (!gst_uri_downloader_set_range (downloader, range_start, range_end)) {
644 GST_WARNING_OBJECT (downloader, "Failed to set range");
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;
660 /* might have been cancelled because of failures in state change */
661 if (downloader->priv->cancelled) {
666 * - the download succeed (EOS in the src pad)
667 * - the download failed (Error message on the fetcher bus)
668 * - the download was canceled
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));
674 if (downloader->priv->cancelled) {
675 if (downloader->priv->download) {
676 g_object_unref (downloader->priv->download);
677 downloader->priv->download = NULL;
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 */
688 g_object_unref (download);
690 GST_ERROR_OBJECT (downloader, "Didn't retrieve a buffer before EOS");
694 if (download != NULL)
695 GST_INFO_OBJECT (downloader, "URI fetched successfully");
697 GST_INFO_OBJECT (downloader, "Error fetching URI");
701 if (downloader->priv->urisrc) {
705 urisrc = downloader->priv->urisrc;
707 GST_DEBUG_OBJECT (downloader, "Stopping source element %s",
708 GST_ELEMENT_NAME (urisrc));
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);
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);
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);
729 gst_query_unref (query);
730 gst_element_set_state (urisrc, GST_STATE_READY);
732 GST_OBJECT_LOCK (downloader);
733 gst_element_set_bus (urisrc, NULL);
735 /* unlink the source element from the internal pad */
736 pad = gst_pad_get_peer (downloader->priv->pad);
738 gst_pad_unlink (pad, downloader->priv->pad);
739 gst_object_unref (pad);
742 GST_OBJECT_UNLOCK (downloader);
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);
749 g_propagate_error (err, downloader->priv->err);
750 downloader->priv->err = NULL;
754 downloader->priv->cancelled = FALSE;
756 g_mutex_unlock (&downloader->priv->download_lock);