LCOV - code coverage report
Current view: top level - ext/soup - gstsouphttpsrc.c (source / functions) Hit Total Coverage
Test: GStreamer Good Plug-ins 0.10.28.1 Lines: 427 664 64.3 %
Date: 2011-03-25 Functions: 40 48 83.3 %
Branches: 174 498 34.9 %

           Branch data     Line data    Source code
       1                 :            : /* GStreamer
       2                 :            :  * Copyright (C) 2007-2008 Wouter Cloetens <wouter@mind.be>
       3                 :            :  *
       4                 :            :  * This library is free software; you can redistribute it and/or
       5                 :            :  * modify it under the terms of the GNU Library General Public
       6                 :            :  * License as published by the Free Software Foundation; either
       7                 :            :  * version 2 of the License, or (at your option) any later version.
       8                 :            :  *
       9                 :            :  * This library is distributed in the hope that it will be useful,
      10                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12                 :            :  * Library General Public License for more
      13                 :            :  */
      14                 :            : 
      15                 :            : /**
      16                 :            :  * SECTION:element-souphttpsrc
      17                 :            :  *
      18                 :            :  * This plugin reads data from a remote location specified by a URI.
      19                 :            :  * Supported protocols are 'http', 'https'.
      20                 :            :  * 
      21                 :            :  * An HTTP proxy must be specified by its URL.
      22                 :            :  * If the "http_proxy" environment variable is set, its value is used.
      23                 :            :  * If built with libsoup's GNOME integration features, the GNOME proxy
      24                 :            :  * configuration will be used, or failing that, proxy autodetection.
      25                 :            :  * The #GstSoupHTTPSrc:proxy property can be used to override the default.
      26                 :            :  *
      27                 :            :  * In case the #GstSoupHTTPSrc:iradio-mode property is set and the location is
      28                 :            :  * an HTTP resource, souphttpsrc will send special Icecast HTTP headers to the
      29                 :            :  * server to request additional Icecast meta-information.
      30                 :            :  * If the server is not an Icecast server, it will behave as if the
      31                 :            :  * #GstSoupHTTPSrc:iradio-mode property were not set. If it is, souphttpsrc will
      32                 :            :  * output data with a media type of application/x-icy, in which case you will
      33                 :            :  * need to use the #ICYDemux element as follow-up element to extract the Icecast
      34                 :            :  * metadata and to determine the underlying media type.
      35                 :            :  *
      36                 :            :  * <refsect2>
      37                 :            :  * <title>Example launch line</title>
      38                 :            :  * |[
      39                 :            :  * gst-launch -v souphttpsrc location=https://some.server.org/index.html
      40                 :            :  *     ! filesink location=/home/joe/server.html
      41                 :            :  * ]| The above pipeline reads a web page from a server using the HTTPS protocol
      42                 :            :  * and writes it to a local file.
      43                 :            :  * |[
      44                 :            :  * gst-launch -v souphttpsrc user-agent="FooPlayer 0.99 beta"
      45                 :            :  *     automatic-redirect=false proxy=http://proxy.intranet.local:8080
      46                 :            :  *     location=http://music.foobar.com/demo.mp3 ! mad ! audioconvert
      47                 :            :  *     ! audioresample ! alsasink
      48                 :            :  * ]| The above pipeline will read and decode and play an mp3 file from a
      49                 :            :  * web server using the HTTP protocol. If the server sends redirects,
      50                 :            :  * the request fails instead of following the redirect. The specified
      51                 :            :  * HTTP proxy server is used. The User-Agent HTTP request header
      52                 :            :  * is set to a custom string instead of "GStreamer souphttpsrc."
      53                 :            :  * |[
      54                 :            :  * gst-launch -v souphttpsrc location=http://10.11.12.13/mjpeg
      55                 :            :  *     do-timestamp=true ! multipartdemux
      56                 :            :  *     ! image/jpeg,width=640,height=480 ! matroskamux
      57                 :            :  *     ! filesink location=mjpeg.mkv
      58                 :            :  * ]| The above pipeline reads a motion JPEG stream from an IP camera
      59                 :            :  * using the HTTP protocol, encoded as mime/multipart image/jpeg
      60                 :            :  * parts, and writes a Matroska motion JPEG file. The width and
      61                 :            :  * height properties are set in the caps to provide the Matroska
      62                 :            :  * multiplexer with the information to set this in the header.
      63                 :            :  * Timestamps are set on the buffers as they arrive from the camera.
      64                 :            :  * These are used by the mime/multipart demultiplexer to emit timestamps
      65                 :            :  * on the JPEG-encoded video frame buffers. This allows the Matroska
      66                 :            :  * multiplexer to timestamp the frames in the resulting file.
      67                 :            :  * </refsect2>
      68                 :            :  */
      69                 :            : 
      70                 :            : #ifdef HAVE_CONFIG_H
      71                 :            : #include "config.h"
      72                 :            : #endif
      73                 :            : 
      74                 :            : #include <string.h>
      75                 :            : #ifdef HAVE_STDLIB_H
      76                 :            : #include <stdlib.h>             /* atoi() */
      77                 :            : #endif
      78                 :            : #include <gst/gstelement.h>
      79                 :            : #include <gst/gst-i18n-plugin.h>
      80                 :            : #ifdef HAVE_LIBSOUP_GNOME
      81                 :            : #include <libsoup/soup-gnome.h>
      82                 :            : #else
      83                 :            : #include <libsoup/soup.h>
      84                 :            : #endif
      85                 :            : #include "gstsouphttpsrc.h"
      86                 :            : 
      87                 :            : #include <gst/tag/tag.h>
      88                 :            : 
      89                 :            : GST_DEBUG_CATEGORY_STATIC (souphttpsrc_debug);
      90                 :            : #define GST_CAT_DEFAULT souphttpsrc_debug
      91                 :            : 
      92                 :            : static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
      93                 :            :     GST_PAD_SRC,
      94                 :            :     GST_PAD_ALWAYS,
      95                 :            :     GST_STATIC_CAPS_ANY);
      96                 :            : 
      97                 :            : enum
      98                 :            : {
      99                 :            :   PROP_0,
     100                 :            :   PROP_LOCATION,
     101                 :            :   PROP_IS_LIVE,
     102                 :            :   PROP_USER_AGENT,
     103                 :            :   PROP_AUTOMATIC_REDIRECT,
     104                 :            :   PROP_PROXY,
     105                 :            :   PROP_USER_ID,
     106                 :            :   PROP_USER_PW,
     107                 :            :   PROP_PROXY_ID,
     108                 :            :   PROP_PROXY_PW,
     109                 :            :   PROP_COOKIES,
     110                 :            :   PROP_IRADIO_MODE,
     111                 :            :   PROP_IRADIO_NAME,
     112                 :            :   PROP_IRADIO_GENRE,
     113                 :            :   PROP_IRADIO_URL,
     114                 :            :   PROP_IRADIO_TITLE,
     115                 :            :   PROP_TIMEOUT,
     116                 :            :   PROP_EXTRA_HEADERS
     117                 :            : };
     118                 :            : 
     119                 :            : #define DEFAULT_USER_AGENT           "GStreamer souphttpsrc "
     120                 :            : 
     121                 :            : static void gst_soup_http_src_uri_handler_init (gpointer g_iface,
     122                 :            :     gpointer iface_data);
     123                 :            : static void gst_soup_http_src_finalize (GObject * gobject);
     124                 :            : 
     125                 :            : static void gst_soup_http_src_set_property (GObject * object, guint prop_id,
     126                 :            :     const GValue * value, GParamSpec * pspec);
     127                 :            : static void gst_soup_http_src_get_property (GObject * object, guint prop_id,
     128                 :            :     GValue * value, GParamSpec * pspec);
     129                 :            : 
     130                 :            : static GstFlowReturn gst_soup_http_src_create (GstPushSrc * psrc,
     131                 :            :     GstBuffer ** outbuf);
     132                 :            : static gboolean gst_soup_http_src_start (GstBaseSrc * bsrc);
     133                 :            : static gboolean gst_soup_http_src_stop (GstBaseSrc * bsrc);
     134                 :            : static gboolean gst_soup_http_src_get_size (GstBaseSrc * bsrc, guint64 * size);
     135                 :            : static gboolean gst_soup_http_src_is_seekable (GstBaseSrc * bsrc);
     136                 :            : static gboolean gst_soup_http_src_do_seek (GstBaseSrc * bsrc,
     137                 :            :     GstSegment * segment);
     138                 :            : static gboolean gst_soup_http_src_query (GstBaseSrc * bsrc, GstQuery * query);
     139                 :            : static gboolean gst_soup_http_src_unlock (GstBaseSrc * bsrc);
     140                 :            : static gboolean gst_soup_http_src_unlock_stop (GstBaseSrc * bsrc);
     141                 :            : static gboolean gst_soup_http_src_set_location (GstSoupHTTPSrc * src,
     142                 :            :     const gchar * uri);
     143                 :            : static gboolean gst_soup_http_src_set_proxy (GstSoupHTTPSrc * src,
     144                 :            :     const gchar * uri);
     145                 :            : static char *gst_soup_http_src_unicodify (const char *str);
     146                 :            : static gboolean gst_soup_http_src_build_message (GstSoupHTTPSrc * src);
     147                 :            : static void gst_soup_http_src_cancel_message (GstSoupHTTPSrc * src);
     148                 :            : static void gst_soup_http_src_queue_message (GstSoupHTTPSrc * src);
     149                 :            : static gboolean gst_soup_http_src_add_range_header (GstSoupHTTPSrc * src,
     150                 :            :     guint64 offset);
     151                 :            : static void gst_soup_http_src_session_unpause_message (GstSoupHTTPSrc * src);
     152                 :            : static void gst_soup_http_src_session_pause_message (GstSoupHTTPSrc * src);
     153                 :            : static void gst_soup_http_src_session_close (GstSoupHTTPSrc * src);
     154                 :            : static void gst_soup_http_src_parse_status (SoupMessage * msg,
     155                 :            :     GstSoupHTTPSrc * src);
     156                 :            : static void gst_soup_http_src_chunk_free (gpointer gstbuf);
     157                 :            : static SoupBuffer *gst_soup_http_src_chunk_allocator (SoupMessage * msg,
     158                 :            :     gsize max_len, gpointer user_data);
     159                 :            : static void gst_soup_http_src_got_chunk_cb (SoupMessage * msg,
     160                 :            :     SoupBuffer * chunk, GstSoupHTTPSrc * src);
     161                 :            : static void gst_soup_http_src_response_cb (SoupSession * session,
     162                 :            :     SoupMessage * msg, GstSoupHTTPSrc * src);
     163                 :            : static void gst_soup_http_src_got_headers_cb (SoupMessage * msg,
     164                 :            :     GstSoupHTTPSrc * src);
     165                 :            : static void gst_soup_http_src_got_body_cb (SoupMessage * msg,
     166                 :            :     GstSoupHTTPSrc * src);
     167                 :            : static void gst_soup_http_src_finished_cb (SoupMessage * msg,
     168                 :            :     GstSoupHTTPSrc * src);
     169                 :            : static void gst_soup_http_src_authenticate_cb (SoupSession * session,
     170                 :            :     SoupMessage * msg, SoupAuth * auth, gboolean retrying,
     171                 :            :     GstSoupHTTPSrc * src);
     172                 :            : 
     173                 :            : static void
     174                 :         18 : _do_init (GType type)
     175                 :            : {
     176                 :            :   static const GInterfaceInfo urihandler_info = {
     177                 :            :     gst_soup_http_src_uri_handler_init,
     178                 :            :     NULL,
     179                 :            :     NULL
     180                 :            :   };
     181                 :            : 
     182                 :         18 :   g_type_add_interface_static (type, GST_TYPE_URI_HANDLER, &urihandler_info);
     183                 :            : 
     184         [ -  + ]:         18 :   GST_DEBUG_CATEGORY_INIT (souphttpsrc_debug, "souphttpsrc", 0,
     185                 :            :       "SOUP HTTP src");
     186                 :         18 : }
     187                 :            : 
     188         [ +  + ]:        259 : GST_BOILERPLATE_FULL (GstSoupHTTPSrc, gst_soup_http_src, GstPushSrc,
     189                 :        259 :     GST_TYPE_PUSH_SRC, _do_init);
     190                 :            : 
     191                 :            : static void
     192                 :         18 : gst_soup_http_src_base_init (gpointer g_class)
     193                 :            : {
     194                 :         18 :   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
     195                 :            : 
     196                 :         18 :   gst_element_class_add_pad_template (element_class,
     197                 :            :       gst_static_pad_template_get (&srctemplate));
     198                 :            : 
     199                 :         18 :   gst_element_class_set_details_simple (element_class, "HTTP client source",
     200                 :            :       "Source/Network",
     201                 :            :       "Receive data as a client over the network via HTTP using SOUP",
     202                 :            :       "Wouter Cloetens <wouter@mind.be>");
     203                 :         18 : }
     204                 :            : 
     205                 :            : static void
     206                 :         18 : gst_soup_http_src_class_init (GstSoupHTTPSrcClass * klass)
     207                 :            : {
     208                 :            :   GObjectClass *gobject_class;
     209                 :            :   GstBaseSrcClass *gstbasesrc_class;
     210                 :            :   GstPushSrcClass *gstpushsrc_class;
     211                 :            : 
     212                 :         18 :   gobject_class = (GObjectClass *) klass;
     213                 :         18 :   gstbasesrc_class = (GstBaseSrcClass *) klass;
     214                 :         18 :   gstpushsrc_class = (GstPushSrcClass *) klass;
     215                 :            : 
     216                 :         18 :   gobject_class->set_property = gst_soup_http_src_set_property;
     217                 :         18 :   gobject_class->get_property = gst_soup_http_src_get_property;
     218                 :         18 :   gobject_class->finalize = gst_soup_http_src_finalize;
     219                 :            : 
     220                 :         18 :   g_object_class_install_property (gobject_class,
     221                 :            :       PROP_LOCATION,
     222                 :            :       g_param_spec_string ("location", "Location",
     223                 :            :           "Location to read from", "",
     224                 :            :           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     225                 :         18 :   g_object_class_install_property (gobject_class,
     226                 :            :       PROP_USER_AGENT,
     227                 :            :       g_param_spec_string ("user-agent", "User-Agent",
     228                 :            :           "Value of the User-Agent HTTP request header field",
     229                 :            :           DEFAULT_USER_AGENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     230                 :         18 :   g_object_class_install_property (gobject_class,
     231                 :            :       PROP_AUTOMATIC_REDIRECT,
     232                 :            :       g_param_spec_boolean ("automatic-redirect", "automatic-redirect",
     233                 :            :           "Automatically follow HTTP redirects (HTTP Status Code 3xx)",
     234                 :            :           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     235                 :         18 :   g_object_class_install_property (gobject_class,
     236                 :            :       PROP_PROXY,
     237                 :            :       g_param_spec_string ("proxy", "Proxy",
     238                 :            :           "HTTP proxy server URI", "",
     239                 :            :           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     240                 :         18 :   g_object_class_install_property (gobject_class,
     241                 :            :       PROP_USER_ID,
     242                 :            :       g_param_spec_string ("user-id", "user-id",
     243                 :            :           "HTTP location URI user id for authentication", "",
     244                 :            :           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     245                 :         18 :   g_object_class_install_property (gobject_class, PROP_USER_PW,
     246                 :            :       g_param_spec_string ("user-pw", "user-pw",
     247                 :            :           "HTTP location URI user password for authentication", "",
     248                 :            :           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     249                 :         18 :   g_object_class_install_property (gobject_class, PROP_PROXY_ID,
     250                 :            :       g_param_spec_string ("proxy-id", "proxy-id",
     251                 :            :           "HTTP proxy URI user id for authentication", "",
     252                 :            :           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     253                 :         18 :   g_object_class_install_property (gobject_class, PROP_PROXY_PW,
     254                 :            :       g_param_spec_string ("proxy-pw", "proxy-pw",
     255                 :            :           "HTTP proxy URI user password for authentication", "",
     256                 :            :           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     257                 :         18 :   g_object_class_install_property (gobject_class, PROP_COOKIES,
     258                 :            :       g_param_spec_boxed ("cookies", "Cookies", "HTTP request cookies",
     259                 :            :           G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     260                 :         18 :   g_object_class_install_property (gobject_class, PROP_IS_LIVE,
     261                 :            :       g_param_spec_boolean ("is-live", "is-live", "Act like a live source",
     262                 :            :           FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     263                 :         18 :   g_object_class_install_property (gobject_class, PROP_TIMEOUT,
     264                 :            :       g_param_spec_uint ("timeout", "timeout",
     265                 :            :           "Value in seconds to timeout a blocking I/O (0 = No timeout).", 0,
     266                 :            :           3600, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     267                 :         18 :   g_object_class_install_property (gobject_class, PROP_EXTRA_HEADERS,
     268                 :            :       g_param_spec_boxed ("extra-headers", "Extra Headers",
     269                 :            :           "Extra headers to append to the HTTP request",
     270                 :            :           GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     271                 :            : 
     272                 :            :   /* icecast stuff */
     273                 :         18 :   g_object_class_install_property (gobject_class,
     274                 :            :       PROP_IRADIO_MODE,
     275                 :            :       g_param_spec_boolean ("iradio-mode",
     276                 :            :           "iradio-mode",
     277                 :            :           "Enable internet radio mode (extraction of shoutcast/icecast metadata)",
     278                 :            :           FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     279                 :         18 :   g_object_class_install_property (gobject_class,
     280                 :            :       PROP_IRADIO_NAME,
     281                 :            :       g_param_spec_string ("iradio-name",
     282                 :            :           "iradio-name", "Name of the stream", NULL,
     283                 :            :           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
     284                 :         18 :   g_object_class_install_property (gobject_class,
     285                 :            :       PROP_IRADIO_GENRE,
     286                 :            :       g_param_spec_string ("iradio-genre",
     287                 :            :           "iradio-genre", "Genre of the stream", NULL,
     288                 :            :           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
     289                 :         18 :   g_object_class_install_property (gobject_class,
     290                 :            :       PROP_IRADIO_URL,
     291                 :            :       g_param_spec_string ("iradio-url",
     292                 :            :           "iradio-url",
     293                 :            :           "Homepage URL for radio stream", NULL,
     294                 :            :           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
     295                 :         18 :   g_object_class_install_property (gobject_class,
     296                 :            :       PROP_IRADIO_TITLE,
     297                 :            :       g_param_spec_string ("iradio-title",
     298                 :            :           "iradio-title",
     299                 :            :           "Name of currently playing song", NULL,
     300                 :            :           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
     301                 :            : 
     302                 :         18 :   gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_soup_http_src_start);
     303                 :         18 :   gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_soup_http_src_stop);
     304                 :         18 :   gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_soup_http_src_unlock);
     305                 :         18 :   gstbasesrc_class->unlock_stop =
     306                 :         18 :       GST_DEBUG_FUNCPTR (gst_soup_http_src_unlock_stop);
     307                 :         18 :   gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_soup_http_src_get_size);
     308                 :         18 :   gstbasesrc_class->is_seekable =
     309                 :         18 :       GST_DEBUG_FUNCPTR (gst_soup_http_src_is_seekable);
     310                 :         18 :   gstbasesrc_class->do_seek = GST_DEBUG_FUNCPTR (gst_soup_http_src_do_seek);
     311                 :         18 :   gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_soup_http_src_query);
     312                 :            : 
     313                 :         18 :   gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_soup_http_src_create);
     314                 :         18 : }
     315                 :            : 
     316                 :            : static void
     317                 :         32 : gst_soup_http_src_reset (GstSoupHTTPSrc * src)
     318                 :            : {
     319                 :         32 :   src->interrupted = FALSE;
     320                 :         32 :   src->retry = FALSE;
     321                 :         32 :   src->have_size = FALSE;
     322                 :         32 :   src->seekable = FALSE;
     323                 :         32 :   src->read_position = 0;
     324                 :         32 :   src->request_position = 0;
     325                 :         32 :   src->content_size = 0;
     326                 :            : 
     327                 :         32 :   gst_caps_replace (&src->src_caps, NULL);
     328                 :         32 :   g_free (src->iradio_name);
     329                 :         32 :   src->iradio_name = NULL;
     330                 :         32 :   g_free (src->iradio_genre);
     331                 :         32 :   src->iradio_genre = NULL;
     332                 :         32 :   g_free (src->iradio_url);
     333                 :         32 :   src->iradio_url = NULL;
     334                 :         32 :   g_free (src->iradio_title);
     335                 :         32 :   src->iradio_title = NULL;
     336                 :         32 : }
     337                 :            : 
     338                 :            : static void
     339                 :         17 : gst_soup_http_src_init (GstSoupHTTPSrc * src, GstSoupHTTPSrcClass * g_class)
     340                 :            : {
     341                 :            :   const gchar *proxy;
     342                 :            : 
     343                 :         17 :   src->location = NULL;
     344                 :         17 :   src->automatic_redirect = TRUE;
     345                 :         17 :   src->user_agent = g_strdup (DEFAULT_USER_AGENT);
     346                 :         17 :   src->user_id = NULL;
     347                 :         17 :   src->user_pw = NULL;
     348                 :         17 :   src->proxy_id = NULL;
     349                 :         17 :   src->proxy_pw = NULL;
     350                 :         17 :   src->cookies = NULL;
     351                 :         17 :   src->iradio_mode = FALSE;
     352                 :         17 :   src->loop = NULL;
     353                 :         17 :   src->context = NULL;
     354                 :         17 :   src->session = NULL;
     355                 :         17 :   src->msg = NULL;
     356                 :         17 :   proxy = g_getenv ("http_proxy");
     357 [ -  + ][ #  # ]:         17 :   if (proxy && !gst_soup_http_src_set_proxy (src, proxy)) {
     358         [ #  # ]:          0 :     GST_WARNING_OBJECT (src,
     359                 :            :         "The proxy in the http_proxy env var (\"%s\") cannot be parsed.",
     360                 :            :         proxy);
     361                 :            :   }
     362                 :            : 
     363                 :         17 :   gst_soup_http_src_reset (src);
     364                 :         17 : }
     365                 :            : 
     366                 :            : static void
     367                 :         14 : gst_soup_http_src_finalize (GObject * gobject)
     368                 :            : {
     369                 :         14 :   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (gobject);
     370                 :            : 
     371         [ -  + ]:         14 :   GST_DEBUG_OBJECT (src, "finalize");
     372                 :            : 
     373                 :         14 :   g_free (src->location);
     374                 :         14 :   g_free (src->user_agent);
     375         [ -  + ]:         14 :   if (src->proxy != NULL) {
     376                 :          0 :     soup_uri_free (src->proxy);
     377                 :            :   }
     378                 :         14 :   g_free (src->user_id);
     379                 :         14 :   g_free (src->user_pw);
     380                 :         14 :   g_free (src->proxy_id);
     381                 :         14 :   g_free (src->proxy_pw);
     382                 :         14 :   g_strfreev (src->cookies);
     383                 :            : 
     384                 :         14 :   G_OBJECT_CLASS (parent_class)->finalize (gobject);
     385                 :         14 : }
     386                 :            : 
     387                 :            : static void
     388                 :         42 : gst_soup_http_src_set_property (GObject * object, guint prop_id,
     389                 :            :     const GValue * value, GParamSpec * pspec)
     390                 :            : {
     391                 :         42 :   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (object);
     392                 :            : 
     393   [ +  -  +  +  :         42 :   switch (prop_id) {
          -  +  -  +  +  
             -  -  -  -  
                      - ]
     394                 :            :     case PROP_LOCATION:
     395                 :            :     {
     396                 :            :       const gchar *location;
     397                 :            : 
     398                 :         15 :       location = g_value_get_string (value);
     399                 :            : 
     400         [ -  + ]:         15 :       if (location == NULL) {
     401         [ #  # ]:          0 :         GST_WARNING ("location property cannot be NULL");
     402                 :          0 :         goto done;
     403                 :            :       }
     404         [ -  + ]:         15 :       if (!gst_soup_http_src_set_location (src, location)) {
     405         [ #  # ]:          0 :         GST_WARNING ("badly formatted location");
     406                 :          0 :         goto done;
     407                 :            :       }
     408                 :         15 :       break;
     409                 :            :     }
     410                 :            :     case PROP_USER_AGENT:
     411         [ #  # ]:          0 :       if (src->user_agent)
     412                 :          0 :         g_free (src->user_agent);
     413                 :          0 :       src->user_agent = g_value_dup_string (value);
     414                 :          0 :       break;
     415                 :            :     case PROP_IRADIO_MODE:
     416                 :          1 :       src->iradio_mode = g_value_get_boolean (value);
     417                 :          1 :       break;
     418                 :            :     case PROP_AUTOMATIC_REDIRECT:
     419                 :         13 :       src->automatic_redirect = g_value_get_boolean (value);
     420                 :         13 :       break;
     421                 :            :     case PROP_PROXY:
     422                 :            :     {
     423                 :            :       const gchar *proxy;
     424                 :            : 
     425                 :          0 :       proxy = g_value_get_string (value);
     426                 :            : 
     427         [ #  # ]:          0 :       if (proxy == NULL) {
     428         [ #  # ]:          0 :         GST_WARNING ("proxy property cannot be NULL");
     429                 :          0 :         goto done;
     430                 :            :       }
     431         [ #  # ]:          0 :       if (!gst_soup_http_src_set_proxy (src, proxy)) {
     432         [ #  # ]:          0 :         GST_WARNING ("badly formatted proxy URI");
     433                 :          0 :         goto done;
     434                 :            :       }
     435                 :          0 :       break;
     436                 :            :     }
     437                 :            :     case PROP_COOKIES:
     438                 :          1 :       g_strfreev (src->cookies);
     439                 :          1 :       src->cookies = g_strdupv (g_value_get_boxed (value));
     440                 :          1 :       break;
     441                 :            :     case PROP_IS_LIVE:
     442                 :          0 :       gst_base_src_set_live (GST_BASE_SRC (src), g_value_get_boolean (value));
     443                 :          0 :       break;
     444                 :            :     case PROP_USER_ID:
     445         [ -  + ]:          6 :       if (src->user_id)
     446                 :          0 :         g_free (src->user_id);
     447                 :          6 :       src->user_id = g_value_dup_string (value);
     448                 :          6 :       break;
     449                 :            :     case PROP_USER_PW:
     450         [ -  + ]:          6 :       if (src->user_pw)
     451                 :          0 :         g_free (src->user_pw);
     452                 :          6 :       src->user_pw = g_value_dup_string (value);
     453                 :          6 :       break;
     454                 :            :     case PROP_PROXY_ID:
     455         [ #  # ]:          0 :       if (src->proxy_id)
     456                 :          0 :         g_free (src->proxy_id);
     457                 :          0 :       src->proxy_id = g_value_dup_string (value);
     458                 :          0 :       break;
     459                 :            :     case PROP_PROXY_PW:
     460         [ #  # ]:          0 :       if (src->proxy_pw)
     461                 :          0 :         g_free (src->proxy_pw);
     462                 :          0 :       src->proxy_pw = g_value_dup_string (value);
     463                 :          0 :       break;
     464                 :            :     case PROP_TIMEOUT:
     465                 :          0 :       src->timeout = g_value_get_uint (value);
     466                 :          0 :       break;
     467                 :            :     case PROP_EXTRA_HEADERS:{
     468                 :          0 :       const GstStructure *s = gst_value_get_structure (value);
     469                 :            : 
     470         [ #  # ]:          0 :       if (src->extra_headers)
     471                 :          0 :         gst_structure_free (src->extra_headers);
     472                 :            : 
     473         [ #  # ]:          0 :       src->extra_headers = s ? gst_structure_copy (s) : NULL;
     474                 :          0 :       break;
     475                 :            :     }
     476                 :            :     default:
     477                 :          0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     478                 :          0 :       break;
     479                 :            :   }
     480                 :            : done:
     481                 :         42 :   return;
     482                 :            : }
     483                 :            : 
     484                 :            : static void
     485                 :          0 : gst_soup_http_src_get_property (GObject * object, guint prop_id,
     486                 :            :     GValue * value, GParamSpec * pspec)
     487                 :            : {
     488                 :          0 :   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (object);
     489                 :            : 
     490   [ #  #  #  #  :          0 :   switch (prop_id) {
          #  #  #  #  #  
          #  #  #  #  #  
             #  #  #  # ]
     491                 :            :     case PROP_LOCATION:
     492                 :          0 :       g_value_set_string (value, src->location);
     493                 :          0 :       break;
     494                 :            :     case PROP_USER_AGENT:
     495                 :          0 :       g_value_set_string (value, src->user_agent);
     496                 :          0 :       break;
     497                 :            :     case PROP_AUTOMATIC_REDIRECT:
     498                 :          0 :       g_value_set_boolean (value, src->automatic_redirect);
     499                 :          0 :       break;
     500                 :            :     case PROP_PROXY:
     501         [ #  # ]:          0 :       if (src->proxy == NULL)
     502                 :          0 :         g_value_set_static_string (value, "");
     503                 :            :       else {
     504                 :          0 :         char *proxy = soup_uri_to_string (src->proxy, FALSE);
     505                 :            : 
     506                 :          0 :         g_value_set_string (value, proxy);
     507                 :          0 :         g_free (proxy);
     508                 :            :       }
     509                 :          0 :       break;
     510                 :            :     case PROP_COOKIES:
     511                 :          0 :       g_value_set_boxed (value, g_strdupv (src->cookies));
     512                 :          0 :       break;
     513                 :            :     case PROP_IS_LIVE:
     514                 :          0 :       g_value_set_boolean (value, gst_base_src_is_live (GST_BASE_SRC (src)));
     515                 :          0 :       break;
     516                 :            :     case PROP_IRADIO_MODE:
     517                 :          0 :       g_value_set_boolean (value, src->iradio_mode);
     518                 :          0 :       break;
     519                 :            :     case PROP_IRADIO_NAME:
     520                 :          0 :       g_value_set_string (value, src->iradio_name);
     521                 :          0 :       break;
     522                 :            :     case PROP_IRADIO_GENRE:
     523                 :          0 :       g_value_set_string (value, src->iradio_genre);
     524                 :          0 :       break;
     525                 :            :     case PROP_IRADIO_URL:
     526                 :          0 :       g_value_set_string (value, src->iradio_url);
     527                 :          0 :       break;
     528                 :            :     case PROP_IRADIO_TITLE:
     529                 :          0 :       g_value_set_string (value, src->iradio_title);
     530                 :          0 :       break;
     531                 :            :     case PROP_USER_ID:
     532                 :          0 :       g_value_set_string (value, src->user_id);
     533                 :          0 :       break;
     534                 :            :     case PROP_USER_PW:
     535                 :          0 :       g_value_set_string (value, src->user_pw);
     536                 :          0 :       break;
     537                 :            :     case PROP_PROXY_ID:
     538                 :          0 :       g_value_set_string (value, src->proxy_id);
     539                 :          0 :       break;
     540                 :            :     case PROP_PROXY_PW:
     541                 :          0 :       g_value_set_string (value, src->proxy_pw);
     542                 :          0 :       break;
     543                 :            :     case PROP_TIMEOUT:
     544                 :          0 :       g_value_set_uint (value, src->timeout);
     545                 :          0 :       break;
     546                 :            :     case PROP_EXTRA_HEADERS:
     547                 :          0 :       gst_value_set_structure (value, src->extra_headers);
     548                 :          0 :       break;
     549                 :            :     default:
     550                 :          0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     551                 :          0 :       break;
     552                 :            :   }
     553                 :          0 : }
     554                 :            : 
     555                 :            : static gchar *
     556                 :          6 : gst_soup_http_src_unicodify (const gchar * str)
     557                 :            : {
     558                 :          6 :   const gchar *env_vars[] = { "GST_ICY_TAG_ENCODING",
     559                 :            :     "GST_TAG_ENCODING", NULL
     560                 :            :   };
     561                 :            : 
     562                 :          6 :   return gst_tag_freeform_string_to_utf8 (str, -1, env_vars);
     563                 :            : }
     564                 :            : 
     565                 :            : static void
     566                 :          0 : gst_soup_http_src_cancel_message (GstSoupHTTPSrc * src)
     567                 :            : {
     568         [ #  # ]:          0 :   if (src->msg != NULL) {
     569                 :          0 :     src->session_io_status = GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_CANCELLED;
     570                 :          0 :     soup_session_cancel_message (src->session, src->msg, SOUP_STATUS_CANCELLED);
     571                 :            :   }
     572                 :          0 :   src->session_io_status = GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_IDLE;
     573                 :          0 :   src->msg = NULL;
     574                 :          0 : }
     575                 :            : 
     576                 :            : static void
     577                 :         15 : gst_soup_http_src_queue_message (GstSoupHTTPSrc * src)
     578                 :            : {
     579                 :         15 :   soup_session_queue_message (src->session, src->msg,
     580                 :            :       (SoupSessionCallback) gst_soup_http_src_response_cb, src);
     581                 :         15 :   src->session_io_status = GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_QUEUED;
     582                 :         15 : }
     583                 :            : 
     584                 :            : static gboolean
     585                 :         15 : gst_soup_http_src_add_range_header (GstSoupHTTPSrc * src, guint64 offset)
     586                 :            : {
     587                 :            :   gchar buf[64];
     588                 :            : 
     589                 :            :   gint rc;
     590                 :            : 
     591                 :         15 :   soup_message_headers_remove (src->msg->request_headers, "Range");
     592         [ -  + ]:         15 :   if (offset) {
     593                 :          0 :     rc = g_snprintf (buf, sizeof (buf), "bytes=%" G_GUINT64_FORMAT "-", offset);
     594 [ #  # ][ #  # ]:          0 :     if (rc > sizeof (buf) || rc < 0)
     595                 :          0 :       return FALSE;
     596                 :          0 :     soup_message_headers_append (src->msg->request_headers, "Range", buf);
     597                 :            :   }
     598                 :         15 :   src->read_position = offset;
     599                 :         15 :   return TRUE;
     600                 :            : }
     601                 :            : 
     602                 :            : static gboolean
     603                 :          0 : _append_extra_header (GQuark field_id, const GValue * value, gpointer user_data)
     604                 :            : {
     605                 :          0 :   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (user_data);
     606                 :          0 :   const gchar *field_name = g_quark_to_string (field_id);
     607                 :          0 :   gchar *field_content = NULL;
     608                 :            : 
     609         [ #  # ]:          0 :   if (G_VALUE_TYPE (value) == G_TYPE_STRING) {
     610                 :          0 :     field_content = g_value_dup_string (value);
     611                 :            :   } else {
     612                 :          0 :     GValue dest = { 0, };
     613                 :            : 
     614                 :          0 :     g_value_init (&dest, G_TYPE_STRING);
     615         [ #  # ]:          0 :     if (g_value_transform (value, &dest)) {
     616                 :          0 :       field_content = g_value_dup_string (&dest);
     617                 :            :     }
     618                 :            :   }
     619                 :            : 
     620         [ #  # ]:          0 :   if (field_content == NULL) {
     621         [ #  # ]:          0 :     GST_ERROR_OBJECT (src, "extra-headers field '%s' contains no value "
     622                 :            :         "or can't be converted to a string", field_name);
     623                 :          0 :     return FALSE;
     624                 :            :   }
     625                 :            : 
     626         [ #  # ]:          0 :   GST_DEBUG_OBJECT (src, "Appending extra header: \"%s: %s\"", field_name,
     627                 :            :       field_content);
     628                 :          0 :   soup_message_headers_append (src->msg->request_headers, field_name,
     629                 :            :       field_content);
     630                 :            : 
     631                 :          0 :   g_free (field_content);
     632                 :            : 
     633                 :          0 :   return TRUE;
     634                 :            : }
     635                 :            : 
     636                 :            : static gboolean
     637                 :          0 : _append_extra_headers (GQuark field_id, const GValue * value,
     638                 :            :     gpointer user_data)
     639                 :            : {
     640         [ #  # ]:          0 :   if (G_VALUE_TYPE (value) == GST_TYPE_ARRAY) {
     641                 :          0 :     guint n = gst_value_array_get_size (value);
     642                 :            :     guint i;
     643                 :            : 
     644         [ #  # ]:          0 :     for (i = 0; i < n; i++) {
     645                 :          0 :       const GValue *v = gst_value_array_get_value (value, i);
     646                 :            : 
     647         [ #  # ]:          0 :       if (!_append_extra_header (field_id, v, user_data))
     648                 :          0 :         return FALSE;
     649                 :            :     }
     650         [ #  # ]:          0 :   } else if (G_VALUE_TYPE (value) == GST_TYPE_LIST) {
     651                 :          0 :     guint n = gst_value_list_get_size (value);
     652                 :            :     guint i;
     653                 :            : 
     654         [ #  # ]:          0 :     for (i = 0; i < n; i++) {
     655                 :          0 :       const GValue *v = gst_value_list_get_value (value, i);
     656                 :            : 
     657         [ #  # ]:          0 :       if (!_append_extra_header (field_id, v, user_data))
     658                 :          0 :         return FALSE;
     659                 :            :     }
     660                 :            :   } else {
     661                 :          0 :     return _append_extra_header (field_id, value, user_data);
     662                 :            :   }
     663                 :            : 
     664                 :          0 :   return TRUE;
     665                 :            : }
     666                 :            : 
     667                 :            : 
     668                 :            : static gboolean
     669                 :         15 : gst_soup_http_src_add_extra_headers (GstSoupHTTPSrc * src)
     670                 :            : {
     671         [ +  - ]:         15 :   if (!src->extra_headers)
     672                 :         15 :     return TRUE;
     673                 :            : 
     674                 :         15 :   return gst_structure_foreach (src->extra_headers, _append_extra_headers, src);
     675                 :            : }
     676                 :            : 
     677                 :            : 
     678                 :            : static void
     679                 :          0 : gst_soup_http_src_session_unpause_message (GstSoupHTTPSrc * src)
     680                 :            : {
     681                 :          0 :   soup_session_unpause_message (src->session, src->msg);
     682                 :          0 : }
     683                 :            : 
     684                 :            : static void
     685                 :         11 : gst_soup_http_src_session_pause_message (GstSoupHTTPSrc * src)
     686                 :            : {
     687                 :         11 :   soup_session_pause_message (src->session, src->msg);
     688                 :         11 : }
     689                 :            : 
     690                 :            : static void
     691                 :         15 : gst_soup_http_src_session_close (GstSoupHTTPSrc * src)
     692                 :            : {
     693         [ +  - ]:         15 :   if (src->session) {
     694                 :         15 :     soup_session_abort (src->session);  /* This unrefs the message. */
     695                 :         15 :     g_object_unref (src->session);
     696                 :         15 :     src->session = NULL;
     697                 :         15 :     src->msg = NULL;
     698                 :            :   }
     699                 :         15 : }
     700                 :            : 
     701                 :            : static void
     702                 :         10 : gst_soup_http_src_authenticate_cb (SoupSession * session, SoupMessage * msg,
     703                 :            :     SoupAuth * auth, gboolean retrying, GstSoupHTTPSrc * src)
     704                 :            : {
     705         [ +  + ]:         10 :   if (!retrying) {
     706                 :            :     /* First time authentication only, if we fail and are called again with retry true fall through */
     707         [ +  - ]:          6 :     if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) {
     708 [ +  - ][ +  - ]:          6 :       if (src->user_id && src->user_pw)
     709                 :          6 :         soup_auth_authenticate (auth, src->user_id, src->user_pw);
     710         [ #  # ]:          0 :     } else if (msg->status_code == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
     711 [ #  # ][ #  # ]:          0 :       if (src->proxy_id && src->proxy_pw)
     712                 :          0 :         soup_auth_authenticate (auth, src->proxy_id, src->proxy_pw);
     713                 :            :     }
     714                 :            :   }
     715                 :         10 : }
     716                 :            : 
     717                 :            : static void
     718                 :        106 : gst_soup_http_src_headers_foreach (const gchar * name, const gchar * val,
     719                 :            :     gpointer src)
     720                 :            : {
     721         [ -  + ]:        106 :   GST_DEBUG_OBJECT (src, " %s: %s", name, val);
     722                 :        106 : }
     723                 :            : 
     724                 :            : static void
     725                 :         22 : gst_soup_http_src_got_headers_cb (SoupMessage * msg, GstSoupHTTPSrc * src)
     726                 :            : {
     727                 :            :   const char *value;
     728                 :            :   GstTagList *tag_list;
     729                 :            :   GstBaseSrc *basesrc;
     730                 :            :   guint64 newsize;
     731                 :         22 :   GHashTable *params = NULL;
     732                 :            : 
     733         [ -  + ]:         22 :   GST_DEBUG_OBJECT (src, "got headers:");
     734                 :         22 :   soup_message_headers_foreach (msg->response_headers,
     735                 :            :       gst_soup_http_src_headers_foreach, src);
     736                 :            : 
     737 [ +  + ][ +  + ]:         22 :   if (src->automatic_redirect && SOUP_STATUS_IS_REDIRECTION (msg->status_code)) {
                 [ +  + ]
     738         [ -  + ]:          1 :     GST_DEBUG_OBJECT (src, "%u redirect to \"%s\"", msg->status_code,
     739                 :            :         soup_message_headers_get (msg->response_headers, "Location"));
     740                 :          1 :     return;
     741                 :            :   }
     742                 :            : 
     743         [ +  + ]:         21 :   if (msg->status_code == SOUP_STATUS_UNAUTHORIZED)
     744                 :         10 :     return;
     745                 :            : 
     746                 :         11 :   src->session_io_status = GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING;
     747                 :            : 
     748                 :            :   /* Parse Content-Length. */
     749         [ +  + ]:         11 :   if (soup_message_headers_get_encoding (msg->response_headers) ==
     750                 :            :       SOUP_ENCODING_CONTENT_LENGTH) {
     751                 :         18 :     newsize = src->request_position +
     752                 :          9 :         soup_message_headers_get_content_length (msg->response_headers);
     753 [ -  + ][ #  # ]:          9 :     if (!src->have_size || (src->content_size != newsize)) {
     754                 :          9 :       src->content_size = newsize;
     755                 :          9 :       src->have_size = TRUE;
     756                 :          9 :       src->seekable = TRUE;
     757         [ -  + ]:          9 :       GST_DEBUG_OBJECT (src, "size = %" G_GUINT64_FORMAT, src->content_size);
     758                 :            : 
     759                 :          9 :       basesrc = GST_BASE_SRC_CAST (src);
     760                 :          9 :       gst_segment_set_duration (&basesrc->segment, GST_FORMAT_BYTES,
     761                 :          9 :           src->content_size);
     762                 :          9 :       gst_element_post_message (GST_ELEMENT (src),
     763                 :          9 :           gst_message_new_duration (GST_OBJECT (src), GST_FORMAT_BYTES,
     764                 :          9 :               src->content_size));
     765                 :            :     }
     766                 :            :   }
     767                 :            : 
     768                 :            :   /* Icecast stuff */
     769                 :         11 :   tag_list = gst_tag_list_new ();
     770                 :            : 
     771         [ +  + ]:         11 :   if ((value =
     772                 :         11 :           soup_message_headers_get (msg->response_headers,
     773                 :            :               "icy-metaint")) != NULL) {
     774                 :          1 :     gint icy_metaint = atoi (value);
     775                 :            : 
     776         [ -  + ]:          1 :     GST_DEBUG_OBJECT (src, "icy-metaint: %s (parsed: %d)", value, icy_metaint);
     777         [ +  - ]:          1 :     if (icy_metaint > 0) {
     778         [ -  + ]:          1 :       if (src->src_caps)
     779                 :          0 :         gst_caps_unref (src->src_caps);
     780                 :            : 
     781                 :          1 :       src->src_caps = gst_caps_new_simple ("application/x-icy",
     782                 :            :           "metadata-interval", G_TYPE_INT, icy_metaint, NULL);
     783                 :            :     }
     784                 :            :   }
     785         [ +  + ]:         11 :   if ((value =
     786                 :         11 :           soup_message_headers_get_content_type (msg->response_headers,
     787                 :            :               &params)) != NULL) {
     788         [ -  + ]:          2 :     GST_DEBUG_OBJECT (src, "Content-Type: %s", value);
     789         [ -  + ]:          2 :     if (g_ascii_strcasecmp (value, "audio/L16") == 0) {
     790                 :          0 :       gint channels = 2;
     791                 :          0 :       gint rate = 44100;
     792                 :            :       char *param;
     793                 :            : 
     794         [ #  # ]:          0 :       if (src->src_caps)
     795                 :          0 :         gst_caps_unref (src->src_caps);
     796                 :            : 
     797                 :          0 :       param = g_hash_table_lookup (params, "channels");
     798         [ #  # ]:          0 :       if (param != NULL)
     799                 :          0 :         channels = atol (param);
     800                 :            : 
     801                 :          0 :       param = g_hash_table_lookup (params, "rate");
     802         [ #  # ]:          0 :       if (param != NULL)
     803                 :          0 :         rate = atol (param);
     804                 :            : 
     805                 :          0 :       src->src_caps = gst_caps_new_simple ("audio/x-raw-int",
     806                 :            :           "channels", G_TYPE_INT, channels,
     807                 :            :           "rate", G_TYPE_INT, rate,
     808                 :            :           "width", G_TYPE_INT, 16,
     809                 :            :           "depth", G_TYPE_INT, 16,
     810                 :            :           "signed", G_TYPE_BOOLEAN, TRUE,
     811                 :            :           "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
     812                 :            :     } else {
     813                 :            :       /* Set the Content-Type field on the caps */
     814         [ +  + ]:          2 :       if (src->src_caps)
     815                 :          1 :         gst_caps_set_simple (src->src_caps, "content-type", G_TYPE_STRING,
     816                 :            :             value, NULL);
     817                 :            :     }
     818                 :            :   }
     819                 :            : 
     820         [ -  + ]:         11 :   if (params != NULL)
     821                 :          0 :     g_hash_table_destroy (params);
     822                 :            : 
     823         [ +  + ]:         11 :   if ((value =
     824                 :         11 :           soup_message_headers_get (msg->response_headers,
     825                 :            :               "icy-name")) != NULL) {
     826                 :          2 :     g_free (src->iradio_name);
     827                 :          2 :     src->iradio_name = gst_soup_http_src_unicodify (value);
     828         [ +  - ]:          2 :     if (src->iradio_name) {
     829                 :          2 :       g_object_notify (G_OBJECT (src), "iradio-name");
     830                 :          2 :       gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE, GST_TAG_ORGANIZATION,
     831                 :            :           src->iradio_name, NULL);
     832                 :            :     }
     833                 :            :   }
     834         [ +  + ]:         11 :   if ((value =
     835                 :         11 :           soup_message_headers_get (msg->response_headers,
     836                 :            :               "icy-genre")) != NULL) {
     837                 :          2 :     g_free (src->iradio_genre);
     838                 :          2 :     src->iradio_genre = gst_soup_http_src_unicodify (value);
     839         [ +  - ]:          2 :     if (src->iradio_genre) {
     840                 :          2 :       g_object_notify (G_OBJECT (src), "iradio-genre");
     841                 :          2 :       gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE, GST_TAG_GENRE,
     842                 :            :           src->iradio_genre, NULL);
     843                 :            :     }
     844                 :            :   }
     845         [ +  + ]:         11 :   if ((value = soup_message_headers_get (msg->response_headers, "icy-url"))
     846                 :            :       != NULL) {
     847                 :          2 :     g_free (src->iradio_url);
     848                 :          2 :     src->iradio_url = gst_soup_http_src_unicodify (value);
     849         [ +  - ]:          2 :     if (src->iradio_url) {
     850                 :          2 :       g_object_notify (G_OBJECT (src), "iradio-url");
     851                 :          2 :       gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE, GST_TAG_LOCATION,
     852                 :            :           src->iradio_url, NULL);
     853                 :            :     }
     854                 :            :   }
     855         [ +  + ]:         11 :   if (!gst_tag_list_is_empty (tag_list)) {
     856         [ -  + ]:          2 :     GST_DEBUG_OBJECT (src,
     857                 :            :         "calling gst_element_found_tags with %" GST_PTR_FORMAT, tag_list);
     858                 :          2 :     gst_element_found_tags (GST_ELEMENT_CAST (src), tag_list);
     859                 :            :   } else {
     860                 :          9 :     gst_tag_list_free (tag_list);
     861                 :            :   }
     862                 :            : 
     863                 :            :   /* Handle HTTP errors. */
     864                 :         11 :   gst_soup_http_src_parse_status (msg, src);
     865                 :            : 
     866                 :            :   /* Check if Range header was respected. */
     867 [ +  + ][ -  + ]:         11 :   if (src->ret == GST_FLOW_CUSTOM_ERROR &&
     868         [ #  # ]:          0 :       src->read_position && msg->status_code != SOUP_STATUS_PARTIAL_CONTENT) {
     869                 :          0 :     src->seekable = FALSE;
     870 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (src, RESOURCE, SEEK,
         [ #  # ][ #  # ]
     871                 :            :         (_("Server does not support seeking.")),
     872                 :            :         ("Server does not accept Range HTTP header, URL: %s", src->location));
     873                 :         22 :     src->ret = GST_FLOW_ERROR;
     874                 :            :   }
     875                 :            : }
     876                 :            : 
     877                 :            : /* Have body. Signal EOS. */
     878                 :            : static void
     879                 :         14 : gst_soup_http_src_got_body_cb (SoupMessage * msg, GstSoupHTTPSrc * src)
     880                 :            : {
     881         [ -  + ]:         14 :   if (G_UNLIKELY (msg != src->msg)) {
     882         [ #  # ]:          0 :     GST_DEBUG_OBJECT (src, "got body, but not for current message");
     883                 :          0 :     return;
     884                 :            :   }
     885         [ +  + ]:         14 :   if (G_UNLIKELY (src->session_io_status !=
     886                 :            :           GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING)) {
     887                 :            :     /* Probably a redirect. */
     888                 :         11 :     return;
     889                 :            :   }
     890         [ -  + ]:          3 :   GST_DEBUG_OBJECT (src, "got body");
     891                 :          3 :   src->ret = GST_FLOW_UNEXPECTED;
     892         [ +  - ]:          3 :   if (src->loop)
     893                 :          3 :     g_main_loop_quit (src->loop);
     894                 :         14 :   gst_soup_http_src_session_pause_message (src);
     895                 :            : }
     896                 :            : 
     897                 :            : /* Finished. Signal EOS. */
     898                 :            : static void
     899                 :         15 : gst_soup_http_src_finished_cb (SoupMessage * msg, GstSoupHTTPSrc * src)
     900                 :            : {
     901         [ -  + ]:         15 :   if (G_UNLIKELY (msg != src->msg)) {
     902         [ #  # ]:          0 :     GST_DEBUG_OBJECT (src, "finished, but not for current message");
     903                 :         15 :     return;
     904                 :            :   }
     905         [ -  + ]:         15 :   GST_DEBUG_OBJECT (src, "finished");
     906                 :         15 :   src->ret = GST_FLOW_UNEXPECTED;
     907         [ +  - ]:         15 :   if (src->session_io_status == GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_CANCELLED) {
     908                 :            :     /* gst_soup_http_src_cancel_message() triggered this; probably a seek
     909                 :            :      * that occurred in the QUEUEING state; i.e. before the connection setup
     910                 :            :      * was complete. Do nothing */
     911         [ +  + ]:         15 :   } else if (src->session_io_status ==
     912         [ +  + ]:         11 :       GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING && src->read_position > 0) {
     913                 :            :     /* The server disconnected while streaming. Reconnect and seeking to the
     914                 :            :      * last location. */
     915                 :          8 :     src->retry = TRUE;
     916                 :          8 :     src->ret = GST_FLOW_CUSTOM_ERROR;
     917         [ +  + ]:          7 :   } else if (G_UNLIKELY (src->session_io_status !=
     918                 :            :           GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING)) {
     919                 :            :     /* FIXME: reason_phrase is not translated, add proper error message */
     920 [ +  - ][ -  + ]:          4 :     GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND,
         [ +  - ][ -  + ]
     921                 :            :         ("%s", msg->reason_phrase),
     922                 :            :         ("libsoup status code %d", msg->status_code));
     923                 :            :   }
     924         [ +  - ]:         15 :   if (src->loop)
     925                 :         15 :     g_main_loop_quit (src->loop);
     926                 :            : }
     927                 :            : 
     928                 :            : /* Buffer lifecycle management.
     929                 :            :  *
     930                 :            :  * gst_soup_http_src_create() runs the GMainLoop for this element, to let
     931                 :            :  * Soup take control.
     932                 :            :  * A GstBuffer is allocated in gst_soup_http_src_chunk_allocator() and
     933                 :            :  * associated with a SoupBuffer.
     934                 :            :  * Soup reads HTTP data in the GstBuffer's data buffer.
     935                 :            :  * The gst_soup_http_src_got_chunk_cb() is then called with the SoupBuffer.
     936                 :            :  * That sets gst_soup_http_src_create()'s return argument to the GstBuffer,
     937                 :            :  * increments its refcount (to 2), pauses the flow of data from the HTTP
     938                 :            :  * source to prevent gst_soup_http_src_got_chunk_cb() from being called
     939                 :            :  * again and breaks out of the GMainLoop.
     940                 :            :  * Because the SOUP_MESSAGE_OVERWRITE_CHUNKS flag is set, Soup frees the
     941                 :            :  * SoupBuffer and calls gst_soup_http_src_chunk_free(), which decrements the
     942                 :            :  * refcount (to 1).
     943                 :            :  * gst_soup_http_src_create() returns the GstBuffer. It will be freed by a
     944                 :            :  * downstream element.
     945                 :            :  * If Soup fails to read HTTP data, it does not call
     946                 :            :  * gst_soup_http_src_got_chunk_cb(), but still frees the SoupBuffer and
     947                 :            :  * calls gst_soup_http_src_chunk_free(), which decrements the GstBuffer's
     948                 :            :  * refcount to 0, freeing it.
     949                 :            :  */
     950                 :            : 
     951                 :            : static void
     952                 :         10 : gst_soup_http_src_chunk_free (gpointer gstbuf)
     953                 :            : {
     954                 :         10 :   gst_buffer_unref (GST_BUFFER_CAST (gstbuf));
     955                 :         10 : }
     956                 :            : 
     957                 :            : static SoupBuffer *
     958                 :         10 : gst_soup_http_src_chunk_allocator (SoupMessage * msg, gsize max_len,
     959                 :            :     gpointer user_data)
     960                 :            : {
     961                 :         10 :   GstSoupHTTPSrc *src = (GstSoupHTTPSrc *) user_data;
     962                 :         10 :   GstBaseSrc *basesrc = GST_BASE_SRC_CAST (src);
     963                 :            :   GstBuffer *gstbuf;
     964                 :            :   SoupBuffer *soupbuf;
     965                 :            :   gsize length;
     966                 :            :   GstFlowReturn rc;
     967                 :            : 
     968         [ +  + ]:         10 :   if (max_len)
     969                 :          7 :     length = MIN (basesrc->blocksize, max_len);
     970                 :            :   else
     971                 :          3 :     length = basesrc->blocksize;
     972         [ -  + ]:         10 :   GST_DEBUG_OBJECT (src, "alloc %" G_GSIZE_FORMAT " bytes <= %" G_GSIZE_FORMAT,
     973                 :            :       length, max_len);
     974                 :            : 
     975                 :            : 
     976         [ +  + ]:         10 :   rc = gst_pad_alloc_buffer (GST_BASE_SRC_PAD (basesrc),
     977                 :            :       GST_BUFFER_OFFSET_NONE, length,
     978                 :         10 :       src->src_caps ? src->src_caps :
     979                 :          9 :       GST_PAD_CAPS (GST_BASE_SRC_PAD (basesrc)), &gstbuf);
     980         [ -  + ]:         10 :   if (G_UNLIKELY (rc != GST_FLOW_OK)) {
     981                 :            :     /* Failed to allocate buffer. Stall SoupSession and return error code
     982                 :            :      * to create(). */
     983                 :          0 :     src->ret = rc;
     984                 :          0 :     g_main_loop_quit (src->loop);
     985                 :          0 :     return NULL;
     986                 :            :   }
     987                 :            : 
     988                 :         10 :   soupbuf = soup_buffer_new_with_owner (GST_BUFFER_DATA (gstbuf), length,
     989                 :            :       gstbuf, gst_soup_http_src_chunk_free);
     990                 :            : 
     991                 :         10 :   return soupbuf;
     992                 :            : }
     993                 :            : 
     994                 :            : static void
     995                 :          8 : gst_soup_http_src_got_chunk_cb (SoupMessage * msg, SoupBuffer * chunk,
     996                 :            :     GstSoupHTTPSrc * src)
     997                 :            : {
     998                 :            :   GstBaseSrc *basesrc;
     999                 :            :   guint64 new_position;
    1000                 :            : 
    1001         [ -  + ]:          8 :   if (G_UNLIKELY (msg != src->msg)) {
    1002         [ #  # ]:          0 :     GST_DEBUG_OBJECT (src, "got chunk, but not for current message");
    1003                 :          0 :     return;
    1004                 :            :   }
    1005         [ -  + ]:          8 :   if (G_UNLIKELY (src->session_io_status !=
    1006                 :            :           GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING)) {
    1007                 :            :     /* Probably a redirect. */
    1008                 :          0 :     return;
    1009                 :            :   }
    1010                 :          8 :   basesrc = GST_BASE_SRC_CAST (src);
    1011         [ -  + ]:          8 :   GST_DEBUG_OBJECT (src, "got chunk of %" G_GSIZE_FORMAT " bytes",
    1012                 :            :       chunk->length);
    1013                 :            : 
    1014                 :            :   /* Extract the GstBuffer from the SoupBuffer and set its fields. */
    1015                 :          8 :   *src->outbuf = GST_BUFFER_CAST (soup_buffer_get_owner (chunk));
    1016                 :            : 
    1017                 :          8 :   GST_BUFFER_SIZE (*src->outbuf) = chunk->length;
    1018                 :          8 :   GST_BUFFER_OFFSET (*src->outbuf) = basesrc->segment.last_stop;
    1019                 :            : 
    1020         [ +  + ]:          8 :   gst_buffer_set_caps (*src->outbuf,
    1021                 :          8 :       (src->src_caps) ? src->src_caps :
    1022                 :          7 :       GST_PAD_CAPS (GST_BASE_SRC_PAD (basesrc)));
    1023                 :            : 
    1024                 :          8 :   gst_buffer_ref (*src->outbuf);
    1025                 :            : 
    1026                 :          8 :   new_position = src->read_position + chunk->length;
    1027         [ +  - ]:          8 :   if (G_LIKELY (src->request_position == src->read_position))
    1028                 :          8 :     src->request_position = new_position;
    1029                 :          8 :   src->read_position = new_position;
    1030                 :            : 
    1031                 :          8 :   src->ret = GST_FLOW_OK;
    1032                 :          8 :   g_main_loop_quit (src->loop);
    1033                 :          8 :   gst_soup_http_src_session_pause_message (src);
    1034                 :            : }
    1035                 :            : 
    1036                 :            : static void
    1037                 :         15 : gst_soup_http_src_response_cb (SoupSession * session, SoupMessage * msg,
    1038                 :            :     GstSoupHTTPSrc * src)
    1039                 :            : {
    1040         [ -  + ]:         15 :   if (G_UNLIKELY (msg != src->msg)) {
    1041         [ #  # ]:          0 :     GST_DEBUG_OBJECT (src, "got response %d: %s, but not for current message",
    1042                 :            :         msg->status_code, msg->reason_phrase);
    1043                 :          0 :     return;
    1044                 :            :   }
    1045         [ +  + ]:         15 :   if (G_UNLIKELY (src->session_io_status !=
    1046                 :            :           GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING)
    1047 [ +  - ][ -  + ]:          4 :       && SOUP_STATUS_IS_REDIRECTION (msg->status_code)) {
    1048                 :            :     /* Ignore redirections. */
    1049                 :          0 :     return;
    1050                 :            :   }
    1051         [ -  + ]:         15 :   GST_DEBUG_OBJECT (src, "got response %d: %s", msg->status_code,
    1052                 :            :       msg->reason_phrase);
    1053 [ +  + ][ +  + ]:         15 :   if (src->session_io_status == GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING &&
    1054                 :         11 :       src->read_position > 0) {
    1055                 :            :     /* The server disconnected while streaming. Reconnect and seeking to the
    1056                 :            :      * last location. */
    1057                 :          8 :     src->retry = TRUE;
    1058                 :            :   } else
    1059                 :          7 :     gst_soup_http_src_parse_status (msg, src);
    1060                 :            :   /* The session's SoupMessage object expires after this callback returns. */
    1061                 :         15 :   src->msg = NULL;
    1062                 :         15 :   g_main_loop_quit (src->loop);
    1063                 :            : }
    1064                 :            : 
    1065                 :            : #define SOUP_HTTP_SRC_ERROR(src,soup_msg,cat,code,error_message)     \
    1066                 :            :   GST_ELEMENT_ERROR ((src), cat, code, ("%s", error_message),        \
    1067                 :            :       ("%s (%d), URL: %s", (soup_msg)->reason_phrase,                \
    1068                 :            :           (soup_msg)->status_code, (src)->location));
    1069                 :            : 
    1070                 :            : static void
    1071                 :         18 : gst_soup_http_src_parse_status (SoupMessage * msg, GstSoupHTTPSrc * src)
    1072                 :            : {
    1073 [ +  - ][ +  + ]:         18 :   if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) {
    1074   [ -  -  -  -  :          3 :     switch (msg->status_code) {
                -  +  - ]
    1075                 :            :       case SOUP_STATUS_CANT_RESOLVE:
    1076                 :            :       case SOUP_STATUS_CANT_RESOLVE_PROXY:
    1077 [ #  # ][ #  # ]:          0 :         SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, NOT_FOUND,
         [ #  # ][ #  # ]
    1078                 :            :             _("Could not resolve server name."));
    1079                 :          0 :         src->ret = GST_FLOW_ERROR;
    1080                 :          0 :         break;
    1081                 :            :       case SOUP_STATUS_CANT_CONNECT:
    1082                 :            :       case SOUP_STATUS_CANT_CONNECT_PROXY:
    1083 [ #  # ][ #  # ]:          0 :         SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, OPEN_READ,
         [ #  # ][ #  # ]
    1084                 :            :             _("Could not establish connection to server."));
    1085                 :          0 :         src->ret = GST_FLOW_ERROR;
    1086                 :          0 :         break;
    1087                 :            :       case SOUP_STATUS_SSL_FAILED:
    1088 [ #  # ][ #  # ]:          0 :         SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, OPEN_READ,
         [ #  # ][ #  # ]
    1089                 :            :             _("Secure connection setup failed."));
    1090                 :          0 :         src->ret = GST_FLOW_ERROR;
    1091                 :          0 :         break;
    1092                 :            :       case SOUP_STATUS_IO_ERROR:
    1093 [ #  # ][ #  # ]:          0 :         SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, READ,
         [ #  # ][ #  # ]
    1094                 :            :             _("A network error occured, or the server closed the connection "
    1095                 :            :                 "unexpectedly."));
    1096                 :          0 :         src->ret = GST_FLOW_ERROR;
    1097                 :          0 :         break;
    1098                 :            :       case SOUP_STATUS_MALFORMED:
    1099 [ #  # ][ #  # ]:          0 :         SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, READ,
         [ #  # ][ #  # ]
    1100                 :            :             _("Server sent bad data."));
    1101                 :          0 :         src->ret = GST_FLOW_ERROR;
    1102                 :          0 :         break;
    1103                 :            :       case SOUP_STATUS_CANCELLED:
    1104                 :            :         /* No error message when interrupted by program. */
    1105                 :          3 :         break;
    1106                 :            :     }
    1107 [ +  + ][ -  + ]:         18 :   } else if (SOUP_STATUS_IS_CLIENT_ERROR (msg->status_code) ||
                 [ +  + ]
    1108 [ -  + ][ -  + ]:          9 :       SOUP_STATUS_IS_REDIRECTION (msg->status_code) ||
    1109         [ #  # ]:          0 :       SOUP_STATUS_IS_SERVER_ERROR (msg->status_code)) {
    1110                 :            :     /* Report HTTP error. */
    1111                 :            :     /* FIXME: reason_phrase is not translated and not suitable for user
    1112                 :            :      * error dialog according to libsoup documentation.
    1113                 :            :      * FIXME: error code (OPEN_READ vs. READ) should depend on http status? */
    1114 [ +  - ][ -  + ]:          7 :     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
         [ +  - ][ -  + ]
    1115                 :            :         ("%s", msg->reason_phrase),
    1116                 :            :         ("%s (%d), URL: %s", msg->reason_phrase, msg->status_code,
    1117                 :            :             src->location));
    1118                 :          7 :     src->ret = GST_FLOW_ERROR;
    1119                 :            :   }
    1120                 :         18 : }
    1121                 :            : 
    1122                 :            : static gboolean
    1123                 :         15 : gst_soup_http_src_build_message (GstSoupHTTPSrc * src)
    1124                 :            : {
    1125         [ +  + ]:         15 :   src->msg = soup_message_new (SOUP_METHOD_GET, src->location);
    1126         [ -  + ]:         15 :   if (!src->msg) {
    1127 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
         [ #  # ][ #  # ]
    1128                 :            :         ("Error parsing URL."), ("URL: %s", src->location));
    1129                 :          0 :     return FALSE;
    1130                 :            :   }
    1131                 :         15 :   src->session_io_status = GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_IDLE;
    1132                 :         15 :   soup_message_headers_append (src->msg->request_headers, "Connection",
    1133                 :            :       "close");
    1134         [ +  + ]:         15 :   if (src->iradio_mode) {
    1135                 :          2 :     soup_message_headers_append (src->msg->request_headers, "icy-metadata",
    1136                 :            :         "1");
    1137                 :            :   }
    1138         [ +  + ]:         15 :   if (src->cookies) {
    1139                 :            :     gchar **cookie;
    1140                 :            : 
    1141         [ +  + ]:          3 :     for (cookie = src->cookies; *cookie != NULL; cookie++) {
    1142                 :          2 :       soup_message_headers_append (src->msg->request_headers, "Cookie",
    1143                 :            :           *cookie);
    1144                 :            :     }
    1145                 :            :   }
    1146                 :         15 :   soup_message_headers_append (src->msg->request_headers,
    1147                 :            :       "transferMode.dlna.org", "Streaming");
    1148                 :         15 :   src->retry = FALSE;
    1149                 :            : 
    1150                 :         15 :   g_signal_connect (src->msg, "got_headers",
    1151                 :            :       G_CALLBACK (gst_soup_http_src_got_headers_cb), src);
    1152                 :         15 :   g_signal_connect (src->msg, "got_body",
    1153                 :            :       G_CALLBACK (gst_soup_http_src_got_body_cb), src);
    1154                 :         15 :   g_signal_connect (src->msg, "finished",
    1155                 :            :       G_CALLBACK (gst_soup_http_src_finished_cb), src);
    1156                 :         15 :   g_signal_connect (src->msg, "got_chunk",
    1157                 :            :       G_CALLBACK (gst_soup_http_src_got_chunk_cb), src);
    1158         [ +  + ]:         15 :   soup_message_set_flags (src->msg, SOUP_MESSAGE_OVERWRITE_CHUNKS |
    1159                 :         15 :       (src->automatic_redirect ? 0 : SOUP_MESSAGE_NO_REDIRECT));
    1160                 :         15 :   soup_message_set_chunk_allocator (src->msg,
    1161                 :            :       gst_soup_http_src_chunk_allocator, src, NULL);
    1162                 :         15 :   gst_soup_http_src_add_range_header (src, src->request_position);
    1163                 :            : 
    1164                 :         15 :   gst_soup_http_src_add_extra_headers (src);
    1165                 :            : 
    1166         [ -  + ]:         15 :   GST_DEBUG_OBJECT (src, "request headers:");
    1167                 :         15 :   soup_message_headers_foreach (src->msg->request_headers,
    1168                 :            :       gst_soup_http_src_headers_foreach, src);
    1169                 :            : 
    1170                 :         15 :   return TRUE;
    1171                 :            : }
    1172                 :            : 
    1173                 :            : static GstFlowReturn
    1174                 :         15 : gst_soup_http_src_create (GstPushSrc * psrc, GstBuffer ** outbuf)
    1175                 :            : {
    1176                 :            :   GstSoupHTTPSrc *src;
    1177                 :            : 
    1178                 :         15 :   src = GST_SOUP_HTTP_SRC (psrc);
    1179                 :            : 
    1180 [ -  + ][ #  # ]:         15 :   if (src->msg && (src->request_position != src->read_position)) {
    1181 [ #  # ][ #  # ]:          0 :     if (src->content_size != 0 && src->request_position >= src->content_size) {
    1182         [ #  # ]:          0 :       GST_WARNING_OBJECT (src, "Seeking behind the end of file -- EOS");
    1183                 :          0 :       return GST_FLOW_UNEXPECTED;
    1184         [ #  # ]:          0 :     } else if (src->session_io_status ==
    1185                 :            :         GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_IDLE) {
    1186                 :          0 :       gst_soup_http_src_add_range_header (src, src->request_position);
    1187                 :            :     } else {
    1188         [ #  # ]:          0 :       GST_DEBUG_OBJECT (src, "Seek from position %" G_GUINT64_FORMAT
    1189                 :            :           " to %" G_GUINT64_FORMAT ": requeueing connection request",
    1190                 :            :           src->read_position, src->request_position);
    1191                 :          0 :       gst_soup_http_src_cancel_message (src);
    1192                 :            :     }
    1193                 :            :   }
    1194         [ +  - ]:         15 :   if (!src->msg)
    1195         [ -  + ]:         15 :     if (!gst_soup_http_src_build_message (src))
    1196                 :          0 :       return GST_FLOW_ERROR;
    1197                 :            : 
    1198                 :         15 :   src->ret = GST_FLOW_CUSTOM_ERROR;
    1199                 :         15 :   src->outbuf = outbuf;
    1200                 :            :   do {
    1201         [ -  + ]:         15 :     if (src->interrupted) {
    1202         [ #  # ]:          0 :       GST_DEBUG_OBJECT (src, "interrupted");
    1203                 :          0 :       break;
    1204                 :            :     }
    1205         [ -  + ]:         15 :     if (src->retry) {
    1206         [ #  # ]:          0 :       GST_DEBUG_OBJECT (src, "Reconnecting");
    1207         [ #  # ]:          0 :       if (!gst_soup_http_src_build_message (src))
    1208                 :          0 :         return GST_FLOW_ERROR;
    1209                 :          0 :       src->retry = FALSE;
    1210                 :          0 :       continue;
    1211                 :            :     }
    1212         [ -  + ]:         15 :     if (!src->msg) {
    1213         [ #  # ]:          0 :       GST_DEBUG_OBJECT (src, "EOS reached");
    1214                 :          0 :       break;
    1215                 :            :     }
    1216                 :            : 
    1217   [ +  -  -  -  :         15 :     switch (src->session_io_status) {
                      - ]
    1218                 :            :       case GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_IDLE:
    1219         [ -  + ]:         15 :         GST_DEBUG_OBJECT (src, "Queueing connection request");
    1220                 :         15 :         gst_soup_http_src_queue_message (src);
    1221                 :         15 :         break;
    1222                 :            :       case GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_QUEUED:
    1223                 :          0 :         break;
    1224                 :            :       case GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING:
    1225                 :          0 :         gst_soup_http_src_session_unpause_message (src);
    1226                 :          0 :         break;
    1227                 :            :       case GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_CANCELLED:
    1228                 :            :         /* Impossible. */
    1229                 :          0 :         break;
    1230                 :            :     }
    1231                 :            : 
    1232         [ +  - ]:         15 :     if (src->ret == GST_FLOW_CUSTOM_ERROR)
    1233                 :         15 :       g_main_loop_run (src->loop);
    1234         [ -  + ]:         15 :   } while (src->ret == GST_FLOW_CUSTOM_ERROR);
    1235                 :            : 
    1236         [ -  + ]:         15 :   if (src->ret == GST_FLOW_CUSTOM_ERROR)
    1237                 :          0 :     src->ret = GST_FLOW_UNEXPECTED;
    1238                 :         15 :   return src->ret;
    1239                 :            : }
    1240                 :            : 
    1241                 :            : static gboolean
    1242                 :         33 : gst_soup_http_src_start (GstBaseSrc * bsrc)
    1243                 :            : {
    1244                 :         33 :   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (bsrc);
    1245                 :            : 
    1246         [ -  + ]:         33 :   GST_DEBUG_OBJECT (src, "start(\"%s\")", src->location);
    1247                 :            : 
    1248         [ +  + ]:         33 :   if (!src->location) {
    1249 [ +  - ][ -  + ]:         18 :     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (_("No URL set.")),
         [ +  - ][ -  + ]
    1250                 :            :         ("Missing location property"));
    1251                 :         18 :     return FALSE;
    1252                 :            :   }
    1253                 :            : 
    1254                 :         15 :   src->context = g_main_context_new ();
    1255                 :            : 
    1256                 :         15 :   src->loop = g_main_loop_new (src->context, TRUE);
    1257         [ -  + ]:         15 :   if (!src->loop) {
    1258 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (src, LIBRARY, INIT,
         [ #  # ][ #  # ]
    1259                 :            :         (NULL), ("Failed to start GMainLoop"));
    1260                 :          0 :     g_main_context_unref (src->context);
    1261                 :          0 :     return FALSE;
    1262                 :            :   }
    1263                 :            : 
    1264         [ +  - ]:         15 :   if (src->proxy == NULL) {
    1265                 :         15 :     src->session =
    1266                 :         15 :         soup_session_async_new_with_options (SOUP_SESSION_ASYNC_CONTEXT,
    1267                 :            :         src->context, SOUP_SESSION_USER_AGENT, src->user_agent,
    1268                 :            :         SOUP_SESSION_TIMEOUT, src->timeout,
    1269                 :            : #ifdef HAVE_LIBSOUP_GNOME
    1270                 :            :         SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_PROXY_RESOLVER_GNOME,
    1271                 :            : #endif
    1272                 :            :         NULL);
    1273                 :            :   } else {
    1274                 :          0 :     src->session =
    1275                 :          0 :         soup_session_async_new_with_options (SOUP_SESSION_ASYNC_CONTEXT,
    1276                 :            :         src->context, SOUP_SESSION_PROXY_URI, src->proxy,
    1277                 :            :         SOUP_SESSION_TIMEOUT, src->timeout,
    1278                 :            :         SOUP_SESSION_USER_AGENT, src->user_agent, NULL);
    1279                 :            :   }
    1280                 :            : 
    1281         [ -  + ]:         15 :   if (!src->session) {
    1282 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (src, LIBRARY, INIT,
         [ #  # ][ #  # ]
    1283                 :            :         (NULL), ("Failed to create async session"));
    1284                 :          0 :     return FALSE;
    1285                 :            :   }
    1286                 :            : 
    1287                 :         15 :   g_signal_connect (src->session, "authenticate",
    1288                 :            :       G_CALLBACK (gst_soup_http_src_authenticate_cb), src);
    1289                 :         33 :   return TRUE;
    1290                 :            : }
    1291                 :            : 
    1292                 :            : static gboolean
    1293                 :         15 : gst_soup_http_src_stop (GstBaseSrc * bsrc)
    1294                 :            : {
    1295                 :            :   GstSoupHTTPSrc *src;
    1296                 :            : 
    1297                 :         15 :   src = GST_SOUP_HTTP_SRC (bsrc);
    1298         [ -  + ]:         15 :   GST_DEBUG_OBJECT (src, "stop()");
    1299                 :         15 :   gst_soup_http_src_session_close (src);
    1300         [ +  - ]:         15 :   if (src->loop) {
    1301                 :         15 :     g_main_loop_unref (src->loop);
    1302                 :         15 :     g_main_context_unref (src->context);
    1303                 :         15 :     src->loop = NULL;
    1304                 :         15 :     src->context = NULL;
    1305                 :            :   }
    1306         [ -  + ]:         15 :   if (src->extra_headers) {
    1307                 :          0 :     gst_structure_free (src->extra_headers);
    1308                 :          0 :     src->extra_headers = NULL;
    1309                 :            :   }
    1310                 :            : 
    1311                 :         15 :   gst_soup_http_src_reset (src);
    1312                 :         15 :   return TRUE;
    1313                 :            : }
    1314                 :            : 
    1315                 :            : /* Interrupt a blocking request. */
    1316                 :            : static gboolean
    1317                 :         15 : gst_soup_http_src_unlock (GstBaseSrc * bsrc)
    1318                 :            : {
    1319                 :            :   GstSoupHTTPSrc *src;
    1320                 :            : 
    1321                 :         15 :   src = GST_SOUP_HTTP_SRC (bsrc);
    1322         [ -  + ]:         15 :   GST_DEBUG_OBJECT (src, "unlock()");
    1323                 :            : 
    1324                 :         15 :   src->interrupted = TRUE;
    1325         [ +  - ]:         15 :   if (src->loop)
    1326                 :         15 :     g_main_loop_quit (src->loop);
    1327                 :         15 :   return TRUE;
    1328                 :            : }
    1329                 :            : 
    1330                 :            : /* Interrupt interrupt. */
    1331                 :            : static gboolean
    1332                 :         30 : gst_soup_http_src_unlock_stop (GstBaseSrc * bsrc)
    1333                 :            : {
    1334                 :            :   GstSoupHTTPSrc *src;
    1335                 :            : 
    1336                 :         30 :   src = GST_SOUP_HTTP_SRC (bsrc);
    1337         [ -  + ]:         30 :   GST_DEBUG_OBJECT (src, "unlock_stop()");
    1338                 :            : 
    1339                 :         30 :   src->interrupted = FALSE;
    1340                 :         30 :   return TRUE;
    1341                 :            : }
    1342                 :            : 
    1343                 :            : static gboolean
    1344                 :         21 : gst_soup_http_src_get_size (GstBaseSrc * bsrc, guint64 * size)
    1345                 :            : {
    1346                 :            :   GstSoupHTTPSrc *src;
    1347                 :            : 
    1348                 :         21 :   src = GST_SOUP_HTTP_SRC (bsrc);
    1349                 :            : 
    1350         [ +  + ]:         21 :   if (src->have_size) {
    1351         [ -  + ]:          6 :     GST_DEBUG_OBJECT (src, "get_size() = %" G_GUINT64_FORMAT,
    1352                 :            :         src->content_size);
    1353                 :          6 :     *size = src->content_size;
    1354                 :          6 :     return TRUE;
    1355                 :            :   }
    1356         [ -  + ]:         15 :   GST_DEBUG_OBJECT (src, "get_size() = FALSE");
    1357                 :         21 :   return FALSE;
    1358                 :            : }
    1359                 :            : 
    1360                 :            : static gboolean
    1361                 :         15 : gst_soup_http_src_is_seekable (GstBaseSrc * bsrc)
    1362                 :            : {
    1363                 :         15 :   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (bsrc);
    1364                 :            : 
    1365                 :         15 :   return src->seekable;
    1366                 :            : }
    1367                 :            : 
    1368                 :            : static gboolean
    1369                 :         15 : gst_soup_http_src_do_seek (GstBaseSrc * bsrc, GstSegment * segment)
    1370                 :            : {
    1371                 :         15 :   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (bsrc);
    1372                 :            : 
    1373         [ -  + ]:         15 :   GST_DEBUG_OBJECT (src, "do_seek(%" G_GUINT64_FORMAT ")", segment->start);
    1374                 :            : 
    1375         [ +  - ]:         15 :   if (src->read_position == segment->start) {
    1376         [ -  + ]:         15 :     GST_DEBUG_OBJECT (src, "Seeking to current read position");
    1377                 :         15 :     return TRUE;
    1378                 :            :   }
    1379                 :            : 
    1380         [ #  # ]:          0 :   if (!src->seekable) {
    1381         [ #  # ]:          0 :     GST_WARNING_OBJECT (src, "Not seekable");
    1382                 :          0 :     return FALSE;
    1383                 :            :   }
    1384                 :            : 
    1385 [ #  # ][ #  # ]:          0 :   if (segment->rate != 1.0 || segment->format != GST_FORMAT_BYTES) {
    1386         [ #  # ]:          0 :     GST_WARNING_OBJECT (src, "Invalid seek segment");
    1387                 :          0 :     return FALSE;
    1388                 :            :   }
    1389                 :            : 
    1390 [ #  # ][ #  # ]:          0 :   if (src->content_size != 0 && segment->start >= src->content_size) {
    1391         [ #  # ]:          0 :     GST_WARNING_OBJECT (src, "Seeking behind end of file, will go to EOS soon");
    1392                 :            :   }
    1393                 :            : 
    1394                 :            :   /* Wait for create() to handle the jump in offset. */
    1395                 :          0 :   src->request_position = segment->start;
    1396                 :         15 :   return TRUE;
    1397                 :            : }
    1398                 :            : 
    1399                 :            : static gboolean
    1400                 :          8 : gst_soup_http_src_query (GstBaseSrc * bsrc, GstQuery * query)
    1401                 :            : {
    1402                 :          8 :   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (bsrc);
    1403                 :            :   gboolean ret;
    1404                 :            : 
    1405         [ -  + ]:          8 :   switch (GST_QUERY_TYPE (query)) {
    1406                 :            :     case GST_QUERY_URI:
    1407                 :          0 :       gst_query_set_uri (query, src->location);
    1408                 :          0 :       ret = TRUE;
    1409                 :          0 :       break;
    1410                 :            :     default:
    1411                 :          8 :       ret = FALSE;
    1412                 :          8 :       break;
    1413                 :            :   }
    1414                 :            : 
    1415         [ +  - ]:          8 :   if (!ret)
    1416                 :          8 :     ret = GST_BASE_SRC_CLASS (parent_class)->query (bsrc, query);
    1417                 :            : 
    1418                 :          8 :   return ret;
    1419                 :            : }
    1420                 :            : 
    1421                 :            : static gboolean
    1422                 :         15 : gst_soup_http_src_set_location (GstSoupHTTPSrc * src, const gchar * uri)
    1423                 :            : {
    1424         [ +  + ]:         15 :   if (src->location) {
    1425                 :          1 :     g_free (src->location);
    1426                 :          1 :     src->location = NULL;
    1427                 :            :   }
    1428                 :         15 :   src->location = g_strdup (uri);
    1429                 :            : 
    1430                 :         15 :   return TRUE;
    1431                 :            : }
    1432                 :            : 
    1433                 :            : static gboolean
    1434                 :          0 : gst_soup_http_src_set_proxy (GstSoupHTTPSrc * src, const gchar * uri)
    1435                 :            : {
    1436         [ #  # ]:          0 :   if (src->proxy) {
    1437                 :          0 :     soup_uri_free (src->proxy);
    1438                 :          0 :     src->proxy = NULL;
    1439                 :            :   }
    1440         [ #  # ]:          0 :   if (g_str_has_prefix (uri, "http://")) {
    1441                 :          0 :     src->proxy = soup_uri_new (uri);
    1442                 :            :   } else {
    1443                 :          0 :     gchar *new_uri = g_strconcat ("http://", uri, NULL);
    1444                 :            : 
    1445                 :          0 :     src->proxy = soup_uri_new (new_uri);
    1446                 :          0 :     g_free (new_uri);
    1447                 :            :   }
    1448                 :            : 
    1449                 :          0 :   return TRUE;
    1450                 :            : }
    1451                 :            : 
    1452                 :            : static guint
    1453                 :          1 : gst_soup_http_src_uri_get_type (void)
    1454                 :            : {
    1455                 :          1 :   return GST_URI_SRC;
    1456                 :            : }
    1457                 :            : 
    1458                 :            : static gchar **
    1459                 :          1 : gst_soup_http_src_uri_get_protocols (void)
    1460                 :            : {
    1461                 :            :   static const gchar *protocols[] = { "http", "https", NULL };
    1462                 :          1 :   return (gchar **) protocols;
    1463                 :            : }
    1464                 :            : 
    1465                 :            : static const gchar *
    1466                 :          0 : gst_soup_http_src_uri_get_uri (GstURIHandler * handler)
    1467                 :            : {
    1468                 :          0 :   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (handler);
    1469                 :            : 
    1470                 :          0 :   return src->location;
    1471                 :            : }
    1472                 :            : 
    1473                 :            : static gboolean
    1474                 :          0 : gst_soup_http_src_uri_set_uri (GstURIHandler * handler, const gchar * uri)
    1475                 :            : {
    1476                 :          0 :   GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (handler);
    1477                 :            : 
    1478                 :          0 :   return gst_soup_http_src_set_location (src, uri);
    1479                 :            : }
    1480                 :            : 
    1481                 :            : static void
    1482                 :         18 : gst_soup_http_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
    1483                 :            : {
    1484                 :         18 :   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
    1485                 :            : 
    1486                 :         18 :   iface->get_type = gst_soup_http_src_uri_get_type;
    1487                 :         18 :   iface->get_protocols = gst_soup_http_src_uri_get_protocols;
    1488                 :         18 :   iface->get_uri = gst_soup_http_src_uri_get_uri;
    1489                 :         18 :   iface->set_uri = gst_soup_http_src_uri_set_uri;
    1490                 :         18 : }
    1491                 :            : 
    1492                 :            : static gboolean
    1493                 :         18 : plugin_init (GstPlugin * plugin)
    1494                 :            : {
    1495         [ +  - ]:         18 :   GST_DEBUG_CATEGORY_INIT (souphttpsrc_debug, "souphttpsrc", 0,
    1496                 :            :       "SOUP HTTP Client Source");
    1497                 :            : 
    1498                 :            : #ifdef ENABLE_NLS
    1499         [ -  + ]:         18 :   GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
    1500                 :            :       LOCALEDIR);
    1501                 :         18 :   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
    1502                 :         18 :   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
    1503                 :            : #endif
    1504                 :            : 
    1505                 :         18 :   return gst_element_register (plugin, "souphttpsrc", GST_RANK_PRIMARY,
    1506                 :            :       GST_TYPE_SOUP_HTTP_SRC);
    1507                 :            : }
    1508                 :            : 
    1509                 :            : GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    1510                 :            :     GST_VERSION_MINOR,
    1511                 :            :     "soup",
    1512                 :            :     "libsoup HTTP client src",
    1513                 :            :     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)

Generated by: LCOV version 1.9