tinycompresssink: add h/w volume set
[platform/upstream/gst-plugins-tizen.git] / tinycompresssink / src / gsttinycompresssink.c
index 8f91102..98242b5 100644 (file)
 #include <stdint.h>
 #include <unistd.h>
 
+#include <sound_manager.h>
+#include <asoundlib.h>
+
+/* FIXME: This might be configurable (eg. property) */
+#define ALSA_DEFAULT_CARD "Exynos9110Sound"
+
+#define COMPRESS_VOL_CTRL "ComprTx0 Volume"
+
 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
     GST_PAD_ALWAYS,
@@ -64,7 +72,7 @@ static void gst_tinycompress_sink_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec);
 static void gst_tinycompress_sink_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec);
-static void gst_tinycompress_sink_finalize (GObject * obj);
+static void gst_tinycompress_sink_finalize (GObject * object);
 
 static GstStateChangeReturn gst_tinycompress_sink_change_state (GstElement *
     element, GstStateChange transition);
@@ -80,7 +88,7 @@ static gboolean gst_tinycompress_sink_event (GstBaseSink * bsink,
     GstEvent * event);
 
 static void
-_dump_htpointer (struct compress *compress)
+_dump_compress_status (GstTinycompressSink * sink, struct compress *compress)
 {
   unsigned int avail;
   unsigned int sampling_rate = 0;
@@ -92,13 +100,91 @@ _dump_htpointer (struct compress *compress)
   compress_get_tstamp (compress, &samples, &sampling_rate);
 
   if (compress_get_hpointer (compress, &avail, &tstamp) != 0) {
-    GST_ERROR ("Error querying timestamp : %s", compress_get_error (compress));
+    GST_ERROR_OBJECT (sink, "Error querying timestamp : %s",
+        compress_get_error (compress));
   } else {
-    GST_ERROR
-        ("samples:%10d,   rate:%7d,   Avail:%7d,   DSP played:%4jd.%012jd\n",
-        samples, sampling_rate, avail, (intmax_t) tstamp.tv_sec,
-        (intmax_t) tstamp.tv_nsec * 1000);
+    GST_DEBUG_OBJECT (sink,
+        "samples:%10d, avail:%7d, DSP played:%4jd.%03jd\n", samples, avail,
+        (intmax_t) (tstamp.tv_sec / sink->t_codec.ch_in),
+        (intmax_t) (tstamp.tv_nsec / sink->t_codec.ch_in) / 1000000);
+  }
+}
+
+static gboolean
+_mixer_control_set_value (GstTinycompressSink * sink, const char *ctl_name,
+    int val)
+{
+  snd_ctl_t *handle;
+  snd_ctl_elem_value_t *control;
+  snd_ctl_elem_id_t *id;
+  snd_ctl_elem_info_t *info;
+  snd_ctl_elem_type_t type;
+  int ret = 0;
+  int count = 0;
+  int i = 0;
+
+  ret = snd_ctl_open (&handle, ALSA_DEFAULT_CARD, 0);
+  if (ret < 0) {
+    GST_ERROR_OBJECT (sink, "snd_ctl_open error, card: %s: %s",
+        ALSA_DEFAULT_CARD, snd_strerror (ret));
+    return FALSE;
+  }
+
+  snd_ctl_elem_id_alloca (&id);
+  snd_ctl_elem_info_alloca (&info);
+  snd_ctl_elem_value_alloca (&control);
+
+  snd_ctl_elem_id_set_interface (id, SND_CTL_ELEM_IFACE_MIXER);
+  snd_ctl_elem_id_set_name (id, ctl_name);
+
+  snd_ctl_elem_info_set_id (info, id);
+  if (snd_ctl_elem_info (handle, info) < 0) {
+    GST_ERROR_OBJECT (sink, "Cannot find control element: %s", ctl_name);
+    goto close;
+  }
+  snd_ctl_elem_info_get_id (info, id);
+
+  type = snd_ctl_elem_info_get_type (info);
+  count = snd_ctl_elem_info_get_count (info);
+
+  snd_ctl_elem_value_set_id (control, id);
+
+  snd_ctl_elem_read (handle, control);
+
+  switch (type) {
+    case SND_CTL_ELEM_TYPE_BOOLEAN:
+      for (i = 0; i < count; i++)
+        snd_ctl_elem_value_set_boolean (control, i, val);
+      break;
+
+    case SND_CTL_ELEM_TYPE_INTEGER:
+      for (i = 0; i < count; i++)
+        snd_ctl_elem_value_set_integer (control, i, val);
+      break;
+
+    case SND_CTL_ELEM_TYPE_ENUMERATED:
+      for (i = 0; i < count; i++)
+        snd_ctl_elem_value_set_enumerated (control, i, val);
+      break;
+
+    default:
+      GST_WARNING_OBJECT (sink, "unsupported control element type");
+      goto close;
   }
+
+  snd_ctl_elem_write (handle, control);
+
+  snd_ctl_close (handle);
+
+  GST_INFO_OBJECT (sink, "set mixer(%s) = %d success", ctl_name, val);
+
+  return TRUE;
+
+close:
+  GST_ERROR_OBJECT (sink, "Error");
+  snd_ctl_close (handle);
+
+  return FALSE;
 }
 
 static gboolean
