gstshout2.c 26.3 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
 *
 * 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
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
18 19
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
Wim Taymans's avatar
Wim Taymans committed
20 21
 */

22 23 24 25 26 27 28 29
/**
 * SECTION:element-shout2send
 *
 * shout2send pushes a media stream to an Icecast server
 *
 * <refsect2>
 * <title>Example launch line</title>
 * |[
30
 * gst-launch-1.0 uridecodebin uri=file:///path/to/audiofile ! audioconvert ! vorbisenc ! oggmux ! shout2send mount=/stream.ogg port=8000 username=source password=somepassword ip=server_IP_address_or_hostname
31 32 33 34 35 36 37
 * ]| This pipeline demuxes, decodes, re-encodes and re-muxes an audio
 * media file into oggvorbis and sends the resulting stream to an Icecast
 * server. Properties mount, port, username and password are all server-config
 * dependent.
 * </refsect2>
 */

38 39 40
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
41

Wim Taymans's avatar
Wim Taymans committed
42 43
#include "gstshout2.h"
#include <stdlib.h>
44
#include <string.h>
Wim Taymans's avatar
Wim Taymans committed
45

46 47 48
#include "gst/gst-i18n-plugin.h"

GST_DEBUG_CATEGORY_STATIC (shout2_debug);
49 50
#define GST_CAT_DEFAULT shout2_debug

Wim Taymans's avatar
Wim Taymans committed
51

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
52 53
enum
{
54
  SIGNAL_CONNECTION_PROBLEM,    /* FIXME 2.0: remove this */
Wim Taymans's avatar
Wim Taymans committed
55 56 57
  LAST_SIGNAL
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
58 59
enum
{
Wim Taymans's avatar
Wim Taymans committed
60
  ARG_0,
61
  ARG_IP,                       /* the IP address or hostname of the server */
62 63
  ARG_PORT,                     /* the encoder port number on the server */
  ARG_PASSWORD,                 /* the encoder password on the server */
64
  ARG_USERNAME,                 /* the encoder username on the server */
65
  ARG_PUBLIC,                   /* is this stream public? */
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
66
  ARG_STREAMNAME,               /* Name of the stream */
67 68 69 70 71 72
  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) */
73
  ARG_URL                       /* the stream's homepage URL */
Wim Taymans's avatar
Wim Taymans committed
74 75
};

76 77 78
#define DEFAULT_IP           "127.0.0.1"
#define DEFAULT_PORT         8000
#define DEFAULT_PASSWORD     "hackme"
79
#define DEFAULT_USERNAME     "source"
80
#define DEFAULT_PUBLIC     FALSE
81 82 83 84 85 86 87
#define DEFAULT_STREAMNAME   ""
#define DEFAULT_DESCRIPTION  ""
#define DEFAULT_GENRE        ""
#define DEFAULT_MOUNT        ""
#define DEFAULT_URL          ""
#define DEFAULT_PROTOCOL     SHOUT2SEND_PROTOCOL_HTTP

88
#ifdef SHOUT_FORMAT_WEBM
89
#define WEBM_CAPS "; video/webm; audio/webm"
90 91 92
#else
#define WEBM_CAPS ""
#endif
93 94 95
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
96
    GST_STATIC_CAPS ("application/ogg; audio/ogg; video/ogg; "
97 98
        "audio/mpeg, mpegversion = (int) 1, layer = (int) [ 1, 3 ]" WEBM_CAPS));

99
static void gst_shout2send_finalize (GstShout2send * shout2send);
Wim Taymans's avatar
Wim Taymans committed
100

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
101
static gboolean gst_shout2send_event (GstBaseSink * sink, GstEvent * event);
Wim Taymans's avatar
Wim Taymans committed
102 103
static gboolean gst_shout2send_unlock (GstBaseSink * basesink);
static gboolean gst_shout2send_unlock_stop (GstBaseSink * basesink);
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
104 105
static GstFlowReturn gst_shout2send_render (GstBaseSink * sink,
    GstBuffer * buffer);
