pulsesink: Unset missing stream callbacks
[platform/upstream/gst-plugins-good.git] / ext / pulse / pulsesink.c
index 50f2849..5ff0559 100644 (file)
  *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with gst-pulse; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
  *  USA.
  */
 
 /**
  * SECTION:element-pulsesink
- * @see_also: pulsesrc, pulsemixer
+ * @see_also: pulsesrc
  *
  * This element outputs audio to a
  * <ulink href="http://www.pulseaudio.org">PulseAudio sound server</ulink>.
  * <refsect2>
  * <title>Example pipelines</title>
  * |[
- * gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! audioconvert ! audioresample ! pulsesink
+ * gst-launch-1.0 -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! audioconvert ! audioresample ! pulsesink
  * ]| Play an Ogg/Vorbis file.
  * |[
- * gst-launch -v audiotestsrc ! audioconvert ! volume volume=0.4 ! pulsesink
+ * gst-launch-1.0 -v audiotestsrc ! audioconvert ! volume volume=0.4 ! pulsesink
  * ]| Play a 440Hz sine wave.
  * |[
- * gst-launch -v audiotestsrc ! pulsesink stream-properties="props,media.title=test"
+ * gst-launch-1.0 -v audiotestsrc ! pulsesink stream-properties="props,media.title=test"
  * ]| Play a sine wave and set a stream property. The property can be checked
  * with "pactl list".
  * </refsect2>
 
 #include <gst/base/gstbasesink.h>
 #include <gst/gsttaglist.h>
-#include <gst/audio/streamvolume.h>
+#include <gst/audio/audio.h>
 #include <gst/gst-i18n-plugin.h>
-#include <gst/audio/gstaudioiec61937.h>
 
 #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"
 
@@ -68,24 +69,42 @@ GST_DEBUG_CATEGORY_EXTERN (pulse_debug);
 
 #define DEFAULT_SERVER          NULL
 #define DEFAULT_DEVICE          NULL
+#define DEFAULT_CURRENT_DEVICE  NULL
 #define DEFAULT_DEVICE_NAME     NULL
 #define DEFAULT_VOLUME          1.0
 #define DEFAULT_MUTE            FALSE
 #define MAX_VOLUME              10.0
+#ifdef __TIZEN__
+#define DEFAULT_AUDIO_LATENCY     "mid"
+#define DEFAULT_AUTO_RENDER_DELAY FALSE
+#endif /* __TIZEN__ */
 
 enum
 {
   PROP_0,
   PROP_SERVER,
   PROP_DEVICE,
+  PROP_CURRENT_DEVICE,
   PROP_DEVICE_NAME,
   PROP_VOLUME,
   PROP_MUTE,
   PROP_CLIENT_NAME,
   PROP_STREAM_PROPERTIES,
+#ifdef __TIZEN__
+  PROP_AUDIO_LATENCY,
+  PROP_AUTO_RENDER_DELAY,
+#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)        \
@@ -106,6 +125,30 @@ typedef struct _GstPulseRingBufferClass GstPulseRingBufferClass;
 
 typedef struct _GstPulseContext GstPulseContext;
 
+/* A note on threading.
+ *
+ * We use a pa_threaded_mainloop to interact with the PulseAudio server. This
+ * starts up a separate thread that runs a mainloop to carry back events,
+ * messages and timing updates from the PulseAudio server.
+ *
+ * In most cases, the PulseAudio API we use communicates with the server and
+ * processes replies asynchronously. Operations on PA objects that result in
+ * such communication are protected with a pa_threaded_mainloop_lock() and
+ * pa_threaded_mainloop_unlock(). These guarantee mutual exclusion with the
+ * mainloop thread -- when an iteration of the mainloop thread begins, it first
+ * tries to acquire this lock, and cannot do so if our code also holds that
+ * lock.
+ *
+ * When we need to complete an operation synchronously, we use
+ * pa_threaded_mainloop_wait() and pa_threaded_mainloop_signal(). These work
+ * much as pthread conditionals do. pa_threaded_mainloop_wait() is called with
+ * the mainloop lock held. It releases the lock (thereby allowing the mainloop
+ * to execute), and waits till one of our callbacks to be executed by the
+ * mainloop thread calls pa_threaded_mainloop_signal(). At the end of the
+ * mainloop iteration, the pa_threaded_mainloop_wait() will reacquire the
+ * mainloop lock and return control to the caller.
+ */
+
 /* Store the PA contexts in a hash table to allow easy sharing among
  * multiple instances of the sink. Keys are $context_name@$server_name
  * (strings) and values should be GstPulseContext pointers.
@@ -140,6 +183,7 @@ struct _GstPulseRingBuffer
 
   pa_context *context;
   pa_stream *stream;
+  pa_stream *probe_stream;
 
   pa_format_info *format;
   guint channels;
@@ -177,6 +221,10 @@ static void gst_pulseringbuffer_clear (GstAudioRingBuffer * buf);
 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);
@@ -226,6 +274,7 @@ gst_pulseringbuffer_init (GstPulseRingBuffer * pbuf)
   pbuf->stream_name = NULL;
   pbuf->context = NULL;
   pbuf->stream = NULL;
+  pbuf->probe_stream = NULL;
 
   pbuf->format = NULL;
   pbuf->channels = 0;
@@ -242,9 +291,32 @@ gst_pulseringbuffer_init (GstPulseRingBuffer * pbuf)
   pbuf->paused = FALSE;
 }
 
+/* Call with mainloop lock held if wait == TRUE) */
+static void
+gst_pulse_destroy_stream (pa_stream * stream, gboolean wait)
+{
+  /* Make sure we don't get any further callbacks */
+  pa_stream_set_write_callback (stream, NULL, NULL);
+  pa_stream_set_underflow_callback (stream, NULL, NULL);
+  pa_stream_set_overflow_callback (stream, NULL, NULL);
+
+  pa_stream_disconnect (stream);
+
+  if (wait)
+    pa_threaded_mainloop_wait (mainloop);
+
+  pa_stream_set_state_callback (stream, NULL, NULL);
+  pa_stream_unref (stream);
+}
+
 static void
 gst_pulsering_destroy_stream (GstPulseRingBuffer * pbuf)
 {
+  if (pbuf->probe_stream) {
+    gst_pulse_destroy_stream (pbuf->probe_stream, FALSE);
+    pbuf->probe_stream = NULL;
+  }
+
   if (pbuf->stream) {
 
     if (pbuf->m_data) {
@@ -272,6 +344,12 @@ gst_pulsering_destroy_stream (GstPulseRingBuffer * pbuf)
     pa_stream_set_write_callback (pbuf->stream, NULL, NULL);
     pa_stream_set_underflow_callback (pbuf->stream, NULL, NULL);
     pa_stream_set_overflow_callback (pbuf->stream, NULL, NULL);
+#ifdef __TIZEN__
+    pa_stream_set_latency_update_callback (pbuf->stream, NULL, NULL);
+    pa_stream_set_suspended_callback (pbuf->stream, NULL, NULL);
+    pa_stream_set_started_callback (pbuf->stream, NULL, NULL);
+    pa_stream_set_event_callback (pbuf->stream, NULL, NULL);
+#endif
 
     pa_stream_unref (pbuf->stream);
     pbuf->stream = NULL;
@@ -407,7 +485,7 @@ gst_pulsering_context_subscribe_cb (pa_context * c,
     GstPulseRingBuffer *pbuf = (GstPulseRingBuffer *) walk->data;
     psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf));
 
-    GST_LOG_OBJECT (psink, "type %d, idx %u", t, idx);
+    GST_LOG_OBJECT (psink, "type %04x, idx %u", t, idx);
 
     if (!pbuf->stream)
       continue;
@@ -532,6 +610,11 @@ gst_pulseringbuffer_open_device (GstAudioRingBuffer * buf)
     pa_threaded_mainloop_wait (mainloop);
   }
 
+  if (pa_context_get_server_protocol_version (pbuf->context) < 22) {
+    /* We need PulseAudio >= 1.0 on the server side for the extended API */
+    goto bad_server_version;
+  }
+
   GST_LOG_OBJECT (psink, "opened the device");
 
   pa_threaded_mainloop_unlock (mainloop);
@@ -560,6 +643,12 @@ connect_failed:
             pa_strerror (pa_context_errno (pctx->context))), (NULL));
     goto unlock_and_fail;
   }
