pulsesink: Add property to apply render delay automatically based on the latency...
[platform/upstream/gst-plugins-good.git] / ext / pulse / pulsesink.c
index 24cca79..b5eadb4 100644 (file)
@@ -74,6 +74,10 @@ GST_DEBUG_CATEGORY_EXTERN (pulse_debug);
 #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
 {
@@ -88,38 +92,18 @@ enum
   PROP_STREAM_PROPERTIES,
 #ifdef __TIZEN__
   PROP_AUDIO_LATENCY,
+  PROP_AUTO_RENDER_DELAY,
 #endif /* __TIZEN__ */
   PROP_LAST
 };
 
-#ifdef __TIZEN__
-#ifdef PCM_DUMP_ENABLE
+#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
-/* stream latency */
-typedef enum {
-  AUDIO_IN_LATENCY_LOW,   /* not used in pulsesink */
-  AUDIO_IN_LATENCY_MID,   /* not used in pulsesink */
-  AUDIO_IN_LATENCY_HIGH,  /* not used in pulsesink */
-  AUDIO_IN_LATENCY_VOIP,  /* not used in pulsesink */
-  AUDIO_OUT_LATENCY_LOW,
-  AUDIO_OUT_LATENCY_MID,
-  AUDIO_OUT_LATENCY_HIGH,
-  AUDIO_OUT_LATENCY_VOIP,
-  AUDIO_LATENCY_MAX
-} StreamLatency;
-/* pulsesink latency */
-typedef enum {
-  PULSESINK_LATENCY_LOW = 0,
-  PULSESINK_LATENCY_MID,
-  PULSESINK_LATENCY_HIGH,
-  PULSESINK_LATENCY_VOIP,
-} GstPulseSinkLatency;
-#endif /* __TIZEN__ */
+#endif /* __TIZEN__ && PCM_DUMP_ENABLE */
 
 #define GST_TYPE_PULSERING_BUFFER        \
         (gst_pulseringbuffer_get_type())
@@ -141,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.
@@ -213,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);
@@ -554,9 +566,12 @@ gst_pulseringbuffer_open_device (GstAudioRingBuffer * buf)
     pa_context_set_subscribe_callback (pctx->context,
         gst_pulsering_context_subscribe_cb, pctx);
 
+    /* try to connect to the server and wait for completion, we don't want to
+     * autospawn a deamon */
     GST_LOG_OBJECT (psink, "connect to server %s",
         GST_STR_NULL (psink->server));
-    if (pa_context_connect (pctx->context, psink->server, 0, NULL) < 0)
+    if (pa_context_connect (pctx->context, psink->server,
+            PA_CONTEXT_NOAUTOSPAWN, NULL) < 0)
       goto connect_failed;
   } else {
     GST_INFO_OBJECT (psink,
@@ -769,6 +784,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->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
@@ -857,6 +885,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);
   }
@@ -884,39 +917,6 @@ gst_pulsering_wait_for_stream_ready (GstPulseSink * psink, pa_stream * stream)
   }
 }
 
-static void
-gst_pulsesink_sink_exist_cb (pa_context * c, const pa_sink_info * i, int eol,
-    void *userdata)
-{
-  gboolean *is_sink_exist = (gboolean *) userdata;
-
-  if (!i)
-    *is_sink_exist = FALSE;
-  else
-    *is_sink_exist = TRUE;
-  pa_threaded_mainloop_signal (mainloop, 0);
-}
-
-static gboolean
-gst_pulsesink_device_exist(pa_context *context, gchar *dev)
-{
-  pa_operation* o = NULL;
-  gboolean device_exist = FALSE;
-
-  if (!(o = pa_context_get_sink_info_by_name (context, dev,
-          gst_pulsesink_sink_exist_cb, &device_exist)))
-    return device_exist;
-
-  while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
-    pa_threaded_mainloop_wait (mainloop);
-    if (!CONTEXT_OK (context))
-      break;
-  }
-
-  pa_operation_unref (o);
-  return device_exist;
-}
-
 
 /* This method should create a new stream of the given @spec. No playback should
  * start yet so we start in the corked state. */
@@ -930,7 +930,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;
@@ -986,8 +988,8 @@ gst_pulseringbuffer_acquire (GstAudioRingBuffer * buf,
     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->rate, suffix);
-
+    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);
@@ -1033,6 +1035,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);
@@ -1046,17 +1049,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;
 
