/* This file is part of the KDE project. Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 or 3 of the License. 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ /***************************************** * * This is an aRts plugin for GStreamer * ****************************************/ #include #include #include #include "artssink.h" QT_BEGIN_NAMESPACE namespace Phonon { namespace Gstreamer { static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ( "audio/x-raw-int, " "width = (int) { 8, 16 }, " "depth = (int) { 8, 16 }, " "endianness = (int) BYTE_ORDER, " "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]" ) ); typedef int (*Ptr_arts_init)(); typedef arts_stream_t (*Ptr_arts_play_stream)(int, int, int, const char*); typedef int (*Ptr_arts_close_stream)(arts_stream_t); typedef int (*Ptr_arts_stream_get)(arts_stream_t, arts_parameter_t_enum); typedef int (*Ptr_arts_stream_set)(arts_stream_t, arts_parameter_t_enum, int value); typedef int (*Ptr_arts_write)(arts_stream_t, const void *, int); typedef int (*Ptr_arts_suspended)(); typedef void (*Ptr_arts_free)(); static Ptr_arts_init p_arts_init = 0; static Ptr_arts_play_stream p_arts_play_stream = 0; static Ptr_arts_close_stream p_arts_close_stream = 0; static Ptr_arts_stream_get p_arts_stream_get= 0; static Ptr_arts_stream_set p_arts_stream_set= 0; static Ptr_arts_write p_arts_write = 0; static Ptr_arts_suspended p_arts_suspended = 0; static Ptr_arts_free p_arts_free = 0; static void arts_sink_dispose (GObject * object); static void arts_sink_reset (GstAudioSink * asink); static void arts_sink_finalize (GObject * object); static GstCaps *arts_sink_get_caps (GstBaseSink * bsink); static gboolean arts_sink_open (GstAudioSink * asink); static gboolean arts_sink_close (GstAudioSink * asink); static gboolean arts_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec); static gboolean arts_sink_unprepare (GstAudioSink * asink); static guint arts_sink_write (GstAudioSink * asink, gpointer data, guint length); static guint arts_sink_delay (GstAudioSink * asink); static gboolean connected = false; static gboolean init = false; static int sinkCount; GST_BOILERPLATE (ArtsSink, arts_sink, GstAudioSink, GST_TYPE_AUDIO_SINK) // ArtsSink args enum { ARG_0, ARG_ARTSSINK }; /* open the device with given specs */ gboolean arts_sink_open(GstAudioSink *sink) { Q_UNUSED(sink); // We already have an open connection to this device if (!init) { GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL), ("Could not connect to aRts", NULL)); return false; } else if (connected) { GST_ELEMENT_ERROR (sink, RESOURCE, BUSY, (NULL), ("Device is busy", NULL)); return false; } // Check if all symbols were resolved if (!(p_arts_init && p_arts_play_stream && p_arts_close_stream && p_arts_stream_get && p_arts_stream_set && p_arts_write && p_arts_free)) return FALSE; // Check if arts_init succeeded if (!init) return false; return true; } /* prepare resources and state to operate with the given specs */ static gboolean arts_sink_prepare(GstAudioSink *sink, GstRingBufferSpec *spec) { ArtsSink *asink = (ArtsSink*)sink; if (!init) return false; asink->samplerate = spec->rate; asink->samplebits = spec->depth; asink->channels = spec->channels; asink->bytes_per_sample = spec->bytes_per_sample; static int id = 0; asink->stream = p_arts_play_stream(spec->rate, spec->depth, spec->channels, QString("gstreamer-%0").arg(id++).toLatin1().constData()); if (asink->stream) connected = true; return connected; } /* undo anything that was done in prepare() */ static gboolean arts_sink_unprepare(GstAudioSink *sink) { Q_UNUSED(sink); ArtsSink *asink = (ArtsSink*)sink; if (init && connected) { p_arts_close_stream(asink->stream); connected = false; } return true; } /* close the device */ static gboolean arts_sink_close(GstAudioSink *sink) { Q_UNUSED(sink); return true; } /* write samples to the device */ static guint arts_sink_write(GstAudioSink *sink, gpointer data, guint length) { ArtsSink *asink = (ArtsSink*)sink; if (!init) return 0; int errorcode = p_arts_write(asink->stream, (char*)data, length); if (errorcode < 0) GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL), ("Could not write to device.", NULL)); return errorcode > 0 ? errorcode : 0; } /* get number of samples queued in the device */ static guint arts_sink_delay(GstAudioSink *sink) { ArtsSink *asink = (ArtsSink*)sink; if (!init) return 0; // We get results in millisecons so we have to caculate the approximate size in samples guint delay = p_arts_stream_get(asink->stream, ARTS_P_SERVER_LATENCY) * (asink->samplerate / 1000); return delay; } /* reset the audio device, unblock from a write */ static void arts_sink_reset(GstAudioSink *sink) { // ### We are currently unable to gracefully recover // after artsd has been restarted or killed. Q_UNUSED(sink); } // Register element details static void arts_sink_base_init (gpointer g_class) { GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); static gchar longname[] = "Experimental aRts sink", klass[] = "Sink/Audio", description[] = "aRts Audio Output Device", author[] = "Nokia Corporation and/or its subsidiary(-ies) "; GstElementDetails details = GST_ELEMENT_DETAILS (longname, klass, description, author); gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&sinktemplate)); gst_element_class_set_details (gstelement_class, &details); } static void arts_sink_class_init (ArtsSinkClass * klass) { parent_class = (GstAudioSinkClass*)g_type_class_peek_parent(klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = GST_DEBUG_FUNCPTR (arts_sink_finalize); gobject_class->dispose = GST_DEBUG_FUNCPTR (arts_sink_dispose); GstBaseSinkClass *gstbasesink_class = (GstBaseSinkClass *) klass; gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (arts_sink_get_caps); GstAudioSinkClass *gstaudiosink_class = (GstAudioSinkClass*)klass; gstaudiosink_class->open = GST_DEBUG_FUNCPTR(arts_sink_open); gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR(arts_sink_prepare); gstaudiosink_class->unprepare = GST_DEBUG_FUNCPTR(arts_sink_unprepare); gstaudiosink_class->close = GST_DEBUG_FUNCPTR(arts_sink_close); gstaudiosink_class->write = GST_DEBUG_FUNCPTR(arts_sink_write); gstaudiosink_class->delay = GST_DEBUG_FUNCPTR(arts_sink_delay); gstaudiosink_class->reset = GST_DEBUG_FUNCPTR(arts_sink_reset); } static void arts_sink_init (ArtsSink * src, ArtsSinkClass * g_class) { Q_UNUSED(g_class); GST_DEBUG_OBJECT (src, "initializing artssink"); src->stream = 0; #ifndef QT_NO_LIBRARY p_arts_init = (Ptr_arts_init)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_init"); p_arts_play_stream = (Ptr_arts_play_stream)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_play_stream"); p_arts_close_stream = (Ptr_arts_close_stream)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_close_stream"); p_arts_stream_get = (Ptr_arts_stream_get)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_stream_get"); p_arts_stream_set = (Ptr_arts_stream_set)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_stream_set"); p_arts_write = (Ptr_arts_write)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_write"); p_arts_suspended = (Ptr_arts_suspended)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_suspended"); p_arts_free = (Ptr_arts_free)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_free"); if (!sinkCount) { int errorcode = p_arts_init(); if (!errorcode) { init = TRUE; } } sinkCount ++; #endif //QT_NO_LIBRARY } static void arts_sink_dispose (GObject * object) { Q_UNUSED(object); if (--sinkCount == 0) { p_arts_free(); } } static void arts_sink_finalize (GObject * object) { G_OBJECT_CLASS (parent_class)->finalize (object); } static GstCaps *arts_sink_get_caps (GstBaseSink * bsink) { Q_UNUSED(bsink); return NULL; } } } //namespace Phonon::Gstreamer QT_END_NAMESPACE