Replace gst-i18n-*.h with gi18n-lib.h
[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 <glib/gi18n-lib.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 #ifdef SHOUTERR_RETRY
580   /* starting with libshout 2.4.2, shout_open() has broken API + ABI and
581    * can also return SHOUTERR_RETRY (a new define) to mean "try again" */
582   while (ret == SHOUTERR_BUSY || ret == SHOUTERR_RETRY) {
583 #else
584   while (ret == SHOUTERR_BUSY) {
585 #endif
586     if (gst_util_get_timestamp () - start_ts > sink->timeout * GST_MSECOND) {
587       goto connection_timeout;
588     }
589     if (gst_poll_wait (sink->timer, 10 * GST_MSECOND) == -1) {
590       GST_LOG_OBJECT (sink, "unlocked");
591
592       fret = gst_base_sink_wait_preroll (GST_BASE_SINK (sink));
593       if (fret != GST_FLOW_OK)
594         goto done;
595     }
596     ret = shout_get_connected (sink->conn);
597   }
598
599   if (ret != SHOUTERR_CONNECTED && ret != SHOUTERR_SUCCESS)
600     goto could_not_connect;
601
602   GST_DEBUG_OBJECT (sink, "connected to server");
603   sink->connected = TRUE;
604
605   /* initialize sending rate monitoring */
606   sink->prev_queuelen = 0;
607   sink->data_sent = 0;
608   sink->stalled = TRUE;
609   sink->datasent_reset_ts = sink->stalled_ts = gst_util_get_timestamp ();
610
611   /* let's set metadata */
612   if (sink->songmetadata) {
613     shout_metadata_t *pmetadata;
614
615     GST_DEBUG_OBJECT (sink, "shout metadata now: %s", sink->songmetadata);
616     pmetadata = shout_metadata_new ();
617     shout_metadata_add (pmetadata, "song", sink->songmetadata);
618     shout_set_metadata (sink->conn, pmetadata);
619     shout_metadata_free (pmetadata);
620   }
621
622 done:
623   return fret;
624
625 /* ERRORS */
626 no_caps:
627   {
628     GST_ELEMENT_ERROR (sink, CORE, NEGOTIATION, (NULL),
629         ("No input caps received."));
630     return GST_FLOW_NOT_NEGOTIATED;
631   }
632
633 could_not_set_nonblocking:
634   {
635     GST_ELEMENT_ERROR (sink, LIBRARY, SETTINGS, (NULL),
636         ("Error configuring libshout to use non-blocking i/o: %s",
637             shout_get_error (sink->conn)));
638     return GST_FLOW_ERROR;
639   }
640
641 could_not_set_format:
642   {
643     GST_ELEMENT_ERROR (sink, LIBRARY, SETTINGS, (NULL),
644         ("Error setting connection format: %s", shout_get_error (sink->conn)));
645     return GST_FLOW_ERROR;
646   }
647
648 could_not_connect:
649   {
650     GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE,
651         (_("Could not connect to server")),
652         ("shout_open() failed: err=%s", shout_get_error (sink->conn)));
653     g_signal_emit (sink, gst_shout2send_signals[SIGNAL_CONNECTION_PROBLEM], 0,
654         shout_get_errno (sink->conn));
655     return GST_FLOW_ERROR;
656   }
657
658 connection_timeout:
659   {
660     GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE,
661         (_("Could not connect to server")), ("connection timed out"));
662     g_signal_emit (sink, gst_shout2send_signals[SIGNAL_CONNECTION_PROBLEM], 0,
663         shout_get_errno (sink->conn));
664     return GST_FLOW_ERROR;
665   }
666 }
667
668 static gboolean
669 gst_shout2send_stop (GstBaseSink * basesink)
670 {
671   GstShout2send *sink = GST_SHOUT2SEND (basesink);
672
673   if (sink->conn) {
674     if (sink->connected)
675       shout_close (sink->conn);
676     shout_free (sink->conn);
677     sink->conn = NULL;
678   }
679
680   if (sink->songmetadata) {
681     g_free (sink->songmetadata);
682     sink->songmetadata = NULL;
683   }
684
685   sink->connected = FALSE;
686   sink->format = -1;
687
688   return TRUE;
689 }
690
691 static gboolean
692 gst_shout2send_unlock (GstBaseSink * basesink)
693 {
694   GstShout2send *sink;
695
696   sink = GST_SHOUT2SEND (basesink);
697
698   GST_DEBUG_OBJECT (basesink, "unlock");
699   gst_poll_set_flushing (sink->timer, TRUE);
700
701   return TRUE;
702 }
703
704 static gboolean
705 gst_shout2send_unlock_stop (GstBaseSink * basesink)
706 {
707   GstShout2send *sink;
708
709   sink = GST_SHOUT2SEND (basesink);
710
711   GST_DEBUG_OBJECT (basesink, "unlock_stop");
712   gst_poll_set_flushing (sink->timer, FALSE);
713
714   return TRUE;
715 }
716
717 static GstFlowReturn
718 gst_shout2send_render (GstBaseSink * basesink, GstBuffer * buf)
719 {
720   GstShout2send *sink;
721   glong ret;
722   gint delay;
723   GstFlowReturn fret = GST_FLOW_OK;
724   GstMapInfo map;
725   GstClockTime now;
726   ssize_t queuelen;
727
728   sink = GST_SHOUT2SEND (basesink);
729
730   /* we connect here because we need to know the format before we can set up
731    * the connection, which we don't know yet in _start(), and also because we
732    * don't want to block the application thread */
733   if (!sink->connected) {
734     fret = gst_shout2send_connect (sink);
735     if (fret != GST_FLOW_OK)
736       goto done;
737   }
738
739   delay = shout_delay (sink->conn);
740
741   if (delay > 0) {
742     GST_LOG_OBJECT (sink, "waiting %d msec", delay);
743     if (gst_poll_wait (sink->timer, GST_MSECOND * delay) == -1) {
744       GST_LOG_OBJECT (sink, "unlocked");
745
746       fret = gst_base_sink_wait_preroll (basesink);
747       if (fret != GST_FLOW_OK)
748         goto done;
749     }
750   } else {
751     GST_LOG_OBJECT (sink, "we're %d msec late", -delay);
752   }
753
754   /* accumulate how much data have actually been sent
755    * to the network since the last call to shout_send() */
756   queuelen = shout_queuelen (sink->conn);
757   if (sink->prev_queuelen > 0)
758     sink->data_sent += sink->prev_queuelen - queuelen;
759
760   gst_buffer_map (buf, &map, GST_MAP_READ);
761
762   /* add map.size instead of re-reading the queue length because
763    * the data may actually be sent immediately */
764   sink->prev_queuelen = queuelen + map.size;
765
766   GST_LOG_OBJECT (sink, "sending %u bytes of data, queue length now is %"
767       G_GUINT64_FORMAT, (guint) map.size, sink->prev_queuelen);
768
769   ret = shout_send (sink->conn, map.data, map.size);
770
771   gst_buffer_unmap (buf, &map);
772   if (ret != SHOUTERR_SUCCESS)
773     goto send_error;
774
775   now = gst_util_get_timestamp ();
776   if (now - sink->datasent_reset_ts >= 500 * GST_MSECOND) {
777     guint64 send_rate;
778
779     send_rate = gst_util_uint64_scale (sink->data_sent, GST_SECOND,
780         now - sink->datasent_reset_ts);
781
782     if (send_rate == 0 && !sink->stalled) {
783       sink->stalled = TRUE;
784       sink->stalled_ts = now;
785     } else if (send_rate > 0 && sink->stalled) {
786       sink->stalled = FALSE;
787     }
788
789     sink->data_sent = 0;
790     sink->datasent_reset_ts = now;
791
792     GST_DEBUG_OBJECT (sink, "sending rate is %" G_GUINT64_FORMAT " bps, "
793         "stalled %d, stalled_ts %" GST_TIME_FORMAT, send_rate, sink->stalled,
794         GST_TIME_ARGS (sink->stalled_ts));
795
796     if (sink->stalled && now - sink->stalled_ts >= sink->timeout * GST_MSECOND) {
797       GST_WARNING_OBJECT (sink, "network send queue is stalled for too long");
798       goto network_error;
799     }
800   }
801
802 done:
803
804   return fret;
805
806 /* ERRORS */
807 send_error:
808   {
809     GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL),
810         ("shout_send() failed: %s", shout_get_error (sink->conn)));
811     g_signal_emit (sink, gst_shout2send_signals[SIGNAL_CONNECTION_PROBLEM], 0,
812         shout_get_errno (sink->conn));
813     return GST_FLOW_ERROR;
814   }
815
816 network_error:
817   {
818     GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL),
819         ("network timeout reached"));
820     g_signal_emit (sink, gst_shout2send_signals[SIGNAL_CONNECTION_PROBLEM], 0,
821         SHOUTERR_BUSY);
822     return GST_FLOW_ERROR;
823   }
824 }
825
826 static void
827 gst_shout2send_set_property (GObject * object, guint prop_id,
828     const GValue * value, GParamSpec * pspec)
829 {
830   GstShout2send *shout2send;
831
832   shout2send = GST_SHOUT2SEND (object);
833   switch (prop_id) {
834
835     case ARG_IP:
836       g_free (shout2send->ip);
837       shout2send->ip = g_value_dup_string (value);
838       break;
839     case ARG_PORT:
840       shout2send->port = g_value_get_int (value);
841       break;
842     case ARG_PASSWORD:
843       g_free (shout2send->password);
844       shout2send->password = g_value_dup_string (value);
845       break;
846     case ARG_USERNAME:
847       g_free (shout2send->username);
848       shout2send->username = g_value_dup_string (value);
849       break;
850     case ARG_PUBLIC:
851       shout2send->ispublic = g_value_get_boolean (value);
852       break;
853     case ARG_STREAMNAME:       /* Name of the stream */
854       g_free (shout2send->streamname);
855       shout2send->streamname = g_value_dup_string (value);
856       break;
857     case ARG_DESCRIPTION:      /* Description of the stream */
858       g_free (shout2send->description);
859       shout2send->description = g_value_dup_string (value);
860       break;
861     case ARG_GENRE:            /* Genre of the stream */
862       g_free (shout2send->genre);
863       shout2send->genre = g_value_dup_string (value);
864       break;
865     case ARG_PROTOCOL:         /* protocol to connect with */
866       shout2send->protocol = g_value_get_enum (value);
867       break;
868     case ARG_MOUNT:            /* mountpoint of stream (icecast only) */
869       g_free (shout2send->mount);
870       shout2send->mount = g_value_dup_string (value);
871       break;
872     case ARG_URL:              /* the stream's homepage URL */
873       g_free (shout2send->url);
874       shout2send->url = g_value_dup_string (value);
875       break;
876     case ARG_TIMEOUT:
877       shout2send->timeout = g_value_get_uint (value);
878       break;
879     default:
880       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
881       break;
882   }
883 }
884
885 static void
886 gst_shout2send_get_property (GObject * object, guint prop_id,
887     GValue * value, GParamSpec * pspec)
888 {
889   GstShout2send *shout2send;
890
891   shout2send = GST_SHOUT2SEND (object);
892   switch (prop_id) {
893
894     case ARG_IP:
895       g_value_set_string (value, shout2send->ip);
896       break;
897     case ARG_PORT:
898       g_value_set_int (value, shout2send->port);
899       break;
900     case ARG_PASSWORD:
901       g_value_set_string (value, shout2send->password);
902       break;
903     case ARG_USERNAME:
904       g_value_set_string (value, shout2send->username);
905       break;
906     case ARG_PUBLIC:
907       g_value_set_boolean (value, shout2send->ispublic);
908       break;
909     case ARG_STREAMNAME:       /* Name of the stream */
910       g_value_set_string (value, shout2send->streamname);
911       break;
912     case ARG_DESCRIPTION:      /* Description of the stream */
913       g_value_set_string (value, shout2send->description);
914       break;
915     case ARG_GENRE:            /* Genre of the stream */
916       g_value_set_string (value, shout2send->genre);
917       break;
918     case ARG_PROTOCOL:         /* protocol to connect with */
919       g_value_set_enum (value, shout2send->protocol);
920       break;
921     case ARG_MOUNT:            /* mountpoint of stream (icecast only) */
922       g_value_set_string (value, shout2send->mount);
923       break;
924     case ARG_URL:              /* the stream's homepage URL */
925       g_value_set_string (value, shout2send->url);
926       break;
927     case ARG_TIMEOUT:
928       g_value_set_uint (value, shout2send->timeout);
929       break;
930     default:
931       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
932       break;
933   }
934 }
935
936 static gboolean
937 gst_shout2send_setcaps (GstBaseSink * basesink, GstCaps * caps)
938 {
939   const gchar *mimetype;
940   GstShout2send *shout2send;
941   gboolean ret = TRUE;
942
943   shout2send = GST_SHOUT2SEND (basesink);
944
945   mimetype = gst_structure_get_name (gst_caps_get_structure (caps, 0));
946
947   GST_DEBUG_OBJECT (shout2send, "mimetype of caps given is: %s", mimetype);
948
949   if (!strcmp (mimetype, "audio/mpeg")) {
950     shout2send->format = SHOUT_FORMAT_MP3;
951   } else if (g_str_has_suffix (mimetype, "/ogg")) {
952     shout2send->format = SHOUT_FORMAT_OGG;
953 #ifdef SHOUT_FORMAT_WEBM
954   } else if (g_str_has_suffix (mimetype, "/webm")) {
955     shout2send->format = SHOUT_FORMAT_WEBM;
956 #endif
957   } else {
958     ret = FALSE;
959   }
960
961   return ret;
962 }
963
964 static gboolean
965 plugin_init (GstPlugin * plugin)
966 {
967   return GST_ELEMENT_REGISTER (shout2send, plugin);
968 }
969
970 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
971     GST_VERSION_MINOR,
972     shout2,
973     "Sends data to an icecast server using libshout2",
974     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)