/* GStreamer * Copyright (C) <1999> Erik Walthinsen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* * Portions derived from maswavplay.c (distributed under the X11 * license): * * Copyright (c) 2001-2003 Shiman Associates Inc. All Rights Reserved. * Copyright (c) 2000, 2001 by Shiman Associates Inc. and Sun * Microsystems, Inc. All Rights Reserved. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "massink.h" #define BUFFER_SIZE 640 /* Signals and args */ enum { /* FILL ME */ LAST_SIGNAL }; enum { ARG_0, ARG_MUTE, ARG_DEPTH, ARG_CHANNELS, ARG_RATE, ARG_HOST, }; static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ( "sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/x-raw-int") ); static void gst_massink_base_init (gpointer g_class); static void gst_massink_class_init (GstMassinkClass *klass); static void gst_massink_init (GstMassink *massink); static void gst_massink_set_clock (GstElement *element, GstClock *clock); static gboolean gst_massink_open_audio (GstMassink *sink); //static void gst_massink_close_audio (GstMassink *sink); static GstElementStateReturn gst_massink_change_state (GstElement *element); static gboolean gst_massink_sync_parms (GstMassink *massink); static GstPadLinkReturn gst_massink_sinkconnect (GstPad *pad, const GstCaps *caps); static void gst_massink_chain (GstPad *pad, GstData *_data); static void gst_massink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void gst_massink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); #define GST_TYPE_MASSINK_DEPTHS (gst_massink_depths_get_type()) static GType gst_massink_depths_get_type (void) { static GType massink_depths_type = 0; static GEnumValue massink_depths[] = { {8, "8", "8 Bits"}, {16, "16", "16 Bits"}, {0, NULL, NULL}, }; if (!massink_depths_type) { massink_depths_type = g_enum_register_static("GstMassinkDepths", massink_depths); } return massink_depths_type; } static GstElementClass *parent_class = NULL; /*static guint gst_massink_signals[LAST_SIGNAL] = { 0 }; */ GType gst_massink_get_type (void) { static GType massink_type = 0; if (!massink_type) { static const GTypeInfo massink_info = { sizeof(GstMassinkClass), gst_massink_base_init, NULL, (GClassInitFunc)gst_massink_class_init, NULL, NULL, sizeof(GstMassink), 0, (GInstanceInitFunc)gst_massink_init, }; massink_type = g_type_register_static(GST_TYPE_ELEMENT, "GstMassink", &massink_info, 0); } return massink_type; } static void gst_massink_base_init (gpointer g_class) { static GstElementDetails massink_details = GST_ELEMENT_DETAILS ( "Esound audio sink", "Sink/Audio", "Plays audio to a MAS server", "Zeeshan Ali " ); GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&sink_factory)); gst_element_class_set_details (element_class, &massink_details); } static void gst_massink_class_init (GstMassinkClass *klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; gobject_class = (GObjectClass*)klass; gstelement_class = (GstElementClass*)klass; parent_class = g_type_class_ref(GST_TYPE_ELEMENT); g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_MUTE, g_param_spec_boolean("mute","mute","mute", TRUE,G_PARAM_READWRITE)); /* CHECKME */ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DEPTH, g_param_spec_enum("depth","depth","depth", GST_TYPE_MASSINK_DEPTHS,16,G_PARAM_READWRITE)); /* CHECKME! */ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_RATE, g_param_spec_int("frequency","frequency","frequency", G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); /* CHECKME */ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_HOST, g_param_spec_string("host","host","host", NULL, G_PARAM_READWRITE)); /* CHECKME */ gobject_class->set_property = gst_massink_set_property; gobject_class->get_property = gst_massink_get_property; gstelement_class->change_state = gst_massink_change_state; gstelement_class->set_clock = gst_massink_set_clock; } static void gst_massink_set_clock (GstElement *element, GstClock *clock) { GstMassink *massink; massink = GST_MASSINK (element); massink->clock = clock; } static void gst_massink_init(GstMassink *massink) { massink->sinkpad = gst_pad_new_from_template ( gst_static_pad_template_get (&sink_factory), "sink"); gst_element_add_pad(GST_ELEMENT(massink), massink->sinkpad); gst_pad_set_chain_function(massink->sinkpad, GST_DEBUG_FUNCPTR(gst_massink_chain)); gst_pad_set_link_function(massink->sinkpad, gst_massink_sinkconnect); massink->mute = FALSE; massink->format = 16; massink->depth = 16; massink->channels = 2; massink->frequency = 44100; massink->host = NULL; } static gboolean gst_massink_sync_parms (GstMassink *massink) { g_return_val_if_fail (massink != NULL, FALSE); g_return_val_if_fail (GST_IS_MASSINK (massink), FALSE); //gst_massink_close_audio (massink); //return gst_massink_open_audio (massink); return 1; } static GstPadLinkReturn gst_massink_sinkconnect (GstPad *pad, const GstCaps *caps) { GstMassink *massink; massink = GST_MASSINK (gst_pad_get_parent (pad)); if (gst_massink_sync_parms (massink)) return GST_PAD_LINK_OK; return GST_PAD_LINK_REFUSED; } static void gst_massink_chain (GstPad *pad, GstData *_data) { GstBuffer *buf = GST_BUFFER (_data); gint32 err; g_return_if_fail(pad != NULL); g_return_if_fail(GST_IS_PAD(pad)); g_return_if_fail(buf != NULL); GstMassink *massink = GST_MASSINK (gst_pad_get_parent (pad)); if (massink->clock) { GstClockID id = gst_clock_new_single_shot_id (massink->clock, GST_BUFFER_TIMESTAMP (buf)); GST_DEBUG ("massink: clock wait: %llu\n", GST_BUFFER_TIMESTAMP (buf)); gst_element_clock_wait (GST_ELEMENT (massink), id, NULL); gst_clock_id_free (id); } if (GST_BUFFER_DATA (buf) != NULL) { if (!massink->mute) { GST_DEBUG ("massink: data=%p size=%d", GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); if (GST_BUFFER_SIZE (buf) > BUFFER_SIZE) { gst_buffer_unref (buf); return; } massink->data->length = GST_BUFFER_SIZE (buf); memcpy (massink->data->segment, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); err = mas_send (massink->audio_channel, massink->data); if (err < 0) { g_print ("error sending data to MAS server\n"); gst_buffer_unref (buf); return; } /* FIXME: Please correct the Timestamping if its wrong */ massink->data->header.media_timestamp += massink->data->length / 4; massink->data->header.sequence++; } } gst_buffer_unref (buf); } static void gst_massink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GstMassink *massink; /* it's not null if we got it, but it might not be ours */ g_return_if_fail(GST_IS_MASSINK(object)); massink = GST_MASSINK(object); switch (prop_id) { case ARG_MUTE: massink->mute = g_value_get_boolean (value); break; case ARG_DEPTH: massink->depth = g_value_get_enum (value); gst_massink_sync_parms (massink); break; case ARG_CHANNELS: massink->channels = g_value_get_enum (value); gst_massink_sync_parms (massink); break; case ARG_RATE: massink->frequency = g_value_get_int (value); gst_massink_sync_parms (massink); break; case ARG_HOST: if (massink->host != NULL) g_free(massink->host); if (g_value_get_string (value) == NULL) massink->host = NULL; else massink->host = g_strdup (g_value_get_string (value)); break; default: break; } } static void gst_massink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GstMassink *massink; /* it's not null if we got it, but it might not be ours */ g_return_if_fail(GST_IS_MASSINK(object)); massink = GST_MASSINK(object); switch (prop_id) { case ARG_MUTE: g_value_set_boolean (value, massink->mute); break; case ARG_DEPTH: g_value_set_enum (value, massink->depth); break; case ARG_CHANNELS: g_value_set_enum (value, massink->channels); break; case ARG_RATE: g_value_set_int (value, massink->frequency); break; case ARG_HOST: g_value_set_string (value, massink->host); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static gboolean plugin_init (GstPlugin *plugin) { if (!gst_element_register (plugin, "massink", GST_RANK_NONE, GST_TYPE_MASSINK)){ return FALSE; } return TRUE; } GST_PLUGIN_DEFINE ( GST_VERSION_MAJOR, GST_VERSION_MINOR, "massink", "uses MAS for audio output", plugin_init, VERSION, "LGPL", GST_PACKAGE, GST_ORIGIN ); static gboolean gst_massink_open_audio (GstMassink *massink) { gint32 err; char *ratestring = g_malloc (16); char *bps = g_malloc (8); struct mas_data_characteristic* dc; g_print ("Connecting to MAS.\n"); masc_log_verbosity (MAS_VERBLVL_DEBUG); err = mas_init(); if (err < 0) { GST_DEBUG ("connection with local MAS server failed."); exit (1); } GST_DEBUG ("Establishing audio output channel."); mas_make_data_channel ("Gstreamer", &massink->audio_channel, &massink->audio_source, &massink->audio_sink); mas_asm_get_port_by_name (0, "default_mix_sink", &massink->mix_sink); GST_DEBUG ("Instantiating endian device."); err = mas_asm_instantiate_device ("endian", 0, 0, &massink->endian); if (err < 0) { GST_DEBUG ("Failed to instantiate endian converter device"); exit(1); } mas_asm_get_port_by_name (massink->endian, "endian_sink", &massink->endian_sink); mas_asm_get_port_by_name (massink->endian, "endian_source", &massink->endian_source); sprintf (bps, "%u", massink->depth); sprintf (ratestring, "%u", massink->frequency); GST_DEBUG ("Connecting net -> endian."); masc_make_dc (&dc, 6); /* wav weirdness: 8 bit data is unsigned, >8 data is signed. */ masc_append_dc_key_value (dc, "format", (massink->depth==8) ? "ulinear":"linear"); masc_append_dc_key_value (dc, "resolution", bps); masc_append_dc_key_value (dc, "sampling rate", ratestring); masc_append_dc_key_value (dc, "channels", "2"); masc_append_dc_key_value (dc, "endian", "little"); err = mas_asm_connect_source_sink (massink->audio_source, massink->endian_sink, dc); if ( err < 0 ) { GST_DEBUG ("Failed to connect net audio output to endian"); return -1; } /* The next device is 'if needed' only. After the following if() statement, open_source will contain the current unconnected source in the path (will be either endian_source or squant_source in this case) */ massink->open_source = massink->endian_source; if (massink->depth != 16) { GST_DEBUG ("Sample resolution is not 16 bit/sample, instantiating squant device."); err = mas_asm_instantiate_device ("squant", 0, 0, &massink->squant); if (err < 0) { GST_DEBUG ("Failed to instantiate squant device"); return -1; } mas_asm_get_port_by_name (massink->squant, "squant_sink", &massink->squant_sink); mas_asm_get_port_by_name (massink->squant, "squant_source", &massink->squant_source); GST_DEBUG ("Connecting endian -> squant."); masc_make_dc (&dc, 6); masc_append_dc_key_value (dc,"format",(massink->depth==8) ? "ulinear":"linear"); masc_append_dc_key_value (dc, "resolution", bps); masc_append_dc_key_value (dc, "sampling rate", ratestring); masc_append_dc_key_value (dc, "channels", "2"); masc_append_dc_key_value (dc, "endian", "host"); err = mas_asm_connect_source_sink (massink->endian_source, massink->squant_sink, dc); if (err < 0) { GST_DEBUG ("Failed to connect endian output to squant"); return -1; } /* sneaky: the squant device is optional -> pretend it isn't there */ massink->open_source = massink->squant_source; } /* Another 'if necessary' device, as above */ if (massink->frequency != 44100) { GST_DEBUG ("Sample rate is not 44100, instantiating srate device."); err = mas_asm_instantiate_device ("srate", 0, 0, &massink->srate); if (err < 0) { GST_DEBUG ("Failed to instantiate srate device"); return -1; } mas_asm_get_port_by_name (massink->srate, "sink", &massink->srate_sink); mas_asm_get_port_by_name (massink->srate, "source", &massink->srate_source); GST_DEBUG ("Connecting to srate."); masc_make_dc (&dc, 6); masc_append_dc_key_value (dc, "format", "linear"); masc_append_dc_key_value (dc, "resolution", "16"); masc_append_dc_key_value (dc, "sampling rate", ratestring); masc_append_dc_key_value (dc, "channels", "2"); masc_append_dc_key_value (dc, "endian", "host"); err = mas_asm_connect_source_sink (massink->open_source, massink->srate_sink, dc); if ( err < 0 ) { GST_DEBUG ("Failed to connect to srate"); return -1; } massink->open_source = massink->srate_source; } GST_DEBUG ("Connecting to mix."); masc_make_dc(&dc, 6); masc_append_dc_key_value (dc, "format", "linear"); masc_append_dc_key_value (dc, "resolution", "16"); masc_append_dc_key_value (dc, "sampling rate", "44100"); masc_append_dc_key_value (dc, "channels", "2"); masc_append_dc_key_value (dc, "endian", "host"); err = mas_asm_connect_source_sink (massink->open_source, massink->mix_sink, dc); if ( err < 0 ) { GST_DEBUG ("Failed to connect to mixer"); return -1; } GST_FLAG_SET (massink, GST_MASSINK_OPEN); masc_make_mas_data (&massink->data, BUFFER_SIZE); massink->data->header.type = 10; massink->data->header.media_timestamp = 0; massink->data->header.sequence = 0; return TRUE; } /*static void gst_massink_close_audio (GstMassink *massink) { mas_free_device(massink->endian); mas_free_device(massink->srate); mas_free_device(massink->squant); mas_free_port(massink->mix_sink); mas_free_port(massink->srate_source); mas_free_port(massink->srate_sink); mas_free_port(massink->audio_source); mas_free_port(massink->audio_sink); mas_free_port(massink->endian_source); mas_free_port(massink->endian_sink); mas_free_port(massink->squant_source); mas_free_port(massink->squant_sink); mas_free_port(massink->open_source); mas_free_channel (massink->audio_channel); masc_destroy_mas_data (massink->data); g_free (ratestring); g_free (bps); GST_FLAG_UNSET (massink, GST_MASSINK_OPEN); GST_DEBUG ("massink: closed sound channel"); }*/ static GstElementStateReturn gst_massink_change_state (GstElement *element) { g_return_val_if_fail (GST_IS_MASSINK (element), FALSE); /* if going down into NULL state, close the fd if it's open */ if (GST_STATE_PENDING (element) == GST_STATE_NULL) { //if (GST_FLAG_IS_SET (element, GST_MASSINK_OPEN)) //gst_massink_close_audio (GST_MASSINK (element)); /* otherwise (READY or higher) we need to open the fd */ } else { if (!GST_FLAG_IS_SET (element, GST_MASSINK_OPEN)) { if (!gst_massink_open_audio (GST_MASSINK (element))) return GST_STATE_FAILURE; } } if (GST_ELEMENT_CLASS (parent_class)->change_state) return GST_ELEMENT_CLASS (parent_class)->change_state (element); return GST_STATE_SUCCESS; }