#include <gst/pbutils/pbutils.h> /* only used for GST_PLUGINS_BASE_VERSION_* */
#include <gst/glib-compat-private.h>
-
+#if defined(__TIZEN__) && defined(PCM_DUMP_ENABLE)
+#include <vconf.h>
+#endif /* __TIZEN__ && PCM_DUMP_ENABLE */
#include "pulsesink.h"
#include "pulseutil.h"
#define DEFAULT_VOLUME 1.0
#define DEFAULT_MUTE FALSE
#define MAX_VOLUME 10.0
+#ifdef __TIZEN__
+#define DEFAULT_AUDIO_LATENCY "mid"
+#endif /* __TIZEN__ */
enum
{
PROP_MUTE,
PROP_CLIENT_NAME,
PROP_STREAM_PROPERTIES,
+#ifdef __TIZEN__
+ PROP_AUDIO_LATENCY,
+#endif /* __TIZEN__ */
PROP_LAST
};
+#if defined(__TIZEN__) && defined(PCM_DUMP_ENABLE)
+#define GST_PULSESINK_DUMP_VCONF_KEY "memory/private/sound/pcm_dump"
+#define GST_PULSESINK_DUMP_INPUT_PATH_PREFIX "/tmp/dump_pulsesink_in_"
+#define GST_PULSESINK_DUMP_OUTPUT_PATH_PREFIX "/tmp/dump_pulsesink_out_"
+#define GST_PULSESINK_DUMP_INPUT_FLAG 0x00000400
+#define GST_PULSESINK_DUMP_OUTPUT_FLAG 0x00000800
+#endif /* __TIZEN__ && PCM_DUMP_ENABLE */
+
#define GST_TYPE_PULSERING_BUFFER \
(gst_pulseringbuffer_get_type())
#define GST_PULSERING_BUFFER(obj) \
static guint gst_pulseringbuffer_commit (GstAudioRingBuffer * buf,
guint64 * sample, guchar * data, gint in_samples, gint out_samples,
gint * accum);
+#ifdef __TIZEN__
+static gboolean gst_pulsering_set_corked (GstPulseRingBuffer * pbuf, gboolean corked,
+ gboolean wait);
+#endif
G_DEFINE_TYPE (GstPulseRingBuffer, gst_pulseringbuffer,
GST_TYPE_AUDIO_RING_BUFFER);
gst_pulsering_destroy_context (pbuf);
pa_threaded_mainloop_unlock (mainloop);
+#if defined(__TIZEN__) && defined(PCM_DUMP_ENABLE)
+ if (psink->dump_fd_input) {
+ fclose(psink->dump_fd_input);
+ psink->dump_fd_input = NULL;
+ }
+#endif /* __TIZEN__ && PCM_DUMP_ENABLE */
+
GST_LOG_OBJECT (psink, "closed device");
return TRUE;
GST_ELEMENT_ERROR (psink, STREAM, FORMAT, ("Sink format changed"),
("Sink format changed"));
}
+#ifdef __TIZEN__
+ } else if (!strcmp (name, PA_STREAM_EVENT_POP_TIMEOUT)) {
+ GST_WARNING_OBJECT (psink, "got event [%s], cork stream now!!!!", name);
+ gst_pulsering_set_corked (pbuf, TRUE, FALSE);
+#endif
} else {
GST_DEBUG_OBJECT (psink, "got unknown event %s", name);
}
const pa_buffer_attr *actual;
pa_channel_map channel_map;
pa_operation *o = NULL;
+#ifndef __TIZEN__
pa_cvolume v;
+#endif
pa_cvolume *pv = NULL;
pa_stream_flags_t flags;
const gchar *name;
else
name = "Playback Stream";
+#if defined(__TIZEN__) && defined(PCM_DUMP_ENABLE)
+ if (psink->need_dump_input == TRUE && psink->dump_fd_input == NULL) {
+ char *suffix , *dump_path;
+ GDateTime *time = g_date_time_new_now_local();
+
+ suffix = g_date_time_format(time, "%m%d_%H%M%S");
+ dump_path = g_strdup_printf("%s%dch_%dhz_%s.pcm", GST_PULSESINK_DUMP_INPUT_PATH_PREFIX, pbuf->channels, spec->info.rate, suffix);
+ GST_WARNING_OBJECT(psink, "pulse-sink dumping enabled: dump path [%s]", dump_path);
+ psink->dump_fd_input = fopen(dump_path, "w+");
+
+ g_free(suffix);
+ g_free(dump_path);
+ g_date_time_unref(time);
+ }
+#endif /* __TIZEN__ && PCM_DUMP_ENABLE */
+
/* create a stream */
formats[0] = pbuf->format;
if (!(pbuf->stream = pa_stream_new_extended (pbuf->context, name, formats, 1,
GST_INFO_OBJECT (psink, "prebuf: %d", wanted.prebuf);
GST_INFO_OBJECT (psink, "minreq: %d", wanted.minreq);
+#ifndef __TIZEN__
/* configure volume when we changed it, else we leave the default */
if (psink->volume_set) {
GST_LOG_OBJECT (psink, "have volume of %f", psink->volume);
} else {
pv = NULL;
}
+#endif
/* construct the flags */
flags = PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE |
PA_STREAM_ADJUST_LATENCY | PA_STREAM_START_CORKED;
+#ifndef __TIZEN__
if (psink->mute_set) {
if (psink->mute)
flags |= PA_STREAM_START_MUTED;
else
flags |= PA_STREAM_START_UNMUTED;
}
+#endif
/* we always start corked (see flags above) */
pbuf->corked = TRUE;
GST_INFO_OBJECT (psink, "negotiated to: %s", print_buf);
#endif
+#ifdef __TIZEN__
+ {
+ uint32_t idx;
+ if ((idx = pa_stream_get_index (pbuf->stream)) == PA_INVALID_INDEX)
+ goto no_index;
+ if (psink->volume_set)
+ gst_pulse_set_volume_ratio (idx, "out", psink->volume);
+ if (psink->mute_set)
+ if (psink->mute)
+ gst_pulse_set_volume_ratio (idx, "out", 0);
+ }
+#endif
/* After we passed the volume off of to PA we never want to set it
again, since it is PA's job to save/restore volumes. */
psink->volume_set = psink->mute_set = FALSE;
pa_strerror (pa_context_errno (pbuf->context))), (NULL));
goto unlock_and_fail;
}
+#ifdef __TIZEN__
+no_index:
+ {
+ GST_ELEMENT_ERROR (psink, RESOURCE, FAILED,
+ ("Failed to get stream index: %s",
+ pa_strerror (pa_context_errno (pbuf->context))), (NULL));
+ goto unlock_and_fail;
+ }
+#endif
}
/* free the stream that we acquired before */
if (pbuf->paused)
goto was_paused;
+#ifdef __TIZEN__
+ /* ensure running clock for whatever out there */
+ if (pbuf->corked) {
+ if (!gst_pulsering_set_corked (pbuf, FALSE, FALSE))
+ goto uncork_failed;
+ }
+#endif
/* offset is in bytes */
offset = *sample * bpf;
}
}
+#if defined(__TIZEN__) && defined(PCM_DUMP_ENABLE)
+static GstPadProbeReturn
+gst_pulsesink_pad_dump_probe (GstPad * pad, GstPadProbeInfo * info, gpointer data)
+{
+ GstPulseSink *psink = GST_PULSESINK_CAST (data);
+ size_t written = 0;
+ GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER (info);
+ GstMapInfo in_map;
+ if (psink->dump_fd_input) {
+ gst_buffer_map(buffer, &in_map, GST_MAP_READ);
+ written = fwrite(in_map.data, 1, in_map.size, psink->dump_fd_input);
+ if (written != in_map.size)
+ GST_WARNING("failed to write!!! ferror=%d", ferror(psink->dump_fd_input));
+ gst_buffer_unmap(buffer, &in_map);
+ }
+ return GST_PAD_PROBE_OK;
+}
+#endif /* __TIZEN__ && PCM_DUMP_ENABLE */
+
static void
gst_pulsesink_class_init (GstPulseSinkClass * klass)
{
"list of pulseaudio stream properties",
GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+#ifdef __TIZEN__
+ g_object_class_install_property (gobject_class,
+ PROP_AUDIO_LATENCY,
+ g_param_spec_string ("latency", "Audio Backend Latency",
+ "Audio Backend Latency (\"low\": Low Latency, \"mid\": Mid Latency, \"high\": High Latency)",
+ DEFAULT_AUDIO_LATENCY,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+#endif /* __TIZEN__ */
+
gst_element_class_set_static_metadata (gstelement_class,
"PulseAudio Audio Sink",
"Sink/Audio", "Plays audio to a PulseAudio server", "Lennart Poettering");
static void
gst_pulsesink_init (GstPulseSink * pulsesink)
{
+#if defined(__TIZEN__) && defined(PCM_DUMP_ENABLE)
+ GstPad *sinkpad = NULL;
+ int vconf_dump = 0;
+#endif /* __TIZEN__ && PCM_DUMP_ENABLE */
+
pulsesink->server = NULL;
pulsesink->device = NULL;
pulsesink->device_info.description = NULL;
pulsesink->properties = NULL;
pulsesink->proplist = NULL;
+#ifdef __TIZEN__
+ pulsesink->latency = g_strdup (DEFAULT_AUDIO_LATENCY);
+ pulsesink->proplist = pa_proplist_new();
+ pa_proplist_sets(pulsesink->proplist, PA_PROP_MEDIA_TIZEN_AUDIO_LATENCY, pulsesink->latency);
+#ifdef PCM_DUMP_ENABLE
+ if (vconf_get_int(GST_PULSESINK_DUMP_VCONF_KEY, &vconf_dump)) {
+ GST_WARNING("vconf_get_int %s failed", GST_PULSESINK_DUMP_VCONF_KEY);
+ }
+ pulsesink->need_dump_input = vconf_dump & GST_PULSESINK_DUMP_INPUT_FLAG ? TRUE : FALSE;
+ pulsesink->dump_fd_input = NULL;
+ if (pulsesink->need_dump_input) {
+ sinkpad = gst_element_get_static_pad((GstElement *)pulsesink, "sink");
+ if (sinkpad) {
+ gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_BUFFER, gst_pulsesink_pad_dump_probe, pulsesink, NULL);
+ gst_object_unref (GST_OBJECT(sinkpad));
+ }
+ }
+#endif
+#endif /* __TIZEN__ */
/* override with a custom clock */
if (GST_AUDIO_BASE_SINK (pulsesink)->provided_clock)
if (pulsesink->proplist)
pa_proplist_free (pulsesink->proplist);
+#ifdef __TIZEN__
+ g_free (pulsesink->latency);
+#endif /* __TIZEN__ */
+
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_pulsesink_set_volume (GstPulseSink * psink, gdouble volume)
{
+#ifndef __TIZEN__
pa_cvolume v;
pa_operation *o = NULL;
+#endif
GstPulseRingBuffer *pbuf;
uint32_t idx;
+#ifndef __TIZEN__
if (!mainloop)
goto no_mainloop;
pa_threaded_mainloop_lock (mainloop);
+#endif
GST_DEBUG_OBJECT (psink, "setting volume to %f", volume);
if ((idx = pa_stream_get_index (pbuf->stream)) == PA_INVALID_INDEX)
goto no_index;
+#ifndef __TIZEN__
if (pbuf->is_pcm)
gst_pulse_cvolume_from_linear (&v, pbuf->channels, volume);
else
&v, NULL, NULL)))
goto volume_failed;
+#else
+ if (!psink->mute)
+ gst_pulse_set_volume_ratio (idx, "out", volume);
+ psink->volume = volume;
+#endif
+
/* We don't really care about the result of this call */
unlock:
+#ifndef __TIZEN__
if (o)
pa_operation_unref (o);
pa_threaded_mainloop_unlock (mainloop);
+#endif
return;
/* ERRORS */
+#ifndef __TIZEN__
no_mainloop:
{
psink->volume = volume;
GST_DEBUG_OBJECT (psink, "we have no mainloop");
return;
}
+#endif
no_buffer:
{
psink->volume = volume;
GST_DEBUG_OBJECT (psink, "we don't have a stream index");
goto unlock;
}
+#ifndef __TIZEN__
volume_failed:
{
GST_ELEMENT_ERROR (psink, RESOURCE, FAILED,
pa_strerror (pa_context_errno (pbuf->context))), (NULL));
goto unlock;
}
+#endif
}
static void
gst_pulsesink_set_mute (GstPulseSink * psink, gboolean mute)
{
+#ifndef __TIZEN__
pa_operation *o = NULL;
+#endif
GstPulseRingBuffer *pbuf;
uint32_t idx;
+#ifndef __TIZEN__
if (!mainloop)
goto no_mainloop;
pa_threaded_mainloop_lock (mainloop);
+#endif
GST_DEBUG_OBJECT (psink, "setting mute state to %d", mute);
if ((idx = pa_stream_get_index (pbuf->stream)) == PA_INVALID_INDEX)
goto no_index;
+#ifndef __TIZEN__
if (!(o = pa_context_set_sink_input_mute (pbuf->context, idx,
mute, NULL, NULL)))
goto mute_failed;
+#else
+ gst_pulse_set_volume_ratio (idx, "out", mute ? 0 : psink->volume);
+ psink->mute = mute;
+#endif
/* We don't really care about the result of this call */
unlock:
+#ifndef __TIZEN__
if (o)
pa_operation_unref (o);
pa_threaded_mainloop_unlock (mainloop);
+#endif
return;
/* ERRORS */
+#ifndef __TIZEN__
no_mainloop:
{
psink->mute = mute;
GST_DEBUG_OBJECT (psink, "we have no mainloop");
return;
}
+#endif
no_buffer:
{
psink->mute = mute;
GST_DEBUG_OBJECT (psink, "we don't have a stream index");
goto unlock;
}
+#ifndef __TIZEN__
mute_failed:
{
GST_ELEMENT_ERROR (psink, RESOURCE, FAILED,
pa_strerror (pa_context_errno (pbuf->context))), (NULL));
goto unlock;
}
+#endif
}
static void
pa_proplist_free (pulsesink->proplist);
pulsesink->proplist = gst_pulse_make_proplist (pulsesink->properties);
break;
+#ifdef __TIZEN__
+ case PROP_AUDIO_LATENCY:
+ g_free (pulsesink->latency);
+ pulsesink->latency = g_value_dup_string (value);
+ /* setting NULL restores the default latency */
+ if (pulsesink->latency == NULL) {
+ pulsesink->latency = g_strdup (DEFAULT_AUDIO_LATENCY);
+ }
+ if (!pulsesink->proplist) {
+ pulsesink->proplist = pa_proplist_new();
+ }
+ pa_proplist_sets(pulsesink->proplist, PA_PROP_MEDIA_TIZEN_AUDIO_LATENCY, pulsesink->latency);
+ GST_DEBUG_OBJECT(pulsesink, "latency(%s)", pulsesink->latency);
+ break;
+#endif /* __TIZEN__ */
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
break;
case PROP_VOLUME:
{
+#ifndef __TIZEN__
gdouble volume;
gst_pulsesink_get_sink_input_info (pulsesink, &volume, NULL);
g_value_set_double (value, volume);
+#else
+ g_value_set_double (value, pulsesink->volume);
+#endif
break;
}
case PROP_MUTE:
{
+#ifndef __TIZEN__
gboolean mute;
gst_pulsesink_get_sink_input_info (pulsesink, NULL, &mute);
g_value_set_boolean (value, mute);
+#else
+ g_value_set_boolean (value, pulsesink->mute);
+#endif
break;
}
case PROP_CLIENT_NAME:
case PROP_STREAM_PROPERTIES:
gst_value_set_structure (value, pulsesink->properties);
break;
+#ifdef __TIZEN__
+ case PROP_AUDIO_LATENCY:
+ g_value_set_string (value, pulsesink->latency);
+ break;
+#endif /* __TIZEN__ */
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;