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>
Wim Taymans's avatar
Wim Taymans committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * 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.
 */

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

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

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

GST_DEBUG_CATEGORY_STATIC (shout2_debug);
32 33
#define GST_CAT_DEFAULT shout2_debug

34
static const GstElementDetails shout2send_details =
35 36 37 38 39 40
GST_ELEMENT_DETAILS ("Icecast network sink",
    "Sink/Network",
    "Sends data to an icecast server",
    "Wim Taymans <wim.taymans@chello.be>\n"
    "Pedro Corte-Real <typo@netcabo.pt>\n"
    "Zaheer Abbas Merali <zaheerabbas at merali dot org>");
Wim Taymans's avatar
Wim Taymans committed
41 42


Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
43 44
enum
{
45
  SIGNAL_CONNECTION_PROBLEM,    /* 0.11 FIXME: remove this */
Wim Taymans's avatar
Wim Taymans committed
46 47 48
  LAST_SIGNAL
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
49 50
enum
{
Wim Taymans's avatar
Wim Taymans committed
51
  ARG_0,
52 53 54
  ARG_IP,                       /* the ip of the server */
  ARG_PORT,                     /* the encoder port number on the server */
  ARG_PASSWORD,                 /* the encoder password on the server */
55
  ARG_USERNAME,                 /* the encoder username on the server */
56
  ARG_PUBLIC,                   /* is this stream public? */
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
57
  ARG_STREAMNAME,               /* Name of the stream */
58 59 60 61 62 63
  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) */
64
  ARG_URL                       /* Url of stream (I'm guessing) */
Wim Taymans's avatar
Wim Taymans committed
65 66
};

67 68 69
#define DEFAULT_IP           "127.0.0.1"
#define DEFAULT_PORT         8000
#define DEFAULT_PASSWORD     "hackme"
70
#define DEFAULT_USERNAME     "source"
71
#define DEFAULT_PUBLIC     FALSE
72 73 74 75 76 77 78
#define DEFAULT_STREAMNAME   ""
#define DEFAULT_DESCRIPTION  ""
#define DEFAULT_GENRE        ""
#define DEFAULT_MOUNT        ""
#define DEFAULT_URL          ""
#define DEFAULT_PROTOCOL     SHOUT2SEND_PROTOCOL_HTTP

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
79 80
static GstElementClass *parent_class = NULL;

81 82 83
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
84
    GST_STATIC_CAPS ("application/ogg; "
85 86
        "audio/mpeg, mpegversion = (int) 1, layer = (int) [ 1, 3 ]")
    );
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
87 88 89
static void gst_shout2send_class_init (GstShout2sendClass * klass);
static void gst_shout2send_base_init (GstShout2sendClass * klass);
static void gst_shout2send_init (GstShout2send * shout2send);
90
static void gst_shout2send_finalize (GstShout2send * shout2send);
Wim Taymans's avatar
Wim Taymans committed
91

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
92
static gboolean gst_shout2send_event (GstBaseSink * sink, GstEvent * event);
93 94
static gboolean gst_shout2send_unlock (GstBaseSink * basesink);
static gboolean gst_shout2send_unlock_stop (GstBaseSink * basesink);
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
95 96
static GstFlowReturn gst_shout2send_render (GstBaseSink * sink,
    GstBuffer * buffer);
97 98
static gboolean gst_shout2send_start (GstBaseSink * basesink);
static gboolean gst_shout2send_stop (GstBaseSink * basesink);
Wim Taymans's avatar
Wim Taymans committed
99

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
100 101 102 103
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
104

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
105
static gboolean gst_shout2send_setcaps (GstPad * pad, GstCaps * caps);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
106

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
109 110
#define GST_TYPE_SHOUT_PROTOCOL (gst_shout2send_protocol_get_type())
static GType
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
111
gst_shout2send_protocol_get_type (void)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
112 113
{
  static GType shout2send_protocol_type = 0;
114
  static const GEnumValue shout2send_protocol[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
115 116 117 118
    {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
119 120
    {0, NULL, NULL},
  };
121

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
122
  if (!shout2send_protocol_type) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
123
    shout2send_protocol_type =
124
        g_enum_register_static ("GstShout2SendProtocol", shout2send_protocol);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
125
  }
126 127


Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
128 129 130
  return shout2send_protocol_type;
}

