src/3rdparty/phonon/gstreamer/artssink.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /*  This file is part of the KDE project.
       
     2 
       
     3 Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 
       
     5 This library is free software: you can redistribute it and/or modify
       
     6 it under the terms of the GNU Lesser General Public License as published by
       
     7 the Free Software Foundation, either version 2.1 or 3 of the License.
       
     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
       
    12 GNU Lesser General Public License for more details.
       
    13 
       
    14 You should have received a copy of the GNU Lesser General Public License
       
    15 along with this library.  If not, see <http://www.gnu.org/licenses/>.
       
    16 */
       
    17 
       
    18 /*****************************************
       
    19  *
       
    20  *  This is an aRts plugin for GStreamer
       
    21  *
       
    22  ****************************************/
       
    23 
       
    24 #include <gst/gst.h>
       
    25 #include <gst/audio/audio.h>
       
    26 #include <gst/audio/gstaudiosink.h>
       
    27 #include "artssink.h"
       
    28 
       
    29 QT_BEGIN_NAMESPACE
       
    30 
       
    31 namespace Phonon
       
    32 {
       
    33 namespace Gstreamer
       
    34 {
       
    35 
       
    36 static GstStaticPadTemplate sinktemplate =
       
    37 GST_STATIC_PAD_TEMPLATE ("sink",
       
    38     GST_PAD_SINK,
       
    39     GST_PAD_ALWAYS,
       
    40     GST_STATIC_CAPS (
       
    41                      "audio/x-raw-int, "
       
    42                      "width = (int) { 8, 16 }, "
       
    43                      "depth = (int) { 8, 16 }, "
       
    44                      "endianness = (int) BYTE_ORDER, "
       
    45                      "channels = (int) { 1, 2 }, "
       
    46                      "rate = (int) [ 8000, 96000 ]"
       
    47                     )
       
    48 );
       
    49 
       
    50 typedef int (*Ptr_arts_init)();
       
    51 typedef arts_stream_t (*Ptr_arts_play_stream)(int, int, int, const char*);
       
    52 typedef int (*Ptr_arts_close_stream)(arts_stream_t);
       
    53 typedef int (*Ptr_arts_stream_get)(arts_stream_t, arts_parameter_t_enum);
       
    54 typedef int (*Ptr_arts_stream_set)(arts_stream_t, arts_parameter_t_enum, int value);
       
    55 typedef int (*Ptr_arts_write)(arts_stream_t, const void *, int);
       
    56 typedef int (*Ptr_arts_suspended)();
       
    57 typedef void (*Ptr_arts_free)();
       
    58 
       
    59 static Ptr_arts_init p_arts_init = 0;
       
    60 static Ptr_arts_play_stream p_arts_play_stream = 0;
       
    61 static Ptr_arts_close_stream p_arts_close_stream = 0;
       
    62 static Ptr_arts_stream_get p_arts_stream_get= 0;
       
    63 static Ptr_arts_stream_set p_arts_stream_set= 0;
       
    64 static Ptr_arts_write p_arts_write = 0;
       
    65 static Ptr_arts_suspended p_arts_suspended = 0;
       
    66 static Ptr_arts_free p_arts_free = 0;
       
    67 
       
    68 static void arts_sink_dispose (GObject * object);
       
    69 static void arts_sink_reset (GstAudioSink * asink);
       
    70 static void arts_sink_finalize (GObject * object);
       
    71 static GstCaps *arts_sink_get_caps (GstBaseSink * bsink);
       
    72 static gboolean arts_sink_open (GstAudioSink * asink);
       
    73 static gboolean arts_sink_close (GstAudioSink * asink);
       
    74 static gboolean arts_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec);
       
    75 static gboolean arts_sink_unprepare (GstAudioSink * asink);
       
    76 static guint arts_sink_write (GstAudioSink * asink, gpointer data, guint length);
       
    77 static guint arts_sink_delay (GstAudioSink * asink);
       
    78 
       
    79 static gboolean connected = false;
       
    80 static gboolean init = false;
       
    81 static int sinkCount;
       
    82 
       
    83 GST_BOILERPLATE (ArtsSink, arts_sink, GstAudioSink, GST_TYPE_AUDIO_SINK)
       
    84 
       
    85 // ArtsSink args
       
    86 enum
       
    87 {
       
    88     ARG_0,
       
    89     ARG_ARTSSINK
       
    90 };
       
    91 
       
    92 /* open the device with given specs */
       
    93 gboolean arts_sink_open(GstAudioSink *sink)
       
    94 {
       
    95     Q_UNUSED(sink);
       
    96 
       
    97     // We already have an open connection to this device
       
    98     if (!init) {
       
    99         GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL), ("Could not connect to aRts", NULL));
       
   100         return false;
       
   101     } else if (connected) {
       
   102         GST_ELEMENT_ERROR (sink, RESOURCE, BUSY, (NULL), ("Device is busy", NULL));
       
   103         return false;
       
   104     }
       
   105 
       
   106     // Check if all symbols were resolved
       
   107     if (!(p_arts_init && p_arts_play_stream && p_arts_close_stream
       
   108          && p_arts_stream_get && p_arts_stream_set && p_arts_write && p_arts_free))
       
   109         return FALSE;
       
   110 
       
   111     // Check if arts_init succeeded
       
   112     if (!init)
       
   113         return false;
       
   114 
       
   115     return true;
       
   116 }
       
   117 
       
   118 /* prepare resources and state to operate with the given specs */
       
   119 static gboolean arts_sink_prepare(GstAudioSink *sink, GstRingBufferSpec *spec)
       
   120 {
       
   121     ArtsSink *asink = (ArtsSink*)sink;
       
   122 
       
   123     if (!init)
       
   124         return false;
       
   125 
       
   126     asink->samplerate = spec->rate;
       
   127     asink->samplebits = spec->depth;
       
   128     asink->channels = spec->channels;
       
   129     asink->bytes_per_sample = spec->bytes_per_sample;
       
   130 
       
   131     static int id = 0;
       
   132     asink->stream = p_arts_play_stream(spec->rate, spec->depth, spec->channels,
       
   133                                         QString("gstreamer-%0").arg(id++).toLatin1().constData());
       
   134     if (asink->stream)
       
   135         connected = true;
       
   136 
       
   137     return connected;
       
   138 }
       
   139 
       
   140 /* undo anything that was done in prepare() */
       
   141 static gboolean arts_sink_unprepare(GstAudioSink *sink)
       
   142 {
       
   143     Q_UNUSED(sink);
       
   144     ArtsSink *asink = (ArtsSink*)sink;
       
   145     if (init && connected) {
       
   146         p_arts_close_stream(asink->stream);
       
   147         connected = false;
       
   148     }
       
   149     return true;
       
   150 }
       
   151 
       
   152 /* close the device */
       
   153 static gboolean arts_sink_close(GstAudioSink *sink)
       
   154 {
       
   155     Q_UNUSED(sink);
       
   156     return true;
       
   157 }
       
   158 
       
   159 /* write samples to the device */
       
   160 static guint arts_sink_write(GstAudioSink *sink, gpointer data, guint length)
       
   161 {
       
   162     ArtsSink *asink = (ArtsSink*)sink;
       
   163 
       
   164     if (!init)
       
   165         return 0;
       
   166 
       
   167     int errorcode = p_arts_write(asink->stream, (char*)data, length);
       
   168 
       
   169     if (errorcode < 0)
       
   170         GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL), ("Could not write to device.", NULL));
       
   171 
       
   172     return errorcode > 0 ? errorcode : 0;
       
   173 }
       
   174 
       
   175 /* get number of samples queued in the device */
       
   176 static guint arts_sink_delay(GstAudioSink *sink)
       
   177 {
       
   178     ArtsSink *asink = (ArtsSink*)sink;
       
   179     if (!init)
       
   180         return 0;
       
   181 
       
   182     // We get results in millisecons so we have to caculate the approximate size in samples
       
   183     guint delay = p_arts_stream_get(asink->stream, ARTS_P_SERVER_LATENCY) * (asink->samplerate / 1000);
       
   184     return delay;
       
   185 }
       
   186 
       
   187 /* reset the audio device, unblock from a write */
       
   188 static void arts_sink_reset(GstAudioSink *sink)
       
   189 {
       
   190     // ### We are currently unable to gracefully recover
       
   191     // after artsd has been restarted or killed.
       
   192     Q_UNUSED(sink);
       
   193 }
       
   194 
       
   195 // Register element details
       
   196 static void arts_sink_base_init (gpointer g_class) {
       
   197     GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
       
   198     static gchar longname[] = "Experimental aRts sink",
       
   199                     klass[] = "Sink/Audio",
       
   200               description[] = "aRts Audio Output Device",
       
   201                    author[] = "Nokia Corporation and/or its subsidiary(-ies) <qt-info@nokia.com>";
       
   202     GstElementDetails details = GST_ELEMENT_DETAILS (longname,
       
   203                                           klass,
       
   204                                           description,
       
   205                                           author);
       
   206     gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&sinktemplate));
       
   207     gst_element_class_set_details (gstelement_class, &details);
       
   208 }
       
   209 
       
   210 static void arts_sink_class_init (ArtsSinkClass * klass)
       
   211 {
       
   212     parent_class = (GstAudioSinkClass*)g_type_class_peek_parent(klass);
       
   213 
       
   214     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
       
   215     gobject_class->finalize = GST_DEBUG_FUNCPTR (arts_sink_finalize);
       
   216     gobject_class->dispose = GST_DEBUG_FUNCPTR (arts_sink_dispose);
       
   217 
       
   218     GstBaseSinkClass *gstbasesink_class = (GstBaseSinkClass *) klass;
       
   219     gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (arts_sink_get_caps);
       
   220 
       
   221     GstAudioSinkClass *gstaudiosink_class = (GstAudioSinkClass*)klass;
       
   222     gstaudiosink_class->open =      GST_DEBUG_FUNCPTR(arts_sink_open);
       
   223     gstaudiosink_class->prepare =   GST_DEBUG_FUNCPTR(arts_sink_prepare);
       
   224     gstaudiosink_class->unprepare = GST_DEBUG_FUNCPTR(arts_sink_unprepare);
       
   225     gstaudiosink_class->close =     GST_DEBUG_FUNCPTR(arts_sink_close);
       
   226     gstaudiosink_class->write =     GST_DEBUG_FUNCPTR(arts_sink_write);
       
   227     gstaudiosink_class->delay =     GST_DEBUG_FUNCPTR(arts_sink_delay);
       
   228     gstaudiosink_class->reset =     GST_DEBUG_FUNCPTR(arts_sink_reset);
       
   229 }
       
   230 
       
   231 static void arts_sink_init (ArtsSink * src, ArtsSinkClass * g_class)
       
   232 {
       
   233     Q_UNUSED(g_class);
       
   234     GST_DEBUG_OBJECT (src, "initializing artssink");
       
   235     src->stream = 0;
       
   236 
       
   237     p_arts_init =  (Ptr_arts_init)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_init");
       
   238     p_arts_play_stream =  (Ptr_arts_play_stream)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_play_stream");
       
   239     p_arts_close_stream =  (Ptr_arts_close_stream)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_close_stream");
       
   240     p_arts_stream_get =  (Ptr_arts_stream_get)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_stream_get");
       
   241     p_arts_stream_set =  (Ptr_arts_stream_set)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_stream_set");
       
   242     p_arts_write =  (Ptr_arts_write)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_write");
       
   243     p_arts_suspended =  (Ptr_arts_suspended)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_suspended");
       
   244     p_arts_free =  (Ptr_arts_free)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_free");
       
   245 
       
   246     if (!sinkCount) {
       
   247         int errorcode = p_arts_init();
       
   248         if (!errorcode) {
       
   249             init = TRUE;
       
   250         }
       
   251     }
       
   252     sinkCount ++;
       
   253 }
       
   254 
       
   255 static void arts_sink_dispose (GObject * object)
       
   256 {
       
   257     Q_UNUSED(object);
       
   258     if (--sinkCount == 0) {
       
   259         p_arts_free();
       
   260     }
       
   261 }
       
   262 
       
   263 static void arts_sink_finalize (GObject * object)
       
   264 {
       
   265     G_OBJECT_CLASS (parent_class)->finalize (object);
       
   266 }
       
   267 
       
   268 static GstCaps *arts_sink_get_caps (GstBaseSink * bsink)
       
   269 {
       
   270     Q_UNUSED(bsink);
       
   271     return NULL;
       
   272 }
       
   273 
       
   274 }
       
   275 } //namespace Phonon::Gstreamer
       
   276 
       
   277 QT_END_NAMESPACE