gstshout2.c 25.7 KB
Newer Older
Wim Taymans's avatar
Wim Taymans committed
1 2
/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3
 * Copyright (C) <2006> Tim-Philipp Müller <tim centricular net>
4
 * Copyright (C) <2012> Ralph Giles <giles@mozilla.com>
Wim Taymans's avatar
Wim Taymans committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * 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., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

22 23 24
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
25

Wim Taymans's avatar
Wim Taymans committed
26 27
#include "gstshout2.h"
#include <stdlib.h>
28
#include <string.h>
Wim Taymans's avatar
Wim Taymans committed
29

30 31 32
#include "gst/gst-i18n-plugin.h"

GST_DEBUG_CATEGORY_STATIC (shout2_debug);
33 34
#define GST_CAT_DEFAULT shout2_debug

Wim Taymans's avatar
Wim Taymans committed
35

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
36 37
enum
{
38
  SIGNAL_CONNECTION_PROBLEM,    /* 0.11 FIXME: remove this */
Wim Taymans's avatar
Wim Taymans committed
39 40 41
  LAST_SIGNAL
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
42 43
enum
{
Wim Taymans's avatar
Wim Taymans committed
44
  ARG_0,
45 46 47
  ARG_IP,                       /* the ip of the server */
  ARG_PORT,                     /* the encoder port number on the server */
  ARG_PASSWORD,                 /* the encoder password on the server */
48
  ARG_USERNAME,                 /* the encoder username on the server */
49
  ARG_PUBLIC,                   /* is this stream public? */
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
50
  ARG_STREAMNAME,               /* Name of the stream */
51 52 53 54 55 56
  ARG_DESCRIPTION,              /* Description of the stream */
  ARG_GENRE,                    /* Genre of the stream */

  ARG_PROTOCOL,                 /* Protocol to connect with */

  ARG_MOUNT,                    /* mountpoint of stream (icecast only) */
57
  ARG_URL                       /* Url of stream (I'm guessing) */
Wim Taymans's avatar
Wim Taymans committed
58 59
};

60 61 62
#define DEFAULT_IP           "127.0.0.1"
#define DEFAULT_PORT         8000
#define DEFAULT_PASSWORD     "hackme"
63
#define DEFAULT_USERNAME     "source"
64
#define DEFAULT_PUBLIC     FALSE
65 66 67 68 69 70 71
#define DEFAULT_STREAMNAME   ""
#define DEFAULT_DESCRIPTION  ""
#define DEFAULT_GENRE        ""
#define DEFAULT_MOUNT        ""
#define DEFAULT_URL          ""
#define DEFAULT_PROTOCOL     SHOUT2SEND_PROTOCOL_HTTP

72 73 74 75 76
#ifdef SHOUT_FORMAT_WEBM
#define WEBM_CAPS "; video/webm"
#else
#define WEBM_CAPS ""
#endif
77 78 79
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
80
    GST_STATIC_CAPS ("application/ogg; "
81 82
        "audio/mpeg, mpegversion = (int) 1, layer = (int) [ 1, 3 ]" WEBM_CAPS));

83
static void gst_shout2send_finalize (GstShout2send * shout2send);
Wim Taymans's avatar
Wim Taymans committed
84

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
85
static gboolean gst_shout2send_event (GstBaseSink * sink, GstEvent * event);
Wim Taymans's avatar
Wim Taymans committed
86 87
static gboolean gst_shout2send_unlock (GstBaseSink * basesink);
static gboolean gst_shout2send_unlock_stop (GstBaseSink * basesink);
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
88 89
static GstFlowReturn gst_shout2send_render (GstBaseSink * sink,
    GstBuffer * buffer);
90 91
static gboolean gst_shout2send_start (GstBaseSink * basesink);
static gboolean gst_shout2send_stop (GstBaseSink * basesink);
Wim Taymans's avatar
Wim Taymans committed
92

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
93 94 95 96
static void gst_shout2send_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_shout2send_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);
Wim Taymans's avatar
Wim Taymans committed
97

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
98
static gboolean gst_shout2send_setcaps (GstBaseSink * basesink, GstCaps * caps);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
99