Wim Taymans's avatar
Wim Taymans committed
131
GType
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
132
gst_shout2send_get_type (void)
Wim Taymans's avatar
Wim Taymans committed
133 134 135 136 137
{
  static GType shout2send_type = 0;

  if (!shout2send_type) {
    static const GTypeInfo shout2send_info = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
138 139
      sizeof (GstShout2sendClass),
      (GBaseInitFunc) gst_shout2send_base_init,
Wim Taymans's avatar
Wim Taymans committed
140
      NULL,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
141
      (GClassInitFunc) gst_shout2send_class_init,
Wim Taymans's avatar
Wim Taymans committed
142 143
      NULL,
      NULL,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
144
      sizeof (GstShout2send),
Wim Taymans's avatar
Wim Taymans committed
145
      0,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
146
      (GInstanceInitFunc) gst_shout2send_init,
Wim Taymans's avatar
Wim Taymans committed
147
    };
148

149 150 151 152 153 154
    static const GInterfaceInfo tag_setter_info = {
      NULL,
      NULL,
      NULL
    };

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
155
    shout2send_type =
156
        g_type_register_static (GST_TYPE_BASE_SINK, "GstShout2send",
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
157 158
        &shout2send_info, 0);

159 160 161
    g_type_add_interface_static (shout2send_type, GST_TYPE_TAG_SETTER,
        &tag_setter_info);

Wim Taymans's avatar
Wim Taymans committed
162 163 164 165
  }
  return shout2send_type;
}

Ronald S. Bultje's avatar
Ronald S. Bultje committed
166
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
167
gst_shout2send_base_init (GstShout2sendClass * klass)
Ronald S. Bultje's avatar
Ronald S. Bultje committed
168 169 170
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);

171 172
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&sink_template));
Ronald S. Bultje's avatar
Ronald S. Bultje committed
173
  gst_element_class_set_details (element_class, &shout2send_details);
174 175

  GST_DEBUG_CATEGORY_INIT (shout2_debug, "shout2", 0, "shout2send element");
Ronald S. Bultje's avatar
Ronald S. Bultje committed
176 177
}

Wim Taymans's avatar
Wim Taymans committed
178
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
179
gst_shout2send_class_init (GstShout2sendClass * klass)
Wim Taymans's avatar
Wim Taymans committed
180 181
{
  GObjectClass *gobject_class;
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
182
  GstBaseSinkClass *gstbasesink_class;
Wim Taymans's avatar
Wim Taymans committed
183

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
184
  gobject_class = (GObjectClass *) klass;
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
185 186
  gstbasesink_class = (GstBaseSinkClass *) klass;

187
  parent_class = g_type_class_peek_parent (klass);
Wim Taymans's avatar
Wim Taymans committed
188

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
189 190
  gobject_class->set_property = gst_shout2send_set_property;
  gobject_class->get_property = gst_shout2send_get_property;
191
  gobject_class->finalize = (GObjectFinalizeFunc) gst_shout2send_finalize;
Wim Taymans's avatar
Wim Taymans committed
192

193 194 195 196 197
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_IP,
      g_param_spec_string ("ip", "ip", "ip", DEFAULT_IP, G_PARAM_READWRITE));
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PORT,
      g_param_spec_int ("port", "port", "port", 1, G_MAXUSHORT, DEFAULT_PORT,
          G_PARAM_READWRITE));
Wim Taymans's avatar
Wim Taymans committed
198

199 200 201
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PASSWORD,
      g_param_spec_string ("password", "password", "password", DEFAULT_PASSWORD,
          G_PARAM_READWRITE));
Wim Taymans's avatar
Wim Taymans committed
202

203 204 205 206
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_USERNAME,
      g_param_spec_string ("username", "username", "username", DEFAULT_USERNAME,
          G_PARAM_READWRITE));

Wim Taymans's avatar
Wim Taymans committed
207
  /* metadata */
208 209 210 211 212
  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",
          DEFAULT_PUBLIC, G_PARAM_READWRITE));

213 214 215
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_STREAMNAME,
      g_param_spec_string ("streamname", "streamname", "name of the stream",
          DEFAULT_STREAMNAME, G_PARAM_READWRITE));
Wim Taymans's avatar
Wim Taymans committed
216

217 218 219
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DESCRIPTION,
      g_param_spec_string ("description", "description", "description",
          DEFAULT_DESCRIPTION, G_PARAM_READWRITE));
Wim Taymans's avatar
Wim Taymans committed
220

221 222 223
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_GENRE,
      g_param_spec_string ("genre", "genre", "genre", DEFAULT_GENRE,
          G_PARAM_READWRITE));
Wim Taymans's avatar
Wim Taymans committed
224

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
225
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PROTOCOL,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
226
      g_param_spec_enum ("protocol", "protocol", "Connection Protocol to use",
227
          GST_TYPE_SHOUT_PROTOCOL, DEFAULT_PROTOCOL, G_PARAM_READWRITE));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
