From 99c663857313a23d307b8d3c48423bdfc9554379 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sat, 6 Nov 2021 18:32:26 +0000 Subject: [PATCH] Ensure the frame rate calculator allows for silent frames added to disable standby. Clean up outmoded and unused stuff in audio_alsa.c. More to do. --- audio_alsa.c | 83 +++++++++++++++++++++------------------------------- player.c | 56 +++++++++++++++++++++++------------ player.h | 2 +- 3 files changed, 71 insertions(+), 70 deletions(-) diff --git a/audio_alsa.c b/audio_alsa.c index f723cb7d..32f6c90f 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -60,7 +60,8 @@ static int play(void *buf, int samples); static void stop(void); static void flush(void); int delay(long *the_delay); -int get_rate_information(uint64_t *elapsed_time, uint64_t *frames_played); +int get_frames_sent_for_output(__attribute__ ((unused)) uint64_t *elapsed_time, uint64_t *frames_sent_to_dac); +// int get_rate_information(uint64_t *elapsed_time, uint64_t *frames_played); void *alsa_buffer_monitor_thread_code(void *arg); static void volume(double vol); @@ -88,7 +89,8 @@ audio_output audio_alsa = { .flush = &flush, .delay = &delay, .play = &play, - .rate_info = &get_rate_information, + .rate_info = &get_frames_sent_for_output, // will also include frames of silence sent to stop standby mode +// .rate_info = NULL, .mute = NULL, // a function will be provided if it can, and is allowed to, // do hardware mute .volume = NULL, // a function will be provided if it can do hardware volume @@ -239,22 +241,13 @@ int precision_delay_available() { return (precision_delay_available_status == YNDK_YES); } -// static int play_number; -// static int64_t accumulated_delay, accumulated_da_delay; int alsa_characteristics_already_listed = 0; static snd_pcm_uframes_t period_size_requested, buffer_size_requested; static int set_period_size_request, set_buffer_size_request; -static uint64_t measurement_start_time; -static uint64_t frames_played_at_measurement_start_time; - -static uint64_t measurement_time; -static uint64_t frames_played_at_measurement_time; - static uint64_t frames_sent_for_playing; -static uint64_t frame_index; -static int measurement_data_is_valid; +static int output_error_occurred; // set to true if an underrun or similar has occurred since last requested static void help(void) { printf(" -d output-device set the output device, default is \"default\".\n" @@ -1407,8 +1400,8 @@ static void start(__attribute__((unused)) int i_sample_rate, __attribute__((unused)) int i_sample_format) { debug(3, "audio_alsa start called."); - frame_index = 0; - measurement_data_is_valid = 0; + // frame_index = 0; + // measurement_data_is_valid = 0; stall_monitor_start_time = 0; stall_monitor_frame_count = 0; @@ -1430,8 +1423,8 @@ int standard_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, } else { // 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; + // frame_index = 0; // we'll be starting over... + // measurement_data_is_valid = 0; *delay = 0; } @@ -1587,8 +1580,8 @@ int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, // 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; + // frame_index = 0; // we'll be starting over... + // measurement_data_is_valid = 0; } } else { debug(1, "alsa: can't get device's status."); @@ -1631,6 +1624,21 @@ int delay(long *the_delay) { return ret; } +int get_frames_sent_for_output(__attribute__ ((unused)) uint64_t *elapsed_time, uint64_t *frames_sent_to_dac) { + int ret = 0; + pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 0); + *frames_sent_to_dac = frames_sent_for_playing; + if (alsa_handle == NULL) + ret = ENODEV; + else + ret = output_error_occurred; // will be zero unless an error occurred + output_error_occurred = 0; // reset it. + debug_mutex_unlock(&alsa_mutex, 0); + pthread_cleanup_pop(0); + return ret; +} + +/* int get_rate_information(uint64_t *elapsed_time, uint64_t *frames_played) { // elapsed_time is in nanoseconds int response = 0; // zero means okay @@ -1644,6 +1652,7 @@ int get_rate_information(uint64_t *elapsed_time, uint64_t *frames_played) { } return response; } +*/ int do_play(void *buf, int samples) { // assuming the alsa_mutex has been acquired @@ -1671,34 +1680,9 @@ int do_play(void *buf, int 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; - } - - const uint64_t start_measurement_from_this_frame = - (2 * config.output_rate) / 352; // two seconds of frames - - frame_index++; - - 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_ns(); - 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; - } - } + frames_sent_for_playing += samples; } else { - frame_index = 0; - measurement_data_is_valid = 0; + output_error_occurred = -ret; // note than an output error has occurred if (ret == -EPIPE) { /* underrun */ // It could be that the DAC was in the SND_PCM_STATE_XRUN state before @@ -1748,8 +1732,6 @@ int do_play(void *buf, int samples) { "alsa: device status returns fault status %d and SND_PCM_STATE_* " "%d for play.", ret, state); - frame_index = 0; - measurement_data_is_valid = 0; } pthread_setcancelstate(oldState, NULL); @@ -1774,7 +1756,8 @@ int do_open(int do_auto_setup) { // set accordingly // do_mute(0); // complete unmute } - + output_error_occurred = 0; + frames_sent_for_playing = 0; alsa_backend_state = abm_connected; // only do this if it really opened it. } } else { @@ -2005,8 +1988,8 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { "alsa_backend_state => abm_connected"); } else if ((alsa_backend_state == abm_connected) && (config.keep_dac_busy == 0)) { stall_monitor_start_time = 0; - frame_index = 0; - measurement_data_is_valid = 0; + // frame_index = 0; + // measurement_data_is_valid = 0; debug(2, "alsa: alsa_buffer_monitor_thread_code() -- closing the output " "device"); do_close(); diff --git a/player.c b/player.c index 6408a006..74c6eac2 100644 --- a/player.c +++ b/player.c @@ -1611,7 +1611,7 @@ void player_thread_cleanup_handler(void *arg) { int elapsedHours = rawSeconds / 3600; int elapsedMin = (rawSeconds / 60) % 60; int elapsedSec = rawSeconds % 60; - if (conn->frame_rate_status) + if (conn->frame_rate_valid) inform("Connection %d: Playback Stopped. Total playing time %02d:%02d:%02d. Input: %0.2f, " "output: %0.2f " "frames per second.", @@ -1714,6 +1714,12 @@ void player_thread_cleanup_handler(void *arg) { void *player_thread_func(void *arg) { rtsp_conn_info *conn = (rtsp_conn_info *)arg; + + uint64_t previous_frames_played; + uint64_t previous_frames_played_time; + int previous_frames_played_valid = 0; + + // pthread_cleanup_push(player_thread_initial_cleanup_handler, arg); conn->packet_count = 0; conn->packet_count_since_flush = 0; @@ -1866,7 +1872,7 @@ void *player_thread_func(void *arg) { conn->playstart = time(NULL); conn->frame_rate = 0.0; - conn->frame_rate_status = 0; + conn->frame_rate_valid = 0; conn->input_frame_rate = 0.0; conn->input_frame_rate_starting_point_is_valid = 0; @@ -2059,7 +2065,7 @@ void *player_thread_func(void *arg) { pthread_testcancel(); // allow a pthread_cancel request to take effect. abuf_t *inframe = buffer_get_frame(conn); // this has cancellation point(s), but it's not // guaranteed that they'll always be executed - + uint64_t local_time_now = get_absolute_time_in_ns(); // types okay if (inframe) { inbuf = inframe->data; inbuflength = inframe->length; @@ -2245,8 +2251,6 @@ void *player_thread_func(void *arg) { at_least_one_frame_seen = 1; - uint64_t local_time_now = get_absolute_time_in_ns(); // types okay - // This is the timing error for the next audio frame in the DAC, if applicable int64_t sync_error = 0; @@ -2780,21 +2784,32 @@ void *player_thread_func(void *arg) { conn->input_frame_rate = 0.0; } + uint64_t frames_sent_for_play; + int status = -1; if ((config.output->delay) && (config.no_sync == 0) && (config.output->rate_info)) { - uint64_t elapsed_play_time, frames_played; - if (config.output->rate_info(&elapsed_play_time, &frames_played) == 0) - conn->frame_rate_status = 1; - else - conn->frame_rate_status = 0; - if (conn->frame_rate_status) { - conn->frame_rate = - (1.0E9 * frames_played) / - elapsed_play_time; // an IEEE double calculation with two 64-bit integers - } else { - conn->frame_rate = 0.0; + uint64_t elapsed_play_time; // dummy + status = config.output->rate_info(&elapsed_play_time, &frames_sent_for_play); + uint64_t frames_played = frames_sent_for_play - play_samples - current_delay; + // If the status is zero, it means that there were no output problems since the + // last time the rate_info call was made. Thus, the frame rate should be valid. + if ((status == 0) && (previous_frames_played_valid)) { + uint64_t frames_played_in_this_interval = frames_played - previous_frames_played; + uint64_t interval = local_time_now - previous_frames_played_time; + conn->frame_rate = (1e9 * frames_played_in_this_interval) / interval; + conn->frame_rate_valid = 1; } + + // uncomment the if statement if your want to get as long a period for + // calculating the frame rate + // if ((status != 0) || (previous_frames_played_valid == 0)) { + // if we have just detected an outputting error, or if we have no + // starting information + previous_frames_played = frames_played; + previous_frames_played_time = local_time_now; + previous_frames_played_valid = 1; + // } } - + // we can now calculate running averages for sync error (frames), corrections (ppm), // insertions plus deletions (ppm), drift (ppm) double moving_average_sync_error = (1.0 * tsum_of_sync_errors) / number_of_statistics; @@ -2830,7 +2845,10 @@ void *player_thread_func(void *arg) { statistics_item("max buffers", "%*" PRIu32 "", 11, maximum_buffer_occupancy); statistics_item("nominal fps", "%*.2f", 11, conn->remote_frame_rate); statistics_item(" actual fps", "%*.2f", 11, conn->input_frame_rate); - statistics_item(" output fps", "%*.2f", 11, conn->frame_rate); + if (conn->frame_rate_valid) + statistics_item(" output fps", "%*.2f", 11, conn->frame_rate); + else + statistics_item(" output fps", " N/A"); statistics_item("source drift ppm", "%*.2f", 16, (conn->local_to_remote_time_gradient - 1.0) * 1000000); statistics_item("drift samples", "%*d", 13, @@ -2838,7 +2856,7 @@ void *player_thread_func(void *arg) { statistics_item( "estimated (unused) correction ppm", "%*.2f", strlen("estimated (unused) correction ppm"), - (conn->frame_rate > 0.0) + (conn->frame_rate_valid != 0) ? ((conn->frame_rate - conn->remote_frame_rate * conn->output_sample_ratio * conn->local_to_remote_time_gradient) * 1000000) / diff --git a/player.h b/player.h index effd8b12..d5719031 100644 --- a/player.h +++ b/player.h @@ -181,7 +181,7 @@ typedef struct { // for holding the output rate information until printed out at the end of a session double frame_rate; - int frame_rate_status; + int frame_rate_valid; // for holding input rate information until printed out at the end of a session