100
static guint gst_shout2send_signals[LAST_SIGNAL] = { 0 };
Wim Taymans's avatar
Wim Taymans committed
101

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
102 103
#define GST_TYPE_SHOUT_PROTOCOL (gst_shout2send_protocol_get_type())
static GType
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
104
gst_shout2send_protocol_get_type (void)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
105 106
{
  static GType shout2send_protocol_type = 0;
107
  static const GEnumValue shout2send_protocol[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
108 109 110 111
    {SHOUT2SEND_PROTOCOL_XAUDIOCAST,
        "Xaudiocast Protocol (icecast 1.3.x)", "xaudiocast"},
    {SHOUT2SEND_PROTOCOL_ICY, "Icy Protocol (ShoutCast)", "icy"},
    {SHOUT2SEND_PROTOCOL_HTTP, "Http Protocol (icecast 2.x)", "http"},
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
112 113
    {0, NULL, NULL},
  };
114

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
115
  if (!shout2send_protocol_type) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
116
    shout2send_protocol_type =
117
        g_enum_register_static ("GstShout2SendProtocol", shout2send_protocol);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
118
  }
119 120


Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
121 122 123
  return shout2send_protocol_type;
}

124 125 126
#define gst_shout2send_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstShout2send, gst_shout2send, GST_TYPE_BASE_SINK,
    G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL));