+bad_server_version:
+  {
+    GST_ELEMENT_ERROR (psink, RESOURCE, FAILED, ("PulseAudio server version "
+            "is too old."), (NULL));
+    goto unlock_and_fail;
+  }
 }
 
 /* close the device */
@@ -578,6 +667,13 @@ gst_pulseringbuffer_close_device (GstAudioRingBuffer * buf)
   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;
@@ -658,6 +754,7 @@ gst_pulsering_stream_latency_cb (pa_stream * s, void *userdata)
 {
   GstPulseSink *psink;
   GstPulseRingBuffer *pbuf;
+  GstAudioRingBuffer *ringbuf;
   const pa_timing_info *info;
   pa_usec_t sink_usec;
 
@@ -665,11 +762,26 @@ gst_pulsering_stream_latency_cb (pa_stream * s, void *userdata)
 
   pbuf = GST_PULSERING_BUFFER_CAST (userdata);
   psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf));
+  ringbuf = GST_AUDIO_RING_BUFFER (pbuf);
 
   if (!info) {
     GST_LOG_OBJECT (psink, "latency update (information unknown)");
     return;
   }
+
+  if (!info->read_index_corrupt) {
+    /* Update segdone based on the read index. segdone is of segment
+     * granularity, while the read index is at byte granularity. We take the
+     * ceiling while converting the latter to the former since it is more
+     * conservative to report that we've read more than we have than to report
+     * less. One concern here is that latency updates happen every 100ms, which
+     * means segdone is not updated very often, but increasing the update
+     * frequency would mean more communication overhead. */
+    g_atomic_int_set (&ringbuf->segdone,
+        (int) gst_util_uint64_scale_ceil (info->read_index, 1,
+            ringbuf->spec.segsize));
+  }
+
   sink_usec = info->configured_sink_usec;
 
   GST_LOG_OBJECT (psink,
@@ -678,6 +790,19 @@ gst_pulsering_stream_latency_cb (pa_stream * s, void *userdata)
       GST_TIMEVAL_TO_TIME (info->timestamp), info->write_index_corrupt,
       info->write_index, info->read_index_corrupt, info->read_index,
       info->sink_usec, sink_usec);
+#ifdef __TIZEN__
+  if (!psink || !psink->auto_render_delay)
+    return;
+
+  if (sink_usec < info->sink_usec)
+    gst_base_sink_set_render_delay (GST_BASE_SINK(psink),
+        (info->sink_usec - sink_usec) * G_GINT64_CONSTANT (1000));
+  else
+    gst_base_sink_set_render_delay (GST_BASE_SINK(psink), 0);
+
+  GST_DEBUG_OBJECT (psink,
+    "Current render delay is %llu", gst_base_sink_get_render_delay (GST_BASE_SINK(psink)));
+#endif
 }
 
 static void
@@ -766,6 +891,11 @@ gst_pulsering_stream_event_cb (pa_stream * p, const char *name,
       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);
   }
