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., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
23 #include "gstfragmented.h"
24 #include "gstfragment.h"
25 #include "gsturidownloader.h"
27 GST_DEBUG_CATEGORY_STATIC (uridownloader_debug);
28 #define GST_CAT_DEFAULT (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;
45 /* add setting UA,cookies */
50 static void gst_uri_downloader_finalize (GObject * object);
51 static void gst_uri_downloader_dispose (GObject * object);
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);
58 static GstStaticPadTemplate sinkpadtemplate = GST_STATIC_PAD_TEMPLATE ("sink",
65 GST_DEBUG_CATEGORY_INIT (uridownloader_debug, "uridownloader", 0, "URI downloader"); \
68 G_DEFINE_TYPE_WITH_CODE (GstUriDownloader, gst_uri_downloader, GST_TYPE_OBJECT,
72 gst_uri_downloader_class_init (GstUriDownloaderClass * klass)
74 GObjectClass *gobject_class;
76 gobject_class = (GObjectClass *) klass;
78 g_type_class_add_private (klass, sizeof (GstUriDownloaderPrivate));
80 gobject_class->dispose = gst_uri_downloader_dispose;
81 gobject_class->finalize = gst_uri_downloader_finalize;
85 gst_uri_downloader_init (GstUriDownloader * downloader)
87 downloader->priv = GST_URI_DOWNLOADER_GET_PRIVATE (downloader);
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);
100 /* Create a bus to handle error and warning message from the source element */
101 downloader->priv->bus = gst_bus_new ();
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;
112 gst_uri_downloader_reset(GstUriDownloader *downloader)
114 downloader->priv->canceled = FALSE;
118 gst_uri_downloader_dispose (GObject * object)
120 GstUriDownloader *downloader = GST_URI_DOWNLOADER (object);
122 if (downloader->priv->urisrc != NULL) {
123 gst_object_unref (downloader->priv->urisrc);
124 downloader->priv->urisrc = NULL;
127 if (downloader->priv->bus != NULL) {
128 gst_object_unref (downloader->priv->bus);
129 downloader->priv->bus = NULL;
132 if (downloader->priv->pad) {
133 gst_object_unref (downloader->priv->pad);
134 downloader->priv->pad = NULL;
137 if (downloader->priv->download) {
138 g_object_unref (downloader->priv->download);
139 downloader->priv->download = NULL;
142 /* add setting UA, cookies */
143 if (downloader->priv->user_agent) {
144 g_free (downloader->priv->user_agent);
145 downloader->priv->user_agent = NULL;
147 if (downloader->priv->cookies) {
148 g_strfreev (downloader->priv->cookies);
149 downloader->priv->cookies = NULL;
151 G_OBJECT_CLASS (gst_uri_downloader_parent_class)->dispose (object);
155 gst_uri_downloader_finalize (GObject * object)
157 GstUriDownloader *downloader = GST_URI_DOWNLOADER (object);
159 g_mutex_free (downloader->priv->lock);
160 g_cond_free (downloader->priv->cond);
162 G_OBJECT_CLASS (gst_uri_downloader_parent_class)->finalize (object);
166 gst_uri_downloader_new (void)
168 return g_object_new (GST_TYPE_URI_DOWNLOADER, NULL);
172 gst_uri_downloader_sink_event (GstPad * pad, GstEvent * event)
174 GstUriDownloader *downloader =
175 (GstUriDownloader *) (gst_pad_get_element_private (pad));
177 switch (event->type) {
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);
188 g_mutex_unlock (downloader->priv->lock);
195 gst_event_unref (event);
199 static GstBusSyncReply
200 gst_uri_downloader_bus_handler (GstBus * bus,
201 GstMessage * message, gpointer data)
203 GstUriDownloader *downloader = (GstUriDownloader *) (data);
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);
215 gst_message_unref (message);
220 gst_uri_downloader_chain (GstPad * pad, GstBuffer * buf)
222 GstUriDownloader *downloader =
223 (GstUriDownloader *) gst_pad_get_element_private (pad);
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.
229 g_mutex_lock (downloader->priv->lock);
230 if (downloader->priv->download == NULL) {
231 /* Download cancelled, quit */
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");
243 g_mutex_unlock (downloader->priv->lock);
249 gst_uri_downloader_stop (GstUriDownloader * downloader)
252 GST_DEBUG_OBJECT (downloader, "Stopping source element");
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);
259 gst_pad_unlink (pad, downloader->priv->pad);
260 gst_object_unref (pad);
262 /* set the element state to NULL */
263 if (downloader->priv->urisrc)
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);
272 gst_uri_downloader_cancel (GstUriDownloader * downloader)
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;
280 GST_DEBUG_OBJECT (downloader,
281 "Trying to cancell a download that was alredy cancelled");
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);
291 gst_uri_downloader_set_uri (GstUriDownloader * downloader, const gchar * uri)
295 if (!gst_uri_is_valid (uri))
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)
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))
308 g_object_set(downloader->priv->urisrc,"ahs-streaming", TRUE,NULL);
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);
316 pad = gst_element_get_static_pad (downloader->priv->urisrc, "src");
319 gst_pad_link (pad, downloader->priv->pad);
320 gst_object_unref (pad);
325 gst_uri_downloader_fetch_uri (GstUriDownloader * downloader, const gchar * uri)
327 GstStateChangeReturn ret;
328 GstFragment *download = NULL;
329 /* add setting UA,cookies */
332 g_mutex_lock (downloader->priv->lock);
334 if(downloader->priv->canceled) {
335 GST_DEBUG_OBJECT (downloader, "Uridownloader was canceled.");
339 if (!gst_uri_downloader_set_uri (downloader, uri)) {
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);
347 downloader->priv->download = gst_fragment_new ();
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;
357 * - the download succeed (EOS in the src pad)
358 * - the download failed (Error message on the fetcher bus)
359 * - the download was canceled
361 GST_DEBUG_OBJECT (downloader, "Waiting to fetch the URI");
362 g_cond_wait (downloader->priv->cond, downloader->priv->lock);
364 /* add setting UA,cookies */
365 g_object_get (downloader->priv->urisrc, "cookies", &cookies, NULL);
366 gst_uri_downloader_set_cookies (downloader, cookies);
368 download = downloader->priv->download;
369 downloader->priv->download = NULL;
371 if (download != NULL)
372 GST_INFO_OBJECT (downloader, "URI fetched successfully");
374 GST_INFO_OBJECT (downloader, "Error fetching URI");
378 g_mutex_unlock (downloader->priv->lock);
379 gst_uri_downloader_stop (downloader);
384 /* add setting UA,cookies */
386 gst_uri_downloader_set_user_agent (GstUriDownloader * downloader, gchar * user_agent)
388 if (downloader->priv->user_agent)
389 g_free (downloader->priv->user_agent);
391 downloader->priv->user_agent = user_agent;
395 gst_uri_downloader_set_cookies (GstUriDownloader * downloader, gchar ** cookies)
397 if (downloader->priv->cookies)
398 g_strfreev (downloader->priv->cookies);
400 downloader->priv->cookies = cookies;