Wim Taymans's avatar
Wim Taymans committed
127 128

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
129
gst_shout2send_class_init (GstShout2sendClass * klass)
Wim Taymans's avatar
Wim Taymans committed
130 131
{
  GObjectClass *gobject_class;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
132
  GstElementClass *gstelement_class;
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
133
  GstBaseSinkClass *gstbasesink_class;
Wim Taymans's avatar
Wim Taymans committed
134

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
135
  gobject_class = (GObjectClass *) klass;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
136
  gstelement_class = (GstElementClass *) klass;
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
137 138
  gstbasesink_class = (GstBaseSinkClass *) klass;

139
  parent_class = g_type_class_peek_parent (klass);
Wim Taymans's avatar
Wim Taymans committed
140

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
141 142
  gobject_class->set_property = gst_shout2send_set_property;
  gobject_class->get_property = gst_shout2send_get_property;
143
  gobject_class->finalize = (GObjectFinalizeFunc) gst_shout2send_finalize;
Wim Taymans's avatar
Wim Taymans committed
144

145
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_IP,
146 147
      g_param_spec_string ("ip", "ip", "ip", DEFAULT_IP,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
148 149
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PORT,
      g_param_spec_int ("port", "port", "port", 1, G_MAXUSHORT, DEFAULT_PORT,
150
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
151

152 153
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PASSWORD,
      g_param_spec_string ("password", "password", "password", DEFAULT_PASSWORD,
154
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
155

156 157
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_USERNAME,
      g_param_spec_string ("username", "username", "username", DEFAULT_USERNAME,
158
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
159

Wim Taymans's avatar
Wim Taymans committed
160
  /* metadata */
161 162 163
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PUBLIC,
      g_param_spec_boolean ("public", "public",
          "If the stream should be listed on the server's stream directory",
164
          DEFAULT_PUBLIC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
165

166 167
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_STREAMNAME,
      g_param_spec_string ("streamname", "streamname", "name of the stream",
168
          DEFAULT_STREAMNAME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
169

170 171
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DESCRIPTION,
      g_param_spec_string ("description", "description", "description",
172
          DEFAULT_DESCRIPTION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
173

174 175
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_GENRE,
      g_param_spec_string ("genre", "genre", "genre", DEFAULT_GENRE,
176
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
177

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
178
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PROTOCOL,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
179
      g_param_spec_enum ("protocol", "protocol", "Connection Protocol to use",
180 181
          GST_TYPE_SHOUT_PROTOCOL, DEFAULT_PROTOCOL,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
182 183


Wim Taymans's avatar
Wim Taymans committed
184
  /* icecast only */
185 186
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MOUNT,
      g_param_spec_string ("mount", "mount", "mount", DEFAULT_MOUNT,
187
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
188

189 190
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_URL,
      g_param_spec_string ("url", "url", "url", DEFAULT_URL,
191
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
192

193 194 195 196 197 198
  /* signals */
  gst_shout2send_signals[SIGNAL_CONNECTION_PROBLEM] =
      g_signal_new ("connection-problem", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_CLEANUP, G_STRUCT_OFFSET (GstShout2sendClass,
          connection_problem), NULL, NULL, g_cclosure_marshal_VOID__INT,
      G_TYPE_NONE, 1, G_TYPE_INT);
199

200 201
  gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_shout2send_start);
  gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_shout2send_stop);
Wim Taymans's avatar
Wim Taymans committed
202 203 204
  gstbasesink_class->unlock = GST_DEBUG_FUNCPTR (gst_shout2send_unlock);
  gstbasesink_class->unlock_stop =
      GST_DEBUG_FUNCPTR (gst_shout2send_unlock_stop);
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
205 206
  gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_shout2send_render);
  gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_shout2send_event);
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
207 208 209 210 211
  gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_shout2send_setcaps);

  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&sink_template));

212
  gst_element_class_set_static_metadata (gstelement_class,
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
213 214 215 216 217 218 219
      "Icecast network sink",
      "Sink/Network", "Sends data to an icecast server",
      "Wim Taymans <wim.taymans@chello.be>, "
      "Pedro Corte-Real <typo@netcabo.pt>, "
      "Zaheer Abbas Merali <zaheerabbas at merali dot org>");

  GST_DEBUG_CATEGORY_INIT (shout2_debug, "shout2", 0, "shout2send element");
Wim Taymans's avatar
Wim Taymans committed
220 221 222
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
223
gst_shout2send_init (GstShout2send * shout2send)
Wim Taymans's avatar
Wim Taymans committed
224
{
225 226
  gst_base_sink_set_sync (GST_BASE_SINK (shout2send), FALSE);

Wim Taymans's avatar
Wim Taymans committed
227 228
  shout2send->timer = gst_poll_new_timer ();

229 230 231
  shout2send->ip = g_strdup (DEFAULT_IP);
  shout2send->port = DEFAULT_PORT;
  shout2send->password = g_strdup (DEFAULT_PASSWORD);
232
  shout2send->username = g_strdup (DEFAULT_USERNAME);
233 234 235 236 237 238
  shout2send->streamname = g_strdup (DEFAULT_STREAMNAME);
  shout2send->description = g_strdup (DEFAULT_DESCRIPTION);
  shout2send->genre = g_strdup (DEFAULT_GENRE);
  shout2send->mount = g_strdup (DEFAULT_MOUNT);
  shout2send->url = g_strdup (DEFAULT_URL);
  shout2send->protocol = DEFAULT_PROTOCOL;
239
  shout2send->ispublic = DEFAULT_PUBLIC;
240

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
241
  shout2send->tags = gst_tag_list_new_empty ();
242 243
  shout2send->conn = NULL;
  shout2send->audio_format = SHOUT_FORMAT_VORBIS;
244
  shout2send->connected = FALSE;
245
  shout2send->songmetadata = NULL;
246 247
  shout2send->songartist = NULL;
  shout2send->songtitle = NULL;
248
}
249

250 251 252 253 254 255 256 257 258 259 260 261 262 263
static void
gst_shout2send_finalize (GstShout2send * shout2send)
{
  g_free (shout2send->ip);
  g_free (shout2send->password);
  g_free (shout2send->username);
  g_free (shout2send->streamname);
  g_free (shout2send->description);
  g_free (shout2send->genre);
  g_free (shout2send->mount);
  g_free (shout2send->url);

  gst_tag_list_free (shout2send->tags);

Wim Taymans's avatar
Wim Taymans committed
264 265
  gst_poll_free (shout2send->timer);

266
  G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (shout2send));
267 268 269 270 271 272
}

static void
set_shout_metadata (const GstTagList * list, const gchar * tag,
    gpointer user_data)
{
273 274 275 276 277 278
  GstShout2send *shout2send = (GstShout2send *) user_data;
  char **shout_metadata = &(shout2send->songmetadata);
  char **song_artist = &(shout2send->songartist);
  char **song_title = &(shout2send->songtitle);

  gchar *value;
279 280 281 282 283

  GST_DEBUG ("tag: %s being added", tag);
  if (strcmp (tag, GST_TAG_ARTIST) == 0) {
    if (gst_tag_get_type (tag) == G_TYPE_STRING) {
      if (!gst_tag_list_get_string (list, tag, &value)) {
284
        GST_DEBUG ("Error reading \"%s\" tag value", tag);
285 286 287
        return;
      }

288 289 290 291
      if (*song_artist != NULL)
        g_free (*song_artist);

      *song_artist = g_strdup (value);
292 293 294 295
    }
  } else if (strcmp (tag, GST_TAG_TITLE) == 0) {
    if (gst_tag_get_type (tag) == G_TYPE_STRING) {
      if (!gst_tag_list_get_string (list, tag, &value)) {
296
        GST_DEBUG ("Error reading \"%s\" tag value", tag);
297 298
        return;
      }
299 300 301 302 303

      if (*song_title != NULL)
        g_free (*song_title);

      *song_title = g_strdup (value);
304 305
    }
  }
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320

  if (*shout_metadata != NULL)
    g_free (*shout_metadata);


  if (*song_title && *song_artist) {
    *shout_metadata = g_strdup_printf ("%s - %s", *song_artist, *song_title);
  } else if (*song_title && *song_artist == NULL) {
    *shout_metadata = g_strdup_printf ("Unknown - %s", *song_title);
  } else if (*song_title == NULL && *song_artist) {
    *shout_metadata = g_strdup_printf ("%s - Unknown", *song_artist);
  } else {
    *shout_metadata = g_strdup_printf ("Unknown - Unknown");
  }

321
  GST_LOG ("shout metadata is now: %s", *shout_metadata);
322 323
}

324
#if 0
325 326 327 328 329 330 331 332 333
static void
gst_shout2send_set_metadata (GstShout2send * shout2send)
{
  const GstTagList *user_tags;
  GstTagList *copy;
  char *tempmetadata;
  shout_metadata_t *pmetadata;

  g_return_if_fail (shout2send != NULL);
334
  user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (shout2send));
335 336 337 338
  if ((shout2send->tags == NULL) && (user_tags == NULL)) {
    return;
  }
  copy = gst_tag_list_merge (user_tags, shout2send->tags,
339
      gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (shout2send)));
340 341 342 343 344 345 346 347 348 349 350 351
  /* lets get the artist and song tags */
  tempmetadata = NULL;
  gst_tag_list_foreach ((GstTagList *) copy, set_shout_metadata,
      (gpointer) & tempmetadata);
  if (tempmetadata) {
    pmetadata = shout_metadata_new ();
    shout_metadata_add (pmetadata, "song", tempmetadata);
    shout_set_metadata (shout2send->conn, pmetadata);
    shout_metadata_free (pmetadata);
  }

  gst_tag_list_free (copy);
Wim Taymans's avatar
Wim Taymans committed
352
}
353
#endif
Wim Taymans's avatar
Wim Taymans committed
354

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
355 356 357

static gboolean
gst_shout2send_event (GstBaseSink * sink, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
358 359
{
  GstShout2send *shout2send;
360
  gboolean ret = TRUE;
361

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
362 363
  shout2send = GST_SHOUT2SEND (sink);

364 365
  GST_LOG_OBJECT (shout2send, "got %s event", GST_EVENT_TYPE_NAME (event));

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
366
  switch (GST_EVENT_TYPE (event)) {
367
    case GST_EVENT_TAG:{
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
368 369
      /* vorbis audio doesnt need metadata setting on the icecast level, only mp3 */
      if (shout2send->tags && shout2send->audio_format == SHOUT_FORMAT_MP3) {
370 371 372
        GstTagList *list;

        gst_event_parse_tag (event, &list);
373
        GST_DEBUG_OBJECT (shout2send, "tags=%" GST_PTR_FORMAT, list);
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
374
        gst_tag_list_insert (shout2send->tags,
375 376
            list,
            gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (shout2send)));
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
377
        /* lets get the artist and song tags */
378 379
        gst_tag_list_foreach ((GstTagList *) list,
            set_shout_metadata, shout2send);
380
        if (shout2send->songmetadata && shout2send->connected) {
381 382
          shout_metadata_t *pmetadata;

383 384 385
          GST_DEBUG_OBJECT (shout2send, "metadata now: %s",
              shout2send->songmetadata);

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
386
          pmetadata = shout_metadata_new ();
387
          shout_metadata_add (pmetadata, "song", shout2send->songmetadata);
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
388 389 390 391 392
          shout_set_metadata (shout2send->conn, pmetadata);
          shout_metadata_free (pmetadata);
        }
      }
      break;
393 394 395 396 397 398 399
    }
    default:{
      GST_LOG_OBJECT (shout2send, "let base class handle event");
      if (GST_BASE_SINK_CLASS (parent_class)->event) {
        event = gst_event_ref (event);
        ret = GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
      }
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
400
      break;
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 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 449 450 451 452
    }
  }

  return ret;
}

static gboolean
gst_shout2send_start (GstBaseSink * basesink)
{
  GstShout2send *sink = GST_SHOUT2SEND (basesink);
  const gchar *cur_prop;
  gshort proto = 3;
  gchar *version_string;

  GST_DEBUG_OBJECT (sink, "starting");

  sink->conn = shout_new ();

  switch (sink->protocol) {
    case SHOUT2SEND_PROTOCOL_XAUDIOCAST:
      proto = SHOUT_PROTOCOL_XAUDIOCAST;
      break;
    case SHOUT2SEND_PROTOCOL_ICY:
      proto = SHOUT_PROTOCOL_ICY;
      break;
    case SHOUT2SEND_PROTOCOL_HTTP:
      proto = SHOUT_PROTOCOL_HTTP;
      break;
  }

  cur_prop = "protocol";
  GST_DEBUG_OBJECT (sink, "setting protocol: %d", sink->protocol);
  if (shout_set_protocol (sink->conn, proto) != SHOUTERR_SUCCESS)
    goto set_failed;

  /* --- FIXME: shout requires an ip, and fails if it is given a host. */
  /* may want to put convert_to_ip(shout2send->ip) here */
  cur_prop = "ip";
  GST_DEBUG_OBJECT (sink, "setting ip: %s", sink->ip);
  if (shout_set_host (sink->conn, sink->ip) != SHOUTERR_SUCCESS)
    goto set_failed;

  cur_prop = "port";
  GST_DEBUG_OBJECT (sink, "setting port: %u", sink->port);
  if (shout_set_port (sink->conn, sink->port) != SHOUTERR_SUCCESS)
    goto set_failed;

  cur_prop = "password";
  GST_DEBUG_OBJECT (sink, "setting password: %s", sink->password);
  if (shout_set_password (sink->conn, sink->password) != SHOUTERR_SUCCESS)
    goto set_failed;

453 454 455 456 457 458
  cur_prop = "public";
  GST_DEBUG_OBJECT (sink, "setting %s: %u", cur_prop, sink->ispublic);
  if (shout_set_public (sink->conn,
          (sink->ispublic ? 1 : 0)) != SHOUTERR_SUCCESS)
    goto set_failed;

459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478
  cur_prop = "streamname";
  GST_DEBUG_OBJECT (sink, "setting %s: %s", cur_prop, sink->streamname);
  if (shout_set_name (sink->conn, sink->streamname) != SHOUTERR_SUCCESS)
    goto set_failed;

  cur_prop = "description";
  GST_DEBUG_OBJECT (sink, "setting %s: %s", cur_prop, sink->description);
  if (shout_set_description (sink->conn, sink->description) != SHOUTERR_SUCCESS)
    goto set_failed;

  cur_prop = "genre";
  GST_DEBUG_OBJECT (sink, "setting %s: %s", cur_prop, sink->genre);
  if (shout_set_genre (sink->conn, sink->genre) != SHOUTERR_SUCCESS)
    goto set_failed;

  cur_prop = "mount";
  GST_DEBUG_OBJECT (sink, "setting %s: %s", cur_prop, sink->mount);
  if (shout_set_mount (sink->conn, sink->mount) != SHOUTERR_SUCCESS)
    goto set_failed;

479
  cur_prop = "username";
480
  GST_DEBUG_OBJECT (sink, "setting %s: %s", cur_prop, "source");
481
  if (shout_set_user (sink->conn, sink->username) != SHOUTERR_SUCCESS)
482 483 484 485 486 487 488 489
    goto set_failed;

  version_string = gst_version_string ();
  cur_prop = "agent";
  GST_DEBUG_OBJECT (sink, "setting %s: %s", cur_prop, version_string);
  if (shout_set_agent (sink->conn, version_string) != SHOUTERR_SUCCESS) {
    g_free (version_string);
    goto set_failed;
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
490
  }
Wim Taymans's avatar
Wim Taymans committed
491

492
  g_free (version_string);
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
493
  return TRUE;
494 495 496 497 498 499 500 501

/* ERROR */
set_failed:
  {
    GST_ELEMENT_ERROR (sink, LIBRARY, SETTINGS, (NULL),
        ("Error setting %s: %s", cur_prop, shout_get_error (sink->conn)));
    return FALSE;
  }
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
502
}
Wim Taymans's avatar
Wim Taymans committed
503

504 505
static gboolean
gst_shout2send_connect (GstShout2send * sink)
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
506
{
507
  const char *format =
508
      (sink->audio_format == SHOUT_FORMAT_VORBIS) ? "vorbis" :
509 510 511 512 513 514
      ((sink->audio_format == SHOUT_FORMAT_MP3) ? "mp3" : "unknown");
#ifdef SHOUT_FORMAT_WEBM
  if (sink->audio_format == SHOUT_FORMAT_WEBM)
    format = "webm";
#endif
  GST_DEBUG_OBJECT (sink, "Connection format is: %s", format);
Wim Taymans's avatar
Wim Taymans committed
515

516 517
  if (shout_set_format (sink->conn, sink->audio_format) != SHOUTERR_SUCCESS)
    goto could_not_set_format;
518

519 520
  if (shout_open (sink->conn) != SHOUTERR_SUCCESS)
    goto could_not_connect;
521

522 523
  GST_DEBUG_OBJECT (sink, "connected to server");
  sink->connected = TRUE;
524

525 526 527
  /* let's set metadata */
  if (sink->songmetadata) {
    shout_metadata_t *pmetadata;
528

529 530 531 532 533
    GST_DEBUG_OBJECT (sink, "shout metadata now: %s", sink->songmetadata);
    pmetadata = shout_metadata_new ();
    shout_metadata_add (pmetadata, "song", sink->songmetadata);
    shout_set_metadata (sink->conn, pmetadata);
    shout_metadata_free (pmetadata);
534 535
  }

536
  return TRUE;
537

538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
/* ERRORS */
could_not_set_format:
  {
    GST_ELEMENT_ERROR (sink, LIBRARY, SETTINGS, (NULL),
        ("Error setting connection format: %s", shout_get_error (sink->conn)));
    return FALSE;
  }

could_not_connect:
  {
    GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE,
        (_("Could not connect to server")),
        ("shout_open() failed: err=%s", shout_get_error (sink->conn)));
    g_signal_emit (sink, gst_shout2send_signals[SIGNAL_CONNECTION_PROBLEM], 0,
        shout_get_errno (sink->conn));
    return FALSE;
  }
}

static gboolean
gst_shout2send_stop (GstBaseSink * basesink)
{
  GstShout2send *sink = GST_SHOUT2SEND (basesink);

  if (sink->conn) {
    if (sink->connected)
      shout_close (sink->conn);
    shout_free (sink->conn);
    sink->conn = NULL;
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
567
  }
568 569 570 571

  if (sink->songmetadata) {
    g_free (sink->songmetadata);
    sink->songmetadata = NULL;
572
  }
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
573

574 575 576 577 578
  sink->connected = FALSE;

  return TRUE;
}

Wim Taymans's avatar
Wim Taymans committed
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
static gboolean
gst_shout2send_unlock (GstBaseSink * basesink)
{
  GstShout2send *sink;

  sink = GST_SHOUT2SEND (basesink);

  GST_DEBUG_OBJECT (basesink, "unlock");
  gst_poll_set_flushing (sink->timer, TRUE);

  return TRUE;
}

static gboolean
gst_shout2send_unlock_stop (GstBaseSink * basesink)
{
  GstShout2send *sink;

  sink = GST_SHOUT2SEND (basesink);

  GST_DEBUG_OBJECT (basesink, "unlock_stop");
  gst_poll_set_flushing (sink->timer, FALSE);

  return TRUE;
}

605 606 607 608 609
static GstFlowReturn
gst_shout2send_render (GstBaseSink * basesink, GstBuffer * buf)
{
  GstShout2send *sink;
  glong ret;
Wim Taymans's avatar
Wim Taymans committed
610 611
  gint delay;
  GstFlowReturn fret;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
612
  GstMapInfo map;
613 614 615 616 617 618 619 620 621 622

  sink = GST_SHOUT2SEND (basesink);

  /* presumably we connect here because we need to know the format before
   * we can set up the connection, which we don't know yet in _start() */
  if (!sink->connected) {
    if (!gst_shout2send_connect (sink))
      return GST_FLOW_ERROR;
  }

Wim Taymans's avatar
Wim Taymans committed
623 624
  delay = shout_delay (sink->conn);

625 626 627 628 629 630 631 632 633 634 635
  if (delay > 0) {
    GST_LOG_OBJECT (sink, "waiting %d msec", delay);
    if (gst_poll_wait (sink->timer, GST_MSECOND * delay) == -1) {
      GST_LOG_OBJECT (sink, "unlocked");

      fret = gst_base_sink_wait_preroll (basesink);
      if (fret != GST_FLOW_OK)
        return fret;
    }
  } else {
    GST_LOG_OBJECT (sink, "we're %d msec late", -delay);
Wim Taymans's avatar
Wim Taymans committed
636
  }
637

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
638 639 640 641
  gst_buffer_map (buf, &map, GST_MAP_READ);
  GST_LOG_OBJECT (sink, "sending %u bytes of data", (guint) map.size);
  ret = shout_send (sink->conn, map.data, map.size);
  gst_buffer_unmap (buf, &map);
642 643 644
  if (ret != SHOUTERR_SUCCESS)
    goto send_error;

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
645
  return GST_FLOW_OK;
646 647 648 649 650 651 652 653 654 655

/* ERRORS */
send_error:
  {
    GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL),
        ("shout_send() failed: %s", shout_get_error (sink->conn)));
    g_signal_emit (sink, gst_shout2send_signals[SIGNAL_CONNECTION_PROBLEM], 0,
        shout_get_errno (sink->conn));
    return GST_FLOW_ERROR;
  }
