gstshout2.c 26 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
  gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_shout2send_setcaps);

226
  gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
227

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

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

243
  shout2send->timer = gst_poll_new (TRUE);
Wim Taymans's avatar
Wim Taymans committed
244

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

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

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

278
  gst_tag_list_unref (shout2send->tags);
279

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

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

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

  gchar *value;
295
296
297
298
299

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

546
  return GST_FLOW_OK;
547

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

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

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

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

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

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

  return TRUE;
}

Wim Taymans's avatar
Wim Taymans committed
597
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
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;
}

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

  sink = GST_SHOUT2SEND (basesink);

634
635
636
  /* 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 */
637
  if (!sink->connected) {
638
639
640
    fret = gst_shout2send_connect (sink);
    if (fret != GST_FLOW_OK)
      goto done;
641
642
  }

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

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

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

665
666
667
done:

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

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

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

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

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

static void
737
738
gst_shout2send_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
Wim Taymans's avatar
Wim Taymans committed
739
740
741
{
  GstShout2send *shout2send;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
742
  shout2send = GST_SHOUT2SEND (object);
Wim Taymans's avatar
Wim Taymans committed
743
  switch (prop_id) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
744
745
746
747
748
749
750
751
752
753

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

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
784
static gboolean
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
785
gst_shout2send_setcaps (GstBaseSink * basesink, GstCaps * caps)
Wim Taymans's avatar
Wim Taymans committed
786
{
787
  const gchar *mimetype;
788
  GstShout2send *shout2send;
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
789
  gboolean ret = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
790

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
791
  shout2send = GST_SHOUT2SEND (basesink);
792

793
  mimetype = gst_structure_get_name (gst_caps_get_structure (caps, 0));
794
795
796

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

797
  if (!strcmp (mimetype, "audio/mpeg")) {
798
    shout2send->format = SHOUT_FORMAT_MP3;
799
  } else if (g_str_has_suffix (mimetype, "/ogg")) {
800
    shout2send->format = SHOUT_FORMAT_OGG;
801
#ifdef SHOUT_FORMAT_WEBM
802
  } else if (g_str_has_suffix (mimetype, "/webm")) {
803
    shout2send->format = SHOUT_FORMAT_WEBM;
804
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
805
  } else {
Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
806
    ret = FALSE;
Wim Taymans's avatar
Wim Taymans committed
807
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
808

Zaheer Abbas Merali's avatar
Zaheer Abbas Merali committed
809
  return ret;
Wim Taymans's avatar
Wim Taymans committed
810
811
812
}

static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
813
plugin_init (GstPlugin * plugin)
Wim Taymans's avatar
Wim Taymans committed
814
{
815
816
#ifdef ENABLE_NLS
  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
817
  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
818
819
#endif /* ENABLE_NLS */

Ronald S. Bultje's avatar
Ronald S. Bultje committed
820
  return gst_element_register (plugin, "shout2send", GST_RANK_NONE,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
821
      GST_TYPE_SHOUT2SEND);
Wim Taymans's avatar
Wim Taymans committed
822
823
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
824
825
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
826
    shout2send,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
827
828
    "Sends data to an icecast server using libshout2",
    plugin_init,
829
    VERSION, "LGPL", "libshout2", "http://www.icecast.org/download/")