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