Merging gst-devtools
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-good / ext / shout2 / gstshout2.c
1 /* GStreamer
2  * 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>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 /**
23  * SECTION:element-shout2send
24  * @title: shout2send
25  *
26  * shout2send pushes a media stream to an Icecast server
27  *
28  * ## Example launch line
29  * |[
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  * ]| This pipeline demuxes, decodes, re-encodes and re-muxes an audio
32  * media file into oggvorbis and sends the resulting stream to an Icecast
33  * server. Properties mount, port, username and password are all server-config
34  * dependent.
35  *
36  */
37
38 #ifdef HAVE_CONFIG_H
39 #include "config.h"
40 #endif
41
42 #include "gstshout2.h"
43 #include <stdlib.h>
44 #include <string.h>
45
46 #include "gst/gst-i18n-plugin.h"
47
48 GST_DEBUG_CATEGORY_STATIC (shout2_debug);
49 #define GST_CAT_DEFAULT shout2_debug
50
51
52 enum
53 {
54   SIGNAL_CONNECTION_PROBLEM,    /* FIXME 2.0: remove this */
55   LAST_SIGNAL
56 };
57
58 enum
59 {
60   ARG_0,
61   ARG_IP,                       /* the IP address or hostname of the server */
62   ARG_PORT,                     /* the encoder port number on the server */
63   ARG_PASSWORD,                 /* the encoder password on the server */
64   ARG_USERNAME,                 /* the encoder username on the server */
65   ARG_PUBLIC,                   /* is this stream public? */
66   ARG_STREAMNAME,               /* Name of the stream */
67   ARG_DESCRIPTION,              /* Description of the stream */
68   ARG_GENRE,                    /* Genre of the stream */
69
70   ARG_PROTOCOL,                 /* Protocol to connect with */
71
72   ARG_MOUNT,                    /* mountpoint of stream (icecast only) */
73   ARG_URL,                      /* the stream's homepage URL */
74
75   ARG_TIMEOUT                   /* The max amount of time to wait for
76                                    network activity */
77 };
78
79 #define DEFAULT_IP           "127.0.0.1"
80 #define DEFAULT_PORT         8000
81 #define DEFAULT_PASSWORD     "hackme"
82 #define DEFAULT_USERNAME     "source"
83 #define DEFAULT_PUBLIC     FALSE
84 #define DEFAULT_STREAMNAME   ""
85 #define DEFAULT_DESCRIPTION  ""
86 #define DEFAULT_GENRE        ""
87 #define DEFAULT_MOUNT        ""
88 #define DEFAULT_URL          ""
89 #define DEFAULT_PROTOCOL     SHOUT2SEND_PROTOCOL_HTTP
90 #define DEFAULT_TIMEOUT      10000
91
92 #ifdef SHOUT_FORMAT_WEBM
93 #define WEBM_CAPS "; video/webm; audio/webm"
94 #else
95 #define WEBM_CAPS ""
96 #endif
97
98 #define SHOUT2SEND_BASIC_CAPS "application/ogg; audio/ogg; video/ogg; "\
99     "audio/mpeg, mpegversion = (int) 1, layer = (int) [ 1, 3 ]"
100
101 #define SHOUT2SEND_DOC_CAPS SHOUT2SEND_BASIC_CAPS "; video/webm; audio/webm"
102
103 #define SHOUT2SEND_CAPS SHOUT2SEND_BASIC_CAPS WEBM_CAPS
104
105 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
106     GST_PAD_SINK,
107     GST_PAD_ALWAYS,
108     GST_STATIC_CAPS (SHOUT2SEND_CAPS));
109
110 static void gst_shout2send_finalize (GstShout2send * shout2send);
111
112 static gboolean gst_shout2send_event (GstBaseSink * sink, GstEvent * event);
113 static gboolean gst_shout2send_unlock (GstBaseSink * basesink);
114 static gboolean gst_shout2send_unlock_stop (GstBaseSink * basesink);
115 static GstFlowReturn gst_shout2send_render (GstBaseSink * sink,
116     GstBuffer * buffer);
117 static gboolean gst_shout2send_start (GstBaseSink * basesink);
118 static gboolean gst_shout2send_stop (GstBaseSink * basesink);
119
120 static void gst_shout2send_set_property (GObject * object, guint prop_id,
121     const GValue * value, GParamSpec * pspec);
122 static void gst_shout2send_get_property (GObject * object, guint prop_id,
123     GValue * value, GParamSpec * pspec);
124
125 static gboolean gst_shout2send_setcaps (GstBaseSink * basesink, GstCaps * caps);
126
127 static guint gst_shout2send_signals[LAST_SIGNAL] = { 0 };
128
129 #define GST_TYPE_SHOUT_PROTOCOL (gst_shout2send_protocol_get_type())
130 static GType
131 gst_shout2send_protocol_get_type (void)
132 {
133   static GType shout2send_protocol_type = 0;
134   static const GEnumValue shout2send_protocol[] = {
135     {SHOUT2SEND_PROTOCOL_XAUDIOCAST,
136         "Xaudiocast Protocol (icecast 1.3.x)", "xaudiocast"},
137     {SHOUT2SEND_PROTOCOL_ICY, "Icy Protocol (ShoutCast)", "icy"},
138     {SHOUT2SEND_PROTOCOL_HTTP, "Http Protocol (icecast 2.x)", "http"},
139     {0, NULL, NULL},
140   };
141
142   if (!shout2send_protocol_type) {
143     shout2send_protocol_type =
144         g_enum_register_static ("GstShout2SendProtocol", shout2send_protocol);
145   }
146
147
148   return shout2send_protocol_type;
149 }
150
151 #define gst_shout2send_parent_class parent_class
152 G_DEFINE_TYPE_WITH_CODE (GstShout2send, gst_shout2send, GST_TYPE_BASE_SINK,
153     G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL));
154 #ifdef ENABLE_NLS
155 #define _do_init \
156   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);\
157   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
158 #else /* ENABLE_NLS */
159 #define _do_init
160 #endif
161 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (shout2send, "shout2send", GST_RANK_NONE,
162     GST_TYPE_SHOUT2SEND, _do_init);
163
164 static void
165 gst_shout2send_class_init (GstShout2sendClass * klass)
166 {
167   GObjectClass *gobject_class;
168   GstElementClass *gstelement_class;
169   GstBaseSinkClass *gstbasesink_class;
170   GstPadTemplate *tmpl;
171   GstCaps *doc_caps;
172
173   gobject_class = (GObjectClass *) klass;
174   gstelement_class = (GstElementClass *) klass;
175   gstbasesink_class = (GstBaseSinkClass *) klass;
176
177   parent_class = g_type_class_peek_parent (klass);
178
179   gobject_class->set_property = gst_shout2send_set_property;
180   gobject_class->get_property = gst_shout2send_get_property;
181   gobject_class->finalize = (GObjectFinalizeFunc) gst_shout2send_finalize;
182
183   /* FIXME: 2.0 Should probably change this prop name to "server" */
184   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_IP,
185       g_param_spec_string ("ip", "ip", "IP address or hostname", DEFAULT_IP,
186           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
187   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PORT,
188       g_param_spec_int ("port", "port", "port", 1, G_MAXUSHORT, DEFAULT_PORT,
189           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
190
191   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PASSWORD,
192       g_param_spec_string ("password", "password", "password", DEFAULT_PASSWORD,
193           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
194
195   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_USERNAME,
196       g_param_spec_string ("username", "username", "username", DEFAULT_USERNAME,
197           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
198
199   /* metadata */
200   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PUBLIC,
201       g_param_spec_boolean ("public", "public",
202           "If the stream should be listed on the server's stream directory",
203           DEFAULT_PUBLIC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
204
205   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_STREAMNAME,
206       g_param_spec_string ("streamname", "streamname", "name of the stream",
207           DEFAULT_STREAMNAME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
208
209   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DESCRIPTION,
210       g_param_spec_string ("description", "description", "description",
211           DEFAULT_DESCRIPTION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
212
213   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_GENRE,
214       g_param_spec_string ("genre", "genre", "genre", DEFAULT_GENRE,
215           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
216
217   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PROTOCOL,
218       g_param_spec_enum ("protocol", "protocol", "Connection Protocol to use",
219           GST_TYPE_SHOUT_PROTOCOL, DEFAULT_PROTOCOL,
220           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
221
222
223   /* icecast only */
224   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MOUNT,
225       g_param_spec_string ("mount", "mount", "mount", DEFAULT_MOUNT,
226           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
227
228   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_URL,
229       g_param_spec_string ("url", "url", "the stream's homepage URL",
230           DEFAULT_URL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
231
232   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TIMEOUT,
233       g_param_spec_uint ("timeout", "timeout",
234           "Max amount of time to wait for network activity, in milliseconds",
235           1, G_MAXUINT, DEFAULT_TIMEOUT,
236           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
237
238   /* signals */
239   gst_shout2send_signals[SIGNAL_CONNECTION_PROBLEM] =
240       g_signal_new ("connection-problem", G_TYPE_FROM_CLASS (klass),
241       0, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_INT);
242
243   gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_shout2send_start);
244   gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_shout2send_stop);
245   gstbasesink_class->unlock = GST_DEBUG_FUNCPTR (gst_shout2send_unlock);
246   gstbasesink_class->unlock_stop =
247       GST_DEBUG_FUNCPTR (gst_shout2send_unlock_stop);
248   gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_shout2send_render);
249   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_shout2send_event);
250   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_shout2send_setcaps);
251
252   tmpl = gst_static_pad_template_get (&sink_template);
253   gst_element_class_add_pad_template (gstelement_class, tmpl);
254
255   /* our caps depend on the libshout2 version */
256   doc_caps = gst_caps_from_string (SHOUT2SEND_DOC_CAPS);
257   gst_pad_template_set_documentation_caps (tmpl, doc_caps);
258   gst_clear_caps (&doc_caps);
259
260   gst_element_class_set_static_metadata (gstelement_class,
261       "Icecast network sink",
262       "Sink/Network", "Sends data to an icecast server",
263       "Wim Taymans <wim.taymans@chello.be>, "
264       "Pedro Corte-Real <typo@netcabo.pt>, "
265       "Zaheer Abbas Merali <zaheerabbas at merali dot org>");
266
267   GST_DEBUG_CATEGORY_INIT (shout2_debug, "shout2", 0, "shout2send element");
268
269   gst_type_mark_as_plugin_api (GST_TYPE_SHOUT_PROTOCOL, 0);
270 }
271
272 static void
273 gst_shout2send_init (GstShout2send * shout2send)
274 {
275   gst_base_sink_set_sync (GST_BASE_SINK (shout2send), FALSE);
276
277   shout2send->timer = gst_poll_new (TRUE);
278
279   shout2send->ip = g_strdup (DEFAULT_IP);
280   shout2send->port = DEFAULT_PORT;
281   shout2send->password = g_strdup (DEFAULT_PASSWORD);
282   shout2send->username = g_strdup (DEFAULT_USERNAME);
283   shout2send->streamname = g_strdup (DEFAULT_STREAMNAME);
284   shout2send->description = g_strdup (DEFAULT_DESCRIPTION);
285   shout2send->genre = g_strdup (DEFAULT_GENRE);
286   shout2send->mount = g_strdup (DEFAULT_MOUNT);
287   shout2send->url = g_strdup (DEFAULT_URL);
288   shout2send->protocol = DEFAULT_PROTOCOL;
289   shout2send->ispublic = DEFAULT_PUBLIC;
290   shout2send->timeout = DEFAULT_TIMEOUT;
291
292   shout2send->format = -1;
293   shout2send->tags = gst_tag_list_new_empty ();
294   shout2send->conn = NULL;
295   shout2send->connected = FALSE;
296   shout2send->songmetadata = NULL;
297   shout2send->songartist = NULL;
298   shout2send->songtitle = NULL;
299 }
300
301 static void
302 gst_shout2send_finalize (GstShout2send * shout2send)
303 {
304   g_free (shout2send->ip);
305   g_free (shout2send->password);
306   g_free (shout2send->username);
307   g_free (shout2send->streamname);
308   g_free (shout2send->description);
309   g_free (shout2send->genre);
310   g_free (shout2send->mount);
311   g_free (shout2send->url);
312
313   gst_tag_list_unref (shout2send->tags);
314
315   gst_poll_free (shout2send->timer);
316
317   G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (shout2send));
318 }
319
320 static void
321 set_shout_metadata (const GstTagList * list, const gchar * tag,
322     gpointer user_data)
323 {
324   GstShout2send *shout2send = (GstShout2send *) user_data;
325   char **shout_metadata = &(shout2send->songmetadata);
326   char **song_artist = &(shout2send->songartist);
327   char **song_title = &(shout2send->songtitle);
328
329   gchar *value;
330
331   GST_DEBUG ("tag: %s being added", tag);
332   if (strcmp (tag, GST_TAG_ARTIST) == 0) {
333     if (gst_tag_get_type (tag) == G_TYPE_STRING) {
334       if (!gst_tag_list_get_string (list, tag, &value)) {
335         GST_DEBUG ("Error reading \"%s\" tag value", tag);
336         return;
337       }
338
339       if (*song_artist != NULL)
340         g_free (*song_artist);
341
342       *song_artist = g_strdup (value);
343     }
344   } else if (strcmp (tag, GST_TAG_TITLE) == 0) {
345     if (gst_tag_get_type (tag) == G_TYPE_STRING) {
346       if (!gst_tag_list_get_string (list, tag, &value)) {
347         GST_DEBUG ("Error reading \"%s\" tag value", tag);
348         return;
349       }
350
351       if (*song_title != NULL)
352         g_free (*song_title);
353
354       *song_title = g_strdup (value);
355     }
356   }
357
358   if (*shout_metadata != NULL)
359     g_free (*shout_metadata);
360
361
362   if (*song_title && *song_artist) {
363     *shout_metadata = g_strdup_printf ("%s - %s", *song_artist, *song_title);
364   } else if (*song_title && *song_artist == NULL) {
365     *shout_metadata = g_strdup_printf ("Unknown - %s", *song_title);
366   } else if (*song_title == NULL && *song_artist) {
367     *shout_metadata = g_strdup_printf ("%s - Unknown", *song_artist);
368   } else {
369     *shout_metadata = g_strdup_printf ("Unknown - Unknown");
370   }
371
372   GST_LOG ("shout metadata is now: %s", *shout_metadata);
373 }
374
375 #if 0
376 static void
377 gst_shout2send_set_metadata (GstShout2send * shout2send)
378 {
379   const GstTagList *user_tags;
380   GstTagList *copy;
381   char *tempmetadata;
382   shout_metadata_t *pmetadata;
383
384   g_return_if_fail (shout2send != NULL);
385   user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (shout2send));
386   if ((shout2send->tags == NULL) && (user_tags == NULL)) {
387     return;
388   }
389   copy = gst_tag_list_merge (user_tags, shout2send->tags,
390       gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (shout2send)));
391   /* lets get the artist and song tags */
392   tempmetadata = NULL;
393   gst_tag_list_foreach ((GstTagList *) copy, set_shout_metadata,
394       (gpointer) & tempmetadata);
395   if (tempmetadata) {
396     pmetadata = shout_metadata_new ();
397     shout_metadata_add (pmetadata, "song", tempmetadata);
398     shout_set_metadata (shout2send->conn, pmetadata);
399     shout_metadata_free (pmetadata);
400   }
401
402   gst_tag_list_unref (copy);
403 }
404 #endif
405
406
407 static gboolean
408 gst_shout2send_event (GstBaseSink * sink, GstEvent * event)
409 {
410   GstShout2send *shout2send;
411   gboolean ret = TRUE;
412
413   shout2send = GST_SHOUT2SEND (sink);
414
415   GST_LOG_OBJECT (shout2send, "got %s event", GST_EVENT_TYPE_NAME (event));
416
417   switch (GST_EVENT_TYPE (event)) {
418     case GST_EVENT_TAG:{
419       /* vorbis audio doesn't need metadata setting on the icecast level, only mp3 */
420       if (shout2send->tags && shout2send->format == SHOUT_FORMAT_MP3) {
421         GstTagList *list;
422
423         gst_event_parse_tag (event, &list);
424         GST_DEBUG_OBJECT (shout2send, "tags=%" GST_PTR_FORMAT, list);
425         gst_tag_list_insert (shout2send->tags,
426             list,
427             gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (shout2send)));
428         /* lets get the artist and song tags */
429         gst_tag_list_foreach ((GstTagList *) list,
430             set_shout_metadata, shout2send);
431         if (shout2send->songmetadata && shout2send->connected) {
432           shout_metadata_t *pmetadata;
433
434           GST_DEBUG_OBJECT (shout2send, "metadata now: %s",
435               shout2send->songmetadata);
436
437           pmetadata = shout_metadata_new ();
438           shout_metadata_add (pmetadata, "song", shout2send->songmetadata);
439           shout_set_metadata (shout2send->conn, pmetadata);
440           shout_metadata_free (pmetadata);
441         }
442       }
443       break;
444     }
445     default:{
446       GST_LOG_OBJECT (shout2send, "let base class handle event");
447       if (GST_BASE_SINK_CLASS (parent_class)->event) {
448         event = gst_event_ref (event);
449         ret = GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
450       }
451       break;
452     }
453   }
454
455   return ret;
456 }
457
458 static gboolean
459 gst_shout2send_start (GstBaseSink * basesink)
460 {
461   GstShout2send *sink = GST_SHOUT2SEND (basesink);
462   const gchar *cur_prop;
463   gshort proto = 3;
464   gchar *version_string;
465
466   GST_DEBUG_OBJECT (sink, "starting");
467
468   sink->conn = shout_new ();
469
470   switch (sink->protocol) {
471     case SHOUT2SEND_PROTOCOL_XAUDIOCAST:
472       proto = SHOUT_PROTOCOL_XAUDIOCAST;
473       break;
474     case SHOUT2SEND_PROTOCOL_ICY:
475       proto = SHOUT_PROTOCOL_ICY;
476       break;
477     case SHOUT2SEND_PROTOCOL_HTTP:
478       proto = SHOUT_PROTOCOL_HTTP;
479       break;
480   }
481
482   cur_prop = "protocol";
483   GST_DEBUG_OBJECT (sink, "setting protocol: %d", sink->protocol);
484   if (shout_set_protocol (sink->conn, proto) != SHOUTERR_SUCCESS)
485     goto set_failed;
486
487   cur_prop = "ip";
488   GST_DEBUG_OBJECT (sink, "setting IP/hostname: %s", sink->ip);
489   if (shout_set_host (sink->conn, sink->ip) != SHOUTERR_SUCCESS)
490     goto set_failed;
491
492   cur_prop = "port";
493   GST_DEBUG_OBJECT (sink, "setting port: %u", sink->port);
494   if (shout_set_port (sink->conn, sink->port) != SHOUTERR_SUCCESS)
495     goto set_failed;
496
497   cur_prop = "password";
498   GST_DEBUG_OBJECT (sink, "setting password: %s", sink->password);
499   if (shout_set_password (sink->conn, sink->password) != SHOUTERR_SUCCESS)
500     goto set_failed;
501
502   cur_prop = "public";
503   GST_DEBUG_OBJECT (sink, "setting %s: %u", cur_prop, sink->ispublic);
504   if (shout_set_public (sink->conn,
505           (sink->ispublic ? 1 : 0)) != SHOUTERR_SUCCESS)
506     goto set_failed;
507
508   cur_prop = "streamname";
509   GST_DEBUG_OBJECT (sink, "setting %s: %s", cur_prop, sink->streamname);
510   if (shout_set_name (sink->conn, sink->streamname) != SHOUTERR_SUCCESS)
511     goto set_failed;
512
513   cur_prop = "description";
514   GST_DEBUG_OBJECT (sink, "setting %s: %s", cur_prop, sink->description);
515   if (shout_set_description (sink->conn, sink->description) != SHOUTERR_SUCCESS)
516     goto set_failed;
517
518   cur_prop = "genre";
519   GST_DEBUG_OBJECT (sink, "setting %s: %s", cur_prop, sink->genre);
520   if (shout_set_genre (sink->conn, sink->genre) != SHOUTERR_SUCCESS)
521     goto set_failed;
522
523   cur_prop = "mount";
524   GST_DEBUG_OBJECT (sink, "setting %s: %s", cur_prop, sink->mount);
525   if (shout_set_mount (sink->conn, sink->mount) != SHOUTERR_SUCCESS)
526     goto set_failed;
527
528   cur_prop = "username";
529   GST_DEBUG_OBJECT (sink, "setting %s: %s", cur_prop, sink->username);
530   if (shout_set_user (sink->conn, sink->username) != SHOUTERR_SUCCESS)
531     goto set_failed;
532
533   version_string = gst_version_string ();
534   cur_prop = "agent";
535   GST_DEBUG_OBJECT (sink, "setting %s: %s", cur_prop, version_string);
536   if (shout_set_agent (sink->conn, version_string) != SHOUTERR_SUCCESS) {
537     g_free (version_string);
538     goto set_failed;
539   }
540
541   g_free (version_string);
542   return TRUE;
543
544 /* ERROR */
545 set_failed:
546   {
547     GST_ELEMENT_ERROR (sink, LIBRARY, SETTINGS, (NULL),
548         ("Error setting %s: %s", cur_prop, shout_get_error (sink->conn)));
549     shout_free (sink->conn);
550     sink->conn = NULL;
551     return FALSE;
552   }
553 }
554
555 static GstFlowReturn
556 gst_shout2send_connect (GstShout2send * sink)
557 {
558   GstFlowReturn fret = GST_FLOW_OK;
559   gint ret;
560   GstClockTime start_ts;
561
562   GST_DEBUG_OBJECT (sink, "Connection format is: %d", sink->format);
563
564   if (sink->format == -1)
565     goto no_caps;
566
567   if (shout_set_nonblocking (sink->conn, 1) != SHOUTERR_SUCCESS)
568     goto could_not_set_nonblocking;
569
570   if (shout_set_format (sink->conn, sink->format) != SHOUTERR_SUCCESS)
571     goto could_not_set_format;
572
573   GST_DEBUG_OBJECT (sink, "connecting");
574
575   start_ts = gst_util_get_timestamp ();
576   ret = shout_open (sink->conn);
577
578   /* wait for connection or timeout */
579   while (ret == SHOUTERR_BUSY) {
580     if (gst_util_get_timestamp () - start_ts > sink->timeout * GST_MSECOND) {
581       goto connection_timeout;
582     }
583     if (gst_poll_wait (sink->timer, 10 * GST_MSECOND) == -1) {
584       GST_LOG_OBJECT (sink, "unlocked");
585
586       fret = gst_base_sink_wait_preroll (GST_BASE_SINK (sink));
587       if (fret != GST_FLOW_OK)
588         goto done;
589     }
590     ret = shout_get_connected (sink->conn);
591   }
592
593   if (ret != SHOUTERR_CONNECTED && ret != SHOUTERR_SUCCESS)
594     goto could_not_connect;
595
596   GST_DEBUG_OBJECT (sink, "connected to server");
597   sink->connected = TRUE;
598
599   /* initialize sending rate monitoring */
600   sink->prev_queuelen = 0;
601   sink->data_sent = 0;
602   sink->stalled = TRUE;
603   sink->datasent_reset_ts = sink->stalled_ts = gst_util_get_timestamp ();
604
605   /* let's set metadata */
606   if (sink->songmetadata) {
607     shout_metadata_t *pmetadata;
608
609     GST_DEBUG_OBJECT (sink, "shout metadata now: %s", sink->songmetadata);
610     pmetadata = shout_metadata_new ();
611     shout_metadata_add (pmetadata, "song", sink->songmetadata);
612     shout_set_metadata (sink->conn, pmetadata);
613     shout_metadata_free (pmetadata);
614   }
615
616 done:
617   return fret;
618
619 /* ERRORS */
620 no_caps:
621   {
622     GST_ELEMENT_ERROR (sink, CORE, NEGOTIATION, (NULL),
623         ("No input caps received."));
624     return GST_FLOW_NOT_NEGOTIATED;
625   }
626
627 could_not_set_nonblocking:
628   {
629     GST_ELEMENT_ERROR (sink, LIBRARY, SETTINGS, (NULL),
630         ("Error configuring libshout to use non-blocking i/o: %s",
631             shout_get_error (sink->conn)));
632     return GST_FLOW_ERROR;
633   }
634
635 could_not_set_format:
636   {
637     GST_ELEMENT_ERROR (sink, LIBRARY, SETTINGS, (NULL),
638         ("Error setting connection format: %s", shout_get_error (sink->conn)));
639     return GST_FLOW_ERROR;
640   }
641
642 could_not_connect:
643   {
644     GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE,
645         (_("Could not connect to server")),
646         ("shout_open() failed: err=%s", shout_get_error (sink->conn)));
647     g_signal_emit (sink, gst_shout2send_signals[SIGNAL_CONNECTION_PROBLEM], 0,
648         shout_get_errno (sink->conn));
649     return GST_FLOW_ERROR;
650   }
651
652 connection_timeout:
653   {
654     GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE,
655         (_("Could not connect to server")), ("connection timed out"));
656     g_signal_emit (sink, gst_shout2send_signals[SIGNAL_CONNECTION_PROBLEM], 0,
657         shout_get_errno (sink->conn));
658     return GST_FLOW_ERROR;
659   }
660 }
661
662 static gboolean
663 gst_shout2send_stop (GstBaseSink * basesink)
664 {
665   GstShout2send *sink = GST_SHOUT2SEND (basesink);
666
667   if (sink->conn) {
668     if (sink->connected)
669       shout_close (sink->conn);
670     shout_free (sink->conn);
671     sink->conn = NULL;
672   }
673
674   if (sink->songmetadata) {
675     g_free (sink->songmetadata);
676     sink->songmetadata = NULL;
677   }
678
679   sink->connected = FALSE;
680   sink->format = -1;
681
682   return TRUE;
683 }
684
685 static gboolean
686 gst_shout2send_unlock (GstBaseSink * basesink)
687 {
688   GstShout2send *sink;
689
690   sink = GST_SHOUT2SEND (basesink);
691
692   GST_DEBUG_OBJECT (basesink, "unlock");
693   gst_poll_set_flushing (sink->timer, TRUE);
694
695   return TRUE;
696 }
697
698 static gboolean
699 gst_shout2send_unlock_stop (GstBaseSink * basesink)
700 {
701   GstShout2send *sink;
702
703   sink = GST_SHOUT2SEND (basesink);
704
705   GST_DEBUG_OBJECT (basesink, "unlock_stop");
706   gst_poll_set_flushing (sink->timer, FALSE);
707
708   return TRUE;
709 }
710
711 static GstFlowReturn
712 gst_shout2send_render (GstBaseSink * basesink, GstBuffer * buf)
713 {
714   GstShout2send *sink;
715   glong ret;
716   gint delay;
717   GstFlowReturn fret = GST_FLOW_OK;
718   GstMapInfo map;
719   GstClockTime now;
720   ssize_t queuelen;
721
722   sink = GST_SHOUT2SEND (basesink);
723
724   /* we connect here because we need to know the format before we can set up
725    * the connection, which we don't know yet in _start(), and also because we
726    * don't want to block the application thread */
727   if (!sink->connected) {
728     fret = gst_shout2send_connect (sink);
729     if (fret != GST_FLOW_OK)
730       goto done;
731   }
732
733   delay = shout_delay (sink->conn);
734
735   if (delay > 0) {
736     GST_LOG_OBJECT (sink, "waiting %d msec", delay);
737     if (gst_poll_wait (sink->timer, GST_MSECOND * delay) == -1) {
738       GST_LOG_OBJECT (sink, "unlocked");
739
740       fret = gst_base_sink_wait_preroll (basesink);
741       if (fret != GST_FLOW_OK)
742         goto done;
743     }
744   } else {
745     GST_LOG_OBJECT (sink, "we're %d msec late", -delay);
746   }
747
748   /* accumulate how much data have actually been sent
749    * to the network since the last call to shout_send() */
750   queuelen = shout_queuelen (sink->conn);
751   if (sink->prev_queuelen > 0)
752     sink->data_sent += sink->prev_queuelen - queuelen;
753
754   gst_buffer_map (buf, &map, GST_MAP_READ);
755
756   /* add map.size instead of re-reading the queue length because
757    * the data may actually be sent immediately */
758   sink->prev_queuelen = queuelen + map.size;
759
760   GST_LOG_OBJECT (sink, "sending %u bytes of data, queue length now is %"
761       G_GUINT64_FORMAT, (guint) map.size, sink->prev_queuelen);
762
763   ret = shout_send (sink->conn, map.data, map.size);
764
765   gst_buffer_unmap (buf, &map);
766   if (ret != SHOUTERR_SUCCESS)
767     goto send_error;
768
769   now = gst_util_get_timestamp ();
770   if (now - sink->datasent_reset_ts >= 500 * GST_MSECOND) {
771     guint64 send_rate;
772
773     send_rate = gst_util_uint64_scale (sink->data_sent, GST_SECOND,
774         now - sink->datasent_reset_ts);
775
776     if (send_rate == 0 && !sink->stalled) {
777       sink->stalled = TRUE;
778       sink->stalled_ts = now;
779     } else if (send_rate > 0 && sink->stalled) {
780       sink->stalled = FALSE;
781     }
782
783     sink->data_sent = 0;
784     sink->datasent_reset_ts = now;
785
786     GST_DEBUG_OBJECT (sink, "sending rate is %" G_GUINT64_FORMAT " bps, "
787         "stalled %d, stalled_ts %" GST_TIME_FORMAT, send_rate, sink->stalled,
788         GST_TIME_ARGS (sink->stalled_ts));
789
790     if (sink->stalled && now - sink->stalled_ts >= sink->timeout * GST_MSECOND) {
791       GST_WARNING_OBJECT (sink, "network send queue is stalled for too long");
792       goto network_error;
793     }
794   }
795
796 done:
797
798   return fret;
799
800 /* ERRORS */
801 send_error:
802   {
803     GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL),
804         ("shout_send() failed: %s", shout_get_error (sink->conn)));
805     g_signal_emit (sink, gst_shout2send_signals[SIGNAL_CONNECTION_PROBLEM], 0,
806         shout_get_errno (sink->conn));
807     return GST_FLOW_ERROR;
808   }
809
810 network_error:
811   {
812     GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL),
813         ("network timeout reached"));
814     g_signal_emit (sink, gst_shout2send_signals[SIGNAL_CONNECTION_PROBLEM], 0,
815         SHOUTERR_BUSY);
816     return GST_FLOW_ERROR;
817   }
818 }
819
820 static void
821 gst_shout2send_set_property (GObject * object, guint prop_id,
822     const GValue * value, GParamSpec * pspec)
823 {
824   GstShout2send *shout2send;
825
826   shout2send = GST_SHOUT2SEND (object);
827   switch (prop_id) {
828
829     case ARG_IP:
830       g_free (shout2send->ip);
831       shout2send->ip = g_value_dup_string (value);
832       break;
833     case ARG_PORT:
834       shout2send->port = g_value_get_int (value);
835       break;
836     case ARG_PASSWORD:
837       g_free (shout2send->password);
838       shout2send->password = g_value_dup_string (value);
839       break;
840     case ARG_USERNAME:
841       g_free (shout2send->username);
842       shout2send->username = g_value_dup_string (value);
843       break;
844     case ARG_PUBLIC:
845       shout2send->ispublic = g_value_get_boolean (value);
846       break;
847     case ARG_STREAMNAME:       /* Name of the stream */
848       g_free (shout2send->streamname);
849       shout2send->streamname = g_value_dup_string (value);
850       break;
851     case ARG_DESCRIPTION:      /* Description of the stream */
852       g_free (shout2send->description);
853       shout2send->description = g_value_dup_string (value);
854       break;
855     case ARG_GENRE:            /* Genre of the stream */
856       g_free (shout2send->genre);
857       shout2send->genre = g_value_dup_string (value);
858       break;
859     case ARG_PROTOCOL:         /* protocol to connect with */
860       shout2send->protocol = g_value_get_enum (value);
861       break;
862     case ARG_MOUNT:            /* mountpoint of stream (icecast only) */
863       g_free (shout2send->mount);
864       shout2send->mount = g_value_dup_string (value);
865       break;
866     case ARG_URL:              /* the stream's homepage URL */
867       g_free (shout2send->url);
868       shout2send->url = g_value_dup_string (value);
869       break;
870     case ARG_TIMEOUT:
871       shout2send->timeout = g_value_get_uint (value);
872       break;
873     default:
874       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
875       break;
876   }
877 }
878
879 static void
880 gst_shout2send_get_property (GObject * object, guint prop_id,
881     GValue * value, GParamSpec * pspec)
882 {
883   GstShout2send *shout2send;
884
885   shout2send = GST_SHOUT2SEND (object);
886   switch (prop_id) {
887
888     case ARG_IP:
889       g_value_set_string (value, shout2send->ip);
890       break;
891     case ARG_PORT:
892       g_value_set_int (value, shout2send->port);
893       break;
894     case ARG_PASSWORD:
895       g_value_set_string (value, shout2send->password);
896       break;
897     case ARG_USERNAME:
898       g_value_set_string (value, shout2send->username);
899       break;
900     case ARG_PUBLIC:
901       g_value_set_boolean (value, shout2send->ispublic);
902       break;
903     case ARG_STREAMNAME:       /* Name of the stream */
904       g_value_set_string (value, shout2send->streamname);
905       break;
906     case ARG_DESCRIPTION:      /* Description of the stream */
907       g_value_set_string (value, shout2send->description);
908       break;
909     case ARG_GENRE:            /* Genre of the stream */
910       g_value_set_string (value, shout2send->genre);
911       break;
912     case ARG_PROTOCOL:         /* protocol to connect with */
913       g_value_set_enum (value, shout2send->protocol);
914       break;
915     case ARG_MOUNT:            /* mountpoint of stream (icecast only) */
916       g_value_set_string (value, shout2send->mount);
917       break;
918     case ARG_URL:              /* the stream's homepage URL */
919       g_value_set_string (value, shout2send->url);
920       break;
921     case ARG_TIMEOUT:
922       g_value_set_uint (value, shout2send->timeout);
923       break;
924     default:
925       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
926       break;
927   }
928 }
929
930 static gboolean
931 gst_shout2send_setcaps (GstBaseSink * basesink, GstCaps * caps)
932 {
933   const gchar *mimetype;
934   GstShout2send *shout2send;
935   gboolean ret = TRUE;
936
937   shout2send = GST_SHOUT2SEND (basesink);
938
939   mimetype = gst_structure_get_name (gst_caps_get_structure (caps, 0));
940
941   GST_DEBUG_OBJECT (shout2send, "mimetype of caps given is: %s", mimetype);
942
943   if (!strcmp (mimetype, "audio/mpeg")) {
944     shout2send->format = SHOUT_FORMAT_MP3;
945   } else if (g_str_has_suffix (mimetype, "/ogg")) {
946     shout2send->format = SHOUT_FORMAT_OGG;
947 #ifdef SHOUT_FORMAT_WEBM
948   } else if (g_str_has_suffix (mimetype, "/webm")) {
949     shout2send->format = SHOUT_FORMAT_WEBM;
950 #endif
951   } else {
952     ret = FALSE;
953   }
954
955   return ret;
956 }
957
958 static gboolean
959 plugin_init (GstPlugin * plugin)
960 {
961   return GST_ELEMENT_REGISTER (shout2send, plugin);
962 }
963
964 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
965     GST_VERSION_MINOR,
966     shout2,
967     "Sends data to an icecast server using libshout2",
968     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)