alsasink: pause/resume
authorAxel MÃ¥rtensson <axelma@axis.com>
Mon, 18 Jun 2018 08:49:28 +0000 (10:49 +0200)
committerRobert Rosengren <robertr@axis.com>
Fri, 27 Sep 2019 05:34:57 +0000 (05:34 +0000)
alsasink can now detect a resume, stop and pause. The sink is now
properly paused using snd_pcm_pause(), and without losing any data

ext/alsa/gstalsasink.c
ext/alsa/gstalsasink.h

index 644e79b..330578a 100644 (file)
@@ -94,7 +94,9 @@ static gboolean gst_alsasink_close (GstAudioSink * asink);
 static gint gst_alsasink_write (GstAudioSink * asink, gpointer data,
     guint length);
 static guint gst_alsasink_delay (GstAudioSink * asink);
-static void gst_alsasink_reset (GstAudioSink * asink);
+static void gst_alsasink_pause (GstAudioSink * asink);
+static void gst_alsasink_resume (GstAudioSink * asink);
+static void gst_alsasink_stop (GstAudioSink * asink);
 static gboolean gst_alsasink_acceptcaps (GstAlsaSink * alsa, GstCaps * caps);
 static GstBuffer *gst_alsasink_payload (GstAudioBaseSink * sink,
     GstBuffer * buf);
@@ -181,7 +183,9 @@ gst_alsasink_class_init (GstAlsaSinkClass * klass)
   gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_alsasink_close);
   gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_alsasink_write);
   gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_alsasink_delay);