106 107
static gboolean gst_shout2send_start (GstBaseSink * basesink);
static gboolean gst_shout2send_stop (GstBaseSink * basesink);
Wim Taymans's avatar
Wim Taymans committed
108

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
109 110 111 112
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
113

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

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
118 119
#define GST_TYPE_SHOUT_PROTOCOL (gst_shout2send_protocol_get_type())
static GType
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
120
gst_shout2send_protocol_get_type (void)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
121 122
{
  static GType shout2send_protocol_type = 0;
123
  static const GEnumValue shout2send_protocol[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
124 125 126 127
    {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
128 129
    {0, NULL, NULL},
  };
130

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
131
  if (!shout2send_protocol_type) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
132
    shout2send_protocol_type =
133
        g_enum_register_static ("GstShout2SendProtocol", shout2send_protocol);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
134
  }
135 136


Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
137 138 139
  return shout2send_protocol_type;
}

140 141 142
#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
143 144

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
145
gst_shout2send_class_init (GstShout2sendClass * klass)
Wim Taymans's avatar
Wim Taymans committed
146 147
{
  GObjectClass *gobject_class;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
148
  GstElementClass *gstelement_class;
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
149
  GstBaseSinkClass *gstbasesink_class;
Wim Taymans's avatar
Wim Taymans committed
150

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
151
  gobject_class = (GObjectClass *) klass;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
152
  gstelement_class = (GstElementClass *) klass;
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
153 154
  gstbasesink_class = (GstBaseSinkClass *) klass;

155
  parent_class = g_type_class_peek_parent (klass);
Wim Taymans's avatar
Wim Taymans committed
156

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
157 158
  gobject_class->set_property = gst_shout2send_set_property;
  gobject_class->get_property = gst_shout2send_get_property;
159
  gobject_class->finalize = (GObjectFinalizeFunc) gst_shout2send_finalize;
Wim Taymans's avatar
Wim Taymans committed
160

161
  /* FIXME: 2.0 Should probably change this prop name to "server" */
162
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_IP,
163
      g_param_spec_string ("ip", "ip", "IP address or hostname", DEFAULT_IP,
164
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
165 166
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PORT,
      g_param_spec_int ("port", "port", "port", 1, G_MAXUSHORT, DEFAULT_PORT,
167
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
168

169 170
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PASSWORD,
      g_param_spec_string ("password", "password", "password", DEFAULT_PASSWORD,
171
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
172

173 174
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_USERNAME,
      g_param_spec_string ("username", "username", "username", DEFAULT_USERNAME,
175
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
176

Wim Taymans's avatar
Wim Taymans committed
177
  /* metadata */
178 179 180
  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",
181
          DEFAULT_PUBLIC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
182

183 184
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_STREAMNAME,
      g_param_spec_string ("streamname", "streamname", "name of the stream",
185
          DEFAULT_STREAMNAME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
186

187 188
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DESCRIPTION,
      g_param_spec_string ("description", "description", "description",
189
          DEFAULT_DESCRIPTION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
190

191 192
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_GENRE,
      g_param_spec_string ("genre", "genre", "genre", DEFAULT_GENRE,
193
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
194

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
195
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PROTOCOL,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
196
      g_param_spec_enum ("protocol", "protocol", "Connection Protocol to use",
197 198
          GST_TYPE_SHOUT_PROTOCOL, DEFAULT_PROTOCOL,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
199 200


Wim Taymans's avatar
Wim Taymans committed
201
  /* icecast only */
202 203
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MOUNT,
      g_param_spec_string ("mount", "mount", "mount", DEFAULT_MOUNT,
204
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
205

206
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_URL,
207 208
      g_param_spec_string ("url", "url", "the stream's homepage URL",
          DEFAULT_URL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
209

210 211 212 213 214 215
  /* 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);
216

217 218
  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
219 220 221
  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
222 223
  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
224 225 226 227 228
  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));

229
  gst_element_class_set_static_metadata (gstelement_class,
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
230 231 232 233 234 235 236
      "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
237 238 239
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
240
gst_shout2send_init (GstShout2send * shout2send)
Wim Taymans's avatar
Wim Taymans committed
241
{
242 243
  gst_base_sink_set_sync (GST_BASE_SINK (shout2send), FALSE);

Wim Taymans's avatar
Wim Taymans committed
244 245
  shout2send->timer = gst_poll_new_timer ();

246 247 248
  shout2send->ip = g_strdup (DEFAULT_IP);
  shout2send->port = DEFAULT_PORT;
  shout2send->password = g_strdup (DEFAULT_PASSWORD);
249
  shout2send->username = g_strdup (DEFAULT_USERNAME);
250 251 252 253 254 255
  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;
256
  shout2send->ispublic = DEFAULT_PUBLIC;
257

258
  shout2send->format = -1;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
259
  shout2send->tags = gst_tag_list_new_empty ();
260
  shout2send->conn = NULL;
261
  shout2send->connected = FALSE;
262
  shout2send->songmetadata = NULL;
263 264
  shout2send->songartist = NULL;
  shout2send->songtitle = NULL;
265
}
266

267 268 269 270 271 272 273 274 275 276 277 278
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);

279
  gst_tag_list_unref (shout2send->tags);
280

Wim Taymans's avatar
Wim Taymans committed
281 282
  gst_poll_free (shout2send->timer);

283
  G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (shout2send));
284 285 286 287 288 289
}

static void
set_shout_metadata (const GstTagList * list, const gchar * tag,
    gpointer user_data)
{
290 291 292 293 294 295
  GstShout2send *shout2send = (GstShout2send *) user_data;
  char **shout_metadata = &(shout2send->songmetadata);
  char **song_artist = &(shout2send->songartist);
  char **song_title = &(shout2send->songtitle);

  gchar *value;
296 297 298 299 300

  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)) {
301
        GST_DEBUG ("Error reading \"%s\" tag value", tag);
302 303 304
        return;
      }

305 306 307 308
      if (*song_artist != NULL)
        g_free (*song_artist);

      *song_artist = g_strdup (value);
309 310 311 312
    }
  } 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)) {
313
        GST_DEBUG ("Error reading \"%s\" tag value", tag);
314 315
        return;
      }
316 317 318 319 320

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

      *song_title = g_strdup (value);
321 322
    }
  }
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337

  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");
  }

338
  GST_LOG ("shout metadata is now: %s", *shout_metadata);
339 340
}

341
#if 0
342 343 344 345 346 347 348 349 350
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);
351
  user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (shout2send));
352 353 354 355
  if ((shout2send->tags == NULL) && (user_tags == NULL)) {
    return;
  }
  copy = gst_tag_list_merge (user_tags, shout2send->tags,
356
      gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (shout2send)));
357 358 359 360 361 362 363 364 365 366 367
  /* 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);
  }

368
  gst_tag_list_unref (copy);
Wim Taymans's avatar
Wim Taymans committed
369
}
370
#endif
Wim Taymans's avatar
Wim Taymans committed
371

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
372 373 374

static gboolean
gst_shout2send_event (GstBaseSink * sink, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
375 376
{
  GstShout2send *shout2send;
377
  gboolean ret = TRUE;
378

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
379 380
  shout2send = GST_SHOUT2SEND (sink);

381 382
  GST_LOG_OBJECT (shout2send, "got %s event", GST_EVENT_TYPE_NAME (event));

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
383
  switch (GST_EVENT_TYPE (event)) {
384
    case GST_EVENT_TAG:{
385 386
      /* vorbis audio doesn't need metadata setting on the icecast level, only mp3 */
      if (shout2send->tags && shout2send->format == SHOUT_FORMAT_MP3) {
387 388 389
        GstTagList *list;

        gst_event_parse_tag (event, &list);
390
        GST_DEBUG_OBJECT (shout2send, "tags=%" GST_PTR_FORMAT, list);
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
391
        gst_tag_list_insert (shout2send->tags,
392 393
            list,
            gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (shout2send)));
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
394
        /* lets get the artist and song tags */
395 396
        gst_tag_list_foreach ((GstTagList *) list,
            set_shout_metadata, shout2send);
397
        if (shout2send->songmetadata && shout2send->connected) {
398 399
          shout_metadata_t *pmetadata;

400 401 402
          GST_DEBUG_OBJECT (shout2send, "metadata now: %s",
              shout2send->songmetadata);

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
403
          pmetadata = shout_metadata_new ();
404
          shout_metadata_add (pmetadata, "song", shout2send->songmetadata);
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
405 406 407 408 409
          shout_set_metadata (shout2send->conn, pmetadata);
          shout_metadata_free (pmetadata);
        }
      }
      break;
410 411 412 413 414 415 416
    }
    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
417
      break;
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 453
    }
  }

  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;

  cur_prop = "ip";
454
  GST_DEBUG_OBJECT (sink, "setting IP/hostname: %s", sink->ip);
455 456 457 458 459 460 461 462 463 464 465 466 467
  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;

468 469 470 471 472 473
  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;

474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
  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;

494
  cur_prop = "username";
495
  GST_DEBUG_OBJECT (sink, "setting %s: %s", cur_prop, "source");
496
  if (shout_set_user (sink->conn, sink->username) != SHOUTERR_SUCCESS)
497 498 499 500 501 502 503 504
    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
505
  }
Wim Taymans's avatar
Wim Taymans committed
506

507
  g_free (version_string);
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
508
  return TRUE;
509 510 511 512 513 514 515 516

/* 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
517
}
Wim Taymans's avatar
Wim Taymans committed
518

519
static GstFlowReturn
520
gst_shout2send_connect (GstShout2send * sink)
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
521
{
522 523 524 525
  GST_DEBUG_OBJECT (sink, "Connection format is: %d", sink->format);

  if (sink->format == -1)
    goto no_caps;
Wim Taymans's avatar
Wim Taymans committed
526

527
  if (shout_set_format (sink->conn, sink->format) != SHOUTERR_SUCCESS)
528
    goto could_not_set_format;
529

530 531
  if (shout_open (sink->conn) != SHOUTERR_SUCCESS)
    goto could_not_connect;
532

533 534
  GST_DEBUG_OBJECT (sink, "connected to server");
  sink->connected = TRUE;
535

536 537 538
  /* let's set metadata */
  if (sink->songmetadata) {
    shout_metadata_t *pmetadata;
539

540 541 542 543 544
    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);
545 546
  }

547
  return GST_FLOW_OK;
548

549
/* ERRORS */
550 551 552 553 554 555 556
no_caps:
  {
    GST_ELEMENT_ERROR (sink, CORE, NEGOTIATION, (NULL),
        ("No input caps received."));
    return GST_FLOW_NOT_NEGOTIATED;
  }

557 558 559 560
could_not_set_format:
  {
    GST_ELEMENT_ERROR (sink, LIBRARY, SETTINGS, (NULL),
        ("Error setting connection format: %s", shout_get_error (sink->conn)));
561
    return GST_FLOW_ERROR;
562 563 564 565 566 567 568 569 570
  }

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));
571
    return GST_FLOW_ERROR;
572 573 574 575 576 577 578 579 580 581 582 583 584
  }
}

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
585
  }
