tizen 2.3 release
[framework/multimedia/gst-plugins-ext0.10.git] / dashdemux / src / 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., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include <glib.h>
23 #include "gstfragmented.h"
24 #include "gstfragment.h"
25 #include "gsturidownloader.h"
26
27 GST_DEBUG_CATEGORY_STATIC (uridownloader_debug);
28 #define GST_CAT_DEFAULT (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   GMutex *lock;
43   GCond *cond;
44   gboolean canceled;
45 /* add setting UA,cookies */
46   gchar *user_agent;
47   gchar **cookies;
48 };
49
50 static void gst_uri_downloader_finalize (GObject * object);
51 static void gst_uri_downloader_dispose (GObject * object);
52
53 static GstFlowReturn gst_uri_downloader_chain (GstPad * pad, GstBuffer * buf);
54 static gboolean gst_uri_downloader_sink_event (GstPad * pad, GstEvent * event);
55 static GstBusSyncReply gst_uri_downloader_bus_handler (GstBus * bus,
56     GstMessage * message, gpointer data);
57
58 static GstStaticPadTemplate sinkpadtemplate = GST_STATIC_PAD_TEMPLATE ("sink",
59     GST_PAD_SINK,
60     GST_PAD_ALWAYS,
61     GST_STATIC_CAPS_ANY);
62
63 #define _do_init \
64 { \
65   GST_DEBUG_CATEGORY_INIT (uridownloader_debug, "uridownloader", 0, "URI downloader"); \
66 }
67
68 G_DEFINE_TYPE_WITH_CODE (GstUriDownloader, gst_uri_downloader, GST_TYPE_OBJECT,
69     _do_init);
70
71 static void
72 gst_uri_downloader_class_init (GstUriDownloaderClass * klass)
73 {
74   GObjectClass *gobject_class;
75
76   gobject_class = (GObjectClass *) klass;
77
78   g_type_class_add_private (klass, sizeof (GstUriDownloaderPrivate));
79
80   gobject_class->dispose = gst_uri_downloader_dispose;
81   gobject_class->finalize = gst_uri_downloader_finalize;
82 }
83
84 static void
85 gst_uri_downloader_init (GstUriDownloader * downloader)
86 {
87   downloader->priv = GST_URI_DOWNLOADER_GET_PRIVATE (downloader);
88
89   /* Initialize the sink pad. This pad will be connected to the src pad of the
90    * element created with gst_element_make_from_uri and will handle the download */
91   downloader->priv->pad =
92       gst_pad_new_from_static_template (&sinkpadtemplate, "sink");
93   gst_pad_set_chain_function (downloader->priv->pad,
94       GST_DEBUG_FUNCPTR (gst_uri_downloader_chain));
95   gst_pad_set_event_function (downloader->priv->pad,
96       GST_DEBUG_FUNCPTR (gst_uri_downloader_sink_event));
97   gst_pad_set_element_private (downloader->priv->pad, downloader);
98   gst_pad_activate_push (downloader->priv->pad, TRUE);
99
100   /* Create a bus to handle error and warning message from the source element */
101   downloader->priv->bus = gst_bus_new ();
102
103   downloader->priv->lock = g_mutex_new ();
104   downloader->priv->cond = g_cond_new ();
105 /* add setting UA, cookies */
106   downloader->priv->user_agent = NULL;
107   downloader->priv->cookies = NULL;
108   downloader->priv->canceled = FALSE;
109 }
110
111 void
112 gst_uri_downloader_reset(GstUriDownloader *downloader)
113 {
114   downloader->priv->canceled = FALSE;
115 }
116
117 static void
118 gst_uri_downloader_dispose (GObject * object)
119 {
120   GstUriDownloader *downloader = GST_URI_DOWNLOADER (object);
121
122   if (downloader->priv->urisrc != NULL) {
123     gst_object_unref (downloader->priv->urisrc);
124     downloader->priv->urisrc = NULL;
125   }
126
127   if (downloader->priv->bus != NULL) {
128     gst_object_unref (downloader->priv->bus);
129     downloader->priv->bus = NULL;
130   }
131
132   if (downloader->priv->pad) {
133     gst_object_unref (downloader->priv->pad);
134     downloader->priv->pad = NULL;
135   }
136
137   if (downloader->priv->download) {
138     g_object_unref (downloader->priv->download);
139     downloader->priv->download = NULL;
140   }
141
142 /* add setting UA, cookies */
143   if (downloader->priv->user_agent) {
144     g_free (downloader->priv->user_agent);
145     downloader->priv->user_agent = NULL;
146   }
147   if (downloader->priv->cookies) {
148     g_strfreev (downloader->priv->cookies);
149     downloader->priv->cookies = NULL;
150   }
151   G_OBJECT_CLASS (gst_uri_downloader_parent_class)->dispose (object);
152 }
153
154 static void
155 gst_uri_downloader_finalize (GObject * object)
156 {
157   GstUriDownloader *downloader = GST_URI_DOWNLOADER (object);
158
159   g_mutex_free (downloader->priv->lock);
160   g_cond_free (downloader->priv->cond);
161
162   G_OBJECT_CLASS (gst_uri_downloader_parent_class)->finalize (object);
163 }
164
165 GstUriDownloader *
166 gst_uri_downloader_new (void)
167 {
168   return g_object_new (GST_TYPE_URI_DOWNLOADER, NULL);
169 }
170
171 static gboolean
172 gst_uri_downloader_sink_event (GstPad * pad, GstEvent * event)
173 {
174   GstUriDownloader *downloader =
175       (GstUriDownloader *) (gst_pad_get_element_private (pad));
176
177   switch (event->type) {
178     case GST_EVENT_EOS:{
179       GST_DEBUG_OBJECT (downloader, "Got EOS on the fetcher pad");
180       g_mutex_lock (downloader->priv->lock);
181       if (downloader->priv->download != NULL) {
182         /* signal we have fetched the URI */
183         downloader->priv->download->completed = TRUE;
184         downloader->priv->download->download_stop_time = g_get_real_time ();
185         GST_DEBUG_OBJECT (downloader, "Signaling chain funtion");
186         g_cond_signal (downloader->priv->cond);
187       }
188       g_mutex_unlock (downloader->priv->lock);
189       break;
190     }
191     default:
192       break;
193   }
194
195   gst_event_unref (event);
196   return FALSE;
197 }
198
199 static GstBusSyncReply
200 gst_uri_downloader_bus_handler (GstBus * bus,
201     GstMessage * message, gpointer data)
202 {
203   GstUriDownloader *downloader = (GstUriDownloader *) (data);
204
205   if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR ||
206       GST_MESSAGE_TYPE (message) == GST_MESSAGE_WARNING) {
207     GST_WARNING_OBJECT (downloader,
208         "Received error in bus from the source element, "
209         "the download will be cancelled");
210     /* remove the sync handler to avoid duplicated messages */
211     gst_bus_set_sync_handler (downloader->priv->bus, NULL, NULL);
212     gst_uri_downloader_cancel (downloader);
213   }
214
215   gst_message_unref (message);
216   return GST_BUS_DROP;
217 }
218
219 static GstFlowReturn
220 gst_uri_downloader_chain (GstPad * pad, GstBuffer * buf)
221 {
222   GstUriDownloader *downloader =
223       (GstUriDownloader *) gst_pad_get_element_private (pad);
224
225   /* HTML errors (404, 500, etc...) are also pushed through this pad as
226    * response but the source element will also post a warning or error message
227    * in the bus, which is handled synchronously cancelling the download.
228    */
229   g_mutex_lock (downloader->priv->lock);
230   if (downloader->priv->download == NULL) {
231     /* Download cancelled, quit */
232     goto done;
233   }
234
235   GST_LOG_OBJECT (downloader,
236       "The uri fetcher received a new buffer of size %u",
237       GST_BUFFER_SIZE (buf));
238   if (!gst_fragment_add_buffer (downloader->priv->download, buf))
239     GST_WARNING_OBJECT (downloader, "Could not add buffer to fragment");
240
241 done:
242   {
243     g_mutex_unlock (downloader->priv->lock);
244     return GST_FLOW_OK;
245   }
246 }
247
248 static void
249 gst_uri_downloader_stop (GstUriDownloader * downloader)
250 {
251   GstPad *pad;
252   GST_DEBUG_OBJECT (downloader, "Stopping source element");
253
254   /* remove the bus' sync handler */
255   gst_bus_set_sync_handler (downloader->priv->bus, NULL, NULL);
256   /* unlink the source element from the internal pad */
257   pad = gst_pad_get_peer (downloader->priv->pad);
258   if (pad) {
259     gst_pad_unlink (pad, downloader->priv->pad);
260     gst_object_unref (pad);
261   }
262   /* set the element state to NULL */
263   if (downloader->priv->urisrc)
264   {
265     gst_element_set_state (downloader->priv->urisrc, GST_STATE_NULL);
266     gst_element_get_state (downloader->priv->urisrc, NULL, NULL,
267         GST_CLOCK_TIME_NONE);
268   }
269 }
270
271 void
272 gst_uri_downloader_cancel (GstUriDownloader * downloader)
273 {
274   g_mutex_lock (downloader->priv->lock);
275   if (downloader->priv->download != NULL) {
276     GST_DEBUG_OBJECT (downloader, "Cancelling download");
277     g_object_unref (downloader->priv->download);
278     downloader->priv->download = NULL;
279   } else {
280     GST_DEBUG_OBJECT (downloader,
281         "Trying to cancell a download that was alredy cancelled");
282   }
283
284   GST_DEBUG_OBJECT (downloader, "Signaling chain funtion");
285   downloader->priv->canceled = TRUE;
286   g_cond_signal (downloader->priv->cond);
287   g_mutex_unlock (downloader->priv->lock);
288 }
289
290 static gboolean
291 gst_uri_downloader_set_uri (GstUriDownloader * downloader, const gchar * uri)
292 {
293   GstPad *pad;
294
295   if (!gst_uri_is_valid (uri))
296     return FALSE;
297
298   if (downloader->priv->urisrc == NULL) {
299     GST_DEBUG_OBJECT (downloader, "Creating source element for the URI:%s", uri);
300     downloader->priv->urisrc = gst_element_make_from_uri (GST_URI_SRC, uri, NULL);
301     if (!downloader->priv->urisrc)
302       return FALSE;
303   } else {
304     GST_DEBUG_OBJECT (downloader, "Reusing existing source element for the URI:%s", uri);
305     if (!gst_uri_handler_set_uri (GST_URI_HANDLER (downloader->priv->urisrc), uri))
306       return FALSE;
307   }
308   g_object_set(downloader->priv->urisrc,"ahs-streaming", TRUE,NULL);
309
310   /* add a sync handler for the bus messages to detect errors in the download */
311   gst_element_set_bus (GST_ELEMENT (downloader->priv->urisrc),
312       downloader->priv->bus);
313   gst_bus_set_sync_handler (downloader->priv->bus,
314       gst_uri_downloader_bus_handler, downloader);
315
316   pad = gst_element_get_static_pad (downloader->priv->urisrc, "src");
317   if (!pad)
318     return FALSE;
319   gst_pad_link (pad, downloader->priv->pad);
320   gst_object_unref (pad);
321   return TRUE;
322 }
323
324 GstFragment *
325 gst_uri_downloader_fetch_uri (GstUriDownloader * downloader, const gchar * uri)
326 {
327   GstStateChangeReturn ret;
328   GstFragment *download = NULL;
329 /* add setting UA,cookies */
330   gchar **cookies;
331
332   g_mutex_lock (downloader->priv->lock);
333
334   if(downloader->priv->canceled) {
335     GST_DEBUG_OBJECT (downloader, "Uridownloader was canceled.");
336     goto quit;
337   }
338
339   if (!gst_uri_downloader_set_uri (downloader, uri)) {
340     goto quit;
341   }
342
343   /* add setting UA,cookies */
344   g_object_set (downloader->priv->urisrc, "cookies", downloader->priv->cookies, NULL);
345   g_object_set (downloader->priv->urisrc, "user-agent", downloader->priv->user_agent, NULL);
346
347   downloader->priv->download = gst_fragment_new ();
348
349   ret = gst_element_set_state (downloader->priv->urisrc, GST_STATE_PLAYING);
350   if (ret == GST_STATE_CHANGE_FAILURE) {
351     g_object_unref (downloader->priv->download);
352     downloader->priv->download = NULL;
353     goto quit;
354   }
355
356   /* wait until:
357    *   - the download succeed (EOS in the src pad)
358    *   - the download failed (Error message on the fetcher bus)
359    *   - the download was canceled
360    */
361   GST_DEBUG_OBJECT (downloader, "Waiting to fetch the URI");
362   g_cond_wait (downloader->priv->cond, downloader->priv->lock);
363
364 /* add setting UA,cookies */
365   g_object_get (downloader->priv->urisrc, "cookies", &cookies, NULL);
366   gst_uri_downloader_set_cookies (downloader, cookies);
367
368   download = downloader->priv->download;
369   downloader->priv->download = NULL;
370
371   if (download != NULL)
372     GST_INFO_OBJECT (downloader, "URI fetched successfully");
373   else
374     GST_INFO_OBJECT (downloader, "Error fetching URI");
375
376 quit:
377   {
378     g_mutex_unlock (downloader->priv->lock);
379     gst_uri_downloader_stop (downloader);
380     return download;
381   }
382 }
383
384 /* add setting UA,cookies */
385 void
386 gst_uri_downloader_set_user_agent (GstUriDownloader * downloader, gchar * user_agent)
387 {
388   if (downloader->priv->user_agent)
389     g_free (downloader->priv->user_agent);
390
391   downloader->priv->user_agent = user_agent;
392 }
393
394 void
395 gst_uri_downloader_set_cookies (GstUriDownloader * downloader, gchar ** cookies)
396 {
397   if (downloader->priv->cookies)
398     g_strfreev (downloader->priv->cookies);
399
400   downloader->priv->cookies = cookies;
401 }