gstsouphttpclientsink.c 23 KB
Newer Older
David Schleef's avatar
David Schleef committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* GStreamer
 * Copyright (C) 2011 David Schleef <ds@entropywave.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
 * Boston, MA 02110-1335, USA.
 */
/**
20
 * SECTION:element-gstsouphttpclientsink
David Schleef's avatar
David Schleef committed
21
 *
22
 * The souphttpclientsink element sends pipeline data to an HTTP server
David Schleef's avatar
David Schleef committed
23 24 25 26 27 28
 * using HTTP PUT commands.
 *
 * <refsect2>
 * <title>Example launch line</title>
 * |[
 * gst-launch -v videotestsrc num-buffers=300 ! theoraenc ! oggmux !
29
 *   souphttpclientsink location=http://server/filename.ogv
David Schleef's avatar
David Schleef committed
30 31 32 33 34 35 36 37 38 39 40 41 42
 * ]|
 * 
 * This example encodes 10 seconds of video and sends it to the HTTP
 * server "server" using HTTP PUT commands.
 * </refsect2>
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <gst/gst.h>
#include <gst/base/gstbasesink.h>
43
#include "gstsouphttpclientsink.h"
David Schleef's avatar
David Schleef committed
44

45 46
#include <gst/glib-compat-private.h>

47 48
GST_DEBUG_CATEGORY_STATIC (souphttpclientsink_dbg);
#define GST_CAT_DEFAULT souphttpclientsink_dbg
David Schleef's avatar
David Schleef committed
49 50 51 52

/* prototypes */


53
static void gst_soup_http_client_sink_set_property (GObject * object,
David Schleef's avatar
David Schleef committed
54
    guint property_id, const GValue * value, GParamSpec * pspec);
55
static void gst_soup_http_client_sink_get_property (GObject * object,
David Schleef's avatar
David Schleef committed
56
    guint property_id, GValue * value, GParamSpec * pspec);
57 58
static void gst_soup_http_client_sink_dispose (GObject * object);
static void gst_soup_http_client_sink_finalize (GObject * object);
David Schleef's avatar
David Schleef committed
59

60
static gboolean gst_soup_http_client_sink_set_caps (GstBaseSink * sink,
David Schleef's avatar
David Schleef committed
61
    GstCaps * caps);
62
static void gst_soup_http_client_sink_get_times (GstBaseSink * sink,
David Schleef's avatar
David Schleef committed
63
    GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
64 65 66 67 68 69 70 71 72
static gboolean gst_soup_http_client_sink_start (GstBaseSink * sink);
static gboolean gst_soup_http_client_sink_stop (GstBaseSink * sink);
static gboolean gst_soup_http_client_sink_unlock (GstBaseSink * sink);
static gboolean gst_soup_http_client_sink_event (GstBaseSink * sink,
    GstEvent * event);
static GstFlowReturn gst_soup_http_client_sink_preroll (GstBaseSink * sink,
    GstBuffer * buffer);
static GstFlowReturn gst_soup_http_client_sink_render (GstBaseSink * sink,
    GstBuffer * buffer);
David Schleef's avatar
David Schleef committed
73 74

static void free_buffer_list (GList * list);
75 76
static void gst_soup_http_client_sink_reset (GstSoupHttpClientSink *
    souphttpsink);
David Schleef's avatar
David Schleef committed
77 78
static void authenticate (SoupSession * session, SoupMessage * msg,
    SoupAuth * auth, gboolean retrying, gpointer user_data);
79 80 81 82
static void callback (SoupSession * session, SoupMessage * msg,
    gpointer user_data);
static gboolean gst_soup_http_client_sink_set_proxy (GstSoupHttpClientSink *
    souphttpsink, const gchar * uri);
David Schleef's avatar
David Schleef committed
83 84 85 86 87 88 89 90 91 92 93 94 95

enum
{
  PROP_0,
  PROP_LOCATION,
  PROP_USER_AGENT,
  PROP_AUTOMATIC_REDIRECT,
  PROP_PROXY,
  PROP_USER_ID,
  PROP_USER_PW,
  PROP_PROXY_ID,
  PROP_PROXY_PW,
  PROP_COOKIES,
David Schleef's avatar
David Schleef committed
96
  PROP_SESSION
David Schleef's avatar
David Schleef committed
97 98
};

99
#define DEFAULT_USER_AGENT           "GStreamer souphttpclientsink "
David Schleef's avatar
David Schleef committed
100 101 102

/* pad templates */

103
static GstStaticPadTemplate gst_soup_http_client_sink_sink_template =
David Schleef's avatar
David Schleef committed
104 105 106 107 108 109 110 111 112
GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS_ANY);