Wim Taymans's avatar
Wim Taymans committed
656 657 658
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
659 660
gst_shout2send_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
Wim Taymans's avatar
Wim Taymans committed
661 662 663
{
  GstShout2send *shout2send;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
664
  shout2send = GST_SHOUT2SEND (object);
Wim Taymans's avatar
Wim Taymans committed
665 666
  switch (prop_id) {

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
667 668
    case ARG_IP:
      if (shout2send->ip)
669
        g_free (shout2send->ip);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
670 671 672 673 674 675 676
      shout2send->ip = g_strdup (g_value_get_string (value));
      break;
    case ARG_PORT:
      shout2send->port = g_value_get_int (value);
      break;
    case ARG_PASSWORD:
      if (shout2send->password)
677
        g_free (shout2send->password);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
678 679
      shout2send->password = g_strdup (g_value_get_string (value));
      break;
680 681 682 683 684
    case ARG_USERNAME:
      if (shout2send->username)
        g_free (shout2send->username);
      shout2send->username = g_strdup (g_value_get_string (value));
      break;
685 686 687
    case ARG_PUBLIC:
      shout2send->ispublic = g_value_get_boolean (value);
      break;
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
688 689 690 691
    case ARG_STREAMNAME:       /* Name of the stream */
      if (shout2send->streamname)
        g_free (shout2send->streamname);
      shout2send->streamname = g_strdup (g_value_get_string (value));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
692
      break;
693
    case ARG_DESCRIPTION:      /* Description of the stream */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
694
      if (shout2send->description)
695
        g_free (shout2send->description);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
696 697
      shout2send->description = g_strdup (g_value_get_string (value));
      break;
698
    case ARG_GENRE:            /* Genre of the stream */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
699
      if (shout2send->genre)
700
        g_free (shout2send->genre);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
701 702
      shout2send->genre = g_strdup (g_value_get_string (value));
      break;
703
    case ARG_PROTOCOL:         /* protocol to connect with */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
704 705
      shout2send->protocol = g_value_get_enum (value);
      break;
706
    case ARG_MOUNT:            /* mountpoint of stream (icecast only) */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
707
      if (shout2send->mount)
708
        g_free (shout2send->mount);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
709 710
      shout2send->mount = g_strdup (g_value_get_string (value));
      break;
711
    case ARG_URL:              /* Url of the stream (I'm guessing) */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
712
      if (shout2send->url)
713
        g_free (shout2send->url);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
714 715 716
      shout2send->url = g_strdup (g_value_get_string (value));
      break;
    default:
717
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
718
      break;
Wim Taymans's avatar
Wim Taymans committed
719 720 721 722
  }
}

