ext/shout2/gstshout2.c: Guimond, fixes #142432)
[platform/upstream/gst-plugins-good.git] / ext / shout2 / gstshout2.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 #include "gstshout2.h"
24 #include <stdlib.h>
25 #include <string.h>
26
27 /* elementfactory information */
28 static GstElementDetails shout2send_details = {
29   "An Icecast plugin",
30   "Sink/Network",
31   "Sends data to an icecast server",
32   "Wim Taymans <wim.taymans@chello.be>\n" "Pedro Corte-Real <typo@netcabo.pt>"
33 };
34
35 unsigned int audio_format = 100;
36
37 /* Shout2send signals and args */
38 enum
39 {
40   /* FILL ME */
41   LAST_SIGNAL
42 };
43
44 enum
45 {
46   ARG_0,
47   ARG_IP,                       /* the ip of the server */
48   ARG_PORT,                     /* the encoder port number on the server */
49   ARG_PASSWORD,                 /* the encoder password on the server */
50   ARG_PUBLIC,                   /* is this stream public? */
51   ARG_NAME,                     /* Name of the stream */
52   ARG_DESCRIPTION,              /* Description of the stream */
53   ARG_GENRE,                    /* Genre of the stream */
54
55   ARG_PROTOCOL,                 /* Protocol to connect with */
56
57   ARG_MOUNT,                    /* mountpoint of stream (icecast only) */
58   ARG_URL,                      /* Url of stream (I'm guessing) */
59 };
60
61 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
62     GST_PAD_SINK,
63     GST_PAD_ALWAYS,
64     GST_STATIC_CAPS ("application/ogg; "
65         "audio/mpeg, mpegversion = (int) 1, layer = (int) [ 1, 3 ]")
66     );
67
68 static void gst_shout2send_class_init (GstShout2sendClass * klass);
69 static void gst_shout2send_base_init (GstShout2sendClass * klass);
70 static void gst_shout2send_init (GstShout2send * shout2send);
71
72 static void gst_shout2send_chain (GstPad * pad, GstData * _data);
73 static GstPadLinkReturn gst_shout2send_connect (GstPad * pad,
74     const GstCaps * caps);
75
76 static void gst_shout2send_set_property (GObject * object, guint prop_id,
77     const GValue * value, GParamSpec * pspec);
78 static void gst_shout2send_get_property (GObject * object, guint prop_id,
79     GValue * value, GParamSpec * pspec);
80
81 static GstElementStateReturn gst_shout2send_change_state (GstElement * element);
82
83 static GstElementClass *parent_class = NULL;
84
85 /*static guint gst_shout2send_signals[LAST_SIGNAL] = { 0 }; */
86
87 #define GST_TYPE_SHOUT_PROTOCOL (gst_shout2send_protocol_get_type())
88 static GType
89 gst_shout2send_protocol_get_type (void)
90 {
91   static GType shout2send_protocol_type = 0;
92   static GEnumValue shout2send_protocol[] = {
93     {SHOUT2SEND_PROTOCOL_XAUDIOCAST, "1",
94         "Xaudiocast Protocol (icecast 1.3.x)"},
95     {SHOUT2SEND_PROTOCOL_ICY, "2", "Icy Protocol (ShoutCast)"},
96     {SHOUT2SEND_PROTOCOL_HTTP, "3", "Http Protocol (icecast 2.x)"},
97     {0, NULL, NULL},
98   };
99
100   if (!shout2send_protocol_type) {
101     shout2send_protocol_type =
102         g_enum_register_static ("GstShout2SendProtocol", shout2send_protocol);
103   }
104   return shout2send_protocol_type;
105 }
106
107 GType
108 gst_shout2send_get_type (void)
109 {
110   static GType shout2send_type = 0;
111
112   if (!shout2send_type) {
113     static const GTypeInfo shout2send_info = {
114       sizeof (GstShout2sendClass),
115       (GBaseInitFunc) gst_shout2send_base_init,
116       NULL,
117       (GClassInitFunc) gst_shout2send_class_init,
118       NULL,
119       NULL,
120       sizeof (GstShout2send),
121       0,
122       (GInstanceInitFunc) gst_shout2send_init,
123     };
124
125     shout2send_type =
126         g_type_register_static (GST_TYPE_ELEMENT, "GstShout2send",
127         &shout2send_info, 0);
128   }
129   return shout2send_type;
130 }
131
132 static void
133 gst_shout2send_base_init (GstShout2sendClass * klass)
134 {
135   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
136
137   gst_element_class_add_pad_template (element_class,
138       gst_static_pad_template_get (&sink_template));
139   gst_element_class_set_details (element_class, &shout2send_details);
140 }
141
142 static void
143 gst_shout2send_class_init (GstShout2sendClass * klass)
144 {
145   GObjectClass *gobject_class;
146   GstElementClass *gstelement_class;
147
148   gobject_class = (GObjectClass *) klass;
149   gstelement_class = (GstElementClass *) klass;
150
151   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
152
153   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_IP, g_param_spec_string ("ip", "ip", "ip", NULL, G_PARAM_READWRITE));    /* CHECKME */
154   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PORT, g_param_spec_int ("port", "port", "port", 1, G_MAXUSHORT, 8000, G_PARAM_READWRITE));       /* CHECKME */
155
156   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PASSWORD, g_param_spec_string ("password", "password", "password", NULL, G_PARAM_READWRITE));    /* CHECKME */
157
158   /* metadata */
159   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NAME, g_param_spec_string ("name", "name", "name", NULL, G_PARAM_READWRITE));    /* CHECKME */
160
161   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DESCRIPTION, g_param_spec_string ("description", "description", "description", NULL, G_PARAM_READWRITE));        /* CHECKME */
162
163   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_GENRE, g_param_spec_string ("genre", "genre", "genre", NULL, G_PARAM_READWRITE));        /* CHECKME */
164
165   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PROTOCOL,
166       g_param_spec_enum ("protocol", "protocol", "Connection Protocol to use",
167           GST_TYPE_SHOUT_PROTOCOL, SHOUT2SEND_PROTOCOL_HTTP,
168           G_PARAM_READWRITE));
169
170
171   /* icecast only */
172   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MOUNT, g_param_spec_string ("mount", "mount", "mount", NULL, G_PARAM_READWRITE));        /* CHECKME */
173
174   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_URL, g_param_spec_string ("url", "url", "url", NULL, G_PARAM_READWRITE));        /* CHECKME */
175
176
177
178   gobject_class->set_property = gst_shout2send_set_property;
179   gobject_class->get_property = gst_shout2send_get_property;
180
181   gstelement_class->change_state = gst_shout2send_change_state;
182 }
183
184 static void
185 gst_shout2send_init (GstShout2send * shout2send)
186 {
187   shout2send->sinkpad =
188       gst_pad_new_from_template (gst_static_pad_template_get (&sink_template),
189       "sink");
190   gst_element_add_pad (GST_ELEMENT (shout2send), shout2send->sinkpad);
191   gst_pad_set_chain_function (shout2send->sinkpad, gst_shout2send_chain);
192
193   gst_pad_set_link_function (shout2send->sinkpad, gst_shout2send_connect);
194
195   shout2send->ip = g_strdup ("127.0.0.1");
196   shout2send->port = 8000;
197   shout2send->password = g_strdup ("hackme");
198   shout2send->name = g_strdup ("");
199   shout2send->description = g_strdup ("");
200   shout2send->genre = g_strdup ("");
201   shout2send->mount = g_strdup ("");
202   shout2send->url = g_strdup ("");
203   shout2send->protocol = SHOUT2SEND_PROTOCOL_HTTP;
204 }
205
206 static void
207 gst_shout2send_chain (GstPad * pad, GstData * _data)
208 {
209   GstBuffer *buf = GST_BUFFER (_data);
210   GstShout2send *shout2send;
211   glong ret;
212
213   g_return_if_fail (pad != NULL);
214   g_return_if_fail (GST_IS_PAD (pad));
215   g_return_if_fail (buf != NULL);
216
217   shout2send = GST_SHOUT2SEND (GST_OBJECT_PARENT (pad));
218
219   g_return_if_fail (shout2send != NULL);
220   g_return_if_fail (GST_IS_SHOUT2SEND (shout2send));
221
222   ret = shout_send (shout2send->conn, GST_BUFFER_DATA (buf),
223       GST_BUFFER_SIZE (buf));
224   if (ret != SHOUTERR_SUCCESS) {
225     g_warning ("send error: %s...\n", shout_get_error (shout2send->conn));
226   }
227
228   shout_sync (shout2send->conn);
229
230   gst_buffer_unref (buf);
231 }
232
233 static void
234 gst_shout2send_set_property (GObject * object, guint prop_id,
235     const GValue * value, GParamSpec * pspec)
236 {
237   GstShout2send *shout2send;
238
239   /* it's not null if we got it, but it might not be ours */
240   g_return_if_fail (GST_IS_SHOUT2SEND (object));
241   shout2send = GST_SHOUT2SEND (object);
242
243   switch (prop_id) {
244
245     case ARG_IP:
246       if (shout2send->ip)
247         g_free (shout2send->ip);
248       shout2send->ip = g_strdup (g_value_get_string (value));
249       break;
250
251     case ARG_PORT:
252       shout2send->port = g_value_get_int (value);
253       break;
254
255     case ARG_PASSWORD:
256       if (shout2send->password)
257         g_free (shout2send->password);
258       shout2send->password = g_strdup (g_value_get_string (value));
259       break;
260
261     case ARG_NAME:             /* Name of the stream */
262       if (shout2send->name)
263         g_free (shout2send->name);
264       shout2send->name = g_strdup (g_value_get_string (value));
265       break;
266
267     case ARG_DESCRIPTION:      /* Description of the stream */
268       if (shout2send->description)
269         g_free (shout2send->description);
270       shout2send->description = g_strdup (g_value_get_string (value));
271       break;
272
273     case ARG_GENRE:            /* Genre of the stream */
274       if (shout2send->genre)
275         g_free (shout2send->genre);
276       shout2send->genre = g_strdup (g_value_get_string (value));
277       break;
278
279     case ARG_PROTOCOL:         /* protocol to connect with */
280       shout2send->protocol = g_value_get_enum (value);
281       break;
282
283     case ARG_MOUNT:            /* mountpoint of stream (icecast only) */
284       if (shout2send->mount)
285         g_free (shout2send->mount);
286       shout2send->mount = g_strdup (g_value_get_string (value));
287       break;
288
289     case ARG_URL:              /* Url of the stream (I'm guessing) */
290       if (shout2send->url)
291         g_free (shout2send->url);
292       shout2send->url = g_strdup (g_value_get_string (value));
293       break;
294
295     default:
296       break;
297   }
298 }
299
300 static void
301 gst_shout2send_get_property (GObject * object, guint prop_id, GValue * value,
302     GParamSpec * pspec)
303 {
304   GstShout2send *shout2send;
305
306   /* it's not null if we got it, but it might not be ours */
307   g_return_if_fail (GST_IS_SHOUT2SEND (object));
308   shout2send = GST_SHOUT2SEND (object);
309
310   switch (prop_id) {
311
312     case ARG_IP:
313       g_value_set_string (value, shout2send->ip);
314       break;
315     case ARG_PORT:
316       g_value_set_int (value, shout2send->port);
317       break;
318     case ARG_PASSWORD:
319       g_value_set_string (value, shout2send->password);
320       break;
321
322     case ARG_NAME:             /* Name of the stream */
323       g_value_set_string (value, shout2send->name);
324       break;
325
326     case ARG_DESCRIPTION:      /* Description of the stream */
327       g_value_set_string (value, shout2send->description);
328       break;
329
330     case ARG_GENRE:            /* Genre of the stream */
331       g_value_set_string (value, shout2send->genre);
332       break;
333
334     case ARG_PROTOCOL:         /* protocol to connect with */
335       g_value_set_enum (value, shout2send->protocol);
336       break;
337
338     case ARG_MOUNT:            /* mountpoint of stream (icecast only) */
339       g_value_set_string (value, shout2send->mount);
340       break;
341
342     case ARG_URL:              /* Url of stream (I'm guessing) */
343       g_value_set_string (value, shout2send->url);
344       break;
345
346
347     default:
348       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
349       break;
350   }
351 }
352
353 static GstPadLinkReturn
354 gst_shout2send_connect (GstPad * pad, const GstCaps * caps)
355 {
356   const gchar *mimetype;
357
358   mimetype = gst_structure_get_name (gst_caps_get_structure (caps, 0));
359   if (!strcmp (mimetype, "audio/mpeg")) {
360     audio_format = SHOUT_FORMAT_MP3;
361     return GST_PAD_LINK_OK;
362   }
363
364   if (!strcmp (mimetype, "application/ogg")) {
365     audio_format = SHOUT_FORMAT_VORBIS;
366     return GST_PAD_LINK_OK;
367   } else {
368     return GST_PAD_LINK_REFUSED;
369   }
370
371 }
372
373 static GstElementStateReturn
374 gst_shout2send_change_state (GstElement * element)
375 {
376   GstShout2send *shout2send;
377
378   guint major, minor, micro;
379   gshort proto = 3;
380
381   gchar *version_string;
382
383   g_return_val_if_fail (GST_IS_SHOUT2SEND (element), GST_STATE_FAILURE);
384
385   shout2send = GST_SHOUT2SEND (element);
386
387   GST_DEBUG ("state pending %d", GST_STATE_PENDING (element));
388
389   /* if going down into NULL state, close the file if it's open */
390   switch (GST_STATE_TRANSITION (element)) {
391     case GST_STATE_NULL_TO_READY:
392       shout2send->conn = shout_new ();
393
394       switch (shout2send->protocol) {
395         case SHOUT2SEND_PROTOCOL_XAUDIOCAST:
396           proto = SHOUT_PROTOCOL_XAUDIOCAST;
397           break;
398         case SHOUT2SEND_PROTOCOL_ICY:
399           proto = SHOUT_PROTOCOL_ICY;
400           break;
401         case SHOUT2SEND_PROTOCOL_HTTP:
402           proto = SHOUT_PROTOCOL_HTTP;
403           break;
404       }
405
406       if (shout_set_protocol (shout2send->conn, proto) != SHOUTERR_SUCCESS) {
407         g_error ("Error setting protocol: %s\n",
408             shout_get_error (shout2send->conn));
409       }
410
411       /* --- FIXME: shout requires an ip, and fails if it is given a host. */
412       /* may want to put convert_to_ip(shout2send->ip) here */
413
414
415       if (shout_set_host (shout2send->conn, shout2send->ip) != SHOUTERR_SUCCESS) {
416         g_error ("Error setting host: %s\n",
417             shout_get_error (shout2send->conn));
418       }
419       /* --- */
420
421       if (shout_set_port (shout2send->conn,
422               shout2send->port) != SHOUTERR_SUCCESS) {
423         g_error ("Error setting port: %s\n",
424             shout_get_error (shout2send->conn));
425       }
426
427       if (shout_set_password (shout2send->conn,
428               shout2send->password) != SHOUTERR_SUCCESS) {
429         g_error ("Error setting password: %s\n",
430             shout_get_error (shout2send->conn));
431       }
432
433       if (shout_set_name (shout2send->conn,
434               shout2send->name) != SHOUTERR_SUCCESS) {
435         g_error ("Error setting name: %s\n",
436             shout_get_error (shout2send->conn));
437       }
438
439       if (shout_set_description (shout2send->conn,
440               shout2send->description) != SHOUTERR_SUCCESS) {
441         g_error ("Error setting name: %s\n",
442             shout_get_error (shout2send->conn));
443       }
444
445       if (shout_set_genre (shout2send->conn,
446               shout2send->genre) != SHOUTERR_SUCCESS) {
447         g_error ("Error setting name: %s\n",
448             shout_get_error (shout2send->conn));
449       }
450
451       if (shout_set_mount (shout2send->conn,
452               shout2send->mount) != SHOUTERR_SUCCESS) {
453         g_error ("Error setting mount point: %s\n",
454             shout_get_error (shout2send->conn));
455       }
456
457       if (shout_set_user (shout2send->conn, "source") != SHOUTERR_SUCCESS) {
458         g_error ("Error setting user: %s\n",
459             shout_get_error (shout2send->conn));
460       }
461
462       gst_version (&major, &minor, &micro);
463
464       version_string =
465           g_strdup_printf ("GStreamer %d.%d.%d", major, minor, micro);
466
467       if (shout_set_agent (shout2send->conn,
468               version_string) != SHOUTERR_SUCCESS) {
469         g_error ("Error setting agent: %s\n",
470             shout_get_error (shout2send->conn));
471       }
472
473       g_free (version_string);
474
475
476
477       break;
478     case GST_STATE_READY_TO_PAUSED:
479
480       /* This sets the format acording to the capabilities of what
481          we are being given as input. */
482
483       if (shout_set_format (shout2send->conn, audio_format) != SHOUTERR_SUCCESS) {
484         g_error ("Error setting connection format: %s\n",
485             shout_get_error (shout2send->conn));
486       }
487
488       if (shout_open (shout2send->conn) == SHOUTERR_SUCCESS) {
489         g_print ("connected to server...\n");
490       } else {
491         g_warning ("Couldn't connect to server: %s",
492             shout_get_error (shout2send->conn));
493         shout_close (shout2send->conn);
494         shout_free (shout2send->conn);
495         return GST_STATE_FAILURE;
496       }
497       break;
498     case GST_STATE_PAUSED_TO_READY:
499       shout_close (shout2send->conn);
500       shout_free (shout2send->conn);
501       break;
502     default:
503       break;
504   }
505
506   /* if we haven't failed already, give the parent class a chance to ;-) */
507   if (GST_ELEMENT_CLASS (parent_class)->change_state)
508     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
509
510   return GST_STATE_SUCCESS;
511 }
512
513 static gboolean
514 plugin_init (GstPlugin * plugin)
515 {
516   return gst_element_register (plugin, "shout2send", GST_RANK_NONE,
517       GST_TYPE_SHOUT2SEND);
518 }
519
520 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
521     GST_VERSION_MINOR,
522     "shout2send",
523     "Sends data to an icecast server using libshout2",
524     plugin_init,
525     VERSION, "LGPL", "libshout2", "http://www.icecast.org/download.html")