/* GStreamer * Copyright (C) <2005> Edgard Lima * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sdlaudiosink.h" #include #include #include GST_DEBUG_CATEGORY_EXTERN (sdl_debug); #define GST_CAT_DEFAULT sdl_debug /* elementfactory information */ static const GstElementDetails gst_sdlaudio_sink_details = GST_ELEMENT_DETAILS ("SDL audio sink", "Sink/Audio", "Output to a sound card via SDLAUDIO", "Edgard Lima "); static void gst_sdlaudio_sink_dispose (GObject * object); static GstCaps *gst_sdlaudio_sink_getcaps (GstBaseSink * bsink); static gboolean gst_sdlaudio_sink_open (GstAudioSink * asink); static gboolean gst_sdlaudio_sink_close (GstAudioSink * asink); static gboolean gst_sdlaudio_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec); static gboolean gst_sdlaudio_sink_unprepare (GstAudioSink * asink); static guint gst_sdlaudio_sink_write (GstAudioSink * asink, gpointer data, guint length); #if 0 static guint gst_sdlaudio_sink_delay (GstAudioSink * asink); static void gst_sdlaudio_sink_reset (GstAudioSink * asink); #endif /* SdlaudioSink signals and args */ enum { LAST_SIGNAL }; #define SEMAPHORE_INIT(s,f) \ do { \ s.cond = g_cond_new(); \ s.mutex = g_mutex_new(); \ s.mutexflag = f; \ } while(0) #define SEMAPHORE_CLOSE(s) \ do { \ if ( s.cond ) { \ g_cond_free(s.cond); \ s.cond = NULL; \ } \ if ( s.mutex ) { \ g_mutex_free(s.mutex); \ s.mutex = NULL; \ } \ } while(0) #define SEMAPHORE_UP(s) \ do \ { \ g_mutex_lock(s.mutex); \ s.mutexflag = TRUE; \ g_mutex_unlock(s.mutex); \ g_cond_signal(s.cond); \ } while(0) #define SEMAPHORE_DOWN(s, e) \ do \ { \ while (1) { \ g_mutex_lock(s.mutex); \ if (!s.mutexflag) { \ if ( e ) { \ g_mutex_unlock(s.mutex); \ break; \ } \ g_cond_wait(s.cond,s.mutex); \ } \ else { \ s.mutexflag = FALSE; \ g_mutex_unlock(s.mutex); \ break; \ } \ g_mutex_unlock(s.mutex); \ } \ } while(0) static GstStaticPadTemplate sdlaudiosink_sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/x-raw-int, " "endianness = (int) { " G_STRINGIFY (G_BYTE_ORDER) " }, " "signed = (boolean) { TRUE, FALSE }, " "width = (int) 16, " "depth = (int) 16, " "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; " "audio/x-raw-int, " "endianness = (int) { " G_STRINGIFY (G_BYTE_ORDER) " }, " "signed = (boolean) { TRUE, FALSE }, " "width = (int) 8, " "depth = (int) 8, " "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]") ); GST_BOILERPLATE (GstSDLAudioSink, gst_sdlaudio_sink, GstAudioSink, GST_TYPE_AUDIO_SINK); static void gst_sdlaudio_sink_dispose (GObject * object) { GstSDLAudioSink *sdlaudiosink = GST_SDLAUDIOSINK (object); SEMAPHORE_CLOSE (sdlaudiosink->semB); SEMAPHORE_CLOSE (sdlaudiosink->semA); if (sdlaudiosink->buffer) { g_free (sdlaudiosink->buffer); } G_OBJECT_CLASS (parent_class)->dispose (object); } static void gst_sdlaudio_sink_base_init (gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); gst_element_class_set_details (element_class, &gst_sdlaudio_sink_details); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&sdlaudiosink_sink_factory)); } static void gst_sdlaudio_sink_class_init (GstSDLAudioSinkClass * klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; GstBaseSinkClass *gstbasesink_class; GstBaseAudioSinkClass *gstbaseaudiosink_class; GstAudioSinkClass *gstaudiosink_class; gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; gstbasesink_class = (GstBaseSinkClass *) klass; gstbaseaudiosink_class = (GstBaseAudioSinkClass *) klass; gstaudiosink_class = (GstAudioSinkClass *) klass; gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_sdlaudio_sink_dispose); gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_sdlaudio_sink_getcaps); gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_sdlaudio_sink_open); gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_sdlaudio_sink_close); gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_sdlaudio_sink_prepare); gstaudiosink_class->unprepare = GST_DEBUG_FUNCPTR (gst_sdlaudio_sink_unprepare); gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_sdlaudio_sink_write); #if 0 gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_sdlaudio_sink_delay); gstaudiosink_class->reset = GST_DEBUG_FUNCPTR (gst_sdlaudio_sink_reset); #endif } static void gst_sdlaudio_sink_init (GstSDLAudioSink * sdlaudiosink, GstSDLAudioSinkClass * g_class) { GST_DEBUG ("initializing sdlaudiosink"); memset (&sdlaudiosink->fmt, 0, sizeof (SDL_AudioSpec)); sdlaudiosink->buffer = NULL; sdlaudiosink->eos = FALSE; SEMAPHORE_INIT (sdlaudiosink->semA, TRUE); SEMAPHORE_INIT (sdlaudiosink->semB, FALSE); } static GstCaps * gst_sdlaudio_sink_getcaps (GstBaseSink * bsink) { GstSDLAudioSink *sdlaudiosink; GstCaps *caps = NULL; sdlaudiosink = GST_SDLAUDIOSINK (bsink); caps = gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink))); return caps; } static gint gst_sdlaudio_sink_get_format (GstBufferFormat fmt) { gint result = GST_UNKNOWN; switch (fmt) { case GST_U8: result = AUDIO_U8; break; case GST_S8: result = AUDIO_S8; break; case GST_S16_LE: result = AUDIO_S16LSB; break; case GST_S16_BE: result = AUDIO_S16MSB; break; case GST_U16_LE: result = AUDIO_U16LSB; break; case GST_U16_BE: result = AUDIO_U16MSB; break; default: break; } return result; } static gboolean gst_sdlaudio_sink_open (GstAudioSink * asink) { GstSDLAudioSink *sdlaudio; sdlaudio = GST_SDLAUDIOSINK (asink); if (SDL_Init (SDL_INIT_AUDIO) < 0) { goto open_failed; } return TRUE; open_failed: { GST_ELEMENT_ERROR (sdlaudio, LIBRARY, INIT, ("Unable to init SDL: %s\n", SDL_GetError ()), (NULL)); return FALSE; } } static gboolean gst_sdlaudio_sink_close (GstAudioSink * asink) { GstSDLAudioSink *sdlaudio = GST_SDLAUDIOSINK (asink); sdlaudio->eos = TRUE; SEMAPHORE_UP (sdlaudio->semA); SEMAPHORE_UP (sdlaudio->semB); SDL_QuitSubSystem (SDL_INIT_AUDIO); return TRUE; } static guint gst_sdlaudio_sink_write (GstAudioSink * asink, gpointer data, guint length) { GstSDLAudioSink *sdlaudio = GST_SDLAUDIOSINK (asink); if (sdlaudio->fmt.size != length) { GST_ERROR ("ring buffer segment length (%u) != sdl buffer len (%u)", length, sdlaudio->fmt.size); } SEMAPHORE_DOWN (sdlaudio->semA, sdlaudio->eos); if (!sdlaudio->eos) memcpy (sdlaudio->buffer, data, length); SEMAPHORE_UP (sdlaudio->semB); return sdlaudio->fmt.size; } void mixaudio (void *unused, Uint8 * stream, int len) { GstSDLAudioSink *sdlaudio; sdlaudio = GST_SDLAUDIOSINK (unused); if (sdlaudio->fmt.size != len) { GST_ERROR ("fmt buffer len (%u) != sdl callback len (%d)", sdlaudio->fmt.size, len); } SEMAPHORE_DOWN (sdlaudio->semB, sdlaudio->eos); if (!sdlaudio->eos) SDL_MixAudio (stream, sdlaudio->buffer, sdlaudio->fmt.size, SDL_MIX_MAXVOLUME); SEMAPHORE_UP (sdlaudio->semA); } static gboolean gst_sdlaudio_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec) { GstSDLAudioSink *sdlaudio; gint power2 = -1; sdlaudio = GST_SDLAUDIOSINK (asink); sdlaudio->fmt.format = gst_sdlaudio_sink_get_format (spec->format); if (sdlaudio->fmt.format == 0) goto wrong_format; if (spec->width != 16 && spec->width != 8) goto dodgy_width; sdlaudio->fmt.freq = spec->rate; sdlaudio->fmt.channels = spec->channels; sdlaudio->fmt.samples = spec->segsize / (spec->channels * ((sdlaudio->fmt.format & 0xFF) >> 3)); sdlaudio->fmt.callback = mixaudio; sdlaudio->fmt.userdata = sdlaudio; GST_DEBUG ("set segsize: %d, segtotal: %d, samples: %d", spec->segsize, spec->segtotal, sdlaudio->fmt.samples); while (sdlaudio->fmt.samples) { sdlaudio->fmt.samples >>= 1; ++power2; } sdlaudio->fmt.samples = 1; sdlaudio->fmt.samples <<= power2; GST_DEBUG ("set segsize: %d, segtotal: %d, samples: %d", spec->segsize, spec->segtotal, sdlaudio->fmt.samples); if (SDL_OpenAudio (&sdlaudio->fmt, NULL) < 0) { goto unable_open; } spec->segsize = sdlaudio->fmt.size; sdlaudio->buffer = g_malloc (sdlaudio->fmt.size); memset (sdlaudio->buffer, sdlaudio->fmt.silence, sdlaudio->fmt.size); GST_DEBUG ("set segsize: %d, segtotal: %d, samples: %d", spec->segsize, spec->segtotal, sdlaudio->fmt.samples); spec->bytes_per_sample = spec->channels * ((sdlaudio->fmt.format & 0xFF) >> 3); memset (spec->silence_sample, sdlaudio->fmt.silence, spec->bytes_per_sample); SDL_PauseAudio (0); return TRUE; unable_open: { GST_ELEMENT_ERROR (sdlaudio, RESOURCE, OPEN_READ, ("Unable to open audio: %s", SDL_GetError ()), (NULL)); return FALSE; } wrong_format: { GST_ELEMENT_ERROR (sdlaudio, RESOURCE, OPEN_READ, ("Unable to get format %d", spec->format), (NULL)); return FALSE; } dodgy_width: { GST_ELEMENT_ERROR (sdlaudio, RESOURCE, OPEN_READ, ("unexpected width %d", spec->width), (NULL)); return FALSE; } } static gboolean gst_sdlaudio_sink_unprepare (GstAudioSink * asink) { SDL_CloseAudio (); return TRUE; #if 0 if (!gst_sdlaudio_sink_close (asink)) goto couldnt_close; if (!gst_sdlaudio_sink_open (asink)) goto couldnt_reopen; return TRUE; couldnt_close: { GST_DEBUG ("Could not close the audio device"); return FALSE; } couldnt_reopen: { GST_DEBUG ("Could not reopen the audio device"); return FALSE; } #endif } #if 0 static guint gst_sdlaudio_sink_delay (GstAudioSink * asink) { GstSDLAudioSink *sdlaudio; sdlaudio = GST_SDLAUDIOSINK (asink); return 0; } static void gst_sdlaudio_sink_reset (GstAudioSink * asink) { } #endif