diff options
Diffstat (limited to 'src/3rdparty/phonon/gstreamer/artssink.cpp')
-rw-r--r-- | src/3rdparty/phonon/gstreamer/artssink.cpp | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/src/3rdparty/phonon/gstreamer/artssink.cpp b/src/3rdparty/phonon/gstreamer/artssink.cpp new file mode 100644 index 0000000..ff56da9 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/artssink.cpp @@ -0,0 +1,277 @@ +/* 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 <http://www.gnu.org/licenses/>. +*/ + +/***************************************** + * + * This is an aRts plugin for GStreamer + * + ****************************************/ + +#include <gst/gst.h> +#include <gst/audio/audio.h> +#include <gst/audio/gstaudiosink.h> +#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) <qt-info@nokia.com>"; + 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; + + 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 ++; +} + +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 |