From 5d3c948572ac86c7eb83785087739e0a154e0bd4 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Axel=20M=C3=A5rtensson?= Date: Mon, 18 Jun 2018 10:49:28 +0200 Subject: [PATCH] alsasink: pause/resume 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 | 97 ++++++++++++++++++++++++++++++++++++++---- ext/alsa/gstalsasink.h | 7 ++- 2 files changed, 95 insertions(+), 9 deletions(-) diff --git a/ext/alsa/gstalsasink.c b/ext/alsa/gstalsasink.c index 644e79b136..330578a87c 100644 --- a/ext/alsa/gstalsasink.c +++ b/ext/alsa/gstalsasink.c @@ -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; diff --git a/ext/alsa/gstalsasink.h b/ext/alsa/gstalsasink.h index b240fe43df..35efa888e0 100644 --- a/ext/alsa/gstalsasink.h +++ b/ext/alsa/gstalsasink.h @@ -1,7 +1,7 @@ /* GStreamer * Copyright (C) 2005 Wim Taymans * - * 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; }; -- 2.34.1