586 587 588 589

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

592
  sink->connected = FALSE;
593
  sink->format = -1;
594 595 596 597

  return TRUE;
}

Wim Taymans's avatar
Wim Taymans committed
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
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;
}

624 625 626 627 628
static GstFlowReturn
gst_shout2send_render (GstBaseSink * basesink, GstBuffer * buf)
{
  GstShout2send *sink;
  glong ret;
Wim Taymans's avatar
Wim Taymans committed
629
  gint delay;
630
  GstFlowReturn fret = GST_FLOW_OK;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
631
  GstMapInfo map;
632 633 634

  sink = GST_SHOUT2SEND (basesink);

635 636 637
  /* 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(), and also because we
   * don't want to block the application thread */
638
  if (!sink->connected) {
639 640 641
    fret = gst_shout2send_connect (sink);
    if (fret != GST_FLOW_OK)
      goto done;
642 643
  }

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

646 647 648 649 650 651 652
  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)
653
        goto done;
654 655 656
    }
  } else {
    GST_LOG_OBJECT (sink, "we're %d msec late", -delay);
Wim Taymans's avatar
Wim Taymans committed
657
  }
658

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
659 660 661 662
  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);
663 664 665
  if (ret != SHOUTERR_SUCCESS)
    goto send_error;

666 667 668
done:

  return fret;
