uridownloader: Store URI and redirect target in the downloaded fragments
[platform/upstream/gstreamer.git] / gst-libs / gst / uridownloader / gsturidownloader.c
1 /* GStreamer
2  * Copyright (C) 2011 Andoni Morales Alastruey <ylatuya@gmail.com>
3  *
4  * gstfragment.c:
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #include <glib.h>
23 #include "gstfragment.h"
24 #include "gsturidownloader.h"
25 #include "gsturidownloader_debug.h"
26
27 #define GST_CAT_DEFAULT uridownloader_debug
28 GST_DEBUG_CATEGORY (uridownloader_debug);
29
30 #define GST_URI_DOWNLOADER_GET_PRIVATE(obj)  \
31    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
32     GST_TYPE_URI_DOWNLOADER, GstUriDownloaderPrivate))
33
34 struct _GstUriDownloaderPrivate
35 {
36   /* Fragments fetcher */
37   GstElement *urisrc;
38   GstBus *bus;
39   GstPad *pad;
40   GTimeVal *timeout;
41   GstFragment *download;
42   gboolean got_buffer;
43   GMutex download_lock;         /* used to restrict to one download only */
44
45   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 GstStaticPadTemplate sinkpadtemplate = GST_STATIC_PAD_TEMPLATE ("sink",
62     GST_PAD_SINK,
63     GST_PAD_ALWAYS,
64     GST_STATIC_CAPS_ANY);
65
66 #define _do_init \
67 { \
68   GST_DEBUG_CATEGORY_INIT (uridownloader_debug, "uridownloader", 0, "URI downloader"); \
69 }
70
71 G_DEFINE_TYPE_WITH_CODE (GstUriDownloader, gst_uri_downloader, GST_TYPE_OBJECT,
72     _do_init);
73
74 static void
75 gst_uri_downloader_class_init (GstUriDownloaderClass * klass)
76 {
77   GObjectClass *gobject_class;
78
79   gobject_class = (GObjectClass *) klass;
80
81   g_type_class_add_private (klass, sizeof (GstUriDownloaderPrivate));
82
83   gobject_class->dispose = gst_uri_downloader_dispose;
84   gobject_class->finalize = gst_uri_downloader_finalize;
85 }
86
87 static void
88 gst_uri_downloader_init (GstUriDownloader * downloader)
89 {
90   downloader->priv = GST_URI_DOWNLOADER_GET_PRIVATE (downloader);
91
92   /* Initialize the sink pad. This pad will be connected to the src pad of the
93    * element created with gst_element_make_from_uri and will handle the download */
94   downloader->priv->pad =
95       gst_pad_new_from_static_template (&sinkpadtemplate, "sink");
96   gst_pad_set_chain_function (downloader->priv->pad,
97       GST_DEBUG_FUNCPTR (gst_uri_downloader_chain));
98   gst_pad_set_event_function (downloader->priv->pad,
99       GST_DEBUG_FUNCPTR (gst_uri_downloader_sink_event));
100   gst_pad_set_element_private (downloader->priv->pad, downloader);
101   gst_pad_set_active (downloader->priv->pad, TRUE);
102
103   /* Create a bus to handle error and warning message from the source element */
104   downloader->priv->bus = gst_bus_new ();
105
106   g_mutex_init (&downloader->priv->download_lock);
107   g_cond_init (&downloader->priv->cond);
108 }
109
110 static void
111 gst_uri_downloader_dispose (GObject * object)
112 {
113   GstUriDownloader *downloader = GST_URI_DOWNLOADER (object);
114
115   if (downloader->priv->urisrc != NULL) {
116     gst_element_set_state (downloader->priv->urisrc, GST_STATE_NULL);
117     gst_object_unref (downloader->priv->urisrc);
118     downloader->priv->urisrc = NULL;
119   }
120
121   if (downloader->priv->bus != NULL) {
122     gst_object_unref (downloader->priv->bus);
123     downloader->priv->bus = NULL;
124   }
125
126   if (downloader->priv->pad) {
127     gst_object_unref (downloader->priv->pad);
128     downloader->priv->pad = NULL;
129   }
130
131   if (downloader->priv->download) {
132     g_object_unref (downloader->priv->download);
133     downloader->priv->download = NULL;
134   }
135
136   G_OBJECT_CLASS (gst_uri_downloader_parent_class)->dispose (object);
137 }
138
139 static void
140 gst_uri_downloader_finalize (GObject * object)
141 {
142   GstUriDownloader *downloader = GST_URI_DOWNLOADER (object);
143
144   g_mutex_clear (&downloader->priv->download_lock);
145   g_cond_clear (&downloader->priv->cond);
146
147   G_OBJECT_CLASS (gst_uri_downloader_parent_class)->finalize (object);
148 }
149
150 GstUriDownloader *
151 gst_uri_downloader_new (void)
152 {
153   return g_object_new (GST_TYPE_URI_DOWNLOADER, NULL);
154 }
155
156 static gboolean
157 gst_uri_downloader_sink_event (GstPad * pad, GstObject * parent,
158     GstEvent * event)
159 {
160   gboolean ret = FALSE;
161   GstUriDownloader *downloader;
162
163   downloader = GST_URI_DOWNLOADER (gst_pad_get_element_private (pad));
164
165   switch (event->type) {
166     case GST_EVENT_EOS:{
167       GST_OBJECT_LOCK (downloader);
168       GST_DEBUG_OBJECT (downloader, "Got EOS on the fetcher pad");
169       if (downloader->priv->download != NULL) {
170         /* signal we have fetched the URI */
171         downloader->priv->download->completed = TRUE;
172         downloader->priv->download->download_stop_time =
173             gst_util_get_timestamp ();
174         GST_DEBUG_OBJECT (downloader, "Signaling chain funtion");
175         g_cond_signal (&downloader->priv->cond);
176       }
177       GST_OBJECT_UNLOCK (downloader);
178       gst_event_unref (event);
179       break;
180     }
181     default:
182       ret = gst_pad_event_default (pad, parent, event);
183       break;
184   }
185
186   return ret;
187 }
188
189 static GstBusSyncReply
190 gst_uri_downloader_bus_handler (GstBus * bus,
191     GstMessage * message, gpointer data)
192 {
193   GstUriDownloader *downloader = (GstUriDownloader *) (data);
194
195   if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR) {
196     GError *err = NULL;
197     gchar *dbg_info = NULL;
198
199     gst_message_parse_error (message, &err, &dbg_info);
200     GST_WARNING_OBJECT (downloader,
201         "Received error: %s from %s, the download will be cancelled",
202         err->message, GST_OBJECT_NAME (message->src));
203     GST_DEBUG ("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");
204
205     if (!downloader->priv->err)
206       downloader->priv->err = err;
207     else
208       g_error_free (err);
209
210     g_free (dbg_info);
211
212     /* remove the sync handler to avoid duplicated messages */
213     gst_bus_set_sync_handler (downloader->priv->bus, NULL, NULL, NULL);
214
215     /* stop the download */
216     GST_OBJECT_LOCK (downloader);
217     if (downloader->priv->download != NULL) {
218       GST_DEBUG_OBJECT (downloader, "Stopping download");
219       g_object_unref (downloader->priv->download);
220       downloader->priv->download = NULL;
221       downloader->priv->cancelled = TRUE;
222       g_cond_signal (&downloader->priv->cond);
223     }
224     GST_OBJECT_UNLOCK (downloader);
225   } else if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_WARNING) {
226     GError *err = NULL;
227     gchar *dbg_info = NULL;
228
229     gst_message_parse_warning (message, &err, &dbg_info);
230     GST_WARNING_OBJECT (downloader,
231         "Received warning: %s from %s",
232         GST_OBJECT_NAME (message->src), err->message);
233     GST_DEBUG ("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");
234     g_error_free (err);
235     g_free (dbg_info);
236   }
237
238   gst_message_unref (message);
239   return GST_BUS_DROP;
240 }
241
242 static GstFlowReturn
243 gst_uri_downloader_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
244 {
245   GstUriDownloader *downloader;
246
247   downloader = GST_URI_DOWNLOADER (gst_pad_get_element_private (pad));
248
249   /* HTML errors (404, 500, etc...) are also pushed through this pad as
250    * response but the source element will also post a warning or error message
251    * in the bus, which is handled synchronously cancelling the download.
252    */
253   GST_OBJECT_LOCK (downloader);
254   if (downloader->priv->download == NULL) {
255     /* Download cancelled, quit */
256     GST_OBJECT_UNLOCK (downloader);
257     goto done;
258   }
259
260   GST_LOG_OBJECT (downloader, "The uri fetcher received a new buffer "
261       "of size %" G_GSIZE_FORMAT, gst_buffer_get_size (buf));
262   downloader->priv->got_buffer = TRUE;
263   if (!gst_fragment_add_buffer (downloader->priv->download, buf))
264     GST_WARNING_OBJECT (downloader, "Could not add buffer to fragment");
265   GST_OBJECT_UNLOCK (downloader);
266
267 done:
268   {
269     return GST_FLOW_OK;
270   }
271 }
272
273 void
274 gst_uri_downloader_reset (GstUriDownloader * downloader)
275 {
276   g_return_if_fail (downloader != NULL);
277
278   GST_OBJECT_LOCK (downloader);
279   downloader->priv->cancelled = FALSE;
280   GST_OBJECT_UNLOCK (downloader);
281 }
282
283 void
284 gst_uri_downloader_cancel (GstUriDownloader * downloader)
285 {
286   GST_OBJECT_LOCK (downloader);
287   if (downloader->priv->download != NULL) {
288     GST_DEBUG_OBJECT (downloader, "Cancelling download");
289     g_object_unref (downloader->priv->download);
290     downloader->priv->download = NULL;
291     downloader->priv->cancelled = TRUE;
292     GST_DEBUG_OBJECT (downloader, "Signaling chain funtion");
293     g_cond_signal (&downloader->priv->cond);
294   } else {
295     gboolean cancelled;
296
297     cancelled = downloader->priv->cancelled;
298     downloader->priv->cancelled = TRUE;
299     if (cancelled)
300       GST_DEBUG_OBJECT (downloader,
301           "Trying to cancel a download that was alredy cancelled");
302   }
303   GST_OBJECT_UNLOCK (downloader);
304 }
305
306 static gboolean
307 gst_uri_downloader_set_range (GstUriDownloader * downloader,
308     gint64 range_start, gint64 range_end)
309 {
310   g_return_val_if_fail (range_start >= 0, FALSE);
311   g_return_val_if_fail (range_end >= -1, FALSE);
312
313   if (range_start || (range_end >= 0)) {
314     GstEvent *seek;
315
316     seek = gst_event_new_seek (1.0, GST_FORMAT_BYTES, GST_SEEK_FLAG_FLUSH,
317         GST_SEEK_TYPE_SET, range_start, GST_SEEK_TYPE_SET, range_end);
318
319     return gst_element_send_event (downloader->priv->urisrc, seek);
320   }
321   return TRUE;
322 }
323
324 static gboolean
325 gst_uri_downloader_set_uri (GstUriDownloader * downloader, const gchar * uri,
326     const gchar * referer, gboolean compress, gboolean refresh,
327     gboolean allow_cache)
328 {
329   GstPad *pad;
330   GObjectClass *gobject_class;
331
332   if (!gst_uri_is_valid (uri))
333     return FALSE;
334
335   if (downloader->priv->urisrc) {
336     gchar *old_protocol, *new_protocol;
337     gchar *old_uri;
338
339     old_uri =
340         gst_uri_handler_get_uri (GST_URI_HANDLER (downloader->priv->urisrc));
341     old_protocol = gst_uri_get_protocol (old_uri);
342     new_protocol = gst_uri_get_protocol (uri);
343
344     if (!g_str_equal (old_protocol, new_protocol)) {
345       gst_element_set_state (downloader->priv->urisrc, GST_STATE_NULL);
346       gst_object_unref (downloader->priv->urisrc);
347       downloader->priv->urisrc = NULL;
348       GST_DEBUG_OBJECT (downloader, "Can't re-use old source element");
349     } else {
350       GError *err = NULL;
351
352       GST_DEBUG_OBJECT (downloader, "Re-using old source element");
353       if (!gst_uri_handler_set_uri (GST_URI_HANDLER (downloader->priv->urisrc),
354               uri, &err)) {
355         GST_DEBUG_OBJECT (downloader, "Failed to re-use old source element: %s",
356             err->message);
357         g_clear_error (&err);
358         gst_element_set_state (downloader->priv->urisrc, GST_STATE_NULL);
359         gst_object_unref (downloader->priv->urisrc);
360         downloader->priv->urisrc = NULL;
361       }
362     }
363     g_free (old_uri);
364     g_free (old_protocol);
365     g_free (new_protocol);
366   }
367
368   if (!downloader->priv->urisrc) {
369     GST_DEBUG_OBJECT (downloader, "Creating source element for the URI:%s",
370         uri);
371     downloader->priv->urisrc =
372         gst_element_make_from_uri (GST_URI_SRC, uri, NULL, NULL);
373     if (!downloader->priv->urisrc)
374       return FALSE;
375   }
376
377   gobject_class = G_OBJECT_GET_CLASS (downloader->priv->urisrc);
378   if (g_object_class_find_property (gobject_class, "compress"))
379     g_object_set (downloader->priv->urisrc, "compress", compress, NULL);
380   if (g_object_class_find_property (gobject_class, "keep-alive"))
381     g_object_set (downloader->priv->urisrc, "keep-alive", TRUE, NULL);
382   if (g_object_class_find_property (gobject_class, "extra-headers")) {
383     if (referer || refresh || !allow_cache) {
384       GstStructure *extra_headers = gst_structure_new_empty ("headers");
385
386       if (referer)
387         gst_structure_set (extra_headers, "Referer", G_TYPE_STRING, referer,
388             NULL);
389
390       if (!allow_cache)
391         gst_structure_set (extra_headers, "Cache-Control", G_TYPE_STRING,
392             "no-cache", NULL);
393       else if (refresh)
394         gst_structure_set (extra_headers, "Cache-Control", G_TYPE_STRING,
395             "max-age=0", NULL);
396
397       g_object_set (downloader->priv->urisrc, "extra-headers", extra_headers,
398           NULL);
399
400       gst_structure_free (extra_headers);
401     } else {
402       g_object_set (downloader->priv->urisrc, "extra-headers", NULL, NULL);
403     }
404   }
405
406   /* add a sync handler for the bus messages to detect errors in the download */
407   gst_element_set_bus (GST_ELEMENT (downloader->priv->urisrc),
408       downloader->priv->bus);
409   gst_bus_set_sync_handler (downloader->priv->bus,
410       gst_uri_downloader_bus_handler, downloader, NULL);
411
412   pad = gst_element_get_static_pad (downloader->priv->urisrc, "src");
413   if (!pad)
414     return FALSE;
415   gst_pad_link (pad, downloader->priv->pad);
416   gst_object_unref (pad);
417   return TRUE;
418 }
419
420 GstFragment *
421 gst_uri_downloader_fetch_uri (GstUriDownloader * downloader,
422     const gchar * uri, const gchar * referer, gboolean compress,
423     gboolean refresh, gboolean allow_cache, GError ** err)
424 {
425   return gst_uri_downloader_fetch_uri_with_range (downloader, uri,
426       referer, compress, refresh, allow_cache, 0, -1, err);
427 }
428
429 /**
430  * gst_uri_downloader_fetch_uri_with_range:
431  * @downloader: the #GstUriDownloader
432  * @uri: the uri
433  * @range_start: the starting byte index
434  * @range_end: the final byte index, use -1 for unspecified
435  *
436  * Returns the downloaded #GstFragment
437  */
438 GstFragment *
439 gst_uri_downloader_fetch_uri_with_range (GstUriDownloader *
440     downloader, const gchar * uri, const gchar * referer, gboolean compress,
441     gboolean refresh, gboolean allow_cache,
442     gint64 range_start, gint64 range_end, GError ** err)
443 {
444   GstStateChangeReturn ret;
445   GstFragment *download = NULL;
446
447   GST_DEBUG_OBJECT (downloader, "Fetching URI %s", uri);
448
449   g_mutex_lock (&downloader->priv->download_lock);
450   downloader->priv->err = NULL;
451   downloader->priv->got_buffer = FALSE;
452
453   GST_OBJECT_LOCK (downloader);
454   if (downloader->priv->cancelled) {
455     GST_DEBUG_OBJECT (downloader, "Cancelled, aborting fetch");
456     goto quit;
457   }
458
459   if (!gst_uri_downloader_set_uri (downloader, uri, referer, compress, refresh,
460           allow_cache)) {
461     GST_WARNING_OBJECT (downloader, "Failed to set URI");
462     goto quit;
463   }
464
465   gst_bus_set_flushing (downloader->priv->bus, FALSE);
466   downloader->priv->download = gst_fragment_new ();
467   GST_OBJECT_UNLOCK (downloader);
468   ret = gst_element_set_state (downloader->priv->urisrc, GST_STATE_READY);
469   GST_OBJECT_LOCK (downloader);
470   if (ret == GST_STATE_CHANGE_FAILURE || downloader->priv->download == NULL) {
471     GST_WARNING_OBJECT (downloader, "Failed to set src to READY");
472     goto quit;
473   }
474
475   /* might have been cancelled because of failures in state change */
476   if (downloader->priv->cancelled) {
477     goto quit;
478   }
479
480   if (!gst_uri_downloader_set_range (downloader, range_start, range_end)) {
481     GST_WARNING_OBJECT (downloader, "Failed to set range");
482     goto quit;
483   }
484
485   GST_OBJECT_UNLOCK (downloader);
486   ret = gst_element_set_state (downloader->priv->urisrc, GST_STATE_PLAYING);
487   GST_OBJECT_LOCK (downloader);
488   if (ret == GST_STATE_CHANGE_FAILURE) {
489     if (downloader->priv->download) {
490       g_object_unref (downloader->priv->download);
491       downloader->priv->download = NULL;
492     }
493     goto quit;
494   }
495
496   /* might have been cancelled because of failures in state change */
497   if (downloader->priv->cancelled) {
498     goto quit;
499   }
500
501   /* wait until:
502    *   - the download succeed (EOS in the src pad)
503    *   - the download failed (Error message on the fetcher bus)
504    *   - the download was canceled
505    */
506   GST_DEBUG_OBJECT (downloader, "Waiting to fetch the URI %s", uri);
507   while (!downloader->priv->cancelled && !downloader->priv->download->completed)
508     g_cond_wait (&downloader->priv->cond, GST_OBJECT_GET_LOCK (downloader));
509
510   if (downloader->priv->cancelled) {
511     if (downloader->priv->download) {
512       g_object_unref (downloader->priv->download);
513       downloader->priv->download = NULL;
514     }
515     goto quit;
516   }
517
518   download = downloader->priv->download;
519   downloader->priv->download = NULL;
520   if (!downloader->priv->got_buffer) {
521     g_object_unref (download);
522     download = NULL;
523     GST_ERROR_OBJECT (downloader, "Didn't retrieve a buffer before EOS");
524   }
525
526   if (download != NULL)
527     GST_INFO_OBJECT (downloader, "URI fetched successfully");
528   else
529     GST_INFO_OBJECT (downloader, "Error fetching URI");
530
531 quit:
532   {
533     if (downloader->priv->urisrc) {
534       GstPad *pad;
535       GstElement *urisrc;
536
537       urisrc = downloader->priv->urisrc;
538
539       GST_DEBUG_OBJECT (downloader, "Stopping source element %s",
540           GST_ELEMENT_NAME (urisrc));
541
542       /* remove the bus' sync handler */
543       gst_bus_set_sync_handler (downloader->priv->bus, NULL, NULL, NULL);
544       gst_bus_set_flushing (downloader->priv->bus, TRUE);
545
546       /* set the element state to NULL */
547       GST_OBJECT_UNLOCK (downloader);
548       if (download == NULL) {
549         gst_element_set_state (urisrc, GST_STATE_NULL);
550       } else {
551         GstQuery *query;
552
553         /* Download successfull, let's query the URI */
554         query = gst_query_new_uri ();
555         if (gst_element_query (urisrc, query)) {
556           gst_query_parse_uri (query, &download->uri);
557           gst_query_parse_uri_redirection (query, &download->redirect_uri);
558           gst_query_parse_uri_redirection_permanent (query,
559               &download->redirect_permanent);
560         }
561         gst_query_unref (query);
562         gst_element_set_state (urisrc, GST_STATE_READY);
563       }
564       GST_OBJECT_LOCK (downloader);
565       gst_element_set_bus (urisrc, NULL);
566
567       /* unlink the source element from the internal pad */
568       pad = gst_pad_get_peer (downloader->priv->pad);
569       if (pad) {
570         gst_pad_unlink (pad, downloader->priv->pad);
571         gst_object_unref (pad);
572       }
573     }
574     GST_OBJECT_UNLOCK (downloader);
575
576     if (download == NULL) {
577       if (!downloader->priv->err) {
578         g_set_error (err, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_OPEN_READ,
579             "Failed to download '%s'", uri);
580       } else {
581         g_propagate_error (err, downloader->priv->err);
582         downloader->priv->err = NULL;
583       }
584     }
585
586     downloader->priv->cancelled = FALSE;
587
588     g_mutex_unlock (&downloader->priv->download_lock);
589     return download;
590   }
591 }