Merge branch 'upstream/1.16' into tizen_gst_1.16.2
[platform/upstream/gst-plugins-good.git] / ext / pulse / pulsesink.c
index ca75915..fca5601 100644 (file)
@@ -58,7 +58,9 @@
 #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"
 
@@ -72,6 +74,9 @@ 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"
+#endif /* __TIZEN__ */
 
 enum
 {
@@ -84,9 +89,20 @@ 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)        \
@@ -203,6 +219,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);
@@ -639,6 +659,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;
@@ -843,6 +870,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);
   }
@@ -883,7 +915,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;
@@ -933,6 +967,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,
@@ -970,6 +1020,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);
@@ -983,17 +1034,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;
@@ -1021,6 +1075,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;
@@ -1079,6 +1145,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 */
@@ -1516,6 +1591,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;
 
@@ -1878,6 +1960,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)
 {
@@ -1976,6 +2077,15 @@ 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");
@@ -2356,6 +2466,11 @@ 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_info.description = NULL;
@@ -2376,6 +2491,25 @@ gst_pulsesink_init (GstPulseSink * pulsesink)
 
   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)
@@ -2403,21 +2537,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);
 
@@ -2428,6 +2570,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
@@ -2439,17 +2582,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;
@@ -2458,6 +2610,7 @@ no_mainloop:
     GST_DEBUG_OBJECT (psink, "we have no mainloop");
     return;
   }
+#endif
 no_buffer:
   {
     psink->volume = volume;
@@ -2471,6 +2624,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,
@@ -2478,19 +2632,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);
 
@@ -2501,21 +2660,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;
@@ -2524,6 +2691,7 @@ no_mainloop:
     GST_DEBUG_OBJECT (psink, "we have no mainloop");
     return;
   }
+#endif
 no_buffer:
   {
     psink->mute = mute;
@@ -2537,6 +2705,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,
@@ -2544,6 +2713,7 @@ mute_failed:
             pa_strerror (pa_context_errno (pbuf->context))), (NULL));
     goto unlock;
   }
+#endif
 }
 
 static void
@@ -2894,6 +3064,21 @@ 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;
+#endif /* __TIZEN__ */
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -2928,18 +3113,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:
@@ -2948,6 +3141,11 @@ gst_pulsesink_get_property (GObject * object,
     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;