228 229


Wim Taymans's avatar
Wim Taymans committed
230
  /* icecast only */
231 232 233
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MOUNT,
      g_param_spec_string ("mount", "mount", "mount", DEFAULT_MOUNT,
          G_PARAM_READWRITE));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
234

235 236
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_URL,
      g_param_spec_string ("url", "url", "url", DEFAULT_URL,
237
          G_PARAM_READWRITE));
Wim Taymans's avatar
Wim Taymans committed
238

239 240 241 242 243 244
  /* 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);
245

246 247
  gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_shout2send_start);
  gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_shout2send_stop);
248 249 250
  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
251 252
  gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_shout2send_render);
  gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_shout2send_event);
Wim Taymans's avatar
Wim Taymans committed
253 254 255
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
256
gst_shout2send_init (GstShout2send * shout2send)
Wim Taymans's avatar
Wim Taymans committed
257
{
258 259 260 261 262
  gst_base_sink_set_sync (GST_BASE_SINK (shout2send), FALSE);

  gst_pad_set_setcaps_function (GST_BASE_SINK_PAD (shout2send),
      GST_DEBUG_FUNCPTR (gst_shout2send_setcaps));

263 264
  shout2send->timer = gst_poll_new_timer ();

265 266 267
  shout2send->ip = g_strdup (DEFAULT_IP);
  shout2send->port = DEFAULT_PORT;
  shout2send->password = g_strdup (DEFAULT_PASSWORD);
268
  shout2send->username = g_strdup (DEFAULT_USERNAME);
269 270 271 272 273 274
  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;
275
  shout2send->ispublic = DEFAULT_PUBLIC;
276

277 278 279
  shout2send->tags = gst_tag_list_new ();
  shout2send->conn = NULL;
  shout2send->audio_format = SHOUT_FORMAT_VORBIS;
280
  shout2send->connected = FALSE;
281
  shout2send->songmetadata = NULL;
282 283
  shout2send->songartist = NULL;
  shout2send->songtitle = NULL;
284
}
285

286 287 288 289 290 291 292 293 294 295 296 297 298 299
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);

300 301
  gst_poll_free (shout2send->timer);

302
  G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (shout2send));
303 304 305 306 307 308
}

static void
set_shout_metadata (const GstTagList * list, const gchar * tag,
    gpointer user_data)
{
309 310 311 312 313 314
  GstShout2send *shout2send = (GstShout2send *) user_data;
  char **shout_metadata = &(shout2send->songmetadata);
  char **song_artist = &(shout2send->songartist);
  char **song_title = &(shout2send->songtitle);

  gchar *value;
315 316 317 318 319

  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)) {
320
        GST_DEBUG ("Error reading \"%s\" tag value", tag);
321 322 323
        return;
      }

324 325 326 327
      if (*song_artist != NULL)
        g_free (*song_artist);

      *song_artist = g_strdup (value);
328 329 330 331
    }
  } 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)) {
332
        GST_DEBUG ("Error reading \"%s\" tag value", tag);
333 334
        return;
      }
335 336 337 338 339

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

      *song_title = g_strdup (value);
340 341
    }
  }
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356

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

357
  GST_LOG ("shout metadata is now: %s", *shout_metadata);
358 359
}

360
#if 0
361 362 363 364 365 366 367 368 369
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);
370
  user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (shout2send));
371 372 373 374
  if ((shout2send->tags == NULL) && (user_tags == NULL)) {
    return;
  }
  copy = gst_tag_list_merge (user_tags, shout2send->tags,
375
      gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (shout2send)));
376 377 378 379 380 381 382 383 384 385 386 387
  /* 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
388
}
389
#endif
Wim Taymans's avatar
Wim Taymans committed
390

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
391 392 393

static gboolean
gst_shout2send_event (GstBaseSink * sink, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
394 395
{
  GstShout2send *shout2send;
396
  gboolean ret = TRUE;
397

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
398 399
  shout2send = GST_SHOUT2SEND (sink);

400 401
  GST_LOG_OBJECT (shout2send, "got %s event", GST_EVENT_TYPE_NAME (event));

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
402
  switch (GST_EVENT_TYPE (event)) {
403
    case GST_EVENT_TAG:{
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
404 405
      /* vorbis audio doesnt need metadata setting on the icecast level, only mp3 */
      if (shout2send->tags && shout2send->audio_format == SHOUT_FORMAT_MP3) {
406 407 408
        GstTagList *list;

        gst_event_parse_tag (event, &list);
409
        GST_DEBUG_OBJECT (shout2send, "tags=%" GST_PTR_FORMAT, list);
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
410
        gst_tag_list_insert (shout2send->tags,
411 412
            list,
            gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (shout2send)));
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
413
        /* lets get the artist and song tags */