669 670 671 672 673 674 675 676 677 678

/* 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
679 680 681
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
682 683
gst_shout2send_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
Wim Taymans's avatar
Wim Taymans committed
684 685 686
{
  GstShout2send *shout2send;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
687
  shout2send = GST_SHOUT2SEND (object);
Wim Taymans's avatar
Wim Taymans committed
688 689
  switch (prop_id) {

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
690 691
    case ARG_IP:
      if (shout2send->ip)
692
        g_free (shout2send->ip);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
693 694 695 696 697 698 699
      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)
700
        g_free (shout2send->password);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
701 702
      shout2send->password = g_strdup (g_value_get_string (value));
      break;
703 704 705 706 707
    case ARG_USERNAME:
      if (shout2send->username)
        g_free (shout2send->username);
      shout2send->username = g_strdup (g_value_get_string (value));
      break;
708 709 710
    case ARG_PUBLIC:
      shout2send->ispublic = g_value_get_boolean (value);
      break;
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
711 712 713 714
    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
715
      break;
716
    case ARG_DESCRIPTION:      /* Description of the stream */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
717
      if (shout2send->description)
718
        g_free (shout2send->description);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
719 720
      shout2send->description = g_strdup (g_value_get_string (value));
      break;
721
    case ARG_GENRE:            /* Genre of the stream */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