@@ -806,7 +936,9 @@ gst_pulseringbuffer_acquire (GstAudioRingBuffer * buf,
   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;
@@ -831,6 +963,13 @@ gst_pulseringbuffer_acquire (GstAudioRingBuffer * buf,
   g_assert (pbuf->context);
   g_assert (!pbuf->stream);
 
+  /* if we have a probe, disconnect it first so that if we're creating a
+   * compressed stream, it doesn't get blocked by a PCM stream */
+  if (pbuf->probe_stream) {
+    gst_pulse_destroy_stream (pbuf->probe_stream, TRUE);
+    pbuf->probe_stream = NULL;
+  }
+
   /* enable event notifications */
   GST_LOG_OBJECT (psink, "subscribing to context events");
   if (!(o = pa_context_subscribe (pbuf->context,
@@ -849,6 +988,22 @@ gst_pulseringbuffer_acquire (GstAudioRingBuffer * buf,
   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,
@@ -886,6 +1041,7 @@ gst_pulseringbuffer_acquire (GstAudioRingBuffer * buf,
   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);
@@ -899,13 +1055,20 @@ gst_pulseringbuffer_acquire (GstAudioRingBuffer * buf,
   } 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;
 
-  if (psink->mute_set && psink->mute)
-    flags |= PA_STREAM_START_MUTED;
+#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;
@@ -933,6 +1096,18 @@ gst_pulseringbuffer_acquire (GstAudioRingBuffer * buf,
   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;
@@ -991,6 +1166,15 @@ connect_failed:
             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 */
@@ -1097,7 +1281,8 @@ gst_pulseringbuffer_clear (GstAudioRingBuffer * buf)
   pa_threaded_mainloop_unlock (mainloop);
 }
 
-/* called from pulse with the mainloop lock */
+#if 0
+/* called from pulse thread with the mainloop lock */
 static void
 mainloop_enter_defer_cb (pa_mainloop_api * api, void *userdata)
 {
@@ -1105,13 +1290,13 @@ mainloop_enter_defer_cb (pa_mainloop_api * api, void *userdata)
   GstMessage *message;
   GValue val = { 0 };
 
-  g_value_init (&val, G_TYPE_POINTER);
-  g_value_set_pointer (&val, g_thread_self ());
-
   GST_DEBUG_OBJECT (pulsesink, "posting ENTER stream status");
   message = gst_message_new_stream_status (GST_OBJECT (pulsesink),
       GST_STREAM_STATUS_TYPE_ENTER, GST_ELEMENT (pulsesink));
+  g_value_init (&val, GST_TYPE_G_THREAD);
+  g_value_set_boxed (&val, g_thread_self ());
   gst_message_set_stream_status_object (message, &val);
+  g_value_unset (&val);
 
   gst_element_post_message (GST_ELEMENT (pulsesink), message);
 
@@ -1119,6 +1304,7 @@ mainloop_enter_defer_cb (pa_mainloop_api * api, void *userdata)
   pulsesink->defer_pending--;
   pa_threaded_mainloop_signal (mainloop, 0);
 }
+#endif
 
 /* start/resume playback ASAP, we don't uncork here but in the commit method */
 static gboolean
@@ -1132,11 +1318,6 @@ gst_pulseringbuffer_start (GstAudioRingBuffer * buf)
 
   pa_threaded_mainloop_lock (mainloop);
 
-  GST_DEBUG_OBJECT (psink, "scheduling stream status");
-  psink->defer_pending++;
-  pa_mainloop_api_once (pa_threaded_mainloop_get_api (mainloop),
-      mainloop_enter_defer_cb, psink);
-
   GST_DEBUG_OBJECT (psink, "starting");
   pbuf->paused = FALSE;
 
@@ -1145,6 +1326,21 @@ gst_pulseringbuffer_start (GstAudioRingBuffer * buf)
       g_atomic_int_get (&GST_AUDIO_BASE_SINK (psink)->eos_rendering))
     gst_pulsering_set_corked (pbuf, FALSE, FALSE);
 
+#if 0
+  GST_DEBUG_OBJECT (psink, "scheduling stream status");
+  psink->defer_pending++;
+  pa_mainloop_api_once (pa_threaded_mainloop_get_api (mainloop),
+      mainloop_enter_defer_cb, psink);
+
+  /* Wait for the stream status message to be posted. This needs to be done
+   * synchronously because the callback will take the mainloop lock
+   * (implicitly) and then take the GST_OBJECT_LOCK. Everywhere else, we take
+   * the locks in the reverse order, so not doing this synchronously could
+   * cause a deadlock. */
+  GST_DEBUG_OBJECT (psink, "waiting for stream status (ENTER) to be posted");
+  pa_threaded_mainloop_wait (mainloop);
+#endif
+
   pa_threaded_mainloop_unlock (mainloop);
 
   return TRUE;
@@ -1176,7 +1372,8 @@ gst_pulseringbuffer_pause (GstAudioRingBuffer * buf)
   return res;
 }
 
-/* called from pulse with the mainloop lock */
+#if 0
+/* called from pulse thread with the mainloop lock */
 static void
 mainloop_leave_defer_cb (pa_mainloop_api * api, void *userdata)
 {
@@ -1184,19 +1381,21 @@ mainloop_leave_defer_cb (pa_mainloop_api * api, void *userdata)
   GstMessage *message;
   GValue val = { 0 };
 
-  g_value_init (&val, G_TYPE_POINTER);
-  g_value_set_pointer (&val, g_thread_self ());
-
   GST_DEBUG_OBJECT (pulsesink, "posting LEAVE stream status");
   message = gst_message_new_stream_status (GST_OBJECT (pulsesink),
       GST_STREAM_STATUS_TYPE_LEAVE, GST_ELEMENT (pulsesink));
+  g_value_init (&val, GST_TYPE_G_THREAD);
+  g_value_set_boxed (&val, g_thread_self ());
   gst_message_set_stream_status_object (message, &val);
+  g_value_unset (&val);
+
   gst_element_post_message (GST_ELEMENT (pulsesink), message);
 
   g_return_if_fail (pulsesink->defer_pending);
   pulsesink->defer_pending--;
   pa_threaded_mainloop_signal (mainloop, 0);
 }
+#endif
 
 /* stop playback, we flush everything. */
 static gboolean
@@ -1244,12 +1443,21 @@ cleanup:
     pa_operation_cancel (o);
     pa_operation_unref (o);
   }
-
+#if 0
   GST_DEBUG_OBJECT (psink, "scheduling stream status");
   psink->defer_pending++;
   pa_mainloop_api_once (pa_threaded_mainloop_get_api (mainloop),
       mainloop_leave_defer_cb, psink);
 
+  /* Wait for the stream status message to be posted. This needs to be done
+   * synchronously because the callback will take the mainloop lock
+   * (implicitly) and then take the GST_OBJECT_LOCK. Everywhere else, we take
+   * the locks in the reverse order, so not doing this synchronously could
+   * cause a deadlock. */
+  GST_DEBUG_OBJECT (psink, "waiting for stream status (LEAVE) to be posted");
+  pa_threaded_mainloop_wait (mainloop);
+#endif
+
   pa_threaded_mainloop_unlock (mainloop);
 
   return res;
@@ -1355,6 +1563,7 @@ gst_pulseringbuffer_commit (GstAudioRingBuffer * buf, guint64 * sample,
   if (g_atomic_int_compare_and_exchange (&psink->notify, 1, 0)) {
     g_object_notify (G_OBJECT (psink), "volume");
     g_object_notify (G_OBJECT (psink), "mute");
+    g_object_notify (G_OBJECT (psink), "current-device");
   }
 
   /* make sure the ringbuffer is started */
@@ -1403,6 +1612,13 @@ gst_pulseringbuffer_commit (GstAudioRingBuffer * buf, guint64 * sample,
   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;
 
@@ -1702,11 +1918,6 @@ static gboolean gst_pulsesink_query (GstBaseSink * sink, GstQuery * query);
 static GstStateChangeReturn gst_pulsesink_change_state (GstElement * element,
     GstStateChange transition);
 
-static GstStaticPadTemplate pad_template = GST_STATIC_PAD_TEMPLATE ("sink",
-    GST_PAD_SINK,
-    GST_PAD_ALWAYS,
-    GST_STATIC_CAPS (PULSE_SINK_TEMPLATE_CAPS));
-
 #define gst_pulsesink_parent_class parent_class
 G_DEFINE_TYPE_WITH_CODE (GstPulseSink, gst_pulsesink, GST_TYPE_AUDIO_BASE_SINK,
     gst_pulsesink_init_contexts ();
@@ -1733,6 +1944,8 @@ gst_pulsesink_payload (GstAudioBaseSink * sink, GstBuffer * buf)
     case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_EAC3:
     case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_DTS:
     case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_MPEG:
+    case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_MPEG2_AAC:
+    case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_MPEG4_AAC:
     {
       /* FIXME: alloc memory from PA if possible */
       gint framesize = gst_audio_iec61937_frame_size (&sink->ringbuffer->spec);
@@ -1749,7 +1962,7 @@ gst_pulsesink_payload (GstAudioBaseSink * sink, GstBuffer * buf)
       gst_buffer_map (out, &outmap, GST_MAP_WRITE);
 
       res = gst_audio_iec61937_payload (inmap.data, inmap.size,
-          outmap.data, outmap.size, &sink->ringbuffer->spec);
+          outmap.data, outmap.size, &sink->ringbuffer->spec, G_BIG_ENDIAN);
 
       gst_buffer_unmap (buf, &inmap);
       gst_buffer_unmap (out, &outmap);
@@ -1768,6 +1981,25 @@ gst_pulsesink_payload (GstAudioBaseSink * sink, GstBuffer * buf)
   }
 }
 
+#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)
 {
@@ -1776,6 +2008,7 @@ gst_pulsesink_class_init (GstPulseSinkClass * klass)
   GstBaseSinkClass *bc;
   GstAudioBaseSinkClass *gstaudiosink_class = GST_AUDIO_BASE_SINK_CLASS (klass);
   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
+  GstCaps *caps;
   gchar *clientname;
 
   gobject_class->finalize = gst_pulsesink_finalize;
@@ -1808,6 +2041,11 @@ gst_pulsesink_class_init (GstPulseSinkClass * klass)
           "The PulseAudio sink device to connect to", DEFAULT_DEVICE,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  g_object_class_install_property (gobject_class, PROP_CURRENT_DEVICE,
+      g_param_spec_string ("current-device", "Current Device",
+          "The current PulseAudio sink device", DEFAULT_CURRENT_DEVICE,
+          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
   g_object_class_install_property (gobject_class,
       PROP_DEVICE_NAME,
       g_param_spec_string ("device-name", "Device name",
@@ -1826,7 +2064,7 @@ gst_pulsesink_class_init (GstPulseSinkClass * klass)
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   /**
-   * GstPulseSink:client-name
+   * GstPulseSink:client-name:
    *
    * The PulseAudio client name to use.
    */
@@ -1840,7 +2078,7 @@ gst_pulsesink_class_init (GstPulseSinkClass * klass)
   g_free (clientname);
 
   /**
-   * GstPulseSink:stream-properties
+   * GstPulseSink:stream-properties:
    *
    * List of pulseaudio stream properties. A list of defined properties can be
    * found in the <ulink url="http://0pointer.de/lennart/projects/pulseaudio/doxygen/proplist_8h.html">pulseaudio api docs</ulink>.
@@ -1853,8 +2091,6 @@ gst_pulsesink_class_init (GstPulseSinkClass * klass)
    * g_object_set (pulse, "stream-properties", props, NULL);
    * gst_structure_free
    * ]|
-   *
-   * Since: 0.10.26
    */
   g_object_class_install_property (gobject_class,
       PROP_STREAM_PROPERTIES,
@@ -1862,14 +2098,48 @@ gst_pulsesink_class_init (GstPulseSinkClass * klass)
           "list of pulseaudio stream properties",
           GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
-  gst_element_class_set_details_simple (gstelement_class,
+#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));
+
+  g_object_class_install_property (gobject_class,
+      PROP_AUTO_RENDER_DELAY,
+      g_param_spec_boolean ("auto-render-delay", "Auto Render Delay",
+          "Apply render delay automatically", DEFAULT_AUTO_RENDER_DELAY,
+          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");
+
+  caps =
+      gst_pulse_fix_pcm_caps (gst_caps_from_string (PULSE_SINK_TEMPLATE_CAPS));
   gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&pad_template));
+      gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps));
+  gst_caps_unref (caps);
+}
+
+static void
+free_device_info (GstPulseDeviceInfo * device_info)
+{
+  GList *l;
+
+  g_free (device_info->description);
+
+  for (l = g_list_first (device_info->formats); l; l = g_list_next (l))
+    pa_format_info_free ((pa_format_info *) l->data);
+
+  g_list_free (device_info->formats);
 }
 
-/* returns the current time of the sink ringbuffer */
+/* Returns the current time of the sink ringbuffer. The timing_info is updated
+ * on every data write/flush and every 100ms (PA_STREAM_AUTO_TIMING_UPDATE).
+ */
 static GstClockTime
 gst_pulsesink_get_time (GstClock * clock, GstAudioBaseSink * sink)
 {
@@ -1921,62 +2191,197 @@ static void
 gst_pulsesink_sink_info_cb (pa_context * c, const pa_sink_info * i, int eol,
     void *userdata)
 {
-  GstPulseRingBuffer *pbuf;
-  GstPulseSink *psink;
-  GList *l;
+  GstPulseDeviceInfo *device_info = (GstPulseDeviceInfo *) userdata;
   guint8 j;
 
-  pbuf = GST_PULSERING_BUFFER_CAST (userdata);
-  psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (pbuf));
-
   if (!i)
     goto done;
 
-  g_free (psink->device_description);
-  psink->device_description = g_strdup (i->description);
-
-  g_mutex_lock (&psink->sink_formats_lock);
-
-  for (l = g_list_first (psink->sink_formats); l; l = g_list_next (l))
-    pa_format_info_free ((pa_format_info *) l->data);
-
-  g_list_free (psink->sink_formats);
-  psink->sink_formats = NULL;
+  device_info->description = g_strdup (i->description);
 
+  device_info->formats = NULL;
   for (j = 0; j < i->n_formats; j++)
-    psink->sink_formats = g_list_prepend (psink->sink_formats,
+    device_info->formats = g_list_prepend (device_info->formats,
         pa_format_info_copy (i->formats[j]));
 
-  g_mutex_unlock (&psink->sink_formats_lock);
-
 done:
   pa_threaded_mainloop_signal (mainloop, 0);
 }
 
+/* Call with mainloop lock held */
+static pa_stream *
+gst_pulsesink_create_probe_stream (GstPulseSink * psink,
+    GstPulseRingBuffer * pbuf, pa_format_info * format)
+{
+  pa_format_info *formats[1] = { format };
+  pa_stream *stream;
+  pa_stream_flags_t flags;
+
+  GST_LOG_OBJECT (psink, "Creating probe stream");
+
+  if (!(stream = pa_stream_new_extended (pbuf->context, "pulsesink probe",
+              formats, 1, psink->proplist)))
+    goto error;
+
+  /* construct the flags */
+  flags = PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE |
+      PA_STREAM_ADJUST_LATENCY | PA_STREAM_START_CORKED;
+
+  pa_stream_set_state_callback (stream, gst_pulsering_stream_state_cb, pbuf);
+
+  if (pa_stream_connect_playback (stream, psink->device, NULL, flags, NULL,
+          NULL) < 0)
+    goto error;
+
+  if (!gst_pulsering_wait_for_stream_ready (psink, stream))
+    goto error;
+
+  return stream;
+
+error:
+  if (stream)
+    pa_stream_unref (stream);
+  return NULL;
+}
+
+static GstCaps *
+gst_pulsesink_query_getcaps (GstPulseSink * psink, GstCaps * filter)
+{
+  GstPulseRingBuffer *pbuf = NULL;
+  GstPulseDeviceInfo device_info = { NULL, NULL };
+  GstCaps *ret = NULL;
+  GList *i;
+  pa_operation *o = NULL;
+  pa_stream *stream;
+
+  GST_OBJECT_LOCK (psink);
+  pbuf = GST_PULSERING_BUFFER_CAST (GST_AUDIO_BASE_SINK (psink)->ringbuffer);
+  if (pbuf != NULL)
+    gst_object_ref (pbuf);
+  GST_OBJECT_UNLOCK (psink);
+
+  if (!pbuf) {
+    ret = gst_pad_get_pad_template_caps (GST_AUDIO_BASE_SINK_PAD (psink));
+    goto out;
+  }
+
+  GST_OBJECT_LOCK (pbuf);
+  pa_threaded_mainloop_lock (mainloop);
+
+  if (!pbuf->context) {
+    ret = gst_pad_get_pad_template_caps (GST_AUDIO_BASE_SINK_PAD (psink));
+    goto unlock;
+  }
+
+  ret = gst_caps_new_empty ();
+
+  if (pbuf->stream) {
+    /* We're in PAUSED or higher */
+    stream = pbuf->stream;
+
+  } else if (pbuf->probe_stream) {
+    /* We're not paused, but have a cached probe stream */
+    stream = pbuf->probe_stream;
+
+  } else {
+    /* We're not yet in PAUSED and still need to create a probe stream.
+     *
+     * FIXME: PA doesn't accept "any" format. We fix something reasonable since
+     * this is merely a probe. This should eventually be fixed in PA and
+     * hard-coding the format should be dropped. */
+    pa_format_info *format = pa_format_info_new ();
+    format->encoding = PA_ENCODING_PCM;
+    pa_format_info_set_sample_format (format, PA_SAMPLE_S16LE);
+    pa_format_info_set_rate (format, GST_AUDIO_DEF_RATE);
+    pa_format_info_set_channels (format, GST_AUDIO_DEF_CHANNELS);
+
+    pbuf->probe_stream = gst_pulsesink_create_probe_stream (psink, pbuf,
+        format);
+
+    pa_format_info_free (format);
+
+    if (!pbuf->probe_stream) {
+      GST_WARNING_OBJECT (psink, "Could not create probe stream");
+      goto unlock;
+    }
+
+    stream = pbuf->probe_stream;
+  }
+
+  if (!(o = pa_context_get_sink_info_by_name (pbuf->context,
+              pa_stream_get_device_name (stream), gst_pulsesink_sink_info_cb,
+              &device_info)))
+    goto info_failed;
+
+  while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
+    pa_threaded_mainloop_wait (mainloop);
+    if (gst_pulsering_is_dead (psink, pbuf, FALSE))
+      goto unlock;
+  }
+
+  for (i = g_list_first (device_info.formats); i; i = g_list_next (i)) {
+    GstCaps *caps = gst_pulse_format_info_to_caps ((pa_format_info *) i->data);
+    if (caps)
+      gst_caps_append (ret, caps);
+  }
+
+unlock:
+  pa_threaded_mainloop_unlock (mainloop);
+  /* FIXME: this could be freed after device_name is got */
+  GST_OBJECT_UNLOCK (pbuf);
+
+  if (filter) {
+    GstCaps *tmp = gst_caps_intersect_full (filter, ret,
+        GST_CAPS_INTERSECT_FIRST);
+    gst_caps_unref (ret);
+    ret = tmp;
+  }
+
+out:
+  free_device_info (&device_info);
+
+  if (o)
+    pa_operation_unref (o);
+
+  if (pbuf)
+    gst_object_unref (pbuf);
+
+  GST_DEBUG_OBJECT (psink, "caps %" GST_PTR_FORMAT, ret);
+
+  return ret;
+
+info_failed:
+  {
+    GST_ELEMENT_ERROR (psink, RESOURCE, FAILED,
+        ("pa_context_get_sink_input_info() failed: %s",
+            pa_strerror (pa_context_errno (pbuf->context))), (NULL));
+    goto unlock;
+  }
+}
+
 static gboolean
 gst_pulsesink_query_acceptcaps (GstPulseSink * psink, GstCaps * caps)
 {
-  GstPulseRingBuffer *pbuf = GST_PULSERING_BUFFER_CAST (GST_AUDIO_BASE_SINK
-      (psink)->ringbuffer);
-  GstPad *pad = GST_BASE_SINK_PAD (psink);
+  GstPulseRingBuffer *pbuf = NULL;
+  GstPulseDeviceInfo device_info = { NULL, NULL };
   GstCaps *pad_caps;
   GstStructure *st;
   gboolean ret = FALSE;
 
   GstAudioRingBufferSpec spec = { 0 };
-  pa_stream *stream = NULL;
   pa_operation *o = NULL;
   pa_channel_map channel_map;
-  pa_stream_flags_t flags;
-  pa_format_info *format = NULL, *formats[1];
+  pa_format_info *format = NULL;
   guint channels;
 
-  pad_caps = gst_pad_query_caps (pad, caps);
-  ret = pad_caps != NULL;
+  pad_caps = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (psink));
+  ret = gst_caps_is_subset (caps, pad_caps);
   gst_caps_unref (pad_caps);
 
-  /* Either template caps didn't match, or we're still in NULL state */
-  if (!ret || !pbuf->context)
+  GST_DEBUG_OBJECT (psink, "caps %" GST_PTR_FORMAT, caps);
+
+  /* Template caps didn't match */
+  if (!ret)
     goto done;
 
   /* If we've not got fixed caps, creating a stream might fail, so let's just
@@ -1984,10 +2389,24 @@ gst_pulsesink_query_acceptcaps (GstPulseSink * psink, GstCaps * caps)
   if (!gst_caps_is_fixed (caps))
     goto done;
 
-  ret = FALSE;
+  GST_OBJECT_LOCK (psink);
+  pbuf = GST_PULSERING_BUFFER_CAST (GST_AUDIO_BASE_SINK (psink)->ringbuffer);
+  if (pbuf != NULL)
+    gst_object_ref (pbuf);
+  GST_OBJECT_UNLOCK (psink);
 
+  /* We're still in NULL state */
+  if (pbuf == NULL)
+    goto done;
+
+  GST_OBJECT_LOCK (pbuf);
   pa_threaded_mainloop_lock (mainloop);
 
+  if (pbuf->context == NULL)
+    goto out;
+
+  ret = FALSE;
+
   spec.latency_time = GST_AUDIO_BASE_SINK (psink)->latency_time;
   if (!gst_audio_ring_buffer_parse_caps (&spec, caps))
     goto out;
@@ -2011,67 +2430,55 @@ gst_pulsesink_query_acceptcaps (GstPulseSink * psink, GstCaps * caps)
       gst_pulse_gst_to_channel_map (&channel_map, &spec))
     pa_format_info_set_channel_map (format, &channel_map);
 
-  if (pbuf->stream) {
+  if (pbuf->stream || pbuf->probe_stream) {
     /* We're already in PAUSED or above, so just reuse this stream to query
      * sink formats and use those. */
     GList *i;
+    const char *device_name = pa_stream_get_device_name (pbuf->stream ?
+        pbuf->stream : pbuf->probe_stream);
 
-    if (!(o = pa_context_get_sink_info_by_name (pbuf->context, psink->device,
-                gst_pulsesink_sink_info_cb, pbuf)))
+    if (!(o = pa_context_get_sink_info_by_name (pbuf->context, device_name,
+                gst_pulsesink_sink_info_cb, &device_info)))
       goto info_failed;
 
     while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
       pa_threaded_mainloop_wait (mainloop);
-      if (gst_pulsering_is_dead (psink, pbuf, TRUE))
+      if (gst_pulsering_is_dead (psink, pbuf, FALSE))
         goto out;
     }
 
-    g_mutex_lock (&psink->sink_formats_lock);
-    for (i = g_list_first (psink->sink_formats); i; i = g_list_next (i)) {
+    for (i = g_list_first (device_info.formats); i; i = g_list_next (i)) {
       if (pa_format_info_is_compatible ((pa_format_info *) i->data, format)) {
         ret = TRUE;
         break;
       }
     }
-    g_mutex_unlock (&psink->sink_formats_lock);
   } else {
     /* We're in READY, let's connect a stream to see if the format is
-     * accpeted by whatever sink we're routed to */
-    formats[0] = format;
-
-    if (!(stream = pa_stream_new_extended (pbuf->context, "pulsesink probe",
-                formats, 1, psink->proplist)))
-      goto out;
-
-    /* construct the flags */
-    flags = PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE |
-        PA_STREAM_ADJUST_LATENCY | PA_STREAM_START_CORKED;
-
-    pa_stream_set_state_callback (stream, gst_pulsering_stream_state_cb, pbuf);
-
-    if (pa_stream_connect_playback (stream, psink->device, NULL, flags, NULL,
-            NULL) < 0)
-      goto out;
-
-    ret = gst_pulsering_wait_for_stream_ready (psink, stream);
+     * accepted by whatever sink we're routed to */
+    pbuf->probe_stream = gst_pulsesink_create_probe_stream (psink, pbuf,
+        format);
+    if (pbuf->probe_stream)
+      ret = TRUE;
   }
 
 out:
   if (format)
     pa_format_info_free (format);
 
+  free_device_info (&device_info);
+
   if (o)
     pa_operation_unref (o);
 
-  if (stream) {
-    pa_stream_set_state_callback (stream, NULL, NULL);
-    pa_stream_disconnect (stream);
-    pa_stream_unref (stream);
-  }
-
   pa_threaded_mainloop_unlock (mainloop);
+  GST_OBJECT_UNLOCK (pbuf);
+
+  gst_caps_replace (&spec.caps, NULL);
+  gst_object_unref (pbuf);
 
 done:
+
   return ret;
 
 info_failed:
@@ -2086,13 +2493,17 @@ info_failed:
 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_description = NULL;
+  pulsesink->device_info.description = NULL;
   pulsesink->client_name = gst_pulse_client_name ();
 
-  g_mutex_init (&pulsesink->sink_formats_lock);
-  pulsesink->sink_formats = NULL;
+  pulsesink->device_info.formats = NULL;
 
   pulsesink->volume = DEFAULT_VOLUME;
   pulsesink->volume_set = FALSE;
@@ -2107,6 +2518,26 @@ gst_pulsesink_init (GstPulseSink * pulsesink)
 
   pulsesink->properties = NULL;
   pulsesink->proplist = NULL;
+#ifdef __TIZEN__
+  pulsesink->latency = g_strdup (DEFAULT_AUDIO_LATENCY);
+  pulsesink->auto_render_delay = DEFAULT_AUTO_RENDER_DELAY;
+  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)
@@ -2115,39 +2546,28 @@ gst_pulsesink_init (GstPulseSink * pulsesink)
   GST_AUDIO_BASE_SINK (pulsesink)->provided_clock =
       gst_audio_clock_new ("GstPulseSinkClock",
       (GstAudioClockGetTimeFunc) gst_pulsesink_get_time, pulsesink, NULL);
-
-  /* TRUE for sinks, FALSE for sources */
-  pulsesink->probe = gst_pulseprobe_new (G_OBJECT (pulsesink),
-      G_OBJECT_GET_CLASS (pulsesink), PROP_DEVICE, pulsesink->device,
-      TRUE, FALSE);
 }
 
 static void
 gst_pulsesink_finalize (GObject * object)
 {
   GstPulseSink *pulsesink = GST_PULSESINK_CAST (object);
-  GList *i;
 
   g_free (pulsesink->server);
   g_free (pulsesink->device);
-  g_free (pulsesink->device_description);
   g_free (pulsesink->client_name);
+  g_free (pulsesink->current_sink_name);
 
-  for (i = g_list_first (pulsesink->sink_formats); i; i = g_list_next (i))
-    pa_format_info_free ((pa_format_info *) i->data);
-
-  g_list_free (pulsesink->sink_formats);
-  g_mutex_clear (&pulsesink->sink_formats_lock);
+  free_device_info (&pulsesink->device_info);
 
   if (pulsesink->properties)
     gst_structure_free (pulsesink->properties);
   if (pulsesink->proplist)
     pa_proplist_free (pulsesink->proplist);
 
-  if (pulsesink->probe) {
-    gst_pulseprobe_free (pulsesink->probe);
-    pulsesink->probe = NULL;
-  }
+#ifdef __TIZEN__
+  g_free (pulsesink->latency);
+#endif /* __TIZEN__ */
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -2155,15 +2575,19 @@ gst_pulsesink_finalize (GObject * 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);
 
@@ -2174,6 +2598,7 @@ gst_pulsesink_set_volume (GstPulseSink * psink, gdouble 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
@@ -2185,17 +2610,26 @@ gst_pulsesink_set_volume (GstPulseSink * psink, gdouble volume)
               &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;
@@ -2204,6 +2638,7 @@ no_mainloop:
     GST_DEBUG_OBJECT (psink, "we have no mainloop");
     return;
   }
+#endif
 no_buffer:
   {
     psink->volume = volume;
@@ -2217,6 +2652,7 @@ no_index:
     GST_DEBUG_OBJECT (psink, "we don't have a stream index");
     goto unlock;
   }
+#ifndef __TIZEN__
 volume_failed:
   {
     GST_ELEMENT_ERROR (psink, RESOURCE, FAILED,
@@ -2224,19 +2660,24 @@ volume_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);
 
@@ -2247,21 +2688,29 @@ gst_pulsesink_set_mute (GstPulseSink * psink, gboolean 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;
@@ -2270,6 +2719,7 @@ no_mainloop:
     GST_DEBUG_OBJECT (psink, "we have no mainloop");
     return;
   }
+#endif
 no_buffer:
   {
     psink->mute = mute;
@@ -2283,6 +2733,7 @@ no_index:
     GST_DEBUG_OBJECT (psink, "we don't have a stream index");
     goto unlock;
   }
+#ifndef __TIZEN__
 mute_failed:
   {
     GST_ELEMENT_ERROR (psink, RESOURCE, FAILED,
@@ -2290,6 +2741,7 @@ mute_failed:
             pa_strerror (pa_context_errno (pbuf->context))), (NULL));
     goto unlock;
   }
+#endif
 }
 
 static void
@@ -2314,18 +2766,25 @@ gst_pulsesink_sink_input_info_cb (pa_context * c, const pa_sink_input_info * i,
   if (i->index == pa_stream_get_index (pbuf->stream)) {
     psink->volume = pa_sw_volume_to_linear (pa_cvolume_max (&i->volume));
     psink->mute = i->mute;
+    psink->current_sink_idx = i->sink;
+
+    if (psink->volume > MAX_VOLUME) {
+      GST_WARNING_OBJECT (psink, "Clipped volume from %f to %f", psink->volume,
+          MAX_VOLUME);
+      psink->volume = MAX_VOLUME;
+    }
   }
 
 done:
   pa_threaded_mainloop_signal (mainloop, 0);
 }
 
-static gdouble
-gst_pulsesink_get_volume (GstPulseSink * psink)
+static void
+gst_pulsesink_get_sink_input_info (GstPulseSink * psink, gdouble * volume,
+    gboolean * mute)
 {
   GstPulseRingBuffer *pbuf;
   pa_operation *o = NULL;
-  gdouble v = DEFAULT_VOLUME;
   uint32_t idx;
 
   if (!mainloop)
@@ -2351,26 +2810,28 @@ gst_pulsesink_get_volume (GstPulseSink * psink)
   }
 
 unlock:
-  v = psink->volume;
+  if (volume)
+    *volume = psink->volume;
+  if (mute)
+    *mute = psink->mute;
 
   if (o)
     pa_operation_unref (o);
 
   pa_threaded_mainloop_unlock (mainloop);
 
-  if (v > MAX_VOLUME) {
-    GST_WARNING_OBJECT (psink, "Clipped volume from %f to %f", v, MAX_VOLUME);
-    v = MAX_VOLUME;
-  }
-
-  return v;
+  return;
 
   /* ERRORS */
 no_mainloop:
   {
-    v = psink->volume;
+    if (volume)
+      *volume = psink->volume;
+    if (mute)
+      *mute = psink->mute;
+
     GST_DEBUG_OBJECT (psink, "we have no mainloop");
-    return v;
+    return;
   }
 no_buffer:
   {
@@ -2391,65 +2852,84 @@ info_failed:
   }
 }
 
-static gboolean
-gst_pulsesink_get_mute (GstPulseSink * psink)
+static void
+gst_pulsesink_current_sink_info_cb (pa_context * c, const pa_sink_info * i,
+    int eol, void *userdata)
+{
+  GstPulseSink *psink;
+
+  psink = GST_PULSESINK_CAST (userdata);
+
+  if (!i)
+    goto done;
+
+  /* If the index doesn't match our current stream,
+   * it implies we just recreated the stream (caps change)
+   */
+  if (i->index == psink->current_sink_idx) {
+    g_free (psink->current_sink_name);
+    psink->current_sink_name = g_strdup (i->name);
+  }
+
+done:
+  pa_threaded_mainloop_signal (mainloop, 0);
+}
+
+static gchar *
+gst_pulsesink_get_current_device (GstPulseSink * pulsesink)
 {
-  GstPulseRingBuffer *pbuf;
   pa_operation *o = NULL;
-  uint32_t idx;
-  gboolean mute = FALSE;
+  GstPulseRingBuffer *pbuf;
+  gchar *current_sink;
 
   if (!mainloop)
     goto no_mainloop;
 
-  pa_threaded_mainloop_lock (mainloop);
-  mute = psink->mute;
-
-  pbuf = GST_PULSERING_BUFFER_CAST (GST_AUDIO_BASE_SINK (psink)->ringbuffer);
+  pbuf =
+      GST_PULSERING_BUFFER_CAST (GST_AUDIO_BASE_SINK (pulsesink)->ringbuffer);
   if (pbuf == NULL || pbuf->stream == NULL)
     goto no_buffer;
 
-  if ((idx = pa_stream_get_index (pbuf->stream)) == PA_INVALID_INDEX)
-    goto no_index;
+  gst_pulsesink_get_sink_input_info (pulsesink, NULL, NULL);
 
-  if (!(o = pa_context_get_sink_input_info (pbuf->context, idx,
-              gst_pulsesink_sink_input_info_cb, pbuf)))
+  pa_threaded_mainloop_lock (mainloop);
+
+  if (!(o = pa_context_get_sink_info_by_index (pbuf->context,
+              pulsesink->current_sink_idx, gst_pulsesink_current_sink_info_cb,
+              pulsesink)))
     goto info_failed;
 
   while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
     pa_threaded_mainloop_wait (mainloop);
-    if (gst_pulsering_is_dead (psink, pbuf, TRUE))
+    if (gst_pulsering_is_dead (pulsesink, pbuf, TRUE))
       goto unlock;
   }
 
 unlock:
+
+  current_sink = g_strdup (pulsesink->current_sink_name);
+
   if (o)
     pa_operation_unref (o);
 
   pa_threaded_mainloop_unlock (mainloop);
 
-  return mute;
+  return current_sink;
 
   /* ERRORS */
 no_mainloop:
   {
-    mute = psink->mute;
-    GST_DEBUG_OBJECT (psink, "we have no mainloop");
-    return mute;
+    GST_DEBUG_OBJECT (pulsesink, "we have no mainloop");
+    return NULL;
   }
 no_buffer:
   {
-    GST_DEBUG_OBJECT (psink, "we have no ringbuffer");
-    goto unlock;
-  }
-no_index:
-  {
-    GST_DEBUG_OBJECT (psink, "we don't have a stream index");
-    goto unlock;
+    GST_DEBUG_OBJECT (pulsesink, "we have no ringbuffer");
+    return NULL;
   }
 info_failed:
   {
-    GST_ELEMENT_ERROR (psink, RESOURCE, FAILED,
+    GST_ELEMENT_ERROR (pulsesink, RESOURCE, FAILED,
         ("pa_context_get_sink_input_info() failed: %s",
             pa_strerror (pa_context_errno (pbuf->context))), (NULL));
     goto unlock;
@@ -2471,8 +2951,9 @@ gst_pulsesink_device_description (GstPulseSink * psink)
   if (pbuf == NULL)
     goto no_buffer;
 
+  free_device_info (&psink->device_info);
   if (!(o = pa_context_get_sink_info_by_name (pbuf->context,
-              psink->device, gst_pulsesink_sink_info_cb, pbuf)))
+              psink->device, gst_pulsesink_sink_info_cb, &psink->device_info)))
     goto info_failed;
 
   while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
@@ -2485,7 +2966,7 @@ unlock:
   if (o)
     pa_operation_unref (o);
 
-  t = g_strdup (psink->device_description);
+  t = g_strdup (psink->device_info.description);
   pa_threaded_mainloop_unlock (mainloop);
 
   return t;
@@ -2511,6 +2992,67 @@ info_failed:
 }
 
 static void
+gst_pulsesink_set_stream_device (GstPulseSink * psink, const gchar * device)
+{
+  pa_operation *o = NULL;
+  GstPulseRingBuffer *pbuf;
+  uint32_t idx;
+
+  if (!mainloop)
+    goto no_mainloop;
+
+  pa_threaded_mainloop_lock (mainloop);
+
+  pbuf = GST_PULSERING_BUFFER_CAST (GST_AUDIO_BASE_SINK (psink)->ringbuffer);
+  if (pbuf == NULL || pbuf->stream == NULL)
+    goto no_buffer;
+
+  if ((idx = pa_stream_get_index (pbuf->stream)) == PA_INVALID_INDEX)
+    goto no_index;
+
+
+  GST_DEBUG_OBJECT (psink, "setting stream device to %s", device);
+
+  if (!(o = pa_context_move_sink_input_by_name (pbuf->context, idx, device,
+              NULL, NULL)))
+    goto move_failed;
+
+unlock:
+
+  if (o)
+    pa_operation_unref (o);
+
+  pa_threaded_mainloop_unlock (mainloop);
+
+  return;
+
+  /* ERRORS */
+no_mainloop:
+  {
+    GST_DEBUG_OBJECT (psink, "we have no mainloop");
+    return;
+  }
+no_buffer:
+  {
+    GST_DEBUG_OBJECT (psink, "we have no ringbuffer");
+    goto unlock;
+  }
+no_index:
+  {
+    GST_DEBUG_OBJECT (psink, "we don't have a stream index");
+    return;
+  }
+move_failed:
+  {
+    GST_ELEMENT_ERROR (psink, RESOURCE, FAILED,
+        ("pa_context_move_sink_input_by_name(%s) failed: %s", device,
+            pa_strerror (pa_context_errno (pbuf->context))), (NULL));
+    goto unlock;
+  }
+}
+
+
+static void
 gst_pulsesink_set_property (GObject * object,
     guint prop_id, const GValue * value, GParamSpec * pspec)
 {
@@ -2520,12 +3062,11 @@ gst_pulsesink_set_property (GObject * object,
     case PROP_SERVER:
       g_free (pulsesink->server);
       pulsesink->server = g_value_dup_string (value);
-      if (pulsesink->probe)
-        gst_pulseprobe_set_server (pulsesink->probe, pulsesink->server);
       break;
     case PROP_DEVICE:
       g_free (pulsesink->device);
       pulsesink->device = g_value_dup_string (value);
+      gst_pulsesink_set_stream_device (pulsesink, pulsesink->device);
       break;
     case PROP_VOLUME:
       gst_pulsesink_set_volume (pulsesink, g_value_get_double (value));
@@ -2551,6 +3092,25 @@ gst_pulsesink_set_property (GObject * object,
         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;
+    case PROP_AUTO_RENDER_DELAY:
+      pulsesink->auto_render_delay = g_value_get_boolean (value);
+      GST_DEBUG_OBJECT (pulsesink, "setting auto-render-delay to %d", g_value_get_boolean (value));
+      break;
+#endif /* __TIZEN__ */
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -2571,21 +3131,56 @@ gst_pulsesink_get_property (GObject * object,
     case PROP_DEVICE:
       g_value_set_string (value, pulsesink->device);
       break;
+    case PROP_CURRENT_DEVICE:
+    {
+      gchar *current_device = gst_pulsesink_get_current_device (pulsesink);
+      if (current_device)
+        g_value_take_string (value, current_device);
+      else
+        g_value_set_string (value, "");
+      break;
+    }
     case PROP_DEVICE_NAME:
       g_value_take_string (value, gst_pulsesink_device_description (pulsesink));
       break;
     case PROP_VOLUME:
-      g_value_set_double (value, gst_pulsesink_get_volume (pulsesink));
+    {
+#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:
-      g_value_set_boolean (value, gst_pulsesink_get_mute (pulsesink));
+    {
+#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:
       g_value_set_string (value, pulsesink->client_name);
       break;
     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;
+    case PROP_AUTO_RENDER_DELAY:
+      g_value_set_boolean (value, pulsesink->auto_render_delay);
+      break;
+#endif /* __TIZEN__ */
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -2679,11 +3274,12 @@ gst_pulsesink_change_props (GstPulseSink * psink, GstTagList * l)
   if (pbuf == NULL || pbuf->stream == NULL)
     goto no_buffer;
 
+  /* We're not interested if this operation failed or not */
   if (!(o = pa_stream_proplist_update (pbuf->stream, PA_UPDATE_REPLACE,
-              pl, NULL, NULL)))
-    goto update_failed;
+              pl, NULL, NULL))) {
+    GST_DEBUG_OBJECT (psink, "pa_stream_proplist_update() failed");
+  }
 
-  /* We're not interested if this operation failed or not */
 unlock:
 
   if (o)
@@ -2704,13 +3300,6 @@ no_buffer:
     GST_DEBUG_OBJECT (psink, "we have no ringbuffer");
     goto unlock;
   }
-update_failed:
-  {
-    GST_ELEMENT_ERROR (psink, RESOURCE, FAILED,
-        ("pa_stream_proplist_update() failed: %s",
-            pa_strerror (pa_context_errno (pbuf->context))), (NULL));
-    goto unlock;
-  }
 }
 
 static void
@@ -2791,6 +3380,14 @@ gst_pulsesink_event (GstBaseSink * sink, GstEvent * event)
 
       break;
     }
+    case GST_EVENT_GAP:{
+      GstClockTime timestamp, duration;
+
+      gst_event_parse_gap (event, &timestamp, &duration);
+      if (duration == GST_CLOCK_TIME_NONE)
+        gst_pulsesink_flush_ringbuffer (pulsesink);
+      break;
+    }
     case GST_EVENT_EOS:
       gst_pulsesink_flush_ringbuffer (pulsesink);
       break;
@@ -2805,9 +3402,23 @@ static gboolean
 gst_pulsesink_query (GstBaseSink * sink, GstQuery * query)
 {
   GstPulseSink *pulsesink = GST_PULSESINK_CAST (sink);
-  gboolean ret;
+  gboolean ret = FALSE;
 
   switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_CAPS:
+    {
+      GstCaps *caps, *filter;
+
+      gst_query_parse_caps (query, &filter);
+      caps = gst_pulsesink_query_getcaps (pulsesink, filter);
+
+      if (caps) {
+        gst_query_set_caps_result (query, caps);
+        gst_caps_unref (caps);
+        ret = TRUE;
+      }
+      break;
+    }
     case GST_QUERY_ACCEPT_CAPS:
     {
       GstCaps *caps;