static void
723 724
gst_shout2send_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
Wim Taymans's avatar
Wim Taymans committed
725 726 727
{
  GstShout2send *shout2send;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
728
  shout2send = GST_SHOUT2SEND (object);
Wim Taymans's avatar
Wim Taymans committed
729
  switch (prop_id) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
730 731 732 733 734 735 736 737 738 739

    case ARG_IP:
      g_value_set_string (value, shout2send->ip);
      break;
    case ARG_PORT:
      g_value_set_int (value, shout2send->port);
      break;
    case ARG_PASSWORD:
      g_value_set_string (value, shout2send->password);
      break;
740 741
    case ARG_USERNAME:
      g_value_set_string (value, shout2send->username);
742 743 744
      break;
    case ARG_PUBLIC:
      g_value_set_boolean (value, shout2send->ispublic);
745
      break;
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
746 747
    case ARG_STREAMNAME:       /* Name of the stream */
      g_value_set_string (value, shout2send->streamname);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
748
      break;
749
    case ARG_DESCRIPTION:      /* Description of the stream */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
750 751
      g_value_set_string (value, shout2send->description);
      break;
752
    case ARG_GENRE:            /* Genre of the stream */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
753 754
      g_value_set_string (value, shout2send->genre);
      break;
755
    case ARG_PROTOCOL:         /* protocol to connect with */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
756 757
      g_value_set_enum (value, shout2send->protocol);
      break;
758
    case ARG_MOUNT:            /* mountpoint of stream (icecast only) */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
759 760
      g_value_set_string (value, shout2send->mount);
      break;
761
    case ARG_URL:              /* Url of stream (I'm guessing) */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
762 763 764 765 766
      g_value_set_string (value, shout2send->url);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
Wim Taymans's avatar
Wim Taymans committed
767 768 769
  }
}

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
770
static gboolean
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
771
gst_shout2send_setcaps (GstBaseSink * basesink, GstCaps * caps)
Wim Taymans's avatar
Wim Taymans committed
772
{
773
  const gchar *mimetype;
774
  GstShout2send *shout2send;
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
775
  gboolean ret = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
776

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
777
  shout2send = GST_SHOUT2SEND (basesink);
778

779
  mimetype = gst_structure_get_name (gst_caps_get_structure (caps, 0));
780 781 782

  GST_DEBUG_OBJECT (shout2send, "mimetype of caps given is: %s", mimetype);

783
  if (!strcmp (mimetype, "audio/mpeg")) {
784
    shout2send->audio_format = SHOUT_FORMAT_MP3;
785
  } else if (!strcmp (mimetype, "application/ogg")) {
786
    shout2send->audio_format = SHOUT_FORMAT_VORBIS;
787 788 789 790
#ifdef SHOUT_FORMAT_WEBM
  } else if (!strcmp (mimetype, "video/webm")) {
    shout2send->audio_format = SHOUT_FORMAT_WEBM;
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
791
  } else {
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
792
    ret = FALSE;
Wim Taymans's avatar
Wim Taymans committed
793
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
794

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
795
  return ret;
Wim Taymans's avatar
Wim Taymans committed
796 797 798
}

static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
799
plugin_init (GstPlugin * plugin)
Wim Taymans's avatar
Wim Taymans committed
800
{
801 802 803
#ifdef ENABLE_NLS
  setlocale (LC_ALL, "");
  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
804
  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
805 806
#endif /* ENABLE_NLS */

Ronald S. Bultje's avatar
Ronald S. Bultje committed
807
  return gst_element_register (plugin, "shout2send", GST_RANK_NONE,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
808
      GST_TYPE_SHOUT2SEND);
Wim Taymans's avatar
Wim Taymans committed
809 810
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
811 812
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
813
    shout2send,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
814 815 816
    "Sends data to an icecast server using libshout2",
    plugin_init,
    VERSION, "LGPL", "libshout2", "http://www.icecast.org/download.html")