722
      if (shout2send->genre)
723
        g_free (shout2send->genre);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
724 725
      shout2send->genre = g_strdup (g_value_get_string (value));
      break;
726
    case ARG_PROTOCOL:         /* protocol to connect with */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
727 728
      shout2send->protocol = g_value_get_enum (value);
      break;
729
    case ARG_MOUNT:            /* mountpoint of stream (icecast only) */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
730
      if (shout2send->mount)
731
        g_free (shout2send->mount);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
732 733
      shout2send->mount = g_strdup (g_value_get_string (value));
      break;
734
    case ARG_URL:              /* the stream's homepage URL */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
735
      if (shout2send->url)
736
        g_free (shout2send->url);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
737 738 739
      shout2send->url = g_strdup (g_value_get_string (value));
      break;
    default:
740
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
741
      break;
Wim Taymans's avatar
Wim Taymans committed
742 743 744 745
  }
}

static void
746 747
gst_shout2send_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
Wim Taymans's avatar
Wim Taymans committed
748 749 750
{
  GstShout2send *shout2send;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
751
  shout2send = GST_SHOUT2SEND (object);
Wim Taymans's avatar
Wim Taymans committed
752
  switch (prop_id) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
753 754 755 756 757 758 759 760 761 762

    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;
763 764
    case ARG_USERNAME:
      g_value_set_string (value, shout2send->username);
765 766 767
      break;
    case ARG_PUBLIC:
      g_value_set_boolean (value, shout2send->ispublic);
768
      break;
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
769 770
    case ARG_STREAMNAME:       /* Name of the stream */
      g_value_set_string (value, shout2send->streamname);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
771
      break;
772
    case ARG_DESCRIPTION:      /* Description of the stream */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
773 774
      g_value_set_string (value, shout2send->description);
      break;
775
    case ARG_GENRE:            /* Genre of the stream */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
776 777
      g_value_set_string (value, shout2send->genre);
      break;
778
    case ARG_PROTOCOL:         /* protocol to connect with */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
779 780
      g_value_set_enum (value, shout2send->protocol);
      break;
781
    case ARG_MOUNT:            /* mountpoint of stream (icecast only) */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
782 783
      g_value_set_string (value, shout2send->mount);
      break;
784
    case ARG_URL:              /* the stream's homepage URL */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
785 786 787 788 789
      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
790 791 792
  }
}

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
793
static gboolean
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
794
gst_shout2send_setcaps (GstBaseSink * basesink, GstCaps * caps)
Wim Taymans's avatar
Wim Taymans committed
795
{
796
  const gchar *mimetype;
797
  GstShout2send *shout2send;
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
798
  gboolean ret = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
799

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
800
  shout2send = GST_SHOUT2SEND (basesink);
801

802
  mimetype = gst_structure_get_name (gst_caps_get_structure (caps, 0));
803 804 805

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

806
  if (!strcmp (mimetype, "audio/mpeg")) {
807
    shout2send->format = SHOUT_FORMAT_MP3;
808
  } else if (g_str_has_suffix (mimetype, "/ogg")) {
809
    shout2send->format = SHOUT_FORMAT_OGG;
810
#ifdef SHOUT_FORMAT_WEBM
811
  } else if (g_str_has_suffix (mimetype, "/webm")) {
812
    shout2send->format = SHOUT_FORMAT_WEBM;
813
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
814
  } else {
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
815
    ret = FALSE;
Wim Taymans's avatar
Wim Taymans committed
816
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
817

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
818
  return ret;
Wim Taymans's avatar
Wim Taymans committed
819 820 821
}

static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
822
plugin_init (GstPlugin * plugin)
Wim Taymans's avatar
Wim Taymans committed
823
{
824 825
#ifdef ENABLE_NLS
  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
826
  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
827 828
#endif /* ENABLE_NLS */

Ronald S. Bultje's avatar
Ronald S. Bultje committed
829
  return gst_element_register (plugin, "shout2send", GST_RANK_NONE,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
830
      GST_TYPE_SHOUT2SEND);
Wim Taymans's avatar
Wim Taymans committed
831 832
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
833 834
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
835
    shout2send,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
836 837 838
    "Sends data to an icecast server using libshout2",
    plugin_init,
    VERSION, "LGPL", "libshout2", "http://www.icecast.org/download.html")