@@ -125,7 +211,8 @@ gst_tinycompress_set_codec_param (GstTinycompressSink * sink, GstCaps * caps)
   sink->t_codec.level = 0;
   sink->t_codec.ch_mode = 0;
   sink->t_codec.format = 0;
-  GST_DEBUG_OBJECT (sink, "t_codec: id(%d), channel(%d), sample_rate(%d)",
+
+  GST_INFO_OBJECT (sink, "t_codec: id(%d), channel(%d), sample_rate(%d)",
       SND_AUDIOCODEC_MP3, channels, rate);
 
   return TRUE;
@@ -154,9 +241,7 @@ gst_tinycompress_sink_class_init (GstTinycompressSinkClass * klass)
 
   gst_element_class_set_static_metadata (gstelement_class,
       "Tinycompress Sink",
-      "Sink",
-      "Render MP3 data in compress device using tinycompress",
-      "Tizen");
+      "Sink", "Render MP3 data in compress device using tinycompress", "Tizen");
 
   gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
 
@@ -174,32 +259,70 @@ gst_tinycompress_sink_class_init (GstTinycompressSinkClass * klass)
 }
 
 static void
+_volume_changed_cb (sound_type_e type, unsigned int volume, void *user_data)
+{
+  GstTinycompressSink *sink = (GstTinycompressSink *) user_data;
+
+  GST_INFO_OBJECT (sink, " type %d, volume %u", type, volume);
+
+  /* FIXME: For now current volume range for media is 0~15,
+     corresponding mixer control range is 0~8192.
+     As level matching will be part of tunning issue in the future,
+     we just set value to level multiplied by 100 which seems to be fine */
+  if (type == SOUND_TYPE_MEDIA)
+    _mixer_control_set_value (sink, COMPRESS_VOL_CTRL, volume * 100);
+}
+
+static void
 gst_tinycompress_sink_init (GstTinycompressSink * sink)
 {
+  int media_volume = -1;
+
   sink->dump = DEFAULT_DUMP;
   sink->card = DEFAULT_CARD;
   sink->device = DEFAULT_DEVICE;
   sink->compress_paused = FALSE;
+  sink->volume_cb_id = -1;
+
+  if (sound_manager_get_volume (SOUND_TYPE_MEDIA,
+          &media_volume) != SOUND_MANAGER_ERROR_NONE)
+    GST_ERROR_OBJECT (sink, "failed to get media volume");
+
+  if (sound_manager_add_volume_changed_cb (_volume_changed_cb, sink,
+          &sink->volume_cb_id) != SOUND_MANAGER_ERROR_NONE)
+    GST_ERROR_OBJECT (sink, "failed to add volume changed cb");
+
+  GST_INFO_OBJECT (sink, "Initial media volume was %d, added volume cb %d",
+      media_volume, sink->volume_cb_id);
+
+  _mixer_control_set_value (sink, "ComprTx0 Volume", media_volume * 100);
 }
 
 static void