/* class initialization */

#define DEBUG_INIT(bla) \
113 114
  GST_DEBUG_CATEGORY_INIT (souphttpclientsink_dbg, "souphttpclientsink", 0, \
      "souphttpclientsink element");
David Schleef's avatar
David Schleef committed
115

116 117
GST_BOILERPLATE_FULL (GstSoupHttpClientSink, gst_soup_http_client_sink,
    GstBaseSink, GST_TYPE_BASE_SINK, DEBUG_INIT);
David Schleef's avatar
David Schleef committed
118 119

static void
120
gst_soup_http_client_sink_base_init (gpointer g_class)
David Schleef's avatar
David Schleef committed
121 122 123
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);

124 125
  gst_element_class_add_static_pad_template (element_class,
      &gst_soup_http_client_sink_sink_template);
David Schleef's avatar
David Schleef committed
126 127 128 129 130 131 132

  gst_element_class_set_details_simple (element_class, "HTTP client sink",
      "Generic", "Sends streams to HTTP server via PUT",
      "David Schleef <ds@entropywave.com>");
}

static void
133
gst_soup_http_client_sink_class_init (GstSoupHttpClientSinkClass * klass)
David Schleef's avatar
David Schleef committed
134 135 136 137
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  GstBaseSinkClass *base_sink_class = GST_BASE_SINK_CLASS (klass);

138 139 140 141 142 143
  gobject_class->set_property = gst_soup_http_client_sink_set_property;
  gobject_class->get_property = gst_soup_http_client_sink_get_property;
  gobject_class->dispose = gst_soup_http_client_sink_dispose;
  gobject_class->finalize = gst_soup_http_client_sink_finalize;
  base_sink_class->set_caps =
      GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_set_caps);
David Schleef's avatar
David Schleef committed
144 145
  if (0)
    base_sink_class->get_times =
146 147 148 149 150 151
        GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_get_times);
  base_sink_class->start = GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_start);
  base_sink_class->stop = GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_stop);
  base_sink_class->unlock =
      GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_unlock);
  base_sink_class->event = GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_event);
David Schleef's avatar
David Schleef committed
152
  if (0)
153 154 155 156
    base_sink_class->preroll =
        GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_preroll);
  base_sink_class->render =
      GST_DEBUG_FUNCPTR (gst_soup_http_client_sink_render);