+#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;
@@ -1084,6 +1090,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;
@@ -1142,6 +1160,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 */
@@ -1248,7 +1275,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)
 {
@@ -1270,6 +1298,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
@@ -1283,11 +1312,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;
 
@@ -1296,6 +1320,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;
@@ -1327,7 +1366,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)
 {
@@ -1349,6 +1389,7 @@ mainloop_leave_defer_cb (pa_mainloop_api * api, void *userdata)
   pulsesink->defer_pending--;
   pa_threaded_mainloop_signal (mainloop, 0);
 }
+#endif
 
 /* stop playback, we flush everything. */
 static gboolean
@@ -1396,12 +1437,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;
@@ -1556,6 +1606,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;
 
@@ -1855,11 +1912,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 ();
@@ -1923,40 +1975,24 @@ gst_pulsesink_payload (GstAudioBaseSink * sink, GstBuffer * buf)
   }
 }
 
-#ifdef __TIZEN__
-GType
-gst_pulsesink_latency_get_type (void)
-{
-  static GType pulseaudio_latency_type = 0;
-  static const GEnumValue pulseaudio_latency[] = {
-    {PULSESINK_LATENCY_LOW,  "Low latency",  "low"},
-    {PULSESINK_LATENCY_MID,  "Mid latency",  "mid"},
-    {PULSESINK_LATENCY_HIGH, "High latency", "high"},
-    {PULSESINK_LATENCY_VOIP, "VOIP latency", "voip"},
-    {0, NULL, NULL},
-  };
-
-  if (!pulseaudio_latency_type) {
-    pulseaudio_latency_type =
-      g_enum_register_static ("GstPulseSinkLatency", pulseaudio_latency);
-  }
-  return pulseaudio_latency_type;
-}
-
-#ifdef PCM_DUMP_ENABLE
-static gboolean
-gst_pulsesink_pad_dump_handler (GstPad *pad, GstBuffer *buffer, gpointer data)
+#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);
-  int ret = -1;
-
-  if (psink->dump_fd_input)
-    ret = fwrite(GST_BUFFER_DATA(buffer), 1, GST_BUFFER_SIZE(buffer), psink->dump_fd_input);
-
-  return !!ret;
+  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
-#endif /* __TIZEN__ */
+#endif /* __TIZEN__ && PCM_DUMP_ENABLE */
 
 static void
 gst_pulsesink_class_init (GstPulseSinkClass * klass)
@@ -1966,6 +2002,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;
@@ -2058,16 +2095,27 @@ gst_pulsesink_class_init (GstPulseSinkClass * klass)
 #ifdef __TIZEN__
   g_object_class_install_property (gobject_class,
       PROP_AUDIO_LATENCY,
-      g_param_spec_enum("latency", "Audio Backend Latency",
-          "Audio backend latency", gst_pulsesink_latency_get_type(), PULSESINK_LATENCY_MID,
-          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS ));
+      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
@@ -2175,12 +2223,6 @@ gst_pulsesink_create_probe_stream (GstPulseSink * psink,
 
   pa_stream_set_state_callback (stream, gst_pulsering_stream_state_cb, pbuf);
 
-  if (psink->device && !gst_pulsesink_device_exist (pbuf->context, psink->device))
-  {
-    GST_WARNING_OBJECT (psink, "Sink:%s is not exist, set sink name to NULL", psink->device);
-    g_free (psink->device);
-    psink->device = NULL;
-  }
   if (pa_stream_connect_playback (stream, psink->device, NULL, flags, NULL,
           NULL) < 0)
     goto error;
@@ -2225,6 +2267,8 @@ gst_pulsesink_query_getcaps (GstPulseSink * psink, GstCaps * filter)
     goto unlock;
   }
 
+  ret = gst_caps_new_empty ();
+
   if (pbuf->stream) {
     /* We're in PAUSED or higher */
     stream = pbuf->stream;
@@ -2247,18 +2291,17 @@ gst_pulsesink_query_getcaps (GstPulseSink * psink, GstCaps * filter)
 
     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;
     }
 
-    pa_format_info_free (format);
-
     stream = pbuf->probe_stream;
   }
 
-  ret = gst_caps_new_empty ();
-
   if (!(o = pa_context_get_sink_info_by_name (pbuf->context,
               pa_stream_get_device_name (stream), gst_pulsesink_sink_info_cb,
               &device_info)))
@@ -2271,10 +2314,16 @@ gst_pulsesink_query_getcaps (GstPulseSink * psink, GstCaps * filter)
   }
 
   for (i = g_list_first (device_info.formats); i; i = g_list_next (i)) {
-    gst_caps_append (ret,
-        gst_pulse_format_info_to_caps ((pa_format_info *) i->data));
+    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);
@@ -2282,11 +2331,6 @@ gst_pulsesink_query_getcaps (GstPulseSink * psink, GstCaps * filter)
     ret = tmp;
   }
 