-gst_tinycompress_sink_finalize (GObject * obj)
+gst_tinycompress_sink_finalize (GObject * object)
 {
-  G_OBJECT_CLASS (parent_class)->finalize (obj);
+  GstTinycompressSink *sink = GST_TINYCOMPRESS_SINK (object);
+
+  if (sink->volume_cb_id != -1)
+    if (sound_manager_remove_volume_changed_cb (sink->volume_cb_id) !=
+        SOUND_MANAGER_ERROR_NONE)
+      GST_ERROR_OBJECT (sink, "failed to remove volume changed cb (id:%d)",
+          sink->volume_cb_id);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
 static void
 gst_tinycompress_sink_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec)
 {
-  GstTinycompressSink *sink;
-
-  sink = GST_TINYCOMPRESS_SINK (object);
+  GstTinycompressSink *sink = GST_TINYCOMPRESS_SINK (object);
 
   switch (prop_id) {
     case PROP_DUMP:
       sink->dump = g_value_get_boolean (value);
       break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -218,6 +341,7 @@ gst_tinycompress_sink_get_property (GObject * object, guint prop_id,
     case PROP_DUMP:
       g_value_set_boolean (value, sink->dump);
       break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -251,17 +375,16 @@ gst_tinycompress_sink_render (GstBaseSink * bsink, GstBuffer * buf)
   GST_DEBUG_OBJECT (sink, " >> Wrote %u bytes, accum %u", info.size,
       sink->written);
 
-  if (sink->dump) {
-    _dump_htpointer (sink->compress);
-  }
+  /* FIXEME: needs separate property for control */
+  _dump_compress_status (sink, sink->compress);
 
   if (!is_compress_running (sink->compress)
       && sink->written >= sink->start_threashold) {
-    GST_DEBUG_OBJECT (sink, "Start Now!!!");
+    GST_INFO_OBJECT (sink, "Start Now!!!");
     if (compress_start (sink->compress) != 0) {
       GST_ERROR_OBJECT (sink, "Could not start!!!");
     } else {
-      GST_ERROR_OBJECT (sink, "Started done!!!");
+      GST_INFO_OBJECT (sink, "Started done!!!");
       sink->compress_paused = FALSE;
     }
   }
@@ -282,7 +405,7 @@ gst_tinycompress_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
   caps = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink));
   GST_OBJECT_UNLOCK (sink);
 
-  GST_DEBUG_OBJECT (sink, "Got caps %" GST_PTR_FORMAT, caps);
+  GST_INFO_OBJECT (sink, "Got caps %" GST_PTR_FORMAT, caps);
 
   if (caps && filter) {
     GstCaps *intersection =
@@ -303,7 +426,7 @@ gst_tinycompress_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
   GstTinycompressSink *sink;
 
   sink = GST_TINYCOMPRESS_SINK (bsink);
-  GST_DEBUG_OBJECT (sink, "set caps %" GST_PTR_FORMAT, caps);
+  GST_INFO_OBJECT (sink, "set caps %" GST_PTR_FORMAT, caps);
 
   gst_tinycompress_set_codec_param (sink, caps);
 
@@ -330,7 +453,7 @@ gst_tinycompress_resume (GstTinycompressSink * sink)
   int ret;
   g_return_val_if_fail (sink != NULL, FALSE);
 
-  GST_DEBUG_OBJECT (sink, "gst_tinycompress_resume");
+  GST_INFO_OBJECT (sink, "gst_tinycompress_resume");
 
   if (!sink->compress)
     goto not_opened;
@@ -342,7 +465,7 @@ gst_tinycompress_resume (GstTinycompressSink * sink)
     goto not_started;
 
   ret = compress_resume (sink->compress);
-  GST_LOG_OBJECT (sink, "resuming the device, ret=%d", ret);
+  GST_INFO_OBJECT (sink, "resuming the device, ret=%d", ret);
   if (ret != 0) {
     GST_ERROR_OBJECT (sink, "Error %d resuming device\n", ret);
     return FALSE;
@@ -361,7 +484,7 @@ not_paused:
   return FALSE;
 
 not_started:
-  GST_LOG_OBJECT (sink, "Note: Tinycompress device is not started!");
+  GST_ERROR_OBJECT (sink, "Note: Tinycompress device is not started!");
   return FALSE;
 }
 
@@ -372,7 +495,7 @@ gst_tinycompress_pause (GstTinycompressSink * sink)
 
   g_return_val_if_fail (sink != NULL, FALSE);
 
-  GST_DEBUG_OBJECT (sink, "gst_tinycompress_pause");
+  GST_INFO_OBJECT (sink, "gst_tinycompress_pause");
 
   if (!sink->compress)
     goto not_opened;
@@ -381,7 +504,7 @@ gst_tinycompress_pause (GstTinycompressSink * sink)
     goto not_started;
 
   ret = compress_pause (sink->compress);
-  GST_LOG_OBJECT (sink, "pausing the device, ret=%d", ret);
+  GST_INFO_OBJECT (sink, "pausing the device, ret=%d", ret);
   if (ret != 0) {
     GST_ERROR_OBJECT (sink, "Error %d pausing device, %s\n", ret,
         compress_get_error (sink->compress));
@@ -396,7 +519,7 @@ not_opened:
   return TRUE;
 
 not_started:
-  GST_LOG_OBJECT (sink, "Note: Tinycompress device is not started!");
+  GST_ERROR_OBJECT (sink, "Note: Tinycompress device is not started!");
   return TRUE;
 }
 
@@ -413,25 +536,26 @@ gst_tinycompress_unprepare (GstTinycompressSink * sink)
     compress_close (sink->compress);
   sink->compress = NULL;
 
-  GST_DEBUG_OBJECT (sink, "Compress device %d:%d closed", sink->card,
+  GST_INFO_OBJECT (sink, "Compress device %d:%d closed", sink->card,
       sink->device);
 
+  sink->compress_paused = FALSE;
+  sink->written = 0;
+
   GST_OBJECT_UNLOCK (sink);
 
   return TRUE;
 }
 
-
 static gboolean
 gst_tinycompress_open (GstTinycompressSink * sink)
 {
   g_return_val_if_fail (sink != NULL, FALSE);
 
-  GST_DEBUG_OBJECT (sink, "gst_tinycompress_open");
+  GST_INFO_OBJECT (sink, "gst_tinycompress_open");
 
   sink->t_config.codec = &sink->t_codec;
 
-
   GST_OBJECT_LOCK (sink);
 
   sink->compress =
@@ -442,13 +566,14 @@ gst_tinycompress_open (GstTinycompressSink * sink)
     goto fail;
   }
 
-  compress_set_max_poll_wait (sink->compress, 3000);    // need to property, wait for writting
+  /* FIXME: need to property, wait for writting */
+  compress_set_max_poll_wait (sink->compress, 3000);
 
   sink->start_threashold =
       sink->t_config.fragment_size * sink->t_config.fragments;
-  GST_DEBUG_OBJECT (sink, "get fragment_size: %d, total number of fragment :%d",
+  GST_INFO_OBJECT (sink, "Got fragment_size: %d, fragments :%d",
       sink->t_config.fragment_size, sink->t_config.fragments);
-  GST_DEBUG_OBJECT (sink, "buffer size for buffering : %d",
+  GST_INFO_OBJECT (sink, "buffer size for buffering : %d",
       sink->start_threashold);
 
   if (!is_compress_ready (sink->compress)) {
@@ -456,7 +581,7 @@ gst_tinycompress_open (GstTinycompressSink * sink)
     goto fail;
   }
 
-  GST_DEBUG_OBJECT (sink, "Compress device %d:%d opened", sink->card,
+  GST_INFO_OBJECT (sink, "Compress device %d:%d opened", sink->card,
       sink->device);
 
   sink->written = 0;
@@ -474,7 +599,6 @@ fail:
   }
 
   return FALSE;
-
 }
 
 static GstStateChangeReturn
@@ -486,18 +610,21 @@ gst_tinycompress_sink_change_state (GstElement * element,
 
   switch (transition) {
     case GST_STATE_CHANGE_NULL_TO_READY:
-      GST_DEBUG_OBJECT (sink, "NULL_TO_READY");
+      GST_INFO_OBJECT (sink, "NULL_TO_READY");
       break;
+
     case GST_STATE_CHANGE_READY_TO_PAUSED:
-      GST_DEBUG_OBJECT (sink, "READY_TO_PAUSED");
+      GST_INFO_OBJECT (sink, "READY_TO_PAUSED");
       break;
+
     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
-      GST_DEBUG_OBJECT (sink, "PAUSED_TO_PLAYING");
+      GST_INFO_OBJECT (sink, "PAUSED_TO_PLAYING");
       if (!sink->compress)
         gst_tinycompress_open (sink);
       if (sink->compress_paused)
         gst_tinycompress_resume (sink);
       break;
+
     default:
       break;
   }
@@ -506,16 +633,19 @@ gst_tinycompress_sink_change_state (GstElement * element,
 
   switch (transition) {
     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
-      GST_DEBUG_OBJECT (sink, "PLAYING_TO_PAUSED");
+      GST_INFO_OBJECT (sink, "PLAYING_TO_PAUSED");
       gst_tinycompress_pause (sink);
       break;
+
     case GST_STATE_CHANGE_PAUSED_TO_READY:
-      GST_DEBUG_OBJECT (sink, "PAUSED_TO_READY");
+      GST_INFO_OBJECT (sink, "PAUSED_TO_READY");
       gst_tinycompress_unprepare (sink);
       break;
+
     case GST_STATE_CHANGE_READY_TO_NULL:
-      GST_DEBUG_OBJECT (sink, "READY_TO_NULL");
+      GST_INFO_OBJECT (sink, "READY_TO_NULL");
       break;
+
     default:
       break;
   }
@@ -528,13 +658,15 @@ gst_tinycompress_sink_event (GstBaseSink * bsink, GstEvent * event)
   GstTinycompressSink *sink;
   sink = GST_TINYCOMPRESS_SINK (bsink);
 
-  GST_DEBUG_OBJECT (sink, "got event (%d)", GST_EVENT_TYPE (event));
+  GST_INFO_OBJECT (sink, "got event (%s)",
+      gst_event_type_get_name (GST_EVENT_TYPE (event)));
 
   switch (GST_EVENT_TYPE (event)) {
     case GST_EVENT_EOS:
-      GST_DEBUG_OBJECT (sink, "get GST_EVENT_EOS event..state is %d",
+      GST_INFO_OBJECT (sink, "get GST_EVENT_EOS event..state is %d",
           GST_STATE (sink));
       break;
+
     default:
       break;
   }
@@ -553,5 +685,5 @@ plugin_init (GstPlugin * plugin)
 }
 
 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, tinycompress,
-    "tinycompress plugin library", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,
-    GST_PACKAGE_ORIGIN)
+    "tinycompress plugin library", plugin_init, VERSION, "LGPL",
+    GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)