David Schleef's avatar
David Schleef committed
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197

  g_object_class_install_property (gobject_class,
      PROP_LOCATION,
      g_param_spec_string ("location", "Location",
          "URI to send to", "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class,
      PROP_USER_AGENT,
      g_param_spec_string ("user-agent", "User-Agent",
          "Value of the User-Agent HTTP request header field",
          DEFAULT_USER_AGENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class,
      PROP_AUTOMATIC_REDIRECT,
      g_param_spec_boolean ("automatic-redirect", "automatic-redirect",
          "Automatically follow HTTP redirects (HTTP Status Code 3xx)",
          TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class,
      PROP_PROXY,
      g_param_spec_string ("proxy", "Proxy",
          "HTTP proxy server URI", "",
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class,
      PROP_USER_ID,
      g_param_spec_string ("user-id", "user-id",
          "user id for authentication", "",
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class, PROP_USER_PW,
      g_param_spec_string ("user-pw", "user-pw",
          "user password for authentication", "",
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class, PROP_PROXY_ID,
      g_param_spec_string ("proxy-id", "proxy-id",
          "user id for proxy authentication", "",
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class, PROP_PROXY_PW,
      g_param_spec_string ("proxy-pw", "proxy-pw",
          "user password for proxy authentication", "",
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class, PROP_SESSION,
      g_param_spec_object ("session", "session",
          "SoupSession object to use for communication",
          SOUP_TYPE_SESSION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
David Schleef's avatar
David Schleef committed
198 199 200
  g_object_class_install_property (gobject_class, PROP_COOKIES,
      g_param_spec_boxed ("cookies", "Cookies", "HTTP request cookies",
          G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
David Schleef's avatar
David Schleef committed
201 202 203 204

}

static void
205 206
gst_soup_http_client_sink_init (GstSoupHttpClientSink * souphttpsink,
    GstSoupHttpClientSinkClass * souphttpsink_class)
David Schleef's avatar
David Schleef committed
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
{
  const char *proxy;

  souphttpsink->mutex = g_mutex_new ();
  souphttpsink->cond = g_cond_new ();

  souphttpsink->location = NULL;
  souphttpsink->automatic_redirect = TRUE;
  souphttpsink->user_agent = g_strdup (DEFAULT_USER_AGENT);
  souphttpsink->user_id = NULL;
  souphttpsink->user_pw = NULL;
  souphttpsink->proxy_id = NULL;
  souphttpsink->proxy_pw = NULL;
  souphttpsink->prop_session = NULL;
  souphttpsink->timeout = 1;
  proxy = g_getenv ("http_proxy");
223
  if (proxy && !gst_soup_http_client_sink_set_proxy (souphttpsink, proxy)) {
David Schleef's avatar
David Schleef committed
224 225 226 227 228
    GST_WARNING_OBJECT (souphttpsink,
        "The proxy in the http_proxy env var (\"%s\") cannot be parsed.",
        proxy);
  }

229
  gst_soup_http_client_sink_reset (souphttpsink);
David Schleef's avatar
David Schleef committed
230 231 232
}

static void
233
gst_soup_http_client_sink_reset (GstSoupHttpClientSink * souphttpsink)
David Schleef's avatar
David Schleef committed
234 235 236 237 238 239 240 241
{
  g_free (souphttpsink->reason_phrase);
  souphttpsink->reason_phrase = NULL;
  souphttpsink->status_code = 0;
  souphttpsink->offset = 0;

}

David Schleef's avatar
David Schleef committed
242
static gboolean
243 244
gst_soup_http_client_sink_set_proxy (GstSoupHttpClientSink * souphttpsink,
    const gchar * uri)
David Schleef's avatar
David Schleef committed
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
{
  if (souphttpsink->proxy) {
    soup_uri_free (souphttpsink->proxy);
    souphttpsink->proxy = NULL;
  }
  if (g_str_has_prefix (uri, "http://")) {
    souphttpsink->proxy = soup_uri_new (uri);
  } else {
    gchar *new_uri = g_strconcat ("http://", uri, NULL);

    souphttpsink->proxy = soup_uri_new (new_uri);
    g_free (new_uri);
  }

  return TRUE;
}

David Schleef's avatar
David Schleef committed
262
void
263
gst_soup_http_client_sink_set_property (GObject * object, guint property_id,
David Schleef's avatar
David Schleef committed
264 265
    const GValue * value, GParamSpec * pspec)
{
266
  GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (object);
David Schleef's avatar
David Schleef committed
267 268 269 270 271 272 273

  g_mutex_lock (souphttpsink->mutex);
  switch (property_id) {
    case PROP_SESSION:
      if (souphttpsink->prop_session) {
        g_object_unref (souphttpsink->prop_session);
      }
274
      souphttpsink->prop_session = g_value_dup_object (value);
David Schleef's avatar
David Schleef committed
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
      break;
    case PROP_LOCATION:
      g_free (souphttpsink->location);
      souphttpsink->location = g_value_dup_string (value);
      souphttpsink->offset = 0;
      break;
    case PROP_USER_AGENT:
      g_free (souphttpsink->user_agent);
      souphttpsink->user_agent = g_value_dup_string (value);
      break;
    case PROP_AUTOMATIC_REDIRECT:
      souphttpsink->automatic_redirect = g_value_get_boolean (value);
      break;
    case PROP_USER_ID:
      g_free (souphttpsink->user_id);
      souphttpsink->user_id = g_value_dup_string (value);
      break;
    case PROP_USER_PW:
      g_free (souphttpsink->user_pw);
      souphttpsink->user_pw = g_value_dup_string (value);
      break;
    case PROP_PROXY_ID:
      g_free (souphttpsink->proxy_id);
      souphttpsink->proxy_id = g_value_dup_string (value);
      break;
    case PROP_PROXY_PW:
      g_free (souphttpsink->proxy_pw);
      souphttpsink->proxy_pw = g_value_dup_string (value);
      break;
David Schleef's avatar
David Schleef committed
304 305 306 307 308 309 310 311 312 313
    case PROP_PROXY:
    {
      const gchar *proxy;

      proxy = g_value_get_string (value);

      if (proxy == NULL) {
        GST_WARNING ("proxy property cannot be NULL");
        goto done;
      }
314
      if (!gst_soup_http_client_sink_set_proxy (souphttpsink, proxy)) {
David Schleef's avatar
David Schleef committed
315 316 317 318 319 320 321 322 323
        GST_WARNING ("badly formatted proxy URI");
        goto done;
      }
      break;
    }
    case PROP_COOKIES:
      g_strfreev (souphttpsink->cookies);
      souphttpsink->cookies = g_strdupv (g_value_get_boxed (value));
      break;
David Schleef's avatar
David Schleef committed
324 325 326 327
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
  }
David Schleef's avatar
David Schleef committed
328
done:
David Schleef's avatar
David Schleef committed
329 330 331 332
  g_mutex_unlock (souphttpsink->mutex);
}

void
333
gst_soup_http_client_sink_get_property (GObject * object, guint property_id,
David Schleef's avatar
David Schleef committed
334 335
    GValue * value, GParamSpec * pspec)
{
336
  GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (object);
David Schleef's avatar
David Schleef committed
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362

  switch (property_id) {
    case PROP_SESSION:
      g_value_set_object (value, souphttpsink->prop_session);
      break;
    case PROP_LOCATION:
      g_value_set_string (value, souphttpsink->location);
      break;
    case PROP_AUTOMATIC_REDIRECT:
      g_value_set_boolean (value, souphttpsink->automatic_redirect);
      break;
    case PROP_USER_AGENT:
      g_value_set_string (value, souphttpsink->user_agent);
      break;
    case PROP_USER_ID:
      g_value_set_string (value, souphttpsink->user_id);
      break;
    case PROP_USER_PW:
      g_value_set_string (value, souphttpsink->user_pw);
      break;
    case PROP_PROXY_ID:
      g_value_set_string (value, souphttpsink->proxy_id);
      break;
    case PROP_PROXY_PW:
      g_value_set_string (value, souphttpsink->proxy_pw);
      break;
David Schleef's avatar
David Schleef committed
363 364 365 366 367 368 369 370 371 372 373 374 375
    case PROP_PROXY:
      if (souphttpsink->proxy == NULL)
        g_value_set_static_string (value, "");
      else {
        char *proxy = soup_uri_to_string (souphttpsink->proxy, FALSE);

        g_value_set_string (value, proxy);
        g_free (proxy);
      }
      break;
    case PROP_COOKIES:
      g_value_set_boxed (value, g_strdupv (souphttpsink->cookies));
      break;
David Schleef's avatar
David Schleef committed
376 377 378 379 380 381 382
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
  }
}

void
383
gst_soup_http_client_sink_dispose (GObject * object)
David Schleef's avatar
David Schleef committed
384
{
385
  GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (object);
David Schleef's avatar
David Schleef committed
386 387

  /* clean up as possible.  may be called multiple times */
388 389 390
  if (souphttpsink->prop_session)
    g_object_unref (souphttpsink->prop_session);
  souphttpsink->prop_session = NULL;
David Schleef's avatar
David Schleef committed
391 392 393 394 395

  G_OBJECT_CLASS (parent_class)->dispose (object);
}

void
396
gst_soup_http_client_sink_finalize (GObject * object)
David Schleef's avatar
David Schleef committed
397
{
398
  GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (object);
David Schleef's avatar
David Schleef committed
399 400 401 402 403 404 405 406

  /* clean up object here */

  g_free (souphttpsink->user_agent);
  g_free (souphttpsink->user_id);
  g_free (souphttpsink->user_pw);
  g_free (souphttpsink->proxy_id);
  g_free (souphttpsink->proxy_pw);
David Schleef's avatar
David Schleef committed
407 408
  if (souphttpsink->proxy)
    soup_uri_free (souphttpsink->proxy);
David Schleef's avatar
David Schleef committed
409 410 411 412 413 414 415 416 417 418 419
  g_free (souphttpsink->location);

  g_cond_free (souphttpsink->cond);
  g_mutex_free (souphttpsink->mutex);

  G_OBJECT_CLASS (parent_class)->finalize (object);
}



static gboolean
420
gst_soup_http_client_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
David Schleef's avatar
David Schleef committed
421
{
422
  GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (sink);
David Schleef's avatar
David Schleef committed
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
  GstStructure *structure;
  const GValue *value_array;
  int i, n;

  structure = gst_caps_get_structure (caps, 0);
  value_array = gst_structure_get_value (structure, "streamheader");
  if (value_array) {
    free_buffer_list (souphttpsink->streamheader_buffers);
    souphttpsink->streamheader_buffers = NULL;

    n = gst_value_array_get_size (value_array);
    for (i = 0; i < n; i++) {
      const GValue *value;
      GstBuffer *buffer;
      value = gst_value_array_get_value (value_array, i);
      buffer = GST_BUFFER (gst_value_get_buffer (value));
      souphttpsink->streamheader_buffers =
          g_list_append (souphttpsink->streamheader_buffers,
          gst_buffer_ref (buffer));
    }
  }

  return TRUE;
}

static void
449
gst_soup_http_client_sink_get_times (GstBaseSink * sink, GstBuffer * buffer,
David Schleef's avatar
David Schleef committed
450 451 452 453 454
    GstClockTime * start, GstClockTime * end)
{

}

455 456 457 458 459 460 461 462 463 464 465 466 467 468
static gboolean
thread_ready_idle_cb (gpointer data)
{
  GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (data);

  GST_LOG_OBJECT (souphttpsink, "thread ready");

  g_mutex_lock (souphttpsink->mutex);
  g_cond_signal (souphttpsink->cond);
  g_mutex_unlock (souphttpsink->mutex);

  return FALSE;                 /* only run once */
}

David Schleef's avatar
David Schleef committed
469 470 471
static gpointer
thread_func (gpointer ptr)
{
472
  GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (ptr);
David Schleef's avatar
David Schleef committed
473 474 475 476 477 478 479 480 481 482 483

  GST_DEBUG ("thread start");

  g_main_loop_run (souphttpsink->loop);

  GST_DEBUG ("thread quit");

  return NULL;
}

static gboolean
484
gst_soup_http_client_sink_start (GstBaseSink * sink)
David Schleef's avatar
David Schleef committed
485
{
486
  GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (sink);
David Schleef's avatar
David Schleef committed
487 488 489 490

  if (souphttpsink->prop_session) {
    souphttpsink->session = souphttpsink->prop_session;
  } else {
491
    GSource *source;
David Schleef's avatar
David Schleef committed
492 493 494 495
    GError *error = NULL;

    souphttpsink->context = g_main_context_new ();

496 497 498 499 500 501 502 503 504 505 506
    /* set up idle source to signal when the main loop is running and
     * it's safe for ::stop() to call g_main_loop_quit() */
    source = g_idle_source_new ();
    g_source_set_callback (source, thread_ready_idle_cb, sink, NULL);
    g_source_attach (source, souphttpsink->context);
    g_source_unref (source);

    souphttpsink->loop = g_main_loop_new (souphttpsink->context, TRUE);

    g_mutex_lock (souphttpsink->mutex);

David Schleef's avatar
David Schleef committed
507 508 509
    souphttpsink->thread = g_thread_create (thread_func, souphttpsink,
        TRUE, &error);

510 511 512 513 514
    GST_LOG_OBJECT (souphttpsink, "waiting for main loop thread to start up");
    g_cond_wait (souphttpsink->cond, souphttpsink->mutex);
    g_mutex_unlock (souphttpsink->mutex);
    GST_LOG_OBJECT (souphttpsink, "main loop thread running");

David Schleef's avatar
David Schleef committed
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
    souphttpsink->session =
        soup_session_async_new_with_options (SOUP_SESSION_ASYNC_CONTEXT,
        souphttpsink->context, SOUP_SESSION_USER_AGENT,
        souphttpsink->user_agent, SOUP_SESSION_TIMEOUT, souphttpsink->timeout,
        NULL);

    //soup_session_add_feature (souphttpsink->session,
    //    SOUP_SESSION_FEATURE (soup_logger_new (SOUP_LOGGER_LOG_BODY, 100)));

    g_signal_connect (souphttpsink->session, "authenticate",
        G_CALLBACK (authenticate), souphttpsink);
  }

  return TRUE;
}

static gboolean
532
gst_soup_http_client_sink_stop (GstBaseSink * sink)
David Schleef's avatar
David Schleef committed
533
{
534
  GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (sink);
David Schleef's avatar
David Schleef committed
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553

  GST_DEBUG ("stop");

  if (souphttpsink->prop_session == NULL) {
    soup_session_abort (souphttpsink->session);
    g_object_unref (souphttpsink->session);
  }

  if (souphttpsink->loop) {
    g_main_loop_quit (souphttpsink->loop);
    g_thread_join (souphttpsink->thread);
    g_main_loop_unref (souphttpsink->loop);
    souphttpsink->loop = NULL;
  }
  if (souphttpsink->context) {
    g_main_context_unref (souphttpsink->context);
    souphttpsink->context = NULL;
  }

554
  gst_soup_http_client_sink_reset (souphttpsink);
David Schleef's avatar
David Schleef committed
555 556 557 558 559

  return TRUE;
}

static gboolean
560
gst_soup_http_client_sink_unlock (GstBaseSink * sink)
David Schleef's avatar
David Schleef committed
561 562 563 564 565 566 567
{
  GST_DEBUG ("unlock");

  return TRUE;
}

static gboolean
568
gst_soup_http_client_sink_event (GstBaseSink * sink, GstEvent * event)
David Schleef's avatar
David Schleef committed
569
{
570
  GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (sink);
David Schleef's avatar
David Schleef committed
571

David Schleef's avatar
David Schleef committed
572
  GST_DEBUG_OBJECT (souphttpsink, "event");
David Schleef's avatar
David Schleef committed
573 574

  if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
David Schleef's avatar
David Schleef committed
575
    GST_DEBUG_OBJECT (souphttpsink, "got eos");
David Schleef's avatar
David Schleef committed
576 577
    g_mutex_lock (souphttpsink->mutex);
    while (souphttpsink->message) {
David Schleef's avatar
David Schleef committed
578
      GST_DEBUG_OBJECT (souphttpsink, "waiting");
David Schleef's avatar
David Schleef committed
579 580 581
      g_cond_wait (souphttpsink->cond, souphttpsink->mutex);
    }
    g_mutex_unlock (souphttpsink->mutex);
David Schleef's avatar
David Schleef committed
582
    GST_DEBUG_OBJECT (souphttpsink, "finished eos");
David Schleef's avatar
David Schleef committed
583 584 585 586 587 588
  }

  return TRUE;
}

static GstFlowReturn
589
gst_soup_http_client_sink_preroll (GstBaseSink * sink, GstBuffer * buffer)
David Schleef's avatar
David Schleef committed
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
{
  GST_DEBUG ("preroll");

  return GST_FLOW_OK;
}

static void
free_buffer_list (GList * list)
{
  GList *g;
  for (g = list; g; g = g_list_next (g)) {
    GstBuffer *buffer = g->data;
    gst_buffer_unref (buffer);
  }
  g_list_free (list);
}

static void
608
send_message_locked (GstSoupHttpClientSink * souphttpsink)
David Schleef's avatar
David Schleef committed
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666
{
  GList *g;
  guint64 n;

  if (souphttpsink->queued_buffers == NULL || souphttpsink->message) {
    return;
  }

  /* If the URI went away, drop all these buffers */
  if (souphttpsink->location == NULL) {
    free_buffer_list (souphttpsink->queued_buffers);
    souphttpsink->queued_buffers = NULL;
    return;
  }

  souphttpsink->message = soup_message_new ("PUT", souphttpsink->location);

  n = 0;
  if (souphttpsink->offset == 0) {
    for (g = souphttpsink->streamheader_buffers; g; g = g_list_next (g)) {
      GstBuffer *buffer = g->data;
      soup_message_body_append (souphttpsink->message->request_body,
          SOUP_MEMORY_STATIC, GST_BUFFER_DATA (buffer),
          GST_BUFFER_SIZE (buffer));
      n += GST_BUFFER_SIZE (buffer);
    }
  }

  for (g = souphttpsink->queued_buffers; g; g = g_list_next (g)) {
    GstBuffer *buffer = g->data;
    if (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_IN_CAPS)) {
      soup_message_body_append (souphttpsink->message->request_body,
          SOUP_MEMORY_STATIC, GST_BUFFER_DATA (buffer),
          GST_BUFFER_SIZE (buffer));
      n += GST_BUFFER_SIZE (buffer);
    }
  }

  if (souphttpsink->offset != 0) {
    char *s;
    s = g_strdup_printf ("bytes %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT "/*",
        souphttpsink->offset, souphttpsink->offset + n - 1);
    soup_message_headers_append (souphttpsink->message->request_headers,
        "Content-Range", s);
    g_free (s);
  }

  if (n == 0) {
    free_buffer_list (souphttpsink->queued_buffers);
    souphttpsink->queued_buffers = NULL;
    g_object_unref (souphttpsink->message);
    souphttpsink->message = NULL;
    return;
  }

  souphttpsink->sent_buffers = souphttpsink->queued_buffers;
  souphttpsink->queued_buffers = NULL;

David Schleef's avatar
David Schleef committed
667 668
  GST_DEBUG_OBJECT (souphttpsink,
      "queue message %" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT,
David Schleef's avatar
David Schleef committed
669
      souphttpsink->offset, n);
David Schleef's avatar
David Schleef committed
670 671
  soup_session_queue_message (souphttpsink->session, souphttpsink->message,
      callback, souphttpsink);
David Schleef's avatar
David Schleef committed
672 673 674 675 676

  souphttpsink->offset += n;
}

static gboolean
677
send_message (GstSoupHttpClientSink * souphttpsink)
David Schleef's avatar
David Schleef committed
678 679 680 681 682 683 684 685 686 687 688
{
  g_mutex_lock (souphttpsink->mutex);
  send_message_locked (souphttpsink);
  g_mutex_unlock (souphttpsink->mutex);

  return FALSE;
}

static void
callback (SoupSession * session, SoupMessage * msg, gpointer user_data)
{
689
  GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (user_data);
David Schleef's avatar
David Schleef committed
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712

  GST_DEBUG_OBJECT (souphttpsink, "callback status=%d %s",
      msg->status_code, msg->reason_phrase);

  g_mutex_lock (souphttpsink->mutex);
  g_cond_signal (souphttpsink->cond);
  souphttpsink->message = NULL;

  if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
    souphttpsink->status_code = msg->status_code;
    souphttpsink->reason_phrase = g_strdup (msg->reason_phrase);
    g_mutex_unlock (souphttpsink->mutex);
    return;
  }

  free_buffer_list (souphttpsink->sent_buffers);
  souphttpsink->sent_buffers = NULL;

  send_message_locked (souphttpsink);
  g_mutex_unlock (souphttpsink->mutex);
}

static GstFlowReturn
713
gst_soup_http_client_sink_render (GstBaseSink * sink, GstBuffer * buffer)
David Schleef's avatar
David Schleef committed
714
{
715
  GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (sink);
David Schleef's avatar
David Schleef committed
716 717 718 719
  GSource *source;
  gboolean wake;

  if (souphttpsink->status_code != 0) {
David Schleef's avatar
David Schleef committed
720
    /* FIXME we should allow a moderate amount of retries. */
David Schleef's avatar
David Schleef committed
721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
    GST_ELEMENT_ERROR (souphttpsink, RESOURCE, WRITE,
        ("Could not write to HTTP URI"),
        ("error: %d %s", souphttpsink->status_code,
            souphttpsink->reason_phrase));
    return GST_FLOW_ERROR;
  }

  g_mutex_lock (souphttpsink->mutex);
  if (souphttpsink->location != NULL) {
    wake = (souphttpsink->queued_buffers == NULL);
    souphttpsink->queued_buffers =
        g_list_append (souphttpsink->queued_buffers, gst_buffer_ref (buffer));

    if (wake) {
      source = g_idle_source_new ();
      g_source_set_callback (source, (GSourceFunc) (send_message),
          souphttpsink, NULL);
      g_source_attach (source, souphttpsink->context);
      g_source_unref (source);
    }
  }
  g_mutex_unlock (souphttpsink->mutex);

  return GST_FLOW_OK;
}

static void
authenticate (SoupSession * session, SoupMessage * msg,
    SoupAuth * auth, gboolean retrying, gpointer user_data)
{
751
  GstSoupHttpClientSink *souphttpsink = GST_SOUP_HTTP_CLIENT_SINK (user_data);
David Schleef's avatar
David Schleef committed
752 753 754 755 756 757 758 759

  if (!retrying) {
    if (souphttpsink->user_id && souphttpsink->user_pw) {
      soup_auth_authenticate (auth,
          souphttpsink->user_id, souphttpsink->user_pw);
    }
  }
}