-  gstaudiosink_class->reset = GST_DEBUG_FUNCPTR (gst_alsasink_reset);
+  gstaudiosink_class->stop = GST_DEBUG_FUNCPTR (gst_alsasink_stop);
+  gstaudiosink_class->pause = GST_DEBUG_FUNCPTR (gst_alsasink_pause);
+  gstaudiosink_class->resume = GST_DEBUG_FUNCPTR (gst_alsasink_resume);
 
   g_object_class_install_property (gobject_class, PROP_DEVICE,
       g_param_spec_string ("device", "Device",
@@ -259,6 +263,9 @@ gst_alsasink_init (GstAlsaSink * alsasink)
   alsasink->device = g_strdup (DEFAULT_DEVICE);
   alsasink->handle = NULL;
   alsasink->cached_caps = NULL;
+  alsasink->is_paused = FALSE;
+  alsasink->after_paused = FALSE;
+  alsasink->hw_support_pause = FALSE;
   g_mutex_init (&alsasink->alsa_lock);
   g_mutex_init (&alsasink->delay_lock);
 
@@ -545,6 +552,11 @@ retry:
   GST_DEBUG_OBJECT (alsa, "buffer size %lu, period size %lu", alsa->buffer_size,
       alsa->period_size);
 
+  /* Check if hardware supports pause */
+  alsa->hw_support_pause = snd_pcm_hw_params_can_pause (params);
+  GST_DEBUG_OBJECT (alsa, "Hw support pause: %s",
+      alsa->hw_support_pause ? "yes" : "no");
+
   snd_pcm_hw_params_free (params);
   return 0;
 
@@ -1084,12 +1096,23 @@ gst_alsasink_delay (GstAudioSink * asink)
 {
   GstAlsaSink *alsa;
   snd_pcm_sframes_t delay;
-  int res;
+  int res = 0;
 
   alsa = GST_ALSA_SINK (asink);
 
   GST_DELAY_SINK_LOCK (asink);
-  res = snd_pcm_delay (alsa->handle, &delay);
+  if (alsa->is_paused == TRUE) {
+    delay = alsa->pos_in_buffer;
+    alsa->is_paused = FALSE;
+    alsa->after_paused = TRUE;
+  } else {
+    if (alsa->after_paused == TRUE) {
+      delay = alsa->pos_in_buffer;
+      alsa->after_paused = FALSE;
+    } else {
+      res = snd_pcm_delay (alsa->handle, &delay);
+    }
+  }
   GST_DELAY_SINK_UNLOCK (asink);
   if (G_UNLIKELY (res < 0)) {
     /* on errors, report 0 delay */
@@ -1106,7 +1129,65 @@ gst_alsasink_delay (GstAudioSink * asink)
 }
 
 static void
-gst_alsasink_reset (GstAudioSink * asink)
+gst_alsasink_pause (GstAudioSink * asink)
+{
+  GstAlsaSink *alsa;
+  gint err;
+  snd_pcm_sframes_t delay;
+
+  alsa = GST_ALSA_SINK (asink);
+
+  if (alsa->hw_support_pause == TRUE) {
+    GST_ALSA_SINK_LOCK (asink);
+    snd_pcm_delay (alsa->handle, &delay);
+    alsa->pos_in_buffer = delay;
+    CHECK (snd_pcm_pause (alsa->handle, 1), pause_error);
+    GST_DEBUG_OBJECT (alsa, "pause done");
+    alsa->is_paused = TRUE;
+    GST_ALSA_SINK_UNLOCK (asink);
+  } else {
+    gst_alsasink_stop (asink);
+  }
+
+  return;
+
+pause_error:
+  {
+    GST_ERROR_OBJECT (alsa, "alsa-pause: pcm pause error: %s",
+        snd_strerror (err));
+    GST_ALSA_SINK_UNLOCK (asink);
+    return;
+  }
+}
+
+static void
+gst_alsasink_resume (GstAudioSink * asink)
+{
+  GstAlsaSink *alsa;
+  gint err;
+
+  alsa = GST_ALSA_SINK (asink);
+
+  if (alsa->hw_support_pause == TRUE) {
+    GST_ALSA_SINK_LOCK (asink);
+    CHECK (snd_pcm_pause (alsa->handle, 0), resume_error);
+    GST_DEBUG_OBJECT (alsa, "resume done");
+    GST_ALSA_SINK_UNLOCK (asink);
+  }
+
+  return;
+
+resume_error:
+  {
+    GST_ERROR_OBJECT (alsa, "alsa-resume: pcm resume error: %s",
+        snd_strerror (err));
+    GST_ALSA_SINK_UNLOCK (asink);
+    return;
+  }
+}
+
+static void
+gst_alsasink_stop (GstAudioSink * asink)
 {
   GstAlsaSink *alsa;
   gint err;
@@ -1118,7 +1199,7 @@ gst_alsasink_reset (GstAudioSink * asink)
   CHECK (snd_pcm_drop (alsa->handle), drop_error);
   GST_DEBUG_OBJECT (alsa, "prepare");
   CHECK (snd_pcm_prepare (alsa->handle), prepare_error);
-  GST_DEBUG_OBJECT (alsa, "reset done");
+  GST_DEBUG_OBJECT (alsa, "stop done");
   GST_ALSA_SINK_UNLOCK (asink);
 
   return;
@@ -1126,14 +1207,14 @@ gst_alsasink_reset (GstAudioSink * asink)
   /* ERRORS */
 drop_error:
   {
-    GST_ERROR_OBJECT (alsa, "alsa-reset: pcm drop error: %s",
+    GST_ERROR_OBJECT (alsa, "alsa-stop: pcm drop error: %s",
         snd_strerror (err));
     GST_ALSA_SINK_UNLOCK (asink);
     return;
   }
 prepare_error:
   {
-    GST_ERROR_OBJECT (alsa, "alsa-reset: pcm prepare error: %s",
+    GST_ERROR_OBJECT (alsa, "alsa-stop: pcm prepare error: %s",
         snd_strerror (err));
     GST_ALSA_SINK_UNLOCK (asink);
     return;
index b240fe4..35efa88 100644 (file)
@@ -1,7 +1,7 @@
 /* GStreamer
  * Copyright (C)  2005 Wim Taymans <wim@fluendo.com>
  *
- * gstalsasink.h: 
+ * gstalsasink.h:
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -74,6 +74,11 @@ struct _GstAlsaSink {
 
   GstCaps *cached_caps;
 
+  gboolean is_paused;
+  gboolean after_paused;
+  gboolean hw_support_pause;
+  snd_pcm_sframes_t pos_in_buffer;
+
   GMutex alsa_lock;
   GMutex delay_lock;
 };