-unlock:
-  pa_threaded_mainloop_unlock (mainloop);
-  /* FIXME: this could be freed after device_name is got */
-  GST_OBJECT_UNLOCK (pbuf);
-
 out:
   free_device_info (&device_info);
 
@@ -2462,7 +2506,17 @@ gst_pulsesink_init (GstPulseSink * pulsesink)
   pulsesink->mute_set = FALSE;
 
   pulsesink->notify = 0;
+
+  g_atomic_int_set (&pulsesink->format_lost, FALSE);
+  pulsesink->format_lost_time = GST_CLOCK_TIME_NONE;
+
+  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);
@@ -2471,17 +2525,14 @@ gst_pulsesink_init (GstPulseSink * pulsesink)
   pulsesink->dump_fd_input = NULL;
   if (pulsesink->need_dump_input) {
     sinkpad = gst_element_get_static_pad((GstElement *)pulsesink, "sink");
-    gst_pad_add_buffer_probe (sinkpad, G_CALLBACK (gst_pulsesink_pad_dump_handler), pulsesink);
+    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__ */
 
-  g_atomic_int_set (&pulsesink->format_lost, FALSE);
-  pulsesink->format_lost_time = GST_CLOCK_TIME_NONE;
-
-  pulsesink->properties = NULL;
-  pulsesink->proplist = NULL;
-
   /* override with a custom clock */
   if (GST_AUDIO_BASE_SINK (pulsesink)->provided_clock)
     gst_object_unref (GST_AUDIO_BASE_SINK (pulsesink)->provided_clock);
@@ -2508,21 +2559,29 @@ gst_pulsesink_finalize (GObject * object)
   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);
 
@@ -2533,6 +2592,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
@@ -2544,17 +2604,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;
@@ -2563,6 +2632,7 @@ no_mainloop:
     GST_DEBUG_OBJECT (psink, "we have no mainloop");
     return;
   }
+#endif
 no_buffer:
   {
     psink->volume = volume;
@@ -2576,6 +2646,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,
@@ -2583,19 +2654,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);
 
@@ -2606,21 +2682,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;
@@ -2629,6 +2713,7 @@ no_mainloop:
     GST_DEBUG_OBJECT (psink, "we have no mainloop");
     return;
   }
+#endif
 no_buffer:
   {
     psink->mute = mute;
@@ -2642,6 +2727,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,
@@ -2649,6 +2735,7 @@ mute_failed:
             pa_strerror (pa_context_errno (pbuf->context))), (NULL));
     goto unlock;
   }
+#endif
 }
 
 static void
@@ -3000,20 +3087,23 @@ gst_pulsesink_set_property (GObject * object,
       pulsesink->proplist = gst_pulse_make_proplist (pulsesink->properties);
       break;
 #ifdef __TIZEN__
-    case PROP_AUDIO_LATENCY: {
-        gint nvalue = 0;
-        if (!pulsesink->proplist)
-          pulsesink->proplist = pa_proplist_new();
-        nvalue = g_value_get_enum(value);
-        if(nvalue > PULSESINK_LATENCY_VOIP)
-          nvalue = PULSESINK_LATENCY_VOIP;
-        if(nvalue < PULSESINK_LATENCY_LOW)
-          nvalue = PULSESINK_LATENCY_LOW;
-        pa_proplist_setf(pulsesink->proplist, PA_PROP_MEDIA_TIZEN_AUDIO_LATENCY, "%d", nvalue + AUDIO_OUT_LATENCY_LOW);
-        pulsesink->latency = nvalue;
-        GST_DEBUG_OBJECT(pulsesink, "latency(%d)", nvalue);
-        break;
+    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);
@@ -3049,18 +3139,26 @@ gst_pulsesink_get_property (GObject * object,
       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:
@@ -3071,7 +3169,10 @@ gst_pulsesink_get_property (GObject * object,
       break;
 #ifdef __TIZEN__
     case PROP_AUDIO_LATENCY:
-      g_value_set_enum (value, pulsesink->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:
@@ -3295,7 +3396,7 @@ 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:
@@ -3308,10 +3409,9 @@ gst_pulsesink_query (GstBaseSink * sink, GstQuery * query)
       if (caps) {
         gst_query_set_caps_result (query, caps);
         gst_caps_unref (caps);
-        return TRUE;
-      } else {
-        return FALSE;
+        ret = TRUE;
       }
+      break;
     }
     case GST_QUERY_ACCEPT_CAPS:
     {