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