414 415
        gst_tag_list_foreach ((GstTagList *) list,
            set_shout_metadata, shout2send);
416
        if (shout2send->songmetadata && shout2send->connected) {
417 418
          shout_metadata_t *pmetadata;

419 420 421
          GST_DEBUG_OBJECT (shout2send, "metadata now: %s",
              shout2send->songmetadata);

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
422
          pmetadata = shout_metadata_new ();
423
          shout_metadata_add (pmetadata, "song", shout2send->songmetadata);
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
424 425 426 427 428
          shout_set_metadata (shout2send->conn, pmetadata);
          shout_metadata_free (pmetadata);
        }
      }
      break;
429 430 431 432 433 434 435
    }
    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
436
      break;
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
    }
  }

  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;

489 490 491 492 493 494
  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;

495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
  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;

515
  cur_prop = "username";
516
  GST_DEBUG_OBJECT (sink, "setting %s: %s", cur_prop, "source");
517
  if (shout_set_user (sink->conn, sink->username) != SHOUTERR_SUCCESS)
518 519 520 521 522 523 524 525
    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
526
  }
Wim Taymans's avatar
Wim Taymans committed
527

528
  g_free (version_string);
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
529
  return TRUE;
530 531 532 533 534 535 536 537

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

540 541
static gboolean
gst_shout2send_connect (GstShout2send * sink)
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
542
{
543 544 545
  GST_DEBUG_OBJECT (sink, "Connection format is: %s",
      (sink->audio_format == SHOUT_FORMAT_VORBIS) ? "vorbis" :
      ((sink->audio_format == SHOUT_FORMAT_MP3) ? "mp3" : "unknown"));
Wim Taymans's avatar
Wim Taymans committed
546

547 548
  if (shout_set_format (sink->conn, sink->audio_format) != SHOUTERR_SUCCESS)
    goto could_not_set_format;
549

550 551
  if (shout_open (sink->conn) != SHOUTERR_SUCCESS)
    goto could_not_connect;
552

553 554
  GST_DEBUG_OBJECT (sink, "connected to server");
  sink->connected = TRUE;
555

556 557 558
  /* let's set metadata */
  if (sink->songmetadata) {
    shout_metadata_t *pmetadata;
559

560 561 562 563 564
    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);
565 566
  }

567
  return TRUE;
568

569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597
/* 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
598
  }
599 600 601 602

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

605 606 607 608 609
  sink->connected = FALSE;

  return TRUE;
}

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

636 637 638 639 640
static GstFlowReturn
gst_shout2send_render (GstBaseSink * basesink, GstBuffer * buf)
{
  GstShout2send *sink;
  glong ret;
641 642
  gint delay;
  GstFlowReturn fret;
643 644 645 646 647 648 649 650 651 652

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

653 654 655
  delay = shout_delay (sink->conn);

  GST_LOG_OBJECT (sink, "waiting %d msec", delay);
656
  if (gst_poll_wait (sink->timer, GST_MSECOND * delay) == -1) {
657 658 659 660 661 662
    GST_LOG_OBJECT (sink, "unlocked");

    fret = gst_base_sink_wait_preroll (basesink);
    if (fret != GST_FLOW_OK)
      return fret;
  }
663 664 665 666 667 668

  GST_LOG_OBJECT (sink, "sending %u bytes of data", GST_BUFFER_SIZE (buf));
  ret = shout_send (sink->conn, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
  if (ret != SHOUTERR_SUCCESS)
    goto send_error;

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
669
  return GST_FLOW_OK;
670 671 672 673 674 675 676 677 678 679

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

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

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

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

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

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

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

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

801
  shout2send = GST_SHOUT2SEND (GST_OBJECT_PARENT (pad));
802

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

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

807
  if (!strcmp (mimetype, "audio/mpeg")) {
808
    shout2send->audio_format = SHOUT_FORMAT_MP3;
809
  } else if (!strcmp (mimetype, "application/ogg")) {
810
    shout2send->audio_format = SHOUT_FORMAT_VORBIS;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
811
  } else {
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
812
    ret = FALSE;
Wim Taymans's avatar
Wim Taymans committed
813
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
814

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
815
  return ret;
Wim Taymans's avatar
Wim Taymans committed
816 817 818
}

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

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

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