/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000,2005 Wim Taymans <wim@fluendo.com>
*
* gstdevsoundsink.c:
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "common.h"
#include "gstdevsoundsink.h"
#include "gsterrorconcealmentinterface.h"
#include "gstg711decoderinterface.h"
#include "gstg729decoderinterface.h"
#include "gstilbcdecoderinterface.h"
#include "string.h"
#include <glib_global.h>
#ifdef AV_SYNC
#include <gst/audio/gstaudioclock.h>
#endif /*AV_SYNC*/
GST_DEBUG_CATEGORY_EXTERN (devsound_debug);
#ifdef GST_CAT_DEFAULT
#undef GST_CAT_DEFAULT
#endif
#define GST_CAT_DEFAULT devsound_debug
/* elementfactory information */
static const GstElementDetails gst_devsound_sink_details=
GST_ELEMENT_DETAILS ("Audio Sink (DEVSOUND)",
"Sink/Audio",
"Output to a speaker via Devsound",
" "
);
static void gst_devsound_sink_base_init(gpointer g_class);
static void gst_devsound_sink_class_init(GstDevsoundSinkClass * klass);
static void gst_devsound_sink_init(GstDevsoundSink * devsoundsink);
static void gst_devsound_sink_dispose(GObject * object);
static void gst_devsound_sink_finalise(GObject * object);
static void gst_devsound_sink_get_property(GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_devsound_sink_set_property(GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static GstCaps *gst_devsound_sink_getcaps(GstBaseSink * bsink);
static gboolean gst_devsound_sink_setcaps(GstBaseSink *bsink, GstCaps *caps);
static gboolean gst_devsound_sink_event(GstBaseSink * asink, GstEvent * event);
#ifdef AV_SYNC
static void gst_devsound_sink_get_times(GstBaseSink * bsink, GstBuffer * buffer,
GstClockTime * start, GstClockTime * end);
static GstClock *gst_devsound_sink_provide_clock (GstElement * element);
static GstClockTime gst_devsound_sink_get_time (GstClock * clock,
gpointer user_data);
#endif /*AV_SYNC*/
static GstStateChangeReturn gst_devsound_sink_change_state (GstElement * element,
GstStateChange transition);
static void *StartDevSoundThread(void *threadid);
//Error concealment interface impl
static void gst_error_concealment_handler_init (gpointer g_iface,
gpointer iface_data);
static gint gst_ConcealErrorForNextBuffer(void);
static gint gst_SetFrameMode(gboolean aFrameMode);
static gint gst_FrameModeRqrdForEC(gboolean* aFrameModeRqrd);
static void gst_Apply_ErrorConcealment_Update(GstDevsoundSink* dssink);
//G711 interface impl
static void gst_g711_decoder_handler_init (gpointer g_iface,
gpointer iface_data);
static gint gst_SetDecoderMode(enum TG711DecodeMode aDecodeMode);
static gint gst_SetCng(gboolean aCng);
static gint gst_GetCng(gboolean* aCng);
static gint gst_SetPlc(gboolean aPlc);
static void gst_Apply_G711_Decoder_Update(GstDevsoundSink* dssink );
//G729 interface impl
static void gst_g729_decoder_handler_init (gpointer g_iface,
gpointer iface_data);
static gint gst_BadLsfNextBuffer(void);
static void gst_Apply_G729_Decoder_Update(GstDevsoundSink* dssink );
//Ilbc interface impl
static void gst_ilbc_decoder_handler_init (gpointer g_iface,
gpointer iface_data);
static gint gst_GetIlbcCng(gboolean* aCng);
static gint gst_SetIlbcCng(gboolean aCng);
static gint gst_SetIlbcDecoderMode(enum TIlbcDecodeMode aDecodeMode);
static void gst_Apply_Ilbc_Decoder_Update(GstDevsoundSink* dssink );
static void get_PopulateIntfcProperties(GstDevsoundSink* dssink);
static gboolean gst_sink_start (GstBaseSink * sink);
static gboolean gst_sink_stop (GstBaseSink * sink);
static GstFlowReturn gst_sink_render (GstBaseSink * sink,
GstBuffer * buffer);
pthread_t ds_thread;
pthread_mutex_t ds_mutex;
pthread_cond_t ds_condition;
enum consumer_thread_state_enum {
CONSUMER_THREAD_UNINITIALIZED,
CONSUMER_THREAD_INITIALIZING,
CONSUMER_THREAD_INITIALIZED
};
enum consumer_thread_state_enum consumer_thread_state;
GQueue * buffer_queue = NULL;
gboolean framemode;
gboolean framemodereq;
gboolean concealerror = FALSE;
gboolean g711decodemode;
gboolean g711cng;
gboolean g711plc;
gboolean ilbccng;
enum TIlbcDecodeMode ilbcdecodemode;
gint output;
enum
{
LAST_SIGNAL
};
typedef struct _GstCustomIfaceUpdate GstCustomIfaceUpdate;
struct _GstCustomIfaceUpdate{
gboolean framemodeupdate;
gboolean framemoderequpdate;
gboolean concealerrorupate;
gboolean g711decodemodeupdate;
gboolean g711setcngupdate;
gboolean g711setplcupdate;
gboolean g729badlsfnextbufferupdate;
gboolean ilbccngupdate;
gboolean ilbcdecodermodeupdate;
};
GstCustomIfaceUpdate customInfaceUpdate = {0,0,0,0,0,0,0,0,0};
#define DEFAULT_DEVICE "default"
enum
{
PROP_0,
PROP_DEVICE,
VOLUME,
MAXVOLUME,
VOLUMERAMP,
/* CHANNELS,*/
LEFTBALANCE,
RIGHTBALANCE,
/* RATE,*/
PRIORITY,
PREFERENCE,
/* SAMPLESPLAYED,*/
/* FOURCC, //FOURCC is not needed*/
/* MIMETYPE,*/
OUTPUTDEVICE
};
enum command_to_consumer_thread_enum
{
OPEN = 2,
PLAYING,
PAUSE,
RESUME,
/*UPDATE,*/
WAIT,
CLOSE
};
enum command_to_consumer_thread_enum cmd;
static GstStaticPadTemplate devsoundsink_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, " "width = (int) 16, " "depth = (int) 16, " "rate = (int) [ 8000, 48000 ]," "channels = (int) [ 1, 2 ]; "
"audio/amr, " "rate = (int) 8000, " "channels = (int) 1 ; "
"audio/AMR, " "rate = (int) 8000, " "channels = (int) 1 ; "
"audio/x-alaw, " "rate = (int) [ 8000, 48000 ], " "channels = (int) [ 1, 2 ]; "
"audio/g729, " "rate = (int) [ 8000, 48000 ], " "channels = (int) [ 1, 2 ]; "
"audio/mpeg, mpegversion = (int) 1, layer = (int) [ 1, 3 ], rate = (int) [ 8000, 48000 ], channels = (int) [ 1, 2 ]; "
"audio/ilbc, " "rate = (int) [ 8000, 48000 ], " "channels = (int) [ 1, 2 ]; "
"audio/x-mulaw, " "rate = (int) [ 8000, 48000 ], " "channels = (int) [ 1, 2 ]")
);
static GstElementClass *parent_class= NULL;
GType gst_devsound_sink_get_type(void)
{
static GType devsoundsink_type = 0;
if (!devsoundsink_type)
{
static const GTypeInfo devsoundsink_info =
{
sizeof(GstDevsoundSinkClass),
gst_devsound_sink_base_init,
NULL,
(GClassInitFunc) gst_devsound_sink_class_init,
NULL,
NULL,
sizeof(GstDevsoundSink),
0,
(GInstanceInitFunc) gst_devsound_sink_init
, };
static const GInterfaceInfo error_concealment_info = {
gst_error_concealment_handler_init,
NULL,
NULL
};
static const GInterfaceInfo g711_decoder_info = {
gst_g711_decoder_handler_init,
NULL,
NULL
};
static const GInterfaceInfo g729_decoder_info = {
gst_g729_decoder_handler_init,
NULL,
NULL
};
static const GInterfaceInfo ilbc_decoder_info = {
gst_ilbc_decoder_handler_init,
NULL,
NULL
};
devsoundsink_type =
g_type_register_static (GST_TYPE_BASE_SINK, "GstDevsoundSink",
&devsoundsink_info, (GTypeFlags)0);
g_type_add_interface_static (devsoundsink_type, GST_TYPE_ERROR_CONCEALMENT,
&error_concealment_info);
g_type_add_interface_static (devsoundsink_type, GST_TYPE_G711_DECODER,
&g711_decoder_info);
g_type_add_interface_static (devsoundsink_type, GST_TYPE_G729_DECODER,
&g729_decoder_info);
g_type_add_interface_static (devsoundsink_type, GST_TYPE_ILBC_DECODER,
&ilbc_decoder_info);
}
return devsoundsink_type;
}
static void gst_devsound_sink_dispose(GObject * object)
{
GstDevsoundSink *devsoundsink = GST_DEVSOUND_SINK (object);
if (devsoundsink->probed_caps)
{
gst_caps_unref(devsoundsink->probed_caps);
devsoundsink->probed_caps = NULL;
}
#ifdef AV_SYNC
if (devsoundsink->clock)
{
gst_object_unref (devsoundsink->clock);
}
devsoundsink->clock = NULL;
#endif /*AV_SYNC*/
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void gst_devsound_sink_base_init(gpointer g_class)
{
GstElementClass *element_class= GST_ELEMENT_CLASS (g_class);
gst_element_class_set_details(element_class, &gst_devsound_sink_details);
gst_element_class_add_pad_template(element_class,
gst_static_pad_template_get(&devsoundsink_sink_factory));
}
static void gst_devsound_sink_class_init(GstDevsoundSinkClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstBaseSinkClass *gstbasesink_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gstbasesink_class = (GstBaseSinkClass *) klass;
parent_class = g_type_class_peek_parent(klass);
gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_devsound_sink_dispose);
gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_devsound_sink_finalise);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_devsound_sink_get_property);
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_devsound_sink_set_property);
gstelement_class->change_state = GST_DEBUG_FUNCPTR(gst_devsound_sink_change_state);
g_object_class_install_property(gobject_class, PROP_DEVICE,
g_param_spec_string("device", "Device", "Devsound device ",
DEFAULT_DEVICE, (GParamFlags)G_PARAM_READWRITE));
g_object_class_install_property(gobject_class, VOLUME,
g_param_spec_int("volume", "Volume", "Devsound volume",
-1, G_MAXINT, -1, (GParamFlags)G_PARAM_READWRITE));
g_object_class_install_property(gobject_class, VOLUMERAMP,
g_param_spec_int("volumeramp", "VolumeRamp", "Devsound volume ramp",
-1, G_MAXINT, -1, (GParamFlags)G_PARAM_READWRITE));
g_object_class_install_property(gobject_class, MAXVOLUME,
g_param_spec_int("maxvolume", "MaxVolume", "Devsound max volume",
-1, G_MAXINT, -1, G_PARAM_READABLE));
g_object_class_install_property(gobject_class, LEFTBALANCE,
g_param_spec_int("leftbalance", "Left Balance", "Left Balance",
-1, G_MAXINT, -1, (GParamFlags)G_PARAM_READWRITE));
g_object_class_install_property(gobject_class, RIGHTBALANCE,
g_param_spec_int("rightbalance", "Right Balance", "Right Balance",
-1, G_MAXINT, -1, (GParamFlags)G_PARAM_READWRITE));
/*
g_object_class_install_property(gobject_class, SAMPLESPLAYED,
g_param_spec_int("samplesplayed", "Samples Played", "Samples Played",
-1, G_MAXINT, -1, G_PARAM_READABLE));
*/
g_object_class_install_property(gobject_class, PRIORITY,
g_param_spec_int("priority", "Priority", "Priority ", -1,
G_MAXINT, -1,
(GParamFlags)G_PARAM_READWRITE));
g_object_class_install_property(gobject_class, PREFERENCE,
g_param_spec_int("preference", "Preference", "Preference ", -1,
G_MAXINT, -1,
(GParamFlags)G_PARAM_READWRITE));
/*
g_object_class_install_property(gobject_class, RATE,
g_param_spec_int("rate", "Rate", "Rate ", -1,
G_MAXINT, -1,
G_PARAM_READWRITE));
g_object_class_install_property(gobject_class, CHANNELS,
g_param_spec_int("channels", "Channels", "Channels ", -1,
G_MAXINT, -1,
G_PARAM_READWRITE));
*/
g_object_class_install_property(gobject_class, OUTPUTDEVICE,
g_param_spec_int("outputdevice", "Output Device", "Output Device ", -1,
G_MAXINT, -1,
(GParamFlags)G_PARAM_READWRITE));
#ifdef AV_SYNC
gstelement_class->provide_clock = GST_DEBUG_FUNCPTR (gst_devsound_sink_provide_clock);
#endif /*AV_SYNC*/
gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_sink_start);
gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_sink_stop);
gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_sink_render);
gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_devsound_sink_getcaps);
gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_devsound_sink_setcaps);
gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_devsound_sink_event);
#ifdef AV_SYNC
gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_devsound_sink_get_times);
#endif /*AV_SYNC*/
}
static void gst_devsound_sink_init(GstDevsoundSink * dssink)
{
GST_DEBUG_OBJECT(dssink, "initializing devsoundsink");
dssink->device = g_strdup(DEFAULT_DEVICE);
dssink->handle = NULL;
dssink->preference = 0; //default=>EMdaPriorityPreferenceNone;
dssink->priority = 0; //default=>EMdaPriorityNormal;
#ifdef AV_SYNC
dssink->time_or_samples_played = 0;
dssink->timeplayedavailable = FALSE;
/* Create the provided clock. */
dssink->clock = gst_audio_clock_new ("clock", gst_devsound_sink_get_time, dssink);
#endif /*AV_SYNC*/
pthread_mutex_init(&ds_mutex, NULL);
pthread_cond_init(&ds_condition, NULL);
}
static void *StartDevSoundThread(void *threadarg)
{
GstDevsoundSink *dssink;
gint remainingDataLen = 0;
GstBuffer *buffer = NULL;
gboolean lastBufferSet=FALSE;
dssink = (GstDevsoundSink*) threadarg;
// TODO handle error here
open_devsound(&(dssink->handle));
#ifdef AV_SYNC
dssink->timeplayedavailable = is_timeplayed_supported(dssink->handle);
#endif /*AV_SYNC*/
//get supported (in/out)put datatypes
//from devsound to build caps
getsupporteddatatypes(dssink);
// TODO obtain mutex to update variable here???
consumer_thread_state = CONSUMER_THREAD_INITIALIZED;
// Signal any waiting threads that consumer thread creation was successful.
pthread_mutex_lock(&ds_mutex);
pthread_cond_signal(&ds_condition);
pthread_mutex_unlock(&ds_mutex);
// Wait until we receive a command from the main thread
// TODO obtain mutex to read variable here???
while ( cmd == OPEN )
{
pthread_mutex_lock(&ds_mutex);
pthread_cond_wait(&ds_condition, &ds_mutex);
pthread_mutex_unlock(&ds_mutex);
}
// This could happen if client creates sink and deletes it
// without putting it to play state
if ( cmd != CLOSE )
{
//TODO if there is preemption we have to somehow signal
//the pipeline in the render
initialize_devsound(dssink);
playinit(dssink->handle);
dssink->eosreceived = FALSE;
initproperties(dssink);
}
while (1)
{
switch (cmd)
{
case PAUSE:
pause_devsound(dssink);
cmd = WAIT;
break;
case RESUME:
resume_devsound(dssink);
cmd = PLAYING;
break;
case WAIT:
pthread_mutex_lock(&ds_mutex);
pthread_cond_signal(&ds_condition);
pthread_mutex_unlock(&ds_mutex);
pthread_mutex_lock(&ds_mutex);
pthread_cond_wait(&ds_condition, &ds_mutex);
pthread_mutex_unlock(&ds_mutex);
break;
case PLAYING:
{
pre_init_setconf(dssink);
gst_Apply_ErrorConcealment_Update(dssink);
gst_Apply_G711_Decoder_Update(dssink);
gst_Apply_G729_Decoder_Update(dssink);
gst_Apply_Ilbc_Decoder_Update(dssink);
// TODO we could do this in BTBF callback
populateproperties(dssink);
get_PopulateIntfcProperties(dssink);
if(buffer_queue->length > 0)
{
if (remainingDataLen == 0)
{
// TODO enable lock and unlock
GST_OBJECT_LOCK (dssink);
buffer = GST_BUFFER_CAST(g_queue_peek_head(buffer_queue));
GST_OBJECT_UNLOCK(dssink);
remainingDataLen = GST_BUFFER_SIZE(buffer);
}
lastBufferSet = GST_BUFFER_FLAG_IS_SET(buffer,GST_BUFFER_FLAG_LAST);
remainingDataLen = write_data(dssink->handle,
GST_BUFFER_DATA(buffer) + (GST_BUFFER_SIZE(buffer) - remainingDataLen),
remainingDataLen,
lastBufferSet);
if (remainingDataLen == 0)
{
GST_OBJECT_LOCK (dssink);
buffer = GST_BUFFER_CAST(g_queue_pop_head(buffer_queue));
GST_OBJECT_UNLOCK(dssink);
gst_buffer_unref(buffer);
buffer = NULL;
}
if (lastBufferSet && remainingDataLen == 0)
{
lastBufferSet = FALSE;
dssink->eosreceived = FALSE;
playinit(dssink->handle);
initproperties(dssink);
get_PopulateIntfcProperties(dssink);
cmd = WAIT;
}
}
else
{
cmd = WAIT;
}
}
break;
case CLOSE:
{
close_devsound(dssink);
dssink->handle= NULL;
pthread_mutex_lock(&ds_mutex);
pthread_cond_signal(&ds_condition);
pthread_mutex_unlock(&ds_mutex);
// TODO obtain mutex here
consumer_thread_state = CONSUMER_THREAD_UNINITIALIZED;
pthread_exit(NULL);
}
break;
default:
// TODO obtain mutex here
consumer_thread_state = CONSUMER_THREAD_UNINITIALIZED;
pthread_exit(NULL);
break;
}
}
// TODO obtain mutex here
consumer_thread_state = CONSUMER_THREAD_UNINITIALIZED;
pthread_exit(NULL);
}
static gboolean gst_sink_start (GstBaseSink * sink)
{
GstBuffer *tmp_gstbuffer=NULL;
GstDevsoundSink *dssink = GST_DEVSOUND_SINK(sink);
if(buffer_queue)
{
while (buffer_queue->length)
{
tmp_gstbuffer = (GstBuffer*)g_queue_pop_tail(buffer_queue);
gst_buffer_unref(tmp_gstbuffer);
}
g_queue_free(buffer_queue);
buffer_queue = NULL;
}
if(buffer_queue == NULL)
{
buffer_queue = g_queue_new();
}
consumer_thread_state = CONSUMER_THREAD_INITIALIZING;
cmd = OPEN;
pthread_create(&ds_thread, NULL, StartDevSoundThread, (void *)dssink);
// Wait until consumer thread is created
// TODO : obtain mutex to retreive thread state?
if (consumer_thread_state == CONSUMER_THREAD_INITIALIZING)
{
pthread_mutex_lock(&ds_mutex);
pthread_cond_wait(&ds_condition, &ds_mutex);
pthread_mutex_unlock(&ds_mutex);
}
return TRUE;
}
static gboolean gst_sink_stop (GstBaseSink * sink)
{
GstBuffer *tmp_gstbuffer=NULL;
GstDevsoundSink *dssink = GST_DEVSOUND_SINK(sink);
cmd = CLOSE;
pthread_mutex_lock(&ds_mutex);
pthread_cond_signal(&ds_condition);
pthread_mutex_unlock(&ds_mutex);
pthread_mutex_lock(&ds_mutex);
pthread_cond_wait(&ds_condition, &ds_mutex);
pthread_mutex_unlock(&ds_mutex);
GST_OBJECT_LOCK(dssink);
while (buffer_queue->length)
{
tmp_gstbuffer = (GstBuffer*)g_queue_pop_tail(buffer_queue);
gst_buffer_unref(tmp_gstbuffer);
}
g_queue_free(buffer_queue);
buffer_queue = NULL;
GST_OBJECT_UNLOCK(dssink);
return TRUE;
}
static GstFlowReturn gst_sink_render (GstBaseSink * sink,
GstBuffer * buffer)
{
GstDevsoundSink *dssink = GST_DEVSOUND_SINK(sink);
GstBuffer* tmp;
if (get_ds_cb_error(dssink->handle))
{
return GST_FLOW_CUSTOM_ERROR;
}
tmp = gst_buffer_copy(buffer);
GST_OBJECT_LOCK (dssink);
g_queue_push_tail (buffer_queue, tmp);
GST_OBJECT_UNLOCK (dssink);
cmd = PLAYING;
pthread_mutex_lock(&ds_mutex);
pthread_cond_signal(&ds_condition);
pthread_mutex_unlock(&ds_mutex);
return GST_FLOW_OK;
}
static void gst_devsound_sink_finalise(GObject * object)
{
GstDevsoundSink *devsoundsink = GST_DEVSOUND_SINK (object);
g_free(devsoundsink->device);
}
static void gst_devsound_sink_set_property(GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstDevsoundSink *sink;
sink = GST_DEVSOUND_SINK (object);
switch (prop_id)
{
case PROP_DEVICE:
if (sink->device)
g_free(sink->device);
sink->device = g_value_dup_string(value);
if (sink->probed_caps)
{
gst_caps_unref(sink->probed_caps);
sink->probed_caps = NULL;
}
break;
/* case CHANNELS:
sink->channels = g_value_get_int(value);
sink->pending.channelsupdate = TRUE;
break;
case RATE:
sink->rate = g_value_get_int(value);
//Convert rate to something devsound understands
sink->rate = gst_devsound_sink_get_rate(sink->rate);
sink->pending.rateupdate = TRUE;
break;
*/ case VOLUME:
sink->volume = g_value_get_int(value);
sink->pending.volumeupdate = TRUE;
break;
case LEFTBALANCE:
sink->leftbalance = g_value_get_int(value);
sink->pending.leftbalanceupdate = TRUE;
break;
case RIGHTBALANCE:
sink->rightbalance = g_value_get_int(value);
sink->pending.rightbalanceupdate = TRUE;
break;
case VOLUMERAMP:
sink->volumeramp = g_value_get_int(value);
sink->pending.volumerampupdate = TRUE;
break;
case PRIORITY:
sink->priority = g_value_get_int(value);
sink->pending.priorityupdate = TRUE;
break;
case PREFERENCE:
sink->preference = g_value_get_int(value);
sink->pending.preferenceupdate = TRUE;
break;
/* case FOURCC: //FOURCC is not needed
sink->fourcc = g_value_get_int(value);
sink->pending.fourccupdate = TRUE;
break;
case MIMETYPE:
sink->mimetype = g_value_dup_string(value);
break;*/
case OUTPUTDEVICE:
sink->output = g_value_get_int(value);
sink->pending.outputupdate = TRUE;
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void gst_devsound_sink_get_property(GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstDevsoundSink *sink;
sink = GST_DEVSOUND_SINK (object);
switch (prop_id)
{
case PROP_DEVICE:
g_value_set_string(value, sink->device);
break;
/* case CHANNELS:
g_value_set_int(value, sink->channels);
break;
case RATE:
g_value_set_int(value, sink->rate);
break;*/
case VOLUME:
g_value_set_int(value, sink->volume);
break;
case MAXVOLUME:
g_value_set_int(value, sink->maxvolume);
break;
/* case SAMPLESPLAYED:
g_value_set_int(value, sink->samplesplayed);
break;*/
case OUTPUTDEVICE:
g_value_set_int(value, sink->output);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gboolean gst_devsound_sink_setcaps(GstBaseSink *bsink, GstCaps *caps)
{
GstDevsoundSink *devsoundsink;
GstStructure *structure;
const gchar *mimetype;
int width,depth,endianness,sign,channels,rate;
devsoundsink = GST_DEVSOUND_SINK (bsink);
structure = gst_caps_get_structure (caps, 0);
mimetype = gst_structure_get_name (structure);
gst_structure_get_int (structure, "width", &width);
gst_structure_get_int (structure, "depth", &depth);
gst_structure_get_int (structure, "endianness", &endianness);
gst_structure_get_int (structure, "rate", &rate);
gst_structure_get_int (structure, "channels",&channels);
gst_structure_get_boolean (structure, "signed", &sign);
devsoundsink->rate = gst_devsound_sink_get_rate(rate);
devsoundsink->channels = channels;
set_channels(devsoundsink->handle,devsoundsink->channels);
set_rate(devsoundsink->handle,devsoundsink->rate);
if (!strncmp (mimetype, "audio/x-raw-int", 15))
{
devsoundsink->fourcc = 0x36315020; //KMMFFourCCCodePCM16
}
else if (!strncmp(mimetype,"audio/amr",9))
{
devsoundsink->fourcc = 0x524d4120; //KMMFFourCCCodeAMR
}
else if (!strncmp (mimetype, "audio/x-mulaw", 13)||
!strncmp (mimetype, "audio/x-alaw", 12))
{
devsoundsink->fourcc = 0x31313747; //KMccFourCCIdG711
}
else if (!strncmp(mimetype, "audio/ilbc", 10))
{
devsoundsink->fourcc = 0x43424c49; //KMccFourCCIdILBC
}
else if (!strncmp(mimetype, "audio/g729", 10))
{
devsoundsink->fourcc = 0x39323747; //KMccFourCCIdG729
}
else if (!strncmp(mimetype, "audio/mpeg", 10))
{
devsoundsink->fourcc = 0x33504d20; //KMMFFourCCCodeMP3
}
else
{
devsoundsink->fourcc = 0x36315020; //KMMFFourCCCodePCM16
}
set_fourcc(devsoundsink->handle,devsoundsink->fourcc);
pthread_mutex_lock(&ds_mutex);
pthread_cond_signal(&ds_condition);
pthread_mutex_unlock(&ds_mutex);
return TRUE;
}
static GstCaps * gst_devsound_sink_getcaps(GstBaseSink * bsink)
{
GstDevsoundSink *devsoundsink;
GstCaps *caps;
GstPadTemplate *pad_template;
// GstStructure *str;
//GstCaps *caps2;
//GstCaps *caps3;
devsoundsink = GST_DEVSOUND_SINK (bsink);
GST_DEBUG_OBJECT(devsoundsink, "getcaps called");
pad_template = gst_static_pad_template_get(&devsoundsink_sink_factory);
caps = gst_caps_copy(gst_pad_template_get_caps(pad_template));
return caps;
//************
/* caps2 = gst_caps_new_empty();
total = g_list_length(devsoundsink->fmt);
for (;devsoundsink->fmt; devsoundsink->fmt = g_list_next(devsoundsink->fmt))
{
data = (guint*)devsoundsink->fmt->data;
switch ((gint)data)
{
case 0x36315020: //KMMFFourCCCodePCM16
str = gst_structure_new("audio/x-raw-int",
"signed",G_TYPE_BOOLEAN, TRUE,
"width", G_TYPE_INT, 16,
"depth", G_TYPE_INT, 16,
"channels", GST_TYPE_INT_RANGE,1, 2,
"rate", GST_TYPE_INT_RANGE,8000, 48000, NULL);
break;
case 0x524d4120: //KMMFFourCCCodeAMR
str = gst_structure_new("audio/amr",
"width", G_TYPE_INT, 8,
"depth", G_TYPE_INT, 8,
"channels", G_TYPE_INT, 1,
"rate", G_TYPE_INT, 8000,
NULL);
break;
case 0x31313747: //KMccFourCCIdG711
str = gst_structure_new("audio/g711",
"width", G_TYPE_INT, 8,
"depth", G_TYPE_INT, 8,
"channels", G_TYPE_INT, 1,
"rate", G_TYPE_INT, 8000,
NULL);
break;
default:
str = NULL;
break;
}
if(str!=NULL)
gst_caps_append_structure(caps2, str);
}
caps3 = gst_caps_union(caps,caps2);
// gst_object_unref(pad_template);
return caps3;
*/
}
static gint gst_devsound_sink_get_rate(gint rate)
{
gint result;
switch (rate)
{
case 8000:
result=0x00000001;//EMMFSampleRate8000Hz;
break;
case 11025:
result=0x00000002;//EMMFSampleRate11025Hz;
break;
case 16000:
result=0x00000004;//EMMFSampleRate16000Hz;
break;
case 22050:
result=0x00000008;//EMMFSampleRate22050Hz;
break;
case 32000:
result=0x00000010;//EMMFSampleRate32000Hz;
break;
case 44100:
result=0x00000020;//EMMFSampleRate44100Hz;
break;
case 48000:
result=0x00000040;//EMMFSampleRate48000Hz;
break;
case 88200:
result=0x00000080;//EMMFSampleRate88200Hz;
break;
case 96000:
result= 0x00000100;//EMMFSampleRate96000Hz;
break;
case 12000:
result=0x00000200;//EMMFSampleRate12000Hz;
break;
case 24000:
result=0x00000400;//EMMFSampleRate24000Hz;
break;
case 64000:
result=0x00000800; //EMMFSampleRate64000Hz;
break;
default:
result=0x00000001;//EMMFSampleRate8000Hz;
break;
}
return result;
}
static gboolean gst_devsound_sink_event(GstBaseSink *asink, GstEvent *event)
{
GstDevsoundSink *sink = GST_DEVSOUND_SINK (asink);
GstBuffer* lastBuffer = NULL;
switch (GST_EVENT_TYPE (event))
{
case GST_EVENT_EOS:
// end-of-stream, we should close down all stream leftovers here
//reset_devsound(sink->handle);
sink->eosreceived = TRUE;
if(buffer_queue->length)
{
GST_OBJECT_LOCK(sink);
lastBuffer = GST_BUFFER(g_queue_peek_tail(buffer_queue));
GST_BUFFER_FLAG_SET(lastBuffer,GST_BUFFER_FLAG_LAST);
GST_OBJECT_UNLOCK(sink);
}
else
{
lastBuffer = gst_buffer_new();
GST_BUFFER_FLAG_SET(lastBuffer,GST_BUFFER_FLAG_LAST);
GST_OBJECT_LOCK(sink);
g_queue_push_tail(buffer_queue,lastBuffer);
GST_OBJECT_UNLOCK(sink);
cmd = PLAYING;
pthread_mutex_lock(&ds_mutex);
pthread_cond_signal(&ds_condition);
pthread_mutex_unlock(&ds_mutex);
}
pthread_mutex_lock(&ds_mutex);
pthread_cond_wait(&ds_condition, &ds_mutex);
pthread_mutex_unlock(&ds_mutex);
break;
default:
break;
}
return TRUE;
}
#ifdef AV_SYNC
static void gst_devsound_sink_get_times (GstBaseSink * bsink, GstBuffer * buffer,
GstClockTime * start, GstClockTime * end)
{
/* Like GstBaseAudioSink, we set these to NONE */
*start = GST_CLOCK_TIME_NONE;
*end = GST_CLOCK_TIME_NONE;
}
static GstClock *gst_devsound_sink_provide_clock (GstElement * element)
{
GstDevsoundSink *sink = GST_DEVSOUND_SINK (element);
return GST_CLOCK (gst_object_ref (sink->clock));
}
static GstClockTime gst_devsound_sink_get_time (GstClock * clock, gpointer user_data)
{
GstClockTime result = 0;
GstDevsoundSink *sink = GST_DEVSOUND_SINK (user_data);
/* The value returned must be in nano seconds. 1 sec = 1000000000 nano seconds (9 zeros)*/
/*If time played is available from DevSound (a3f devsound onwards) get it*/
if (sink->timeplayedavailable)
{
result = sink->time_or_samples_played;
}
else if ((sink->time_or_samples_played > 0 ) && (sink->rate > 0 ))/*This is a pre-a3f devsound. So calculate times played based on samples played*/
{ /*GST_SECOND = 1000000000*/
result = gst_util_uint64_scale_int (sink->time_or_samples_played, GST_SECOND, sink->rate);
}
GST_LOG_OBJECT (sink, "Time: %" GST_TIME_FORMAT, GST_TIME_ARGS (result));
return result;
}
#endif /*AV_SYNC*/
static GstStateChangeReturn gst_devsound_sink_change_state (GstElement * element, GstStateChange transition)
{
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
GstDevsoundSink *sink= GST_DEVSOUND_SINK (element);
switch (transition)
{
case GST_STATE_CHANGE_NULL_TO_READY:
{
#ifdef AV_SYNC
sink->time_or_samples_played = 0;
#endif /*AV_SYNC*/
}
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
if(cmd == WAIT)
{
cmd = RESUME;
pthread_mutex_lock(&ds_mutex);
pthread_cond_signal(&ds_condition);
pthread_mutex_unlock(&ds_mutex);
pthread_mutex_lock(&ds_mutex);
pthread_cond_wait(&ds_condition, &ds_mutex);
pthread_mutex_unlock(&ds_mutex);
}
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (G_UNLIKELY (ret == GST_STATE_CHANGE_FAILURE))
goto activate_failed;
switch (transition) {
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
cmd = PAUSE;
pthread_mutex_lock(&ds_mutex);
pthread_cond_signal(&ds_condition);
pthread_mutex_unlock(&ds_mutex);
pthread_mutex_lock(&ds_mutex);
pthread_cond_wait(&ds_condition, &ds_mutex);
pthread_mutex_unlock(&ds_mutex);
break;
default:
break;
}
return ret;
activate_failed:
{
GST_DEBUG_OBJECT (sink,
"element failed to change states -- activation problem?");
return GST_STATE_CHANGE_FAILURE;
}
}
/************************************
* Error Concealment Interface begins
* **********************************/
static void gst_error_concealment_handler_init (gpointer g_iface,
gpointer iface_data)
{
GstErrorConcealmentIntfc *iface = (GstErrorConcealmentIntfc *) g_iface;
iface->ConcealErrorForNextBuffer = gst_ConcealErrorForNextBuffer;
iface->SetFrameMode = gst_SetFrameMode;
iface->FrameModeRqrdForEC = gst_FrameModeRqrdForEC;
}
static gint gst_ConcealErrorForNextBuffer()
{
customInfaceUpdate.concealerrorupate = TRUE;
return 0;
}
static gint gst_SetFrameMode(gboolean aFrameMode)
{
customInfaceUpdate.framemodeupdate = TRUE;
framemode = aFrameMode;
return 0;
}
static gint gst_FrameModeRqrdForEC(gboolean* aFrameModeRqrd)
{
*aFrameModeRqrd = framemodereq;
return 0;
}
static void gst_Apply_ErrorConcealment_Update(GstDevsoundSink* dssink)
{
if(customInfaceUpdate.concealerrorupate == TRUE)
{
conceal_error_for_next_buffer(dssink->handle);
customInfaceUpdate.concealerrorupate = FALSE;
}
if(customInfaceUpdate.framemodeupdate == TRUE)
{
set_framemode(dssink->handle,framemode);
customInfaceUpdate.framemodeupdate = FALSE;
}
}
/***********************************************************
* G711 Decoder interface begins
* *********************************************************/
static void gst_g711_decoder_handler_init (gpointer g_iface,
gpointer iface_data)
{
GstG711DecoderIntfc *iface = (GstG711DecoderIntfc *) g_iface;
iface->GetCng = gst_GetCng;
iface->SetCng = gst_SetCng;
iface->SetDecoderMode = gst_SetDecoderMode;
iface->SetPlc = gst_SetPlc;
}
static gint gst_SetDecoderMode(enum TG711DecodeMode aDecodeMode)
{
customInfaceUpdate.g711decodemodeupdate = TRUE;
g711decodemode = aDecodeMode;
return 0;
}
static gint gst_SetCng(gboolean aCng)
{
customInfaceUpdate.g711setcngupdate = TRUE;
g711cng = aCng;
return 0;
}
static gint gst_GetCng(gboolean* aCng)
{
*aCng = g711cng;
return 0;
}
static gint gst_SetPlc(gboolean aPlc)
{
customInfaceUpdate.g711setplcupdate = TRUE;
g711plc = aPlc;
return 0;
}
static void gst_Apply_G711_Decoder_Update(GstDevsoundSink* dssink )
{
if(customInfaceUpdate.g711decodemodeupdate == TRUE)
{
set_decodermode(dssink->handle,g711decodemode);
customInfaceUpdate.g711decodemodeupdate = FALSE;
}
if(customInfaceUpdate.g711setcngupdate == TRUE)
{
set_cng(dssink->handle,g711cng);
customInfaceUpdate.g711setcngupdate = FALSE;
}
if(customInfaceUpdate.g711setplcupdate == TRUE)
{
set_plc(dssink->handle,g711plc);
customInfaceUpdate.g711setplcupdate = FALSE;
}
}
//G729 interface impl
static void gst_g729_decoder_handler_init (gpointer g_iface,
gpointer iface_data)
{
GstG729DecoderIntfc *iface = (GstG729DecoderIntfc *) g_iface;
iface->BadLsfNextBuffer = gst_BadLsfNextBuffer;
}
static gint gst_BadLsfNextBuffer()
{
customInfaceUpdate.g729badlsfnextbufferupdate = TRUE;
return 0;
}
static void gst_Apply_G729_Decoder_Update(GstDevsoundSink* dssink )
{
if(customInfaceUpdate.g729badlsfnextbufferupdate)
{
badlsfnextbuffer(dssink->handle);
customInfaceUpdate.g729badlsfnextbufferupdate = FALSE;
}
}
//Ilbc interface impl
static void gst_ilbc_decoder_handler_init (gpointer g_iface,
gpointer iface_data)
{
GstIlbcDecoderIntfc *iface = (GstIlbcDecoderIntfc *) g_iface;
iface->GetCng = gst_GetIlbcCng;
iface->SetCng = gst_SetIlbcCng;
iface->SetDecoderMode = gst_SetIlbcDecoderMode;
}
static gint gst_GetIlbcCng(gboolean* aCng)
{
*aCng = ilbccng;
return 0;
}
static gint gst_SetIlbcCng(gboolean aCng)
{
customInfaceUpdate.ilbccngupdate = TRUE;
ilbccng = aCng;
return 0;
}
static gint gst_SetIlbcDecoderMode(enum TIlbcDecodeMode aDecodeMode)
{
customInfaceUpdate.ilbcdecodermodeupdate = TRUE;
ilbcdecodemode = aDecodeMode;
return 0;
}
static void gst_Apply_Ilbc_Decoder_Update(GstDevsoundSink* dssink )
{
if(customInfaceUpdate.ilbccngupdate)
{
set_ilbccng(dssink->handle,ilbccng);
customInfaceUpdate.ilbccngupdate = FALSE;
}
if(customInfaceUpdate.ilbcdecodermodeupdate)
{
set_ilbcdecodermode(dssink->handle,ilbcdecodemode);
customInfaceUpdate.ilbcdecodermodeupdate = FALSE;
}
}
static void get_PopulateIntfcProperties(GstDevsoundSink* dssink)
{
framemode_rqrd_for_ec(dssink->handle,&framemodereq);
get_cng(dssink->handle,&g711cng);
get_ilbccng(dssink->handle,&ilbccng);
}