]> git.ipfire.org Git - thirdparty/shairport-sync.git/commitdiff
Use snd_pcm_recover only _after_ an XRUN or suspend has happened. Don't use it to...
authorMike Brady <mikebrady@eircom.net>
Fri, 4 Jan 2019 13:50:14 +0000 (13:50 +0000)
committerMike Brady <mikebrady@eircom.net>
Fri, 4 Jan 2019 13:50:14 +0000 (13:50 +0000)
audio_alsa.c

index 0528c270844bb1d18f1cf509f1caded1747f727d..20c30bd10c4b4fb562f7b6aa6d38eed47465b461 100644 (file)
@@ -1020,82 +1020,51 @@ int delay_prep_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay) {
   if (ret == 0) {
     *state = snd_pcm_status_get_state(alsa_snd_pcm_status);
 
-    if ((*state == SND_PCM_STATE_SUSPENDED) || (*state == SND_PCM_STATE_XRUN)) {
-      debug(1, "alsa: recovering from SND_PCM_STATE_* %d in delay_prep_and_status.", *state); 
-      ret = snd_pcm_recover(alsa_handle, ret, 1);
-      if (ret == 0) {
-        ret = snd_pcm_status(alsa_handle, alsa_snd_pcm_status);
-        if (ret == 0)
-          *state = snd_pcm_status_get_state(alsa_snd_pcm_status);
-        if (*state != SND_PCM_STATE_PREPARED) {
-         debug(1, "alsa: snd_pcm_recover failed -- have to use snd_pcm_prepare.");     
-          ret = snd_pcm_prepare(alsa_handle);
-          if (ret == 0)
-            ret = snd_pcm_recover(alsa_handle, ret, 1);
-          if (ret == 0) {
-            ret = snd_pcm_status(alsa_handle, alsa_snd_pcm_status);
-            if (ret == 0)
-              *state = snd_pcm_status_get_state(alsa_snd_pcm_status);
-          }
-        }
-      }
-    }
-
-    // here, the device must be either SND_PCM_STATE_RUNNING
-    // or SND_PCM_STATE_PREPARED or SND_PCM_STATE_DRAINING
-
-    if (ret == 0) {
-
-      if ((*state == SND_PCM_STATE_RUNNING) || (*state == SND_PCM_STATE_DRAINING)) {
+    if ((*state == SND_PCM_STATE_RUNNING) || (*state == SND_PCM_STATE_DRAINING)) {
 
-        snd_pcm_status_get_htstamp(alsa_snd_pcm_status, &update_timestamp);
-        *delay = snd_pcm_status_get_delay(alsa_snd_pcm_status);
+      snd_pcm_status_get_htstamp(alsa_snd_pcm_status, &update_timestamp);
+      *delay = snd_pcm_status_get_delay(alsa_snd_pcm_status);
 
-        if (*state == SND_PCM_STATE_DRAINING)
-          debug(1, "alsa: draining with a delay of %d.", delay);
+      if (*state == SND_PCM_STATE_DRAINING)
+        debug(1, "alsa: draining with a delay of %d.", delay);
 
-        clock_gettime(CLOCK_MONOTONIC, &tn);
-        uint64_t time_now_ns = tn.tv_sec * (uint64_t)1000000000 + tn.tv_nsec;
-        uint64_t update_timestamp_ns =
-            update_timestamp.tv_sec * (uint64_t)1000000000 + update_timestamp.tv_nsec;
+      clock_gettime(CLOCK_MONOTONIC, &tn);
+      uint64_t time_now_ns = tn.tv_sec * (uint64_t)1000000000 + tn.tv_nsec;
+      uint64_t update_timestamp_ns =
+          update_timestamp.tv_sec * (uint64_t)1000000000 + update_timestamp.tv_nsec;
 
-        // see if it's stalled
+      // see if it's stalled
 
-        if ((stall_monitor_start_time != 0) && (stall_monitor_frame_count == *delay)) {
-          // hasn't outputted anything since the last call to delay()
+      if ((stall_monitor_start_time != 0) && (stall_monitor_frame_count == *delay)) {
+        // hasn't outputted anything since the last call to delay()
 
-          if (((update_timestamp_ns - stall_monitor_start_time) > stall_monitor_error_threshold) ||
-              ((time_now_ns - stall_monitor_start_time) > stall_monitor_error_threshold)) {
-            ret = sps_extra_code_output_stalled;
-          }
-        } else {
-          stall_monitor_start_time = update_timestamp_ns;
-          stall_monitor_frame_count = *delay;
+        if (((update_timestamp_ns - stall_monitor_start_time) > stall_monitor_error_threshold) ||
+            ((time_now_ns - stall_monitor_start_time) > stall_monitor_error_threshold)) {
+          ret = sps_extra_code_output_stalled;
         }
+      } else {
+        stall_monitor_start_time = update_timestamp_ns;
+        stall_monitor_frame_count = *delay;
+      }
 
-        if (ret == 0) {
-          uint64_t delta = time_now_ns - update_timestamp_ns;
-
-          uint64_t frames_played_since_last_interrupt =
-              ((uint64_t)desired_sample_rate * delta) / 1000000000;
-          snd_pcm_sframes_t frames_played_since_last_interrupt_sized =
-              frames_played_since_last_interrupt;
-
-          *delay = *delay - frames_played_since_last_interrupt_sized;
-        }
-      } else { // not running, thus no delay information, thus can't check for stall
-        stall_monitor_start_time = 0;  // zero if not initialised / not started / zeroed by flush
-        stall_monitor_frame_count = 0; // set to delay at start of time, incremented by any writes
+      if (ret == 0) {
+        uint64_t delta = time_now_ns - update_timestamp_ns;
 
-        // not running, thus no delay information, thus can't check for frame rates
-        frame_index = 0; // we'll be starting over...
-        measurement_data_is_valid = 0;
+        uint64_t frames_played_since_last_interrupt =
+            ((uint64_t)desired_sample_rate * delta) / 1000000000;
+        snd_pcm_sframes_t frames_played_since_last_interrupt_sized =
+            frames_played_since_last_interrupt;
 
-        if ((*state != SND_PCM_STATE_PREPARED)) {
-          debug(1, "alsa: can't get device into valid state in delay. State is %d.", *state);
-          ret = sps_extra_code_output_state_cannot_make_ready;
-        }
+        *delay = *delay - frames_played_since_last_interrupt_sized;
       }
+    } else { // not running, thus no delay information, thus can't check for stall
+      *delay = 0;
+      stall_monitor_start_time = 0;  // zero if not initialised / not started / zeroed by flush
+      stall_monitor_frame_count = 0; // set to delay at start of time, incremented by any writes
+
+      // not running, thus no delay information, thus can't check for frame rates
+      frame_index = 0; // we'll be starting over...
+      measurement_data_is_valid = 0;
     }
   }
   return ret;
@@ -1180,41 +1149,59 @@ int untimed_play(void *buf, int samples) {
     ret = delay_prep_and_status(&state, &my_delay);
 
     if (ret == 0) { // will be non-zero if an error or a stall
-      if ((state == SND_PCM_STATE_PREPARED) || (state == SND_PCM_STATE_RUNNING)) {
 
-        if ((samples != 0) && (buf != NULL)) {
+      if ((samples != 0) && (buf != NULL)) {
 
-          // debug(3, "write %d frames.", samples);
-          ret = alsa_pcm_write(alsa_handle, buf, samples);
-          if (ret == samples) {
-            stall_monitor_frame_count += samples;
+        // debug(3, "write %d frames.", samples);
+        ret = alsa_pcm_write(alsa_handle, buf, samples);
+        if (ret == samples) {
+          stall_monitor_frame_count += samples;
 
-            if (frame_index == 0) {
-              frames_sent_for_playing = samples;
-            } else {
-              frames_sent_for_playing += samples;
-            }
+          if (frame_index == 0) {
+            frames_sent_for_playing = samples;
+          } else {
+            frames_sent_for_playing += samples;
+          }
 
-            const uint64_t start_measurement_from_this_frame =
-                (2 * 44100) / 352; // two seconds of frames…
+          const uint64_t start_measurement_from_this_frame =
+              (2 * 44100) / 352; // two seconds of frames…
 
-            frame_index++;
+          frame_index++;
 
-            if ((frame_index == start_measurement_from_this_frame) ||
-                ((frame_index > start_measurement_from_this_frame) && (frame_index % 32 == 0))) {
+          if ((frame_index == start_measurement_from_this_frame) ||
+              ((frame_index > start_measurement_from_this_frame) && (frame_index % 32 == 0))) {
 
-              measurement_time = get_absolute_time_in_fp();
-              frames_played_at_measurement_time = frames_sent_for_playing - my_delay - samples;
+            measurement_time = get_absolute_time_in_fp();
+            frames_played_at_measurement_time = frames_sent_for_playing - my_delay - samples;
 
-              if (frame_index == start_measurement_from_this_frame) {
-                // debug(1, "Start frame counting");
-                frames_played_at_measurement_start_time = frames_played_at_measurement_time;
-                measurement_start_time = measurement_time;
-                measurement_data_is_valid = 1;
+            if (frame_index == start_measurement_from_this_frame) {
+              // debug(1, "Start frame counting");
+              frames_played_at_measurement_start_time = frames_played_at_measurement_time;
+              measurement_start_time = measurement_time;
+              measurement_data_is_valid = 1;
+            }
+          }
+        } else {
+          debug(1, "alsa: error %d writing %d samples to alsa device.", ret, samples);
+          if (ret == -EPIPE) { /* underrun */
+            ret = snd_pcm_recover(alsa_handle, ret, debuglev > 0 ? 1 : 0);
+            if (ret < 0) {
+              ret = snd_pcm_prepare(alsa_handle);
+              if (ret < 0)
+                warn("alsa: can't recover from SND_PCM_STATE_XRUN, snd_pcm_recover() and "
+                     "snd_pcm_prepare() failed: %s.",
+                     snd_strerror(ret));
+            } else if (ret == -ESTRPIPE) { /* suspended */
+              while ((ret = snd_pcm_resume(alsa_handle)) == -EAGAIN)
+                sleep(1); /* wait until the suspend flag is released */
+              if (ret < 0) {
+                ret = snd_pcm_prepare(alsa_handle);
+                if (ret < 0)
+                  warn("alsa: can't recover from SND_PCM_STATE_SUSPENDED state, snd_pcm_prepare() "
+                       "failed: %s.",
+                       snd_strerror(ret));
               }
             }
-          } else {
-            debug(1, "alsa: error writing to alsa device.");
           }
         }
       } else {