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;
44 /* add setting UA,cookies */
49 static void gst_uri_downloader_finalize (GObject * object);
50 static void gst_uri_downloader_dispose (GObject * object);
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);
57 static GstStaticPadTemplate sinkpadtemplate = GST_STATIC_PAD_TEMPLATE ("sink",
64 GST_DEBUG_CATEGORY_INIT (uridownloader_debug, "uridownloader", 0, "URI downloader"); \
67 G_DEFINE_TYPE_WITH_CODE (GstUriDownloader, gst_uri_downloader, GST_TYPE_OBJECT,
71 gst_uri_downloader_class_init (GstUriDownloaderClass * klass)
73 GObjectClass *gobject_class;
75 gobject_class = (GObjectClass *) klass;
77 g_type_class_add_private (klass, sizeof (GstUriDownloaderPrivate));
79 gobject_class->dispose = gst_uri_downloader_dispose;
80 gobject_class->finalize = gst_uri_downloader_finalize;
84 gst_uri_downloader_init (GstUriDownloader * downloader)
86 downloader->priv = GST_URI_DOWNLOADER_GET_PRIVATE (downloader);
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);
99 /* Create a bus to handle error and warning message from the source element */
100 downloader->priv->bus = gst_bus_new ();
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;
110 gst_uri_downloader_dispose (GObject * object)
112 GstUriDownloader *downloader = GST_URI_DOWNLOADER (object);
114 if (downloader->priv->urisrc != NULL) {
115 gst_object_unref (downloader->priv->urisrc);
116 downloader->priv->urisrc = NULL;
119 if (downloader->priv->bus != NULL) {
120 gst_object_unref (downloader->priv->bus);
121 downloader->priv->bus = NULL;
124 if (downloader->priv->pad) {
125 gst_object_unref (downloader->priv->pad);
126 downloader->priv->pad = NULL;
129 if (downloader->priv->download) {
130 g_object_unref (downloader->priv->download);
131 downloader->priv->download = NULL;
134 /* add setting UA, cookies */
135 if (downloader->priv->user_agent) {
136 g_free (downloader->priv->user_agent);
137 downloader->priv->user_agent = NULL;
139 if (downloader->priv->cookies) {
140 g_strfreev (downloader->priv->cookies);
141 downloader->priv->cookies = NULL;
143 G_OBJECT_CLASS (gst_uri_downloader_parent_class)->dispose (object);
147 gst_uri_downloader_finalize (GObject * object)
149 GstUriDownloader *downloader = GST_URI_DOWNLOADER (object);
151 g_mutex_free (downloader->priv->lock);
152 g_cond_free (downloader->priv->cond);
154 G_OBJECT_CLASS (gst_uri_downloader_parent_class)->finalize (object);
158 gst_uri_downloader_new (void)
160 return g_object_new (GST_TYPE_URI_DOWNLOADER, NULL);
164 gst_uri_downloader_sink_event (GstPad * pad, GstEvent * event)
166 GstUriDownloader *downloader =
167 (GstUriDownloader *) (gst_pad_get_element_private (pad));
169 switch (event->type) {
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);
182 GST_OBJECT_UNLOCK (downloader);
190 gst_event_unref (event);
194 static GstBusSyncReply
195 gst_uri_downloader_bus_handler (GstBus * bus,
196 GstMessage * message, gpointer data)
198 GstUriDownloader *downloader = (GstUriDownloader *) (data);
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);
210 gst_message_unref (message);
215 gst_uri_downloader_chain (GstPad * pad, GstBuffer * buf)
217 GstUriDownloader *downloader =
218 (GstUriDownloader *) gst_pad_get_element_private (pad);
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.
224 GST_OBJECT_LOCK (downloader);
225 if (downloader->priv->download == NULL) {
226 /* Download cancelled, quit */
227 GST_OBJECT_UNLOCK (downloader);
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);
245 gst_uri_downloader_stop (GstUriDownloader * downloader)
249 GST_DEBUG_OBJECT (downloader, "Stopping source element");
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);
256 gst_pad_unlink (pad, downloader->priv->pad);
257 gst_object_unref (pad);
259 /* set the element state to NULL */
260 if (downloader->priv->urisrc)
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);
269 gst_uri_downloader_cancel (GstUriDownloader * downloader)
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);
280 GST_OBJECT_UNLOCK (downloader);
281 GST_DEBUG_OBJECT (downloader,
282 "Trying to cancell a download that was alredy cancelled");
287 gst_uri_downloader_set_uri (GstUriDownloader * downloader, const gchar * uri)
291 if (!gst_uri_is_valid (uri))
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)
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))
304 g_object_set(downloader->priv->urisrc,"ahs-streaming", TRUE,NULL);
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);
312 pad = gst_element_get_static_pad (downloader->priv->urisrc, "src");
315 gst_pad_link (pad, downloader->priv->pad);
316 gst_object_unref (pad);
321 gst_uri_downloader_fetch_uri (GstUriDownloader * downloader, const gchar * uri)
323 GstStateChangeReturn ret;
324 GstFragment *download = NULL;
325 /* add setting UA,cookies */
328 g_mutex_lock (downloader->priv->lock);
330 if (!gst_uri_downloader_set_uri (downloader, uri)) {
334 downloader->priv->download = gst_fragment_new ();
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;
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);
348 * - the download succeed (EOS in the src pad)
349 * - the download failed (Error message on the fetcher bus)
350 * - the download was canceled
352 GST_DEBUG_OBJECT (downloader, "Waiting to fetch the URI");
353 g_cond_wait (downloader->priv->cond, downloader->priv->lock);
355 /* add setting UA,cookies */
356 g_object_get (downloader->priv->urisrc, "cookies", &cookies, NULL);
357 gst_uri_downloader_set_cookies (downloader, cookies);
359 GST_OBJECT_LOCK (downloader);
360 download = downloader->priv->download;
361 downloader->priv->download = NULL;
362 GST_OBJECT_UNLOCK (downloader);
364 if (download != NULL)
365 GST_INFO_OBJECT (downloader, "URI fetched successfully");
367 GST_INFO_OBJECT (downloader, "Error fetching URI");
371 gst_uri_downloader_stop (downloader);
372 g_mutex_unlock (downloader->priv->lock);
377 /* add setting UA,cookies */
379 gst_uri_downloader_set_user_agent (GstUriDownloader * downloader, gchar * user_agent)
381 if (downloader->priv->user_agent)
382 g_free (downloader->priv->user_agent);
384 downloader->priv->user_agent = user_agent;
388 gst_uri_downloader_set_cookies (GstUriDownloader * downloader, gchar ** cookies)
390 if (downloader->priv->cookies)
391 g_strfreev (downloader->priv->cookies);
393 downloader->priv->cookies = cookies;