2 * Copyright (C) <2001> Richard Boulton <richard-gst@tartarus.org>
5 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
31 GST_DEBUG_CATEGORY_EXTERN (esd_debug);
32 #define GST_CAT_DEFAULT esd_debug
34 /* elementfactory information */
35 static GstElementDetails esdsink_details = {
38 "Plays audio to an esound server",
39 "Richard Boulton <richard-gst@tartarus.org>",
42 /* Signals and args */
56 static GstStaticPadTemplate sink_factory =
57 GST_STATIC_PAD_TEMPLATE (
63 "endianness = (int) " G_STRINGIFY (G_BYTE_ORDER) ", "
64 "signed = (boolean) TRUE, "
72 static void gst_esdsink_base_init (gpointer g_class);
73 static void gst_esdsink_class_init (gpointer g_class, gpointer class_data);
74 static void gst_esdsink_init (GTypeInstance *instance, gpointer g_class);
76 static gboolean gst_esdsink_open_audio (GstEsdsink *sink);
77 static void gst_esdsink_close_audio (GstEsdsink *sink);
78 static GstElementStateReturn gst_esdsink_change_state (GstElement *element);
80 static GstClockTime gst_esdsink_get_time (GstClock *clock, gpointer data);
81 static GstClock * gst_esdsink_get_clock (GstElement *element);
82 static void gst_esdsink_set_clock (GstElement *element, GstClock *clock);
83 static void gst_esdsink_chain (GstPad *pad, GstData *_data);
85 static void gst_esdsink_set_property (GObject *object, guint prop_id,
86 const GValue *value, GParamSpec *pspec);
87 static void gst_esdsink_get_property (GObject *object, guint prop_id,
88 GValue *value, GParamSpec *pspec);
90 static GstElementClass *parent_class = NULL;
91 /*static guint gst_esdsink_signals[LAST_SIGNAL] = { 0 }; */
94 gst_esdsink_get_type (void)
96 static GType esdsink_type = 0;
99 static const GTypeInfo esdsink_info = {
100 sizeof(GstEsdsinkClass),
101 gst_esdsink_base_init,
103 gst_esdsink_class_init,
110 esdsink_type = g_type_register_static(GST_TYPE_ELEMENT, "GstEsdsink", &esdsink_info, 0);
116 gst_esdsink_base_init (gpointer g_class)
118 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
120 gst_element_class_add_pad_template (element_class,
121 gst_static_pad_template_get (&sink_factory));
122 gst_element_class_set_details (element_class, &esdsink_details);
126 gst_esdsink_class_init (gpointer g_class, gpointer class_data)
128 GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
129 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
131 parent_class = g_type_class_peek_parent (g_class);
133 g_object_class_install_property(gobject_class, ARG_MUTE,
134 g_param_spec_boolean("mute","mute","mute",
135 TRUE,G_PARAM_READWRITE)); /* CHECKME */
136 g_object_class_install_property(gobject_class, ARG_HOST,
137 g_param_spec_string("host","host","host",
138 NULL, G_PARAM_READWRITE)); /* CHECKME */
139 g_object_class_install_property(gobject_class, ARG_SYNC,
140 g_param_spec_boolean("sync","sync","Synchronize output to clock",
141 TRUE,G_PARAM_READWRITE));
143 /* This option is disabled because it is dumb in GStreamer's architecture. */
144 g_object_class_install_property(gobject_class, ARG_FALLBACK,
145 g_param_spec_boolean("fallback","fallback","Fall back to using OSS if Esound daemon is not present",
146 FALSE,G_PARAM_READWRITE));
149 gobject_class->set_property = gst_esdsink_set_property;
150 gobject_class->get_property = gst_esdsink_get_property;
152 gstelement_class->change_state = gst_esdsink_change_state;
153 gstelement_class->set_clock = gst_esdsink_set_clock;
154 gstelement_class->get_clock = gst_esdsink_get_clock;
158 gst_esdsink_init(GTypeInstance *instance, gpointer g_class)
160 GstEsdsink *esdsink = GST_ESDSINK (instance);
162 esdsink->sinkpad = gst_pad_new_from_template (
163 gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (instance), "sink"),
165 gst_element_add_pad(GST_ELEMENT(esdsink), esdsink->sinkpad);
166 gst_pad_set_chain_function(esdsink->sinkpad, GST_DEBUG_FUNCPTR(gst_esdsink_chain));
168 GST_FLAG_SET (esdsink, GST_ELEMENT_EVENT_AWARE);
170 esdsink->mute = FALSE;
172 /* FIXME: get default from somewhere better than just putting them inline. */
173 /*esdsink->negotiated = FALSE;*/
174 /* we have static caps on our template, so it always is negotiated */
175 esdsink->negotiated = TRUE;
176 esdsink->format = 16;
178 esdsink->channels = 2;
179 esdsink->frequency = 44100;
180 esdsink->bytes_per_sample = esdsink->channels * (esdsink->depth/8);
181 esdsink->host = getenv ("ESPEAKER");
182 esdsink->provided_clock = gst_audio_clock_new("esdclock", gst_esdsink_get_time, esdsink);
183 gst_object_set_parent(GST_OBJECT(esdsink->provided_clock), GST_OBJECT(esdsink));
184 esdsink->sync = TRUE;
185 esdsink->fallback = FALSE;
189 static GstPadLinkReturn
190 gst_esdsink_link (GstPad *pad, const GstCaps *caps)
193 GstStructure *structure;
195 esdsink = GST_ESDSINK (gst_pad_get_parent (pad));
197 structure = gst_caps_get_structure (caps, 0);
198 gst_structure_get_int (structure, "depth", &esdsink->depth);
199 gst_structure_get_int (structure, "channels", &esdsink->channels);
200 gst_structure_get_int (structure, "rate", &esdsink->frequency);
202 esdsink->bytes_per_sample = esdsink->channels * (esdsink->depth/8);
204 gst_esdsink_close_audio (esdsink);
205 if (gst_esdsink_open_audio (esdsink)) {
206 esdsink->negotiated = TRUE;
207 return GST_PAD_LINK_OK;
209 /* FIXME: is it supposed to be correct to have closed audio when caps nego
212 GST_DEBUG ("esd link function could not negotiate, returning delayed");
213 return GST_PAD_LINK_REFUSED;
218 gst_esdsink_get_time (GstClock *clock, gpointer data)
220 GstEsdsink *esdsink = GST_ESDSINK(data);
223 res = (esdsink->handled * GST_SECOND) / esdsink->frequency;
230 gst_esdsink_get_clock (GstElement *element)
234 esdsink = GST_ESDSINK (element);
236 return GST_CLOCK(esdsink->provided_clock);
240 gst_esdsink_set_clock (GstElement *element, GstClock *clock)
244 esdsink = GST_ESDSINK (element);
246 esdsink->clock = clock;
250 gst_esdsink_chain (GstPad *pad, GstData *_data)
252 GstBuffer *buf = GST_BUFFER (_data);
255 esdsink = GST_ESDSINK (gst_pad_get_parent (pad));
257 if (!esdsink->negotiated) {
258 GST_ELEMENT_ERROR (esdsink, CORE, NEGOTIATION, (NULL),
259 ("element wasn't negotiated before chain function"));
263 if (GST_IS_EVENT(buf)){
264 GstEvent *event = GST_EVENT(buf);
266 switch(GST_EVENT_TYPE(event)){
268 gst_audio_clock_set_active (GST_AUDIO_CLOCK (esdsink->provided_clock),
270 gst_pad_event_default (pad, event);
273 gst_pad_event_default(pad, event);
276 gst_event_unref(event);
280 if (GST_BUFFER_DATA (buf) != NULL) {
281 if (!esdsink->mute && esdsink->fd >= 0) {
282 guchar *data = GST_BUFFER_DATA (buf);
283 gint size = GST_BUFFER_SIZE (buf);
288 GST_LOG ("fd=%d data=%p size=%d",
289 esdsink->fd, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
290 while (to_write > 0){
293 done = write (esdsink->fd, data, to_write);
299 g_assert_not_reached();
304 esdsink->handled += done / esdsink->bytes_per_sample;
310 gst_audio_clock_update_time ((GstAudioClock *)esdsink->provided_clock,
311 gst_esdsink_get_time (esdsink->provided_clock, esdsink));
314 gst_buffer_unref (buf);
318 gst_esdsink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
322 /* it's not null if we got it, but it might not be ours */
323 g_return_if_fail(GST_IS_ESDSINK(object));
324 esdsink = GST_ESDSINK(object);
328 esdsink->mute = g_value_get_boolean (value);
331 g_free(esdsink->host);
332 if (g_value_get_string (value) == NULL)
333 esdsink->host = NULL;
335 esdsink->host = g_strdup (g_value_get_string (value));
338 esdsink->sync = g_value_get_boolean (value);
341 esdsink->fallback = g_value_get_boolean (value);
349 gst_esdsink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
353 esdsink = GST_ESDSINK(object);
357 g_value_set_boolean (value, esdsink->mute);
360 g_value_set_string (value, esdsink->host);
363 g_value_set_boolean (value, esdsink->sync);
366 g_value_set_boolean (value, esdsink->fallback);
369 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
375 gst_esdsink_factory_init (GstPlugin *plugin)
377 if (!gst_element_register (plugin, "esdsink", GST_RANK_NONE, GST_TYPE_ESDSINK))
384 gst_esdsink_open_audio (GstEsdsink *sink)
386 /* Name used by esound for this connection. */
387 const char * connname = "GStreamer";
389 /* Bitmap describing audio format. */
390 esd_format_t esdformat = ESD_STREAM | ESD_PLAY;
392 g_return_val_if_fail (sink->fd == -1, FALSE);
394 if (sink->depth == 16) esdformat |= ESD_BITS16;
395 else if (sink->depth == 8) esdformat |= ESD_BITS8;
397 GST_ELEMENT_ERROR (sink, STREAM, FORMAT, (NULL),
398 ("invalid bit depth (%d)", sink->depth));
402 if (sink->channels == 2) esdformat |= ESD_STEREO;
403 else if (sink->channels == 1) esdformat |= ESD_MONO;
405 GST_ELEMENT_ERROR (sink, STREAM, FORMAT, (NULL),
406 ("invalid number of channels (%d)", sink->channels));
410 GST_INFO ("attempting to open connection to esound server");
412 sink->fd = esd_play_stream_fallback(esdformat, sink->frequency, sink->host, connname);
414 sink->fd = esd_play_stream(esdformat, sink->frequency, sink->host, connname);
416 if ( sink->fd < 0 ) {
417 GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL),
418 ("can't open connection to esound server"));
421 GST_INFO ("successfully opened connection to esound server");
427 gst_esdsink_close_audio (GstEsdsink *sink)
435 GST_INFO ("esdsink: closed sound device");
438 static GstElementStateReturn
439 gst_esdsink_change_state (GstElement *element)
443 esdsink = GST_ESDSINK (element);
445 switch (GST_STATE_TRANSITION (element)) {
446 case GST_STATE_NULL_TO_READY:
447 if (!gst_esdsink_open_audio (GST_ESDSINK (element))) {
448 return GST_STATE_FAILURE;
451 case GST_STATE_READY_TO_PAUSED:
453 case GST_STATE_PAUSED_TO_PLAYING:
454 gst_audio_clock_set_active (GST_AUDIO_CLOCK (esdsink->provided_clock),
457 case GST_STATE_PLAYING_TO_PAUSED:
458 gst_audio_clock_set_active (GST_AUDIO_CLOCK (esdsink->provided_clock),
460 esdsink->resync = TRUE;
462 case GST_STATE_PAUSED_TO_READY:
464 case GST_STATE_READY_TO_NULL:
465 gst_esdsink_close_audio (GST_ESDSINK (element));
471 if (GST_ELEMENT_CLASS (parent_class)->change_state)
472 return GST_ELEMENT_CLASS (parent_class)->change_state (element);
474 return GST_STATE_SUCCESS;