/*
 * Slave-clocked ALAC stream player. This file is part of Shairport.
 * Copyright (c) James Laird 2011, 2013
 * All rights reserved.
 *
 * Modifications for audio synchronisation, AirPlay 2
 * and related work, copyright (c) Mike Brady 2014--2025
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <math.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/syslog.h>
#include <sys/types.h>
#include <unistd.h>

#include "config.h"

#ifdef CONFIG_MBEDTLS
#include <mbedtls/aes.h>
#endif

#ifdef CONFIG_POLARSSL
#include <polarssl/aes.h>
#include <polarssl/havege.h>
#endif

#ifdef CONFIG_OPENSSL
#include <openssl/aes.h> // needed for older AES stuff
#include <openssl/bio.h> // needed for BIO_new_mem_buf
#include <openssl/err.h> // needed for ERR_error_string, ERR_get_error
#include <openssl/evp.h> // needed for EVP_PKEY_CTX_new, EVP_PKEY_sign_init, EVP_PKEY_sign
#include <openssl/pem.h> // needed for PEM_read_bio_RSAPrivateKey, EVP_PKEY_CTX_set_rsa_padding
#include <openssl/rsa.h> // needed for EVP_PKEY_CTX_set_rsa_padding
#endif

#ifdef CONFIG_SOXR
#include <soxr.h>
#endif

#ifdef CONFIG_CONVOLUTION
#include <FFTConvolver/convolver.h>
#endif

#ifdef CONFIG_METADATA_HUB
#include "metadata_hub.h"
#endif

#ifdef CONFIG_DACP_CLIENT
#include "dacp.h"
#endif

#include "common.h"
#include "mdns.h"
#include "player.h"
#include "rtp.h"
#include "rtsp.h"

#include "alac.h"

#ifdef CONFIG_APPLE_ALAC
#include "apple_alac.h"
#endif

#ifdef CONFIG_AIRPLAY_2
#include "ptp-utilities.h"
#endif

#ifdef CONFIG_FFMPEG
#include <libavutil/version.h>
#endif

#include "loudness.h"

#include "activity_monitor.h"

const unsigned int silent_channel_index = 65;
const unsigned int front_mono_channel_index = 66;

// default buffer size
// needs to be a power of 2 because of the way BUFIDX(seqno) works
// #define BUFFER_FRAMES 512

// DAC buffer occupancy stuff
#define DAC_BUFFER_QUEUE_MINIMUM_LENGTH 2500

// static abuf_t audio_buffer[BUFFER_FRAMES];
#define BUFIDX(seqno) ((seq_t)(seqno) % BUFFER_FRAMES)

void do_flush(uint32_t timestamp, rtsp_conn_info *conn);

#ifdef CONFIG_FFMPEG
size_t avflush(rtsp_conn_info *conn);
#endif

int free_audio_buffer_payload(abuf_t *abuf) {
  int items_freed = 0;
  if (abuf) {
    if (abuf->data != NULL) {
      free(abuf->data);
      items_freed++;
      abuf->data = NULL;
    }
#ifdef CONFIG_FFMPEG
    if (abuf->avframe != NULL) {
      av_frame_free(&abuf->avframe);
      items_freed++;
      abuf->avframe = NULL;
      abuf->ssrc = SSRC_NONE;
    }
#endif
  } else {
    debug(1, "null buffer pointer!");
  }
  return items_freed;
}

void ab_resync(rtsp_conn_info *conn) {
  int i;
  for (i = 0; i < BUFFER_FRAMES; i++) {
    free_audio_buffer_payload(&conn->audio_buffer[i]);
    conn->audio_buffer[i].ready = 0;
    conn->audio_buffer[i].resend_request_number = 0;
    conn->audio_buffer[i].resend_time =
        0; // this is either zero or the time the last resend was requested.
    conn->audio_buffer[i].initialisation_time =
        0; // this is either the time the packet was received or the time it was noticed the packet
           // was missing.
    conn->audio_buffer[i].sequence_number = 0;
  }
  conn->ab_synced = 0;
  conn->last_seqno_valid = 0;
  conn->ab_buffering = 1;
}

void reset_input_flow_metrics(rtsp_conn_info *conn) {
  conn->play_number_after_flush = 0;
  conn->packet_count_since_flush = 0;
  conn->input_frame_rate_starting_point_is_valid = 0;
  conn->initial_reference_time = 0;
  conn->initial_reference_timestamp = 0;
}

void unencrypted_packet_decode(rtsp_conn_info *conn, unsigned char *packet, int length,
                               short *dest) {
  if (conn->stream.type == ast_apple_lossless) {
#ifdef CONFIG_APPLE_ALAC
    if (config.decoder_in_use == 1 << decoder_apple_alac) {
      int frames_decoded;
      apple_alac_decode_frame(packet, length, (unsigned char *)dest, &frames_decoded);
    } else
#endif
#ifdef CONFIG_HAMMERTON
        if (config.decoder_in_use == 1 << decoder_hammerton) {
      int buffer_size = conn->frames_per_packet * conn->input_bytes_per_frame;
      alac_decode_frame(conn->decoder_info, packet, (unsigned char *)dest, &buffer_size);
    } else
#endif
    {
      die("No ALAC decoder included!");
    }
  } else if (conn->stream.type == ast_uncompressed) {
    int i;
    short *source = (short *)packet;
    for (i = 0; i < length / 2; i++) {
      // assuming each input sample is 16 bits.
      *dest = ntohs(*source);
      dest++;
      source++;
    }
  }
}

#ifdef CONFIG_HAMMERTON
static int init_alac_decoder(int32_t fmtp[12], rtsp_conn_info *conn) {

  // clang-format off

  // This is a guess, but the format of the fmtp looks identical to the format of an
  // ALACSpecificCOnfig which is detailed in the file ALACMagicCookieDescription.txt
  // in the Apple ALAC sample implementation
  // Here it is:

  /*

    * ALAC Specific Info (24 bytes) (mandatory)
    __________________________________________________________________________________________________________________________________

    The Apple Lossless codec stores specific information about the encoded stream in the ALACSpecificConfig. This
    info is vended by the encoder and is used to setup the decoder for a given encoded bitstream.

    When read from and written to a file, the fields of this struct must be in big-endian order.
    When vended by the encoder (and received by the decoder) the struct values will be in big-endian order.


        struct      ALACSpecificConfig (defined in ALACAudioTypes.h)
        abstract    This struct is used to describe codec provided information about the encoded Apple Lossless bitstream.
                    It must accompany the encoded stream in the containing audio file and be provided to the decoder.

        field       frameLength             uint32_t        indicating the frames per packet when no explicit frames per packet setting is
                                                            present in the packet header. The encoder frames per packet can be explicitly set
                                                            but for maximum compatibility, the default encoder setting of 4096 should be used.

        field       compatibleVersion       uint8_t         indicating compatible version,
                                                            value must be set to 0

        field       bitDepth                uint8_t         describes the bit depth of the source PCM data (maximum value = 32)

        field       pb                      uint8_t         currently unused tuning parameter.
                                                            value should be set to 40

        field       mb                      uint8_t         currently unused tuning parameter.
                                                            value should be set to 10

        field       kb                      uint8_t         currently unused tuning parameter.
                                                            value should be set to 14

        field       numChannels             uint8_t         describes the channel count (1 = mono, 2 = stereo, etc...)
                                                            when channel layout info is not provided in the 'magic cookie', a channel count > 2
                                                            describes a set of discreet channels with no specific ordering

        field       maxRun                  uint16_t        currently unused.
                                                            value should be set to 255

        field       maxFrameBytes           uint32_t        the maximum size of an Apple Lossless packet within the encoded stream.
                                                            value of 0 indicates unknown

        field       avgBitRate              uint32_t        the average bit rate in bits per second of the Apple Lossless stream.
                                                            value of 0 indicates unknown

        field       sampleRate              uint32_t        sample rate of the encoded stream


    typedef struct ALACSpecificConfig
    {
            uint32_t        frameLength;
            uint8_t         compatibleVersion;
            uint8_t         bitDepth;
            uint8_t         pb;
            uint8_t         mb;
            uint8_t         kb;
            uint8_t         numChannels;
            uint16_t        maxRun;
            uint32_t        maxFrameBytes;
            uint32_t        avgBitRate;
            uint32_t        sampleRate;

    } ALACSpecificConfig;

   */

   // We are going to go on that basis

  // clang-format on

  alac_file *alac;

  alac = alac_create(conn->input_bit_depth,
                     conn->input_num_channels); // no pthread cancellation point in here
  if (!alac)
    return 1;
  conn->decoder_info = alac;

  alac->setinfo_max_samples_per_frame = conn->frames_per_packet;
  alac->setinfo_7a = fmtp[2];
  alac->setinfo_sample_size = conn->input_bit_depth;
  alac->setinfo_rice_historymult = fmtp[4];
  alac->setinfo_rice_initialhistory = fmtp[5];
  alac->setinfo_rice_kmodifier = fmtp[6];
  alac->setinfo_7f = fmtp[7];
  alac->setinfo_80 = fmtp[8];
  alac->setinfo_82 = fmtp[9];
  alac->setinfo_86 = fmtp[10];
  alac->setinfo_8a_rate = fmtp[11];
  alac_allocate_buffers(alac); // no pthread cancellation point in here
  return 0;
}
#endif

static void init_buffer(rtsp_conn_info *conn) {
  int i;
  for (i = 0; i < BUFFER_FRAMES; i++) {
    conn->audio_buffer[i].data = NULL;
#ifdef CONFIG_FFMPEG
    conn->audio_buffer[i].avframe = NULL;
    conn->audio_buffer[i].ssrc = SSRC_NONE;
#endif
  }
}

static void free_audio_buffers(rtsp_conn_info *conn) {
  int i;
  for (i = 0; i < BUFFER_FRAMES; i++) {
    free_audio_buffer_payload(&conn->audio_buffer[i]);
  }
}

int first_possibly_missing_frame = -1;

void reset_buffer(rtsp_conn_info *conn) {
  pthread_cleanup_debug_mutex_lock(&conn->ab_mutex, 30000, 0);
  ab_resync(conn);
  pthread_cleanup_pop(1);
#if CONFIG_FFMPEG
  avflush(conn);
#endif
  if (config.output->flush) {
    config.output->flush(); // no cancellation points
                            //            debug(1, "reset_buffer: flush output device.");
  }
}

// returns the total number of blocks and the number occupied, but not their size,
// because the size is determined by the block size sent

size_t get_audio_buffer_occupancy(rtsp_conn_info *conn) {
  size_t response = 0;
  pthread_cleanup_debug_mutex_lock(&conn->ab_mutex, 30000, 0);
  if (conn->ab_synced) {
    int16_t occ =
        conn->ab_write - conn->ab_read; // will be zero or positive if read and write are within
                                        // 2^15 of each other and write is at or after read
    response = occ;
  }
  pthread_cleanup_pop(1);
  return response;
}

const char *get_category_string(airplay_stream_c cat) {
  char *category;
  switch (cat) {
  case unspecified_stream_category:
    category = "unspecified stream";
    break;
  case ptp_stream:
    category = "PTP stream";
    break;
  case ntp_stream:
    category = "NTP stream";
    break;
  case remote_control_stream:
    category = "Remote Control stream";
    break;
  case classic_airplay_stream:
    category = "Classic AirPlay stream";
    break;
  default:
    category = "Unexpected stream code";
    break;
  }
  return category;
}

#ifdef CONFIG_FFMPEG

static void avcodec_alloc_context3_cleanup_handler(void *arg) {
  debug(3, "avcodec_alloc_context3_cleanup_handler");
  AVCodecContext *codec_context = arg;
  av_free(codec_context);
}

static void avcodec_open2_cleanup_handler(__attribute__((unused)) void *arg) {
  debug(3, "avcodec_open2_cleanup_handler");
  // AVCodecContext *codec_context = arg;
  // avcodec_close(codec_context);
}

static void swr_alloc_cleanup_handler(void *arg) {
  debug(3, "swr_alloc_cleanup_handler");
  SwrContext **swr = arg;
  swr_free(swr);
}

static void av_packet_alloc_cleanup_handler(void *arg) {
  debug(4, "av_packet_alloc_cleanup_handler");
  AVPacket **pkt = arg;
  av_packet_free(pkt);
}

/*
static void av_frame_alloc_cleanup_handler(void *arg) {
  debug(3, "av_frame_alloc_cleanup_handler");
  AVFrame **frame = arg;
  av_frame_free(frame);
}
*/

void clear_decoding_chain(rtsp_conn_info *conn) {
  if (conn->incoming_ssrc != 0) {
    //    debug_mutex_lock(&conn->ab_mutex, 30000, 0);
    //    ab_resync(conn);
    //    debug_mutex_unlock(&conn->ab_mutex, 0);
    pthread_cleanup_push(avcodec_alloc_context3_cleanup_handler, conn->codec_context);
    pthread_cleanup_push(malloc_cleanup, &conn->codec_context->extradata);
    pthread_cleanup_push(avcodec_open2_cleanup_handler, conn->codec_context);
    pthread_cleanup_pop(1); // avcodec_open2_cleanup_handler
    pthread_cleanup_pop(1); // deallocate the malloc
    pthread_cleanup_pop(1); // avcodec_alloc_context3_cleanup_handler
    conn->incoming_ssrc = SSRC_NONE;
  }
}

void clear_software_resampler(rtsp_conn_info *conn) {
  if (conn->swr != NULL) {
    debug(2, "clear_software_resampler");
    pthread_cleanup_push(swr_alloc_cleanup_handler, &conn->swr);
    pthread_cleanup_pop(1); // deallocate the swr
    conn->swr = NULL;
    conn->resampler_ssrc = SSRC_NONE;
  }
}

int ssrc_is_recognised(ssrc_t ssrc) {
  int response = 0;
  switch (ssrc) {
  case ALAC_44100_S16_2:
  case ALAC_48000_S24_2:
  case AAC_44100_F24_2:
  case AAC_48000_F24_2:
  case AAC_48000_F24_5P1:
  case AAC_48000_F24_7P1:
    response = 1;
    break;
  default:
    break;
  }
  return response;
}

int ssrc_is_aac(ssrc_t ssrc) {
  int response = 0;
  switch (ssrc) {
  case AAC_44100_F24_2:
  case AAC_48000_F24_2:
  case AAC_48000_F24_5P1:
  case AAC_48000_F24_7P1:
    response = 1;
    break;
  default:
    break;
  }
  return response;
}

char ssrc_name[1024];
const char *get_ssrc_name(ssrc_t ssrc) {
  const char *response = NULL;
  switch (ssrc) {
  case ALAC_44100_S16_2:
    response = "ALAC/44100/S16_LE/2";
    break;
  case ALAC_48000_S24_2:
    response = "ALAC/48000/S24_LE/2";
    break;
  case AAC_44100_F24_2:
    response = "AAC/44100/F24/2";
    break;
  case AAC_48000_F24_2:
    response = "AAC/48000/F24/2";
    break;
  case AAC_48000_F24_5P1:
    response = "AAC/48000/F24/5.1";
    break;
  case AAC_48000_F24_7P1:
    response = "AAC/48000/F24/7.1";
    break;
  case SSRC_NONE:
    response = "None (0)";
    break;
  default: {
    snprintf(ssrc_name, sizeof(ssrc_name), "<unknown ssrc> (0x%" PRIx32 ")", ssrc);
    response = ssrc_name;
  } break;
  }
  return response;
}

uint32_t get_ssrc_rate(ssrc_t ssrc) {
  uint32_t response = 0;
  switch (ssrc) {
  case ALAC_44100_S16_2:
  case AAC_44100_F24_2:
    response = 44100;
    break;
  case ALAC_48000_S24_2:
  case AAC_48000_F24_2:
  case AAC_48000_F24_5P1:
  case AAC_48000_F24_7P1:
    response = 48000;
    break;
  default:
    break;
  }
  return response;
}

size_t get_ssrc_block_length(ssrc_t ssrc) {
  size_t response = 0;
  switch (ssrc) {
  case ALAC_44100_S16_2:
  case ALAC_48000_S24_2:
    response = 352;
    break;
  case AAC_44100_F24_2:
  case AAC_48000_F24_2:
  case AAC_48000_F24_5P1:
  case AAC_48000_F24_7P1:
    response = 1024;
    break;
  default:
    break;
  }
  return response;
}

int setup_software_resampler(rtsp_conn_info *conn, ssrc_t ssrc) {
  int response = 0;

  unsigned int channels;

  // the output from the software resampler will be the input to the rest of
  // the player chain, so we need to set those parameters according to the SSRC:

  // default values...
  conn->input_bit_depth = 16;
  conn->input_effective_bit_depth = 16;
  conn->input_bytes_per_frame = 4;
  conn->frames_per_packet = 352;

  // most common values first, changed in the switch statement
  conn->input_rate = 48000;
  channels = 2;
  conn->frames_per_packet = 1024;

  sps_format_t suggested_output_format = SPS_FORMAT_S32; // this may be ignored

  switch (ssrc) {
  case ALAC_44100_S16_2:
    conn->input_rate = 44100;
    conn->frames_per_packet = 352;
    suggested_output_format = SPS_FORMAT_S16;
    break;
  case ALAC_48000_S24_2:
    conn->frames_per_packet = 352;
    suggested_output_format = SPS_FORMAT_S24;
    break;
  case AAC_44100_F24_2:
    conn->input_rate = 44100;
    break;
  case AAC_48000_F24_2:
    break;
  case AAC_48000_F24_5P1:
    channels = 6;
    break;
  case AAC_48000_F24_7P1:
    channels = 8;
    break;
  default:
    debug(1, "Can't set rate for %s.", get_ssrc_name(ssrc));
    break;
  }

// Now we ask the backend for its best format, giving it the channels, rate and format

// default format is S32_LE/48000/2 for AP2, S16_LE/44100/2 otherwise
#ifdef CONFIG_AIRPLAY_2
  uint32_t output_configuration = CHANNELS_TO_ENCODED_FORMAT(2) | RATE_TO_ENCODED_FORMAT(48000) |
                                  FORMAT_TO_ENCODED_FORMAT(SPS_FORMAT_S32_LE);
#else
  uint32_t output_configuration = CHANNELS_TO_ENCODED_FORMAT(2) | RATE_TO_ENCODED_FORMAT(44100) |
                                  FORMAT_TO_ENCODED_FORMAT(SPS_FORMAT_S16_LE);
#endif

  int output_configuration_changed = 0;

  if (config.output->get_configuration) {
    output_configuration =
        config.output->get_configuration(channels, conn->input_rate, suggested_output_format);
  }

  // if you can set up a configuration...
  if (output_configuration != 0) {
    if (config.current_output_configuration != output_configuration) {
      output_configuration_changed = 1;
      debug(2, "Connection %d: outgoing audio switching to: %s.", conn->connection_number,
            short_format_description(output_configuration));
    }
    config.current_output_configuration = output_configuration;
    char *output_device_channel_map = NULL;
    if (config.output->configure) {
      config.output->configure(output_configuration, &output_device_channel_map);
    }

    // create a software resampler
    if (conn->swr != NULL) {
      debug(3, "software resampler already set up");
      if (swr_is_initialized(conn->swr)) {
        debug(3, "software resampler already initialised -- close it...");
        swr_close(conn->swr);
      }
      debug(3, "software resampler free it...");
      swr_free(&conn->swr);
      if (conn->swr == NULL) {
        debug(3, "software resampler released");
      }
    }

    // input channels to the player
    conn->input_num_channels = CHANNELS_FROM_ENCODED_FORMAT(output_configuration);

    SwrContext *swr = swr_alloc();
    conn->swr = swr;
    if (swr == NULL) {
      die("can not allocate an swr context");
    }

    // push a deallocator -- av_packet_free(pkt);
    pthread_cleanup_push(swr_alloc_cleanup_handler, &conn->swr);

    enum AVSampleFormat input_format = AV_SAMPLE_FMT_FLTP; // default
    int64_t input_layout = AV_CH_LAYOUT_STEREO;            // default
    int64_t output_layout = AV_CH_LAYOUT_STEREO;           // default

    switch (ssrc) {
    case ALAC_44100_S16_2:
    case ALAC_48000_S24_2: {
      // seems as if the codec_context is correctly set up for ALAC but not for AAC-LC
      input_format = conn->codec_context->sample_fmt;
    } break;
    case AAC_44100_F24_2:
    case AAC_48000_F24_2: {
      // defaults are fine...
    } break;
    case AAC_48000_F24_5P1: {
      input_layout = config.six_channel_layout;
      output_layout = config.six_channel_layout; // assume no mixdown
    } break;
    case AAC_48000_F24_7P1: {
      input_layout = config.eight_channel_layout;
      output_layout = config.eight_channel_layout; // assume no mixdown
    } break;
    default:
      debug(1, "unexpected SSRC: 0x%0x", ssrc);
      break;
    }

    av_opt_set_sample_fmt(swr, "in_sample_fmt", input_format, 0);

    // remember that if mixdown is enabled,
    // set the resampler's channel layout either automatically or use the
    // setting that has been given

#if LIBAVUTIL_VERSION_MAJOR >= 57
    {
      AVChannelLayout input_channel_layout;
      av_channel_layout_from_mask(&input_channel_layout, input_layout);
      av_opt_set_chlayout(swr, "in_chlayout", &input_channel_layout, 0);
      av_channel_layout_uninit(&input_channel_layout);

      AVChannelLayout output_channel_layout;
      if (config.mixdown_enable != 0) {
        if (config.mixdown_channel_layout == 0) {
          av_channel_layout_default(&output_channel_layout,
                                    CHANNELS_FROM_ENCODED_FORMAT(output_configuration));
        } else {
          av_channel_layout_from_mask(&output_channel_layout, config.mixdown_channel_layout);
        }
      } else {
        av_channel_layout_from_mask(&output_channel_layout, output_layout);
      }
      av_opt_set_chlayout(swr, "out_chlayout", &output_channel_layout, 0);
      av_channel_layout_uninit(&output_channel_layout);
    }
#else
    av_opt_set_int(swr, "in_channel_layout", input_layout, 0);
    if (config.mixdown_enable != 0) {
      if (config.mixdown_channel_layout == 0) {
        output_layout =
            av_get_default_channel_layout(CHANNELS_FROM_ENCODED_FORMAT(output_configuration));
      } else {
        output_layout = config.mixdown_channel_layout;
      }
    }
    av_opt_set_int(swr, "out_channel_layout", output_layout, 0); // assume no mixdown
#endif

    av_opt_set_int(swr, "in_sample_rate", conn->input_rate, 0);
    // now set the resampler's output rate to match the output device's rate
    av_opt_set_int(swr, "out_sample_rate", RATE_FROM_ENCODED_FORMAT(output_configuration), 0);

    // Ask for S16 output for AAC/S16 input and for S32 output from resampler for F24 and S24.
    // This is to avoid FFmpeg unnecessarily transcoding S16 to S32.
    // Dither will be added by Shairport Sync itself later, if needed.

    if (ssrc == ALAC_44100_S16_2) {
      av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
      conn->input_bytes_per_frame =
          2 * CHANNELS_FROM_ENCODED_FORMAT(
                  output_configuration); // the output from the decoder will be input to the player
      conn->input_bit_depth = 16;
      conn->input_effective_bit_depth = 16;
    } else {
      av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S32, 0);
      conn->input_bytes_per_frame =
          4 * CHANNELS_FROM_ENCODED_FORMAT(
                  output_configuration); // the output from the decoder will be input to the player
      conn->input_bit_depth = 32;
      // this is important when it comes to deciding on dither
      // AFAIK 24-bit ALAC comes out in 32-bit format but is actually 24 bit
      // so don't dither if it is truncated from 32 to 24 bit
      if (ssrc == ALAC_48000_S24_2)
        conn->input_effective_bit_depth = 24;
      else
        conn->input_effective_bit_depth = 32;
    }

    // now, having set up the resampler, we can initialise it

    // disabling this, as the soxr-based resampler seems not to give exactly the right number of
    // frames going from 44100 to 48000 and requires stuffing to compensate.

    // also, the soxr resampling engine isn't included in the Docker image.
    // #ifdef CONFIG_SOXR
    //      av_opt_set(swr, "resampler", "soxr", 0);
    // #endif
    int sres = swr_init(swr);
    if (sres != 0)
      debug(1, "swr_init returned %d with SSRC of 0x%0x and LIBAVUTIL_VERSION_MAJOR of %u.", sres,
            ssrc, LIBAVUTIL_VERSION_MAJOR);

    typedef struct {
      char *name;
      int allocated;
    } channel_info_t;

    char resampler_channel_list[1024] = "";
    unsigned int c;
    channel_info_t resampler_channels[64]; // can't be more than 64. This will list the channel
                                           // names in the order they appear in the output from
                                           // the software resampler.
    for (c = 0; c < sizeof(resampler_channels) / sizeof(channel_info_t); c++) {
      resampler_channels[c].name = NULL;
      resampler_channels[c].allocated = 0;
    }

    // get information about the output from the resampler
    int64_t resampler_output_format = 0;
    int resampler_channels_found = 0;

#if LIBAVUTIL_VERSION_MAJOR >= 57

    AVChannelLayout output_channel_layout = {0};
    av_opt_get_chlayout(swr, "out_chlayout", 0, &output_channel_layout);
    conn->resampler_output_channels = output_channel_layout.nb_channels;
    for (c = 0; c < 64; c++) {
      enum AVChannel channel = av_channel_layout_channel_from_index(&output_channel_layout, c);
      if (channel != AV_CHAN_NONE) {
        char buffer[32];
        if (av_channel_name(buffer, 32, channel) > 0) {
          if (resampler_channels_found == 0) {
            strcat(resampler_channel_list, "\"");
          } else {
            strcat(resampler_channel_list, "\", \"");
          }
          strcat(resampler_channel_list, buffer);
          resampler_channels[resampler_channels_found].name = strdup(buffer);
          resampler_channels_found++;
        }
      }
    }
    av_channel_layout_uninit(&output_channel_layout);

#else

    int64_t resampler_output_channel_layout = 0;
    {
      int res = av_opt_get_int(swr, "out_channel_layout", 0, &resampler_output_channel_layout);
      if (res == 0) {
        conn->resampler_output_channels =
            (int64_t)av_get_channel_layout_nb_channels((uint64_t)resampler_output_channel_layout);
      } else {
        debug(1, "Error %d getting resampler output channel layout.", res);
      }
    }
    int64_t mask = 1;
    for (c = 0; c < 64; c++) {
      if ((resampler_output_channel_layout & mask) != 0) {
        if (resampler_channels_found == 0) {
          strcat(resampler_channel_list, "\"");
        } else {
          strcat(resampler_channel_list, "\", \"");
        }
        strcat(resampler_channel_list, av_get_channel_name(1 << c));
        resampler_channels[resampler_channels_found].name = strdup(av_get_channel_name(1 << c));
        resampler_channels_found++;
      }
      mask = mask << 1;
    }

#endif

    if (resampler_channels_found != 0) {
      strcat(resampler_channel_list, "\"");
    }

    if (strlen(resampler_channel_list) == 0) {
      debug(3, "resampler output channel list is empty.");
    } else {
      debug(3, "resampler output channel list: %s.", resampler_channel_list);
    }

    if (output_device_channel_map != NULL) {
      debug(3, "output device's channel map is: \"%s\".", output_device_channel_map);
      // free(output_device_channel_map);
      // output_device_channel_map = NULL;
    }
    int output_channel_map_faulty = 0;
    if (resampler_channels_found != 0) {
      // now we have the names of the channels produced by the resampler in the order they
      // appear in the output from the resampler. We need to map them to the channel ordering
      // of the output device.

      // create an output channel list. It will be 64 channels long.
      // It may not have names for all channels.
      // In fact, it will have no names at all if mapping is disabled or set to auto
      // with no device channel map. That will be okay, as unallocated resampler
      // channels will be assigned to unused output channels at the end anyway

      channel_info_t
          output_channels[64]; // can't be more than 64. This will list the output channel names
                               // in the order they appear in the device channel map.
      for (c = 0; c < sizeof(output_channels) / sizeof(channel_info_t); c++) {
        output_channels[c].name = NULL;
        output_channels[c].allocated = 0;
      }

      // if channel mapping is enabled
      if (config.output_channel_mapping_enable != 0) {
        // if a channel map is given
        if (config.output_channel_map_size != 0) {
          for (c = 0; c < config.output_channel_map_size; c++) {
            output_channels[c].name = strdup(config.output_channel_map[c]);
          }
        } else if (output_device_channel_map != NULL) { // if there is a device channel map...
          char *device_channels = strdup(output_device_channel_map);
          char delim[] = " ";
          char *ptr = strtok(device_channels, delim);
          c = 0;
          while (ptr != NULL) {
            output_channels[c].name = strdup(ptr);
            if (strcasecmp(ptr, "UNKNOWN") == 0)
              output_channel_map_faulty = 1;
            ptr = strtok(NULL, delim);
            c++;
          }
          free(device_channels);
        }
      }

      if (output_channel_map_faulty != 0) {
        once(inform("The output device's %u-channel map is incomplete or faulty: \"%s\".",
                    CHANNELS_FROM_ENCODED_FORMAT(config.current_output_configuration),
                    output_device_channel_map));
      }

      // at this point, we should have two arrays
      // the first is all the resampler channels
      // the second is device channel map channels, which may be empty or incomplete

      for (c = 0; c < 64; c++)
        if (resampler_channels[c].name != NULL)
          debug(3, "audio channel %u is \"%s\".", c, resampler_channels[c].name);
      for (c = 0; c < 64; c++)
        if (output_channels[c].name != NULL)
          debug(3, "output device channel %u is \"%s\".", c, output_channels[c].name);

      conn->output_channel_map_size =
          CHANNELS_FROM_ENCODED_FORMAT(config.current_output_configuration);

      // construct a map to match named resampler channels to named output channels

      unsigned int cmi;
      for (cmi = 0; cmi < conn->output_channel_map_size; cmi++) {
        // debug(1,"checking output channel %u, (\"%s\").", cmi, output_channels[cmi].name);
        conn->output_channel_to_resampler_channel_map[cmi] =
            silent_channel_index; // by default the channel is silent
        if ((output_channels[cmi].name != NULL) && (strcmp(output_channels[cmi].name, "--") == 0)) {
          conn->output_channel_to_resampler_channel_map[cmi] = silent_channel_index;
          output_channels[cmi].allocated = 1;
          debug(1, "output device channel %u (\"--\") will be silent.", cmi);
        } else {
          int resampler_channel_index;
          int found = 0;
          for (resampler_channel_index = 0;
               ((resampler_channel_index < resampler_channels_found) && (found == 0));
               resampler_channel_index++) {
            if ((output_channels[cmi].name != NULL) &&
                (resampler_channels[resampler_channel_index].name != NULL) &&
                (strcmp(output_channels[cmi].name,
                        resampler_channels[resampler_channel_index].name) == 0)) {
              conn->output_channel_to_resampler_channel_map[cmi] = resampler_channel_index;
              output_channels[cmi].allocated = 1;
              resampler_channels[resampler_channel_index].allocated = 1;
              found = 1;
              if ((resampler_channels_found > 2) && (output_configuration_changed != 0)) {
                if (output_channel_map_faulty != 0)
                  debug(3, "%s -> %s/%u.", resampler_channels[resampler_channel_index].name,
                        output_channels[cmi].name, cmi);
                else
                  debug(3, "%s -> %s/%u.", resampler_channels[resampler_channel_index].name,
                        output_channels[cmi].name, cmi);
              }
            }
          }
        }
      }

      // now there may be unmapped resampler channels and unallocated output channels
      // allocate them on a first-come-first-served basis

      for (cmi = 0; cmi < conn->output_channel_map_size; cmi++) {
        if (output_channels[cmi].allocated == 0)
          debug(3, "output device channel %u (\"%s\") is unallocated.", cmi,
                output_channels[cmi].name);
      }
      for (c = 0; c < 64; c++) {
        if ((resampler_channels[c].name != NULL) && (resampler_channels[c].allocated == 0))
          debug(3, "audio channel %u (\"%s\") is unmapped.", c, resampler_channels[c].name);
      }

      c = 0; // for indexing through the unmapped resampler channels
      for (cmi = 0; (cmi < conn->output_channel_map_size) && (c < 64); cmi++) {
        if (output_channels[cmi].allocated == 0) {
          do {
            if ((resampler_channels[c].name != NULL) && (resampler_channels[c].allocated == 0)) {
              output_channels[cmi].allocated = 1;
              resampler_channels[c].allocated = 1;
              conn->output_channel_to_resampler_channel_map[cmi] = c;
              if (output_channel_map_faulty != 0)
                debug(3, "%s -> %s/%u.", resampler_channels[c].name, output_channels[cmi].name,
                      cmi);
              else
                debug(3, "%s -> %s/%u.", resampler_channels[c].name, output_channels[cmi].name,
                      cmi);
            } else {
              c++;
            }
          } while ((output_channels[cmi].allocated == 0) && (c < 64));
        }
      }

      if (output_configuration_changed != 0) {
        char channel_mapping_list[256] = "";
        for (c = 0; c < 8; c++) {
          if ((output_channels[c].allocated != 0) &&
              (conn->output_channel_to_resampler_channel_map[c] != silent_channel_index)) {
            char channel_mapping[32] = "";
            if (output_channels[c].name != NULL)
              snprintf(channel_mapping, sizeof(channel_mapping) - 1, " %u (\"%s\") <- %s |", c,
                       output_channels[c].name,
                       resampler_channels[conn->output_channel_to_resampler_channel_map[c]].name);
            else
              snprintf(channel_mapping, sizeof(channel_mapping) - 1, " %u <- %s |", c,
                       resampler_channels[conn->output_channel_to_resampler_channel_map[c]].name);
            strncat(channel_mapping_list, channel_mapping,
                    sizeof(channel_mapping_list) - 1 - strlen(channel_mapping_list));
          }
        }
        debug(1, "Channel Mapping: |%s", channel_mapping_list);
      }

      for (c = 0; c < 64; c++) {
        if (output_channels[c].name != NULL)
          free(output_channels[c].name);
      }
      for (c = 0; c < 64; c++) {
        if (resampler_channels[c].name != NULL)
          free(resampler_channels[c].name);
      }
    }

    {
      int res = av_opt_get_int(swr, "out_sample_fmt", 0, &resampler_output_format);
      if (res == 0) {
        conn->resampler_output_bytes_per_sample = av_get_bytes_per_sample(resampler_output_format);
        debug(3, "resampler output bytes per sample in swr: %d.",
              conn->resampler_output_bytes_per_sample);
      } else {
        debug(1, "Error %d getting resampler output bytes per sample.", res);
      }
    }
    conn->resampler_ssrc = ssrc;

    pthread_cleanup_pop(0); // successful exit -- don't deallocate the swr
  } else {
    debug(1, "Error setting the configuration of the output backend.");
  }
  return response; // 0 if everything is okay
}
void prepare_decoding_chain(rtsp_conn_info *conn, ssrc_t ssrc) {
  if ((ssrc_is_recognised(ssrc)) && (ssrc != conn->incoming_ssrc)) {

    if ((config.statistics_requested != 0) && (ssrc != SSRC_NONE) &&
        (conn->incoming_ssrc != SSRC_NONE)) {
      debug(2, "Connection %d: incoming audio switching to \"%s\".", conn->connection_number,
            get_ssrc_name(ssrc));
#ifdef CONFIG_METADATA
      send_ssnc_metadata('sdsc', get_ssrc_name(ssrc), strlen(get_ssrc_name(ssrc)), 1);
#endif
    }

    // the ssrc of the incoming packet is different to the ssrc of the decoding chain
    // so the decoding chain must be rebuilt

    clear_decoding_chain(conn);
    // create the new decoding chain
    conn->incoming_ssrc = ssrc;
    if (conn->incoming_ssrc != SSRC_NONE) {

      // get a codec
      // ideas and some code from https://rodic.fr/blog/libavcodec-tutorial-decode-audio-file/
      // with thanks

      // Set up the decoder depending on the ssrc code.
      switch (ssrc) {
      case ALAC_44100_S16_2:
      case ALAC_48000_S24_2:
        conn->codec = avcodec_find_decoder(AV_CODEC_ID_ALAC);
        break;
      case AAC_44100_F24_2:
      case AAC_48000_F24_2:
      case AAC_48000_F24_5P1:
      case AAC_48000_F24_7P1:
        conn->codec = avcodec_find_decoder(AV_CODEC_ID_AAC);
        break;
      default:
        die("Can't find a suitable codec for SSRC: %s", get_ssrc_name(ssrc));
        break;
      }

      // Get a decoder-dependent codec context
      conn->codec_context = avcodec_alloc_context3(conn->codec);
      if (conn->codec_context == NULL) {
        debug(1, "Could not allocate a codec context!");
      }
      // push a deallocator -- av_free(codec_context)
      pthread_cleanup_push(avcodec_alloc_context3_cleanup_handler, conn->codec_context);

      // prepare to open the codec context with that codec
      // but first, if it's the ALAC decoder, prepare a magic cookie
      if ((ssrc == ALAC_48000_S24_2) || (ssrc == ALAC_44100_S16_2)) {
        alac_ffmpeg_magic_cookie *extradata =
            malloc(sizeof(alac_ffmpeg_magic_cookie)); // might not use it
        if (extradata == NULL)
          die("Could not allocate memory for a magic cookie.");
        // creata a magic cookie preceded by the 12-byte "atom" (?) expected by FFMPEG (?)
        memset(extradata, 0, sizeof(alac_ffmpeg_magic_cookie));
        extradata->cookie_size = htonl(sizeof(alac_ffmpeg_magic_cookie));
        extradata->cookie_tag = htonl('alac');
        extradata->alac_config.frameLength = htonl(352);
        if (ssrc == ALAC_48000_S24_2) {
          extradata->alac_config.bitDepth = 24; // Seems to be S24
          extradata->alac_config.sampleRate = htonl(48000);
        } else {
          extradata->alac_config.bitDepth = 16; // Seems to be S16
          extradata->alac_config.sampleRate = htonl(44100);
        }
        extradata->alac_config.pb = 40;
        extradata->alac_config.mb = 10;
        extradata->alac_config.kb = 14;
        extradata->alac_config.numChannels = 2;
        extradata->alac_config.maxRun = htons(255);
        conn->codec_context->extradata = (uint8_t *)extradata;
        conn->codec_context->extradata_size = sizeof(alac_ffmpeg_magic_cookie);
      } else {
        conn->codec_context->extradata = NULL;
      }
      pthread_cleanup_push(malloc_cleanup, &conn->codec_context->extradata);

      if (avcodec_open2(conn->codec_context, conn->codec, NULL) < 0) {
        die("Could not initialise the codec context");
      }

      // push a closer -- avcodec_close(codec_context);
      pthread_cleanup_push(avcodec_open2_cleanup_handler, conn->codec_context);

      conn->input_rate = get_ssrc_rate(ssrc);
      if ((ssrc == ALAC_48000_S24_2) || (ssrc == ALAC_44100_S16_2)) {
        conn->frames_per_packet = 352;
      } else {
        conn->frames_per_packet = 1024;
      }

      conn->codec_context->sample_rate = conn->input_rate;

      conn->ffmpeg_decoding_chain_initialised = 1;
      pthread_cleanup_pop(0); // successful exit -- don't run the avcodec_open2_cleanup_handler
      pthread_cleanup_pop(0); // successful exit -- don't deallocate the malloc
      pthread_cleanup_pop(
          0); // successful exit -- don't run the avcodec_alloc_context3_cleanup_handler
    }
  }
}

// take an AV Frame, run it through the swr resampler and map the output to the
// appropriate channels for the output device

// returns the length of time in nanoseconds associated with the frames that are being retained

int64_t avframe_to_audio(rtsp_conn_info *conn, AVFrame *decoded_frame, uint8_t **decoded_audio,
                         size_t *decoded_audio_data_length, size_t *decoded_audio_samples_count) {
  uint8_t *pcm_audio = NULL;
  int dst_linesize;

  int number_of_output_samples_expected = swr_get_out_samples(conn->swr, decoded_frame->nb_samples);

  debug(4, "A maximum of %d output samples expected for %d input samples.",
        number_of_output_samples_expected, decoded_frame->nb_samples);
  // allocate enough space for the required number of output channels
  // and the number of samples decoded
  // the format is always S32
  av_samples_alloc(&pcm_audio, &dst_linesize, conn->resampler_output_channels,
                   number_of_output_samples_expected, AV_SAMPLE_FMT_S32, 0);
  uint64_t conversion_start_time = get_absolute_time_in_ns();
  int samples_generated =
      swr_convert(conn->swr, &pcm_audio, number_of_output_samples_expected,
                  (const uint8_t **)decoded_frame->extended_data, decoded_frame->nb_samples);
  debug(4, "conversion time for %u incoming samples: %.3f milliseconds.", decoded_frame->nb_samples,
        (get_absolute_time_in_ns() - conversion_start_time) * 0.000001);
  if (samples_generated > 0) {
    debug(4, "swr generated %d frames of %" PRId64 " channels.", samples_generated,
          conn->resampler_output_channels);
    // samples_generated will be different from
    // the number of samples input if the output rate is different from the input
    // now, allocate a buffer and transfer the audio into it.

    ssize_t sample_buffer_size = conn->resampler_output_bytes_per_sample *
                                 CHANNELS_FROM_ENCODED_FORMAT(config.current_output_configuration) *
                                 samples_generated;
    void *sample_buffer = malloc(sample_buffer_size);

    memset(sample_buffer, 0, sample_buffer_size); // silence

    unsigned int input_stride = conn->resampler_output_channels;
    unsigned int output_stride = CHANNELS_FROM_ENCODED_FORMAT(config.current_output_configuration);

    unsigned int channels_to_map =
        CHANNELS_FROM_ENCODED_FORMAT(config.current_output_configuration); // the output width
    // if (conn->output_channel_map_size < channels_to_map)
    //  channels_to_map = conn->output_channel_map_size; // or the channel map given

    switch (conn->resampler_output_bytes_per_sample) {
    case 4: {
      int32_t *inframe = (int32_t *)pcm_audio;
      int32_t *outframe = (int32_t *)sample_buffer;
      int i;
      for (i = 0; i < samples_generated; i++) {
        unsigned int j;
        for (j = 0; j < channels_to_map; j++) {
          if (conn->output_channel_to_resampler_channel_map[j] == front_mono_channel_index) {
            // asking for the "FM" channel, which is a made-up name for
            // Front Mono
            int32_t monoValue = (inframe[0] / 2) + (inframe[1] / 2);
            outframe[j] = monoValue;
          } else if (conn->output_channel_to_resampler_channel_map[j] !=
                     silent_channel_index) // means you're asking for the
                                           // silent channel
            outframe[j] = inframe[conn->output_channel_to_resampler_channel_map[j]];
        }
        inframe += input_stride;   // address increment is scaled by the
                                   // size of an int32_t
        outframe += output_stride; // address increment is scaled by the
                                   // size of an int32_t
      }
    } break;
    case 2: {
      int16_t *inframe = (int16_t *)pcm_audio;
      int16_t *outframe = (int16_t *)sample_buffer;
      int i;
      for (i = 0; i < samples_generated; i++) {
        unsigned int j;
        for (j = 0; j < channels_to_map; j++) {
          if (conn->output_channel_to_resampler_channel_map[j] == front_mono_channel_index) {
            // asking for the "FM" channel, which is a made-up name for
            // Front Mono
            int16_t monoValue = (inframe[0] / 2) + (inframe[1] / 2);
            outframe[j] = monoValue;
          } else if (conn->output_channel_to_resampler_channel_map[j] !=
                     silent_channel_index) // means you're asking for the
                                           // silent channel
            outframe[j] = inframe[conn->output_channel_to_resampler_channel_map[j]];
        }
        inframe += input_stride;   // address increment is scaled by the
                                   // size of an int16_t
        outframe += output_stride; // address increment is scaled by the
                                   // size of an int16_t
      }
    } break;
    default:
      debug(1, "resampler output byte size of %u not handled.",
            conn->resampler_output_bytes_per_sample);
      break;
    }

    *decoded_audio = sample_buffer;
    *decoded_audio_data_length = sample_buffer_size;
    *decoded_audio_samples_count = samples_generated;
  } else {
    // samples_generated contains the negative of the error code
    debug(1, "swr_convert error %d. No samples generated from this avframe", -samples_generated);
    *decoded_audio = NULL;
    *decoded_audio_data_length = 0;
    *decoded_audio_samples_count = 0;
  }
  av_freep(&pcm_audio);
  return swr_get_delay(
      conn->swr,
      RATE_FROM_ENCODED_FORMAT(
          config.current_output_configuration)); // number of frames left in the resampler
}

// take a block of incoming data and decode it.
// it might get decoded into fltp ot lpcm or something -- it'll be
// transcoded and maybe resampled later
AVFrame *block_to_avframe(rtsp_conn_info *conn, uint8_t *incoming_data,
                          size_t incoming_data_length) {
  AVFrame *decoded_frame = NULL;
  if (incoming_data_length > 8) {
    AVPacket *pkt = av_packet_alloc();
    if (pkt) {
      // push a deallocator -- av_packet_free(pkt);
      pthread_cleanup_push(av_packet_alloc_cleanup_handler, &pkt);
      pkt->data = incoming_data;
      pkt->size = incoming_data_length;
      int ret = avcodec_send_packet(conn->codec_context, pkt);
      if (ret == 0) {
        decoded_frame = av_frame_alloc();
        if (decoded_frame == NULL) {
          debug(1, "Can't allocate an AVFrame!");
        } else {
          ret = avcodec_receive_frame(conn->codec_context, decoded_frame);

          if (ret < 0) {
            av_frame_free(&decoded_frame);
            decoded_frame = NULL;
            debug(1, "error %d during decoding. Data size: %zd", ret, incoming_data_length);
            /*
                      char *obf = malloc(incoming_data_length * 3);
                      char *obfp = obf;
                      unsigned int obfc;
                      for (obfc = 0; obfc < incoming_data_length; obfc++) {
                        snprintf(obfp, 3, "%02X", incoming_data[obfc]);
                        obfp += 2;
                        if ((obfc & 7) == 7) {
                          snprintf(obfp, 2, " ");
                          obfp += 1;
                        }
                      };
                      *obfp = 0;
                      debug(1, "%s...", obf);
                      free(obf);
            */
          }
        }
      } else {
        debug(1, "error %d during decoding. Gross data size: %zd", ret, incoming_data_length);
        /*
              char obf[128];
              char *obfp = obf;
              int obfc;
              for (obfc = 0; obfc < 32; obfc++) {
                snprintf(obfp, 3, "%02X", incoming_data[obfc]);
                obfp += 2;
                if ((obfc & 7) == 7) {
                  snprintf(obfp, 2, " ");
                  obfp += 1;
                }
              }
              *obfp = 0;
              debug(1, "%s", obf);
        */
      }
      pthread_cleanup_pop(1); // deallocate the AVPacket;
    } else {
      debug(1, "Can't allocate an AVPacket!");
    }
  }
  return decoded_frame;
}

size_t avflush(rtsp_conn_info *conn) {
  size_t response = 0;
  if (conn->swr != NULL) {

    int number_of_output_samples_expected = swr_get_out_samples(conn->swr, 0);
    debug(3, "avflush of %d samples.", number_of_output_samples_expected);
    int ret = swr_init(conn->swr);
    if (ret)
      debug(1, "error %d in swr_init().", ret);
    response = (size_t)number_of_output_samples_expected;
  }
  return response;
}

#endif

#ifdef CONFIG_OPENSSL
// Thanks to
// https://stackoverflow.com/questions/27558625/how-do-i-use-aes-cbc-encrypt-128-openssl-properly-in-ubuntu
// for inspiration. Changed to a 128-bit key and no padding.

int openssl_aes_decrypt_cbc(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,
                            unsigned char *iv, unsigned char *plaintext) {
  EVP_CIPHER_CTX *ctx;
  int len;
  int plaintext_len = 0;
  ctx = EVP_CIPHER_CTX_new();
  if (ctx != NULL) {
    if (EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv) == 1) {
      EVP_CIPHER_CTX_set_padding(ctx, 0); // no padding -- always returns 1
      // no need to allow space for padding in the output, as padding is disabled
      if (EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len) == 1) {
        plaintext_len = len;
        if (EVP_DecryptFinal_ex(ctx, plaintext + len, &len) == 1) {
          plaintext_len += len;
        } else {
          debug(1, "EVP_DecryptFinal_ex error \"%s\".", ERR_error_string(ERR_get_error(), NULL));
        }
      } else {
        debug(1, "EVP_DecryptUpdate error \"%s\".", ERR_error_string(ERR_get_error(), NULL));
      }
    } else {
      debug(1, "EVP_DecryptInit_ex error \"%s\".", ERR_error_string(ERR_get_error(), NULL));
    }
    EVP_CIPHER_CTX_free(ctx);
  } else {
    debug(1, "EVP_CIPHER_CTX_new error \"%s\".", ERR_error_string(ERR_get_error(), NULL));
  }
  return plaintext_len;
}
#endif

#ifdef CONFIG_AIRPLAY_2

#ifdef CONFIG_AIRPLAY_2
// This is a big dirty hack to try to accommodate packets that come in in sequence but are timed to
// be earlier that what went before them. This happens when the feed is switching from AAC to ALAC.
// So basically we will look back through the buffers in the queue until we find the last buffer
// that predates the incoming one. We will make the subsequent buffer the revised_seqno. If we can't
// find an older buffer, that means we can't go back far enough to find an older buffer and then the
// ab_read buffer becomes the revised_seqno.
seq_t get_revised_seqno(rtsp_conn_info *conn, uint32_t timestamp) {
  // go back through the buffers to find the first buffer following a buffer that predates
  // the given timestamp, if any.
  seq_t revised_seqno = conn->ab_write;
  pthread_cleanup_debug_mutex_lock(&conn->ab_mutex, 30000, 0);
  int older_seqno_found = 0;
  while ((older_seqno_found == 0) && (revised_seqno != conn->ab_read)) {
    revised_seqno--;
    abuf_t *tbuf = conn->audio_buffer + BUFIDX(revised_seqno);
    if (tbuf->ready != 0) {
      int32_t timestamp_difference = timestamp - tbuf->timestamp;
      if (timestamp_difference >= 0) {
        older_seqno_found = 1;
      }
    }
  }
  if (older_seqno_found)
    revised_seqno++;

  pthread_cleanup_pop(1);
  return revised_seqno;
}

void clear_buffers_from(rtsp_conn_info *conn, seq_t from_here) {
  seq_t bi = from_here;
  while (bi != conn->ab_write) {
    abuf_t *tbuf = conn->audio_buffer + BUFIDX(bi);
    free_audio_buffer_payload(tbuf);
    bi++;
  }
}

#endif

#endif

#ifdef CONFIG_FFMPEG
uint32_t player_put_packet(uint32_t ssrc, seq_t seqno, uint32_t actual_timestamp, uint8_t *data,
                           size_t len, int mute, int32_t timestamp_gap, rtsp_conn_info *conn) {
#else
uint32_t player_put_packet(uint32_t ssrc, seq_t seqno, uint32_t actual_timestamp, uint8_t *data,
                           size_t len, __attribute__((unused)) int mute, int32_t timestamp_gap,
                           rtsp_conn_info *conn) {
#endif

  // clang-format off
  
  // The timestamp_gap is the difference between the timestamp and the expected timestamp.
  // It should normally be zero.

  // It can be decoded by the Hammerton or Apple ALAC decoders, or by the FFmpeg decoder.

  // The SSRC signifies the encoding used for that block of audio.
  // It is used to select the type of decoding to be done by the FFMPEG-based
  // decoding chain

  // If mute is true, then decode the packet to get its length, but mute it -- i.e.
  // replace it with the same duration of silence.
  // This is useful because the first block of an AAC play sequence usually contains
  // noisy transients.
  // Not needed in Classic airPlay as there's no AAC in it.

  // Function returns the number of samples in the packet so that callers can watch for
  // anomalies in sequencing.
  // clang-format on

  uint32_t input_packets_used = 0;

  // ignore a request to flush that has been made before the first packet...
  if (conn->packet_count == 0) {
    debug_mutex_lock(&conn->flush_mutex, 1000, 4);
    conn->flush_requested = 0;
    conn->flush_rtp_timestamp = 0;
    debug_mutex_unlock(&conn->flush_mutex, 4);
  }

  pthread_cleanup_debug_mutex_lock(&conn->ab_mutex, 30000, 0);
  uint64_t time_now = get_absolute_time_in_ns();
  conn->packet_count++;
  conn->packet_count_since_flush++;
  conn->time_of_last_audio_packet = time_now;
  if (conn->connection_state_to_output) { // if we are supposed to be processing these packets
    abuf_t *abuf = 0;
    if (!conn->ab_synced) {
      conn->ab_write = seqno;
      conn->ab_read = seqno;
      conn->ab_synced = 1;
      conn->first_packet_timestamp = 0;
      debug(2, "Connection %d: synced by first packet, timestamp %u, seqno %u.",
            conn->connection_number, actual_timestamp, seqno);
    }
    if (conn->ab_write ==
        seqno) { // if this is the expected packet (which could be the first packet...)
      if (conn->input_frame_rate_starting_point_is_valid == 0) {
        if ((conn->packet_count_since_flush >= 500) && (conn->packet_count_since_flush <= 510)) {
          conn->frames_inward_measurement_start_time = time_now;
          conn->frames_inward_frames_received_at_measurement_start_time = actual_timestamp;
          conn->input_frame_rate_starting_point_is_valid = 1; // valid now
        }
      }
      conn->frames_inward_measurement_time = time_now;
      conn->frames_inward_frames_received_at_measurement_time = actual_timestamp;
      abuf = conn->audio_buffer + BUFIDX(seqno);
      conn->ab_write = seqno + 1; // move the write pointer to the next free space
    } else {
      int16_t after_ab_write_gap = seqno - conn->ab_write;
      if (after_ab_write_gap > 0) {
        int i;
        for (i = 0; i < after_ab_write_gap; i++) {
          abuf = conn->audio_buffer + BUFIDX(conn->ab_write + i);
          abuf->ready = 0; // to be sure, to be sure
          abuf->resend_request_number = 0;
          abuf->initialisation_time =
              time_now;          // this represents when the packet was noticed to be missing
          abuf->status = 1 << 0; // signifying missing
          abuf->resend_time = 0;
          abuf->timestamp = 0;
          abuf->sequence_number = 0;
        }
        abuf = conn->audio_buffer + BUFIDX(seqno);
        //        rtp_request_resend(ab_write, gap);
        //        resend_requests++;
        conn->ab_write = seqno + 1;
      } else {
        int16_t after_ab_read_gap = seqno - conn->ab_read;
        if (after_ab_read_gap >= 0) { // older than expected but not too late
          debug(3, "buffer %u is older than expected but not too late", seqno);
          conn->late_packets++;
          abuf = conn->audio_buffer + BUFIDX(seqno);
        } else { // too late.
          debug(3, "buffer %u is too late", seqno);
          conn->too_late_packets++;
        }
      }
    }
    if (abuf) {
      if (free_audio_buffer_payload(abuf)) {
        if (seqno == abuf->sequence_number)
          debug(3, "audio block %u received for a second (or more) time?", seqno);
        else
          debug(3, "audio block %u with prior sequence number %u  -- payload not freed until now!",
                seqno, abuf->sequence_number);
      }
      abuf->initialisation_time = time_now;
      abuf->resend_time = 0;
      abuf->length = 0; // may not be needed

      if (ssrc == ALAC_44100_S16_2) {
        // This could be a Classic AirPlay or an AirPlay 2 Realtime packet.
        // It always has a length of 352 frames per packet.
        // And it's always 16-bit interleaved stereo.
        uint8_t *data_to_use = data;
        uint8_t *intermediate_buffer = malloc(len); // encryption is not compression...

        // decrypt it if necessary
        if (conn->stream.encrypted) {
          unsigned char iv[16];
          int aeslen = len & ~0xf;
          memcpy(iv, conn->stream.aesiv, sizeof(iv));
#ifdef CONFIG_MBEDTLS
          mbedtls_aes_crypt_cbc(&conn->dctx, MBEDTLS_AES_DECRYPT, aeslen, iv, data,
                                intermediate_buffer);
#endif
#ifdef CONFIG_POLARSSL
          aes_crypt_cbc(&conn->dctx, AES_DECRYPT, aeslen, iv, data, intermediate_buffer);
#endif
#ifdef CONFIG_OPENSSL
          openssl_aes_decrypt_cbc(data, aeslen, conn->stream.aeskey, iv, intermediate_buffer);
          // AES_cbc_encrypt(data, intermediate_buffer, aeslen, &conn->aes, iv, AES_DECRYPT);
#endif
          memcpy(intermediate_buffer + aeslen, data + aeslen, len - aeslen);
          data_to_use = intermediate_buffer;
        }

        // Use the selected decoder
        if ((config.decoder_in_use == 1 << decoder_hammerton) ||
            (config.decoder_in_use == 1 << decoder_apple_alac)) {
          abuf->data = malloc(conn->frames_per_packet * conn->input_bytes_per_frame);
          if (abuf->data != NULL) {
            unencrypted_packet_decode(conn, data_to_use, len, abuf->data);
            input_packets_used = conn->frames_per_packet; // return this to the caller
            abuf->length = conn->frames_per_packet;       // these decoders don't transcode
          } else {
            debug(1, "audio block not allocated!");
          }
        } else if (config.decoder_in_use == 1 << decoder_ffmpeg_alac) {
#ifdef CONFIG_FFMPEG
          prepare_decoding_chain(conn, ALAC_44100_S16_2);
          // if (len > 8) {
            abuf->avframe = block_to_avframe(conn, data_to_use, len);
            abuf->ssrc = ALAC_44100_S16_2;
            if (abuf->avframe) {
              input_packets_used = abuf->avframe->nb_samples;
            }
            if (mute) {
              // it's important to have already run it through the decoder before dropping it
              // especially if it an AAC decoder
              debug(2, "ap1 muting frame %u.", actual_timestamp);
              abuf->length = abuf->avframe->nb_samples;
              av_frame_free(&abuf->avframe);
              abuf->avframe = NULL;
            }
          // } else {
        if (len <= 8) {
          debug(1,
                "Using the FFMPEG ALAC_44100_S16_2 decoder, a short audio packet %u, rtptime %u, of length %zu has been decoded but not discarded. Contents follow:", seqno,
                actual_timestamp, len);
          debug_print_buffer(1, data, len);
            // abuf->length = conn->frames_per_packet;
            // abuf->avframe = NULL;
          }
#else
          debug(1, "FFMPEG support has not been built into this version Shairport Sync!");
#endif
        } else {
          debug(1, "Unknown decoder!");
        }

        // may be used during decryption
        if (intermediate_buffer != NULL) {
          free(intermediate_buffer);
          intermediate_buffer = NULL;
        }

        abuf->ready = 1;
        abuf->status = 0; // signifying that it was received
        abuf->timestamp = actual_timestamp;
        abuf->timestamp_gap = timestamp_gap; // needed to decide if a resync is needed
        abuf->sequence_number = seqno;
      } else {
        // This is AirPlay 2 -- always use FFmpeg
#ifdef CONFIG_FFMPEG

        // Use the appropriate FFMPEG decoder

        // decoding is done now, transcoding to S32 and resampling is
        // deferred to the player thread, to be sure all the blocks
        // of data are present

        prepare_decoding_chain(conn, ssrc); // dynamically set the decoding environment

        // if (len > 8) {
          abuf->avframe = block_to_avframe(conn, data, len);
          abuf->ssrc = ssrc; // tag the avframe with its specific SSRC
          if (abuf->avframe) {
            input_packets_used = abuf->avframe->nb_samples;
          }
          if (mute) {
            // it's important to have already run it through the decoder before dropping it
            debug(2, "ap2 muting frame %u.", actual_timestamp);
            abuf->length = abuf->avframe->nb_samples;
            av_frame_free(&abuf->avframe);
            abuf->avframe = NULL;
          }
        //} else {
        if (len <= 8) {
          debug(1,
                "Using an FFMPEG decoder, a short audio packet %u, rtptime %u, of length %zu has been decoded but not discarded. Contents follow:", seqno,
                actual_timestamp, len);
          debug_print_buffer(1, data, len);
          // abuf->length = 0;
          // abuf->avframe = NULL;
        }
        abuf->ready = 1;
        abuf->status = 0; // signifying that it was received
        abuf->timestamp = actual_timestamp;
        abuf->timestamp_gap = timestamp_gap;
        abuf->sequence_number = seqno;
#else
        debug(1, "FFMPEG support has not been included, so the packet is discarded.");
        abuf->ready = 0;
        abuf->status = 1 << 1; // bad packet, discarded
        abuf->resend_request_number = 0;
        abuf->timestamp = 0;
        abuf->timestamp_gap = 0;
        abuf->sequence_number = 0;
#endif
      }
    }
    /*
    {
    uint64_t the_time_this_frame_should_be_played;
                  frame_to_local_time(abuf->timestamp,
                                      &the_time_this_frame_should_be_played, conn);
    int64_t lead_time = the_time_this_frame_should_be_played - get_absolute_time_in_ns();
    debug(1, "put_packet %u, lead time is %.3f ms.", abuf->timestamp, lead_time * 0.000001);
    }
    */
    int rc = pthread_cond_signal(&conn->flowcontrol);
    if (rc)
      debug(1, "Error signalling flowcontrol.");

    // resend checks
    {
      uint64_t minimum_wait_time =
          (uint64_t)(config.resend_control_first_check_time * (uint64_t)1000000000);
      uint64_t resend_repeat_interval =
          (uint64_t)(config.resend_control_check_interval_time * (uint64_t)1000000000);
      uint64_t minimum_remaining_time = (uint64_t)((config.resend_control_last_check_time +
                                                    config.audio_backend_buffer_desired_length) *
                                                   (uint64_t)1000000000);
      uint64_t latency_time = (uint64_t)(conn->latency * (uint64_t)1000000000);
      latency_time = latency_time / (uint64_t)conn->input_rate;

      // find the first frame that is missing, if known
      int x = conn->ab_read;
      if (first_possibly_missing_frame >= 0) {
        // if it's within the range
        int16_t buffer_size = conn->ab_write - conn->ab_read; // must be positive
        if (buffer_size >= 0) {
          int16_t position_in_buffer = first_possibly_missing_frame - conn->ab_read;
          if ((position_in_buffer >= 0) && (position_in_buffer < buffer_size))
            x = first_possibly_missing_frame;
        }
      }

      first_possibly_missing_frame = -1; // has not been set

      int missing_frame_run_count = 0;
      int start_of_missing_frame_run = -1;
      int number_of_missing_frames = 0;
      while (x != conn->ab_write) {
        abuf_t *check_buf = conn->audio_buffer + BUFIDX(x);
        if (!check_buf->ready) {
          if (first_possibly_missing_frame < 0)
            first_possibly_missing_frame = x;
          number_of_missing_frames++;
          // debug(1, "frame %u's initialisation_time is 0x%" PRIx64 ", latency_time is 0x%"
          // PRIx64 ", time_now is 0x%" PRIx64 ", minimum_remaining_time is 0x%" PRIx64 ".", x,
          // check_buf->initialisation_time, latency_time, time_now, minimum_remaining_time);
          int too_late = ((check_buf->initialisation_time < (time_now - latency_time)) ||
                          ((check_buf->initialisation_time - (time_now - latency_time)) <
                           minimum_remaining_time));
          int too_early = ((time_now - check_buf->initialisation_time) < minimum_wait_time);
          int too_soon_after_last_request =
              ((check_buf->resend_time != 0) &&
               ((time_now - check_buf->resend_time) <
                resend_repeat_interval)); // time_now can never be less than the time_tag

          if (too_late)
            check_buf->status |= 1 << 2; // too late
          else
            check_buf->status &= 0xFF - (1 << 2); // not too late
          if (too_early)
            check_buf->status |= 1 << 3; // too early
          else
            check_buf->status &= 0xFF - (1 << 3); // not too early
          if (too_soon_after_last_request)
            check_buf->status |= 1 << 4; // too soon after last request
          else
            check_buf->status &= 0xFF - (1 << 4); // not too soon after last request

          if ((!too_soon_after_last_request) && (!too_late) && (!too_early)) {
            if (start_of_missing_frame_run == -1) {
              start_of_missing_frame_run = x;
              missing_frame_run_count = 1;
            } else {
              missing_frame_run_count++;
            }
            check_buf->resend_time = time_now; // setting the time to now because we are
                                               // definitely going to take action
            check_buf->resend_request_number++;
            debug(3, "Frame %d is missing with ab_read of %u and ab_write of %u.", x, conn->ab_read,
                  conn->ab_write);
          }
          // if (too_late) {
          //   debug(1,"too late to get missing frame %u.", x);
          // }
        }
        // if (number_of_missing_frames != 0)
        //  debug(1,"check with x = %u, ab_read = %u, ab_write = %u, first_possibly_missing_frame
        //  = %d.", x, conn->ab_read, conn->ab_write, first_possibly_missing_frame);
        x = (x + 1) & 0xffff;
        if (((check_buf->ready) || (x == conn->ab_write)) && (missing_frame_run_count > 0)) {
          // send a resend request
          if (missing_frame_run_count > 1)
            debug(3, "request resend of %d packets starting at seqno %u.", missing_frame_run_count,
                  start_of_missing_frame_run);
          if (config.disable_resend_requests == 0) {
            // debug_mutex_unlock(&conn->ab_mutex, 3);
            rtp_request_resend(start_of_missing_frame_run, missing_frame_run_count, conn);
            // debug_mutex_lock(&conn->ab_mutex, 20000, 1);
            conn->resend_requests++;
          }
          start_of_missing_frame_run = -1;
          missing_frame_run_count = 0;
        }
      }
      if (number_of_missing_frames == 0)
        first_possibly_missing_frame = conn->ab_write;
    }
  }
  pthread_cleanup_pop(1);
  // debug_mutex_unlock(&conn->ab_mutex, 0);
  return input_packets_used;
}

int32_t rand_in_range(int32_t exclusive_range_limit) {
  static uint32_t lcg_prev = 12345;
  // returns a pseudo random integer in the range 0 to (exclusive_range_limit-1) inclusive
  int64_t sp = lcg_prev;
  int64_t rl = exclusive_range_limit;
  lcg_prev = lcg_prev * 69069 + 3; // crappy psrg
  sp = sp * rl; // 64 bit calculation. Interesting part is above the 32 rightmost bits;
  return sp >> 32;
}

static inline void process_sample(int32_t sample, char **outp, sps_format_t format, int volume,
                                  int dither, rtsp_conn_info *conn) {
  int64_t hyper_sample = sample;
  int result = 0;

  if (conn->do_loudness != 0) {
    hyper_sample <<=
        32; // Do not apply volume as it has already been done with the Loudness DSP filter
  } else {
    int64_t hyper_volume = (int64_t)volume << 16;
    hyper_sample = hyper_sample * hyper_volume; // this is 64 bit bit multiplication -- we may need
                                                // to dither it down to its target resolution
  }

  // next, do dither, if necessary
  if (dither) {

    // Add a TPDF dither -- see
    // http://educypedia.karadimov.info/library/DitherExplained.pdf
    // and the discussion around https://www.hydrogenaud.io/forums/index.php?showtopic=16963&st=25

    // I think, for a 32 --> 16 bits, the range of
    // random numbers needs to be from -2^16 to 2^16, i.e. from -65536 to 65536 inclusive, not from
    // -32768 to +32767

    // Actually, what would be generated here is from -65535 to 65535, i.e. one less on the limits.

    // See the original paper at
    // http://www.ece.rochester.edu/courses/ECE472/resources/Papers/Lipshitz_1992.pdf
    // by Lipshitz, Wannamaker and Vanderkooy, 1992.

    int64_t dither_mask = 0;
    switch (format) {
    case SPS_FORMAT_S32:
    case SPS_FORMAT_S32_LE:
    case SPS_FORMAT_S32_BE:
      dither_mask = (int64_t)1 << (64 - 32);
      break;
    case SPS_FORMAT_S24:
    case SPS_FORMAT_S24_LE:
    case SPS_FORMAT_S24_BE:
    case SPS_FORMAT_S24_3LE:
    case SPS_FORMAT_S24_3BE:
      dither_mask = (int64_t)1 << (64 - 24);
      break;
    case SPS_FORMAT_S16:
    case SPS_FORMAT_S16_LE:
    case SPS_FORMAT_S16_BE:
      dither_mask = (int64_t)1 << (64 - 16);
      break;
    case SPS_FORMAT_S8:
    case SPS_FORMAT_U8:
      dither_mask = (int64_t)1 << (64 - 8);
      break;
    case SPS_FORMAT_UNKNOWN:
      die("Unexpected SPS_FORMAT_UNKNOWN while calculating dither mask.");
      break;
    case SPS_FORMAT_AUTO:
      die("Unexpected SPS_FORMAT_AUTO while calculating dither mask.");
      break;
    case SPS_FORMAT_INVALID:
      die("Unexpected SPS_FORMAT_INVALID while calculating dither mask.");
      break;
    }
    dither_mask -= 1;
    int64_t r = r64i();

    int64_t tpdf = (r & dither_mask) - (conn->previous_random_number & dither_mask);
    conn->previous_random_number = r;
    // add dither, allowing for clipping

    if (tpdf >= 0) {
      if (INT64_MAX - tpdf >= hyper_sample)
        hyper_sample += tpdf;
      else
        hyper_sample = INT64_MAX;
    } else {
      if (INT64_MIN - tpdf <= hyper_sample)
        hyper_sample += tpdf;
      else
        hyper_sample = INT64_MIN;
    }
    // dither is complete here
  }

  // move the result to the desired position in the int64_t
  char *op = *outp;
  uint8_t byt;
  switch (format) {
  case SPS_FORMAT_S32_LE:
    hyper_sample >>= (64 - 32);
    byt = (uint8_t)hyper_sample;
    *op++ = byt;
    byt = (uint8_t)(hyper_sample >> 8);
    *op++ = byt;
    byt = (uint8_t)(hyper_sample >> 16);
    *op++ = byt;
    byt = (uint8_t)(hyper_sample >> 24);
    *op++ = byt;
    result = 4;
    break;
  case SPS_FORMAT_S32_BE:
    hyper_sample >>= (64 - 32);
    byt = (uint8_t)(hyper_sample >> 24);
    *op++ = byt;
    byt = (uint8_t)(hyper_sample >> 16);
    *op++ = byt;
    byt = (uint8_t)(hyper_sample >> 8);
    *op++ = byt;
    byt = (uint8_t)hyper_sample;
    *op++ = byt;
    result = 4;
    break;
  case SPS_FORMAT_S32:
    hyper_sample >>= (64 - 32);
    *(int32_t *)op = hyper_sample;
    result = 4;
    break;
  case SPS_FORMAT_S24_3LE:
    hyper_sample >>= (64 - 24);
    byt = (uint8_t)hyper_sample;
    *op++ = byt;
    byt = (uint8_t)(hyper_sample >> 8);
    *op++ = byt;
    byt = (uint8_t)(hyper_sample >> 16);
    *op++ = byt;
    result = 3;
    break;
  case SPS_FORMAT_S24_3BE:
    hyper_sample >>= (64 - 24);
    byt = (uint8_t)(hyper_sample >> 16);
    *op++ = byt;
    byt = (uint8_t)(hyper_sample >> 8);
    *op++ = byt;
    byt = (uint8_t)hyper_sample;
    *op++ = byt;
    result = 3;
    break;
  case SPS_FORMAT_S24_LE:
    hyper_sample >>= (64 - 24);
    byt = (uint8_t)hyper_sample;
    *op++ = byt;
    byt = (uint8_t)(hyper_sample >> 8);
    *op++ = byt;
    byt = (uint8_t)(hyper_sample >> 16);
    *op++ = byt;
    *op++ = 0;
    result = 4;
    break;
  case SPS_FORMAT_S24_BE:
    hyper_sample >>= (64 - 24);
    *op++ = 0;
    byt = (uint8_t)(hyper_sample >> 16);
    *op++ = byt;
    byt = (uint8_t)(hyper_sample >> 8);
    *op++ = byt;
    byt = (uint8_t)hyper_sample;
    *op++ = byt;
    result = 4;
    break;
  case SPS_FORMAT_S24:
    hyper_sample >>= (64 - 24);
    *(int32_t *)op = hyper_sample;
    result = 4;
    break;
  case SPS_FORMAT_S16_LE:
    hyper_sample >>= (64 - 16);
    byt = (uint8_t)hyper_sample;
    *op++ = byt;
    byt = (uint8_t)(hyper_sample >> 8);
    *op++ = byt;
    result = 2;
    break;
  case SPS_FORMAT_S16_BE:
    hyper_sample >>= (64 - 16);
    byt = (uint8_t)(hyper_sample >> 8);
    *op++ = byt;
    byt = (uint8_t)hyper_sample;
    *op++ = byt;
    result = 2;
    break;
  case SPS_FORMAT_S16:
    hyper_sample >>= (64 - 16);
    *(int16_t *)op = (int16_t)hyper_sample;
    result = 2;
    break;
  case SPS_FORMAT_S8:
    hyper_sample >>= (int8_t)(64 - 8);
    *op = hyper_sample;
    result = 1;
    break;
  case SPS_FORMAT_U8:
    hyper_sample >>= (uint8_t)(64 - 8);
    hyper_sample += 128;
    *op = hyper_sample;
    result = 1;
    break;
  case SPS_FORMAT_UNKNOWN:
    die("Unexpected SPS_FORMAT_UNKNOWN while outputting samples");
    break;
  case SPS_FORMAT_AUTO:
    die("Unexpected SPS_FORMAT_AUTO while outputting samples");
    break;
  case SPS_FORMAT_INVALID:
    die("Unexpected SPS_FORMAT_INVALID while outputting samples");
    break;
  }

  *outp += result;
}

void buffer_get_frame_cleanup_handler(__attribute__((unused)) void *arg) {
  // rtsp_conn_info *conn = (rtsp_conn_info *)arg;
  // debug_mutex_unlock(&conn->ab_mutex, 0);
}

// get the next frame, when available. return 0 if underrun/stream reset.
static abuf_t *buffer_get_frame(rtsp_conn_info *conn, int resync_requested) {
  // int16_t buf_fill;
  // struct timespec tn;

  /*
  {
    abuf_t *curframe = conn->audio_buffer + BUFIDX(conn->ab_read);
    if (curframe != NULL) {
      debug(1, "get seqno %u with ready: %u.", curframe->sequence_number, curframe->ready);
    }
  }
  */

  abuf_t *curframe = NULL;
  int notified_buffer_empty = 0; // diagnostic only

  pthread_cleanup_debug_mutex_lock(&conn->ab_mutex, 30000, 0);

  int wait;
  long dac_delay = 0; // long because alsa returns a long

  int output_device_has_been_primed =
      0; // set to true when we have sent at least one silent frame to the DAC

  pthread_cleanup_push(buffer_get_frame_cleanup_handler,
                       (void *)conn); // undo what's been done so far
  do {
    // debug(3, "buffer_get_frame is iterating");
    // we must have timing information before we can do anything here
    if (have_timestamp_timing_information(conn)) {

      int rco = get_requested_connection_state_to_output();

      if (conn->connection_state_to_output != rco) {
        conn->connection_state_to_output = rco;
        // change happening
        if (conn->connection_state_to_output == 0) { // going off
          debug(2, "request flush because connection_state_to_output is off");
          debug_mutex_lock(&conn->flush_mutex, 1000, 1);
          conn->flush_requested = 1;
          conn->flush_rtp_timestamp = 0;
          debug_mutex_unlock(&conn->flush_mutex, 3);
        }
      }

      if (config.output->is_running)
        if (config.output->is_running() != 0) { // if the back end isn't running for any reason
          debug(2, "request flush because back end is not running");
          debug_mutex_lock(&conn->flush_mutex, 1000, 0);
          conn->flush_requested = 1;
          conn->flush_rtp_timestamp = 0;
          debug_mutex_unlock(&conn->flush_mutex, 0);
        }

      pthread_cleanup_debug_mutex_lock(&conn->flush_mutex, 1000, 0);
      if (conn->flush_requested == 1) {
        if (conn->flush_output_flushed == 0) {
#if CONFIG_FFMPEG
          avflush(conn);
#endif
          if (config.output->flush) {
            config.output->flush(); // no cancellation points
            debug(2, "flush request: flush output device.");
          }
        }
        conn->flush_output_flushed = 1;
      }
      // now check to see it the flush request is for frames in the buffer or not
      // if the first_packet_timestamp is zero, don't check
      int flush_needed = 0;
      int drop_request = 0;
      if (conn->flush_requested == 1) {
        if (conn->flush_rtp_timestamp == 0) {
          debug(1, "flush request: flush frame 0 -- flush assumed to be needed.");
          flush_needed = 1;
          drop_request = 1;
        } else {
          if ((conn->ab_synced) && ((conn->ab_write - conn->ab_read) > 0)) {
            abuf_t *firstPacket = conn->audio_buffer + BUFIDX(conn->ab_read);
            abuf_t *lastPacket = conn->audio_buffer + BUFIDX(conn->ab_write - 1);
            if ((firstPacket != NULL) && (firstPacket->ready)) {
              uint32_t first_frame_in_buffer = firstPacket->timestamp;
              int32_t offset_from_first_frame = conn->flush_rtp_timestamp - first_frame_in_buffer;
              if ((lastPacket != NULL) && (lastPacket->ready)) {
                // we have enough information to check if the flush is needed or can be discarded
                uint32_t last_frame_in_buffer = lastPacket->timestamp + lastPacket->length - 1;

                // clang-format off
                // Now we have to work out if the flush frame is in the buffer.
                
                // If it is later than the end of the buffer, flush everything and keep the
                // request active.
                
                // If it is in the buffer, we need to flush part of the buffer.
                // (Actually we flush the entire buffer and drop the request.)
                
                // If it is before the buffer, no flush is needed. Drop the request.
                // clang-format on

                if (offset_from_first_frame > 0) {
                  int32_t offset_to_last_frame = last_frame_in_buffer - conn->flush_rtp_timestamp;
                  if (offset_to_last_frame >= 0) {
                    debug(2,
                          "flush request: flush frame %u active -- buffer contains %u frames, from "
                          "%u to %u.",
                          conn->flush_rtp_timestamp,
                          last_frame_in_buffer - first_frame_in_buffer + 1, first_frame_in_buffer,
                          last_frame_in_buffer);

                    // We need to drop all complete frames leading up to the frame containing
                    // the flush request frame.
                    int32_t offset_to_flush_frame = 0;
                    abuf_t *current_packet = NULL;
                    do {
                      current_packet = conn->audio_buffer + BUFIDX(conn->ab_read);
                      if (current_packet != NULL) {
                        uint32_t last_frame_in_current_packet =
                            current_packet->timestamp + current_packet->length - 1;
                        offset_to_flush_frame =
                            conn->flush_rtp_timestamp - last_frame_in_current_packet;
                        if (offset_to_flush_frame > 0) {
                          debug(2,
                                "flush to %u request: flush buffer %u, from "
                                "%u to %zu. ab_write is: %u.",
                                conn->flush_rtp_timestamp, conn->ab_read, current_packet->timestamp,
                                current_packet->timestamp + current_packet->length - 1,
                                conn->ab_write);
                          conn->ab_read++;
                        }
                      } else {
                        debug(1, "NULL current_packet");
                      }
                      pthread_testcancel(); // even if no packets are coming in...
                    } while ((current_packet == NULL) || (offset_to_flush_frame > 0));
                    // now remove any frames from the buffer that are before the flush frame itself.
                    int32_t frames_to_remove =
                        conn->flush_rtp_timestamp - current_packet->timestamp;
                    if (frames_to_remove > 0) {
                      debug(2, "%u frames to remove from current buffer", frames_to_remove);
                      void *dest = (void *)current_packet->data;
                      void *source = dest + conn->input_bytes_per_frame * frames_to_remove;
                      size_t frames_remaining = (current_packet->length - frames_to_remove);
                      memmove(dest, source, frames_remaining * conn->input_bytes_per_frame);
                      current_packet->timestamp = conn->flush_rtp_timestamp;
                      current_packet->length = frames_remaining;
                    }
                    debug(
                        2,
                        "flush request: flush frame %u complete -- buffer contains %u frames, from "
                        "%u to %u -- flushed to %u in buffer %u, with %u frames remaining.",
                        conn->flush_rtp_timestamp, last_frame_in_buffer - first_frame_in_buffer + 1,
                        first_frame_in_buffer, last_frame_in_buffer, current_packet->timestamp,
                        conn->ab_read, last_frame_in_buffer - current_packet->timestamp + 1);
                    drop_request = 1;
                  } else {
                    if (conn->flush_rtp_timestamp == last_frame_in_buffer + 1) {
                      debug(
                          2,
                          "flush request: flush frame %u completed -- buffer contained %u frames, "
                          "from "
                          "%u to %u",
                          conn->flush_rtp_timestamp,
                          last_frame_in_buffer - first_frame_in_buffer + 1, first_frame_in_buffer,
                          last_frame_in_buffer);
                      drop_request = 1;
                    } else {
                      debug(2,
                            "flush request: flush frame %u pending -- buffer contains %u frames, "
                            "from "
                            "%u to %u",
                            conn->flush_rtp_timestamp,
                            last_frame_in_buffer - first_frame_in_buffer + 1, first_frame_in_buffer,
                            last_frame_in_buffer);
                    }
                    flush_needed = 1;
                  }
                } else {
                  debug(2,
                        "flush request: flush frame %u expired -- buffer contains %u frames, "
                        "from %u "
                        "to %u",
                        conn->flush_rtp_timestamp, last_frame_in_buffer - first_frame_in_buffer + 1,
                        first_frame_in_buffer, last_frame_in_buffer);
                  drop_request = 1;
                }
              }
            }
          } else {
            debug(3,
                  "flush request: flush frame %u  -- buffer not synced or empty: synced: %d, "
                  "ab_read: "
                  "%u, ab_write: %u",
                  conn->flush_rtp_timestamp, conn->ab_synced, conn->ab_read, conn->ab_write);
            conn->flush_requested = 0; // remove the request
            // leave flush request pending and don't do a buffer flush, because there isn't one
          }
        }
      }
      if (flush_needed) {
        debug(2, "flush request: flush done.");
        ab_resync(conn); // no cancellation points
        conn->first_packet_timestamp = 0;
        conn->first_packet_time_to_play = 0;
        conn->time_since_play_started = 0;
        output_device_has_been_primed = 0;
        dac_delay = 0;
      }
      if (drop_request) {
        conn->flush_requested = 0;
        conn->flush_rtp_timestamp = 0;
        conn->flush_output_flushed = 0;
      }
      pthread_cleanup_pop(1); // unlock the conn->flush_mutex

      // skip out-of-date frames, and even more if we haven't seen the first frame
      int out_of_date = 1;
      uint32_t should_be_frame;

      uint64_t time_to_aim_for = get_absolute_time_in_ns();
      uint64_t desired_lead_time = 0;
      if (conn->first_packet_timestamp == 0)
        time_to_aim_for = time_to_aim_for + desired_lead_time;

      while ((conn->ab_synced) && ((conn->ab_write - conn->ab_read) > 0) && (out_of_date != 0)) {
        abuf_t *thePacket = conn->audio_buffer + BUFIDX(conn->ab_read);
        if ((thePacket != NULL) && (thePacket->ready)) {
          local_time_to_frame(time_to_aim_for, &should_be_frame, conn);
          // debug(1,"should_be frame is %u.",should_be_frame);
          int32_t frame_difference = thePacket->timestamp - should_be_frame;
          if (frame_difference < 0) {
            debug(3,
                  "Connection %d: dropping-out-of-date packet %u with timestamp %u. Lead time is "
                  "%f seconds.",
                  conn->connection_number, conn->ab_read, thePacket->timestamp,
                  frame_difference * 1.0 / conn->input_rate + desired_lead_time * 0.000000001);
            free_audio_buffer_payload(thePacket);
            conn->last_seqno_read = conn->ab_read;
            conn->ab_read++;
          } else {
            if (conn->first_packet_timestamp == 0)
              debug(3,
                    "Connection %d: accepting packet sequence number %u, ab_read: %u with "
                    "timestamp %u. Lead time is %f seconds.",
                    conn->connection_number, thePacket->sequence_number, conn->ab_read,
                    thePacket->timestamp,
                    frame_difference * 1.0 / conn->input_rate + desired_lead_time * 0.000000001);
            out_of_date = 0;
          }
        } else {
          if (thePacket == NULL)
            debug(2, "Connection %d: packet %u is empty.", conn->connection_number, conn->ab_read);
          else
            debug(3, "Connection %d: packet %u not ready.", conn->connection_number, conn->ab_read);
          conn->ab_read++;
          conn->last_seqno_read++; // don' let it trigger the missing packet warning...
        }
        pthread_testcancel(); // even if no packets are coming in...
      }
      int16_t buffers_available = conn->ab_write - conn->ab_read;

      if ((conn->ab_synced) && (buffers_available > 0)) {
        curframe = conn->audio_buffer + BUFIDX(conn->ab_read);
        if (resync_requested != 0) {
          /*
          if (((curframe != NULL) && ((conn->first_packet_timestamp != curframe->timestamp) &&
                                      (curframe->timestamp_gap < 0))) ||
              (resync_requested != 0)) {
            // ignore a timestamp gap that occurs before the first_packet_timestamp
            if (curframe == NULL)
              debug(1, "Connection %d: reset first_packet_timestamp because curframe is NULL.",
                    conn->connection_number);
            if (curframe->timestamp_gap != 0)
              debug(1,
                    "Connection %d: reset first_packet_timestamp because curframe %u's timestamp_gap
          is negative: "
                    "%d.",
                    conn->connection_number, curframe->timestamp, curframe->timestamp_gap);
            if (resync_requested != 0)
          */
          debug(2, "Connection %d: reset first_packet_timestamp resync_requested.",
                conn->connection_number);
          conn->ab_buffering = 1;
          conn->first_packet_timestamp = 0;
          conn->first_packet_time_to_play = 0;
          output_device_has_been_primed = 1; // so that it can rely on the delay provided by it
        }

        if (conn->ab_synced) {

          if (curframe != NULL) {
            uint64_t should_be_time;
            frame_to_local_time(curframe->timestamp, &should_be_time, conn);
            int64_t time_difference = should_be_time - get_absolute_time_in_ns();
            debug(4, "Check packet from buffer %u, timestamp %u, %f seconds ahead.", conn->ab_read,
                  curframe->timestamp, 0.000000001 * time_difference);
          } else {
            debug(3, "Check packet from buffer %u, empty.", conn->ab_read);
          }

          if ((conn->ab_read != conn->ab_write) &&
              (curframe->ready)) { // it could be synced and empty, under
                                   // exceptional circumstances, with the
                                   // frame unused, thus apparently ready

            if (curframe->sequence_number != conn->ab_read) {
              // some kind of sync problem has occurred.
              if (BUFIDX(curframe->sequence_number) == BUFIDX(conn->ab_read)) {
                // it looks like aliasing has happened
                // jump to the new incoming stuff...
                conn->ab_read = curframe->sequence_number;
                debug(1, "Connection %d: aliasing of buffer index -- reset.",
                      conn->connection_number);
              } else {
                debug(1, "Connection %d: inconsistent sequence numbers detected",
                      conn->connection_number);
              }
            }
          }

          if ((curframe) && (curframe->ready)) {
            notified_buffer_empty = 0; // at least one buffer now -- diagnostic only.
            if (conn->ab_buffering) {  // if we are getting packets but not yet forwarding them to
                                       // the player
              if (conn->first_packet_timestamp == 0) { // if this is the very first packet
                conn->first_packet_timestamp =
                    curframe->timestamp; // we will keep buffering until we are
                                         // supposed to start playing this
                debug(2, "Connection %d: first packet timestamp is %u.", conn->connection_number,
                      conn->first_packet_timestamp);

                // Even though it'll be some time before the first frame will be output
                // (and thus some time before the resampling chain is needed),
                // we need to set up the output device to correspond to
                // the input format w.r.t. rate, depth and channels
                // because we'll be sending silence before the first real frame.
                debug(3, "reset loudness filters.");
                loudness_reset();
#ifdef CONFIG_FFMPEG
                // Set up the output chain, including the software resampler.
                debug(2, "set up the output chain to %s for FFmpeg.",
                      get_ssrc_name(curframe->ssrc));
                setup_software_resampler(conn, curframe->ssrc);
                conn->output_sample_ratio = 1; // it's always 1 if we're using FFmpeg
#else
                // here, in the non-FFmpeg decoder case, we have the first frame, so
                // we should set up the output device now.
                debug(3,
                      "set up the output chain for the non-FFmpeg case with incoming audio at %u "
                      "FPS.",
                      conn->input_rate);

                // ask the backend if it can give us its best choice for a non-ffmpeg configuration:
                if (config.output->get_configuration) {
                  config.current_output_configuration = config.output->get_configuration(
                      2, conn->input_rate, (unsigned int)(SPS_FORMAT_S16));
                } else {
                  // otherwise, use the standard 44100/S16_LE/2 for non-ffmpeg operation
                  config.current_output_configuration = CHANNELS_TO_ENCODED_FORMAT(2) |
                                                        RATE_TO_ENCODED_FORMAT(44100) |
                                                        FORMAT_TO_ENCODED_FORMAT(SPS_FORMAT_S16_LE);
                }

                // tell the output device, if possible
                if (config.output->configure) {
                  config.output->configure(config.current_output_configuration, NULL);
                }

                if (conn->input_rate == 0)
                  debug(1, "input rate not set!");
                else
                  conn->output_sample_ratio =
                      RATE_FROM_ENCODED_FORMAT(config.current_output_configuration) /
                      conn->input_rate;

#endif
                // calculate the output bit depth
                conn->output_bit_depth = 16; // default;
                switch (FORMAT_FROM_ENCODED_FORMAT(config.current_output_configuration)) {
                case SPS_FORMAT_S8:
                case SPS_FORMAT_U8:
                  conn->output_bit_depth = 8;
                  break;
                case SPS_FORMAT_S16:
                case SPS_FORMAT_S16_LE:
                case SPS_FORMAT_S16_BE:
                  conn->output_bit_depth = 16;
                  break;
                case SPS_FORMAT_S24:
                case SPS_FORMAT_S24_LE:
                case SPS_FORMAT_S24_BE:
                case SPS_FORMAT_S24_3LE:
                case SPS_FORMAT_S24_3BE:
                  conn->output_bit_depth = 24;
                  break;
                case SPS_FORMAT_S32:
                case SPS_FORMAT_S32_LE:
                case SPS_FORMAT_S32_BE:
                  conn->output_bit_depth = 32;
                  break;
                case SPS_FORMAT_UNKNOWN:
                  die("An unknown format was encountered while choosing output bit depth. Please "
                      "check your configuration and settings.");
                  break;
                case SPS_FORMAT_AUTO:
                  die("Invalid format -- SPS_FORMAT_AUTO -- choosing output bit depth. Please "
                      "check your configuration and settings.");
                  break;
                case SPS_FORMAT_INVALID:
                  die("Invalid format -- SPS_FORMAT_INVALID -- choosing output bit depth. Please "
                      "check your configuration and settings.");
                  break;
                }
                debug(3, "output bit depth is %u.", conn->output_bit_depth);

                uint64_t should_be_time;
                frame_to_local_time(conn->first_packet_timestamp, // this will go modulo 2^32
                                    &should_be_time, conn);

                conn->first_packet_time_to_play = should_be_time;

                int64_t lt = conn->first_packet_time_to_play - get_absolute_time_in_ns();

                // can't be too late because we skipped late packets already, FLW.
                debug(2, "Connection %d: lead time for first frame %u: %f seconds.",
                      conn->connection_number, conn->first_packet_timestamp, lt * 0.000000001);
              }

              if (conn->first_packet_time_to_play != 0) {
                // Now that we know the timing of the first packet...
                if (config.output->delay) {
                  // and that the output device is capable of synchronization...

                  // We may send packets of
                  // silence from now until the time the first audio packet should be sent
                  // and then we will send the first packet, which will be followed by
                  // the subsequent packets.
                  // here, we figure out whether and what silence to send.

                  uint64_t should_be_time;

                  // readjust first packet time to play
                  frame_to_local_time(conn->first_packet_timestamp, &should_be_time, conn);

                  int64_t change_in_should_be_time =
                      (int64_t)(should_be_time - conn->first_packet_time_to_play);

                  if (fabs(0.000001 * change_in_should_be_time) >
                      0.001) // ignore this unless if's more than a microsecond
                    debug(
                        2,
                        "Change in estimated first_packet_time: %f milliseconds for first_packet.",
                        0.000001 * change_in_should_be_time);

                  conn->first_packet_time_to_play = should_be_time;

                  int64_t lead_time = conn->first_packet_time_to_play -
                                      get_absolute_time_in_ns(); // negative means late
                  if (lead_time < 0) {
                    debug(2, "Gone past starting time for %u by %" PRId64 " nanoseconds.",
                          conn->first_packet_timestamp, -lead_time);
                    conn->ab_buffering = 0;
                  } else {
                    // do some calculations
                    if ((config.audio_backend_silent_lead_in_time_auto == 1) ||
                        (lead_time <= (int64_t)(config.audio_backend_silent_lead_in_time *
                                                (int64_t)1000000000))) {
                      debug(3, "Lead time: %" PRId64 " nanoseconds.", lead_time);
                      int resp = 0;
                      dac_delay = 0;
                      if (output_device_has_been_primed != 0)
                        resp = config.output->delay(
                            &dac_delay); // we know the output device must have a delay function
                      if (resp == 0) {
                        int64_t gross_frame_gap =
                            ((conn->first_packet_time_to_play - get_absolute_time_in_ns()) *
                             RATE_FROM_ENCODED_FORMAT(config.current_output_configuration)) /
                            1000000000;
                        int64_t exact_frame_gap = gross_frame_gap - dac_delay;
                        debug(3,
                              "Exact frame gap: %" PRId64
                              ". DAC delay: %ld. First packet timestamp: %u.",
                              exact_frame_gap, dac_delay, conn->first_packet_timestamp);
                        // int64_t frames_needed_to_maintain_desired_buffer =
                        //     (int64_t)(config.audio_backend_buffer_desired_length *
                        //               config.current_output_configuration->rate) -
                        //     dac_delay;
                        // below, remember that exact_frame_gap and
                        // frames_needed_to_maintain_desired_buffer could both be negative
                        int64_t fs =
                            (RATE_FROM_ENCODED_FORMAT(config.current_output_configuration) * 100) /
                            1000; // 100 milliseconds
                        // if there isn't enough time to have the desired buffer size
                        // if (exact_frame_gap <= fs) {
                        //  fs = conn->frames_per_packet * 2;
                        // }
                        // if we are close to the end of buffering,
                        // just add the remaining silence needed and end buffering
                        if (exact_frame_gap < fs) {
                          debug(3, "exact frame below fs of %" PRId64 " frames.", fs);
                          fs = exact_frame_gap;
                          conn->ab_buffering = 0;
                        }
                        void *silence;
                        if (fs > 0) {
                          silence = malloc(
                              sps_format_sample_size(
                                  FORMAT_FROM_ENCODED_FORMAT(config.current_output_configuration)) *
                              CHANNELS_FROM_ENCODED_FORMAT(config.current_output_configuration) *
                              fs);
                          if (silence == NULL)
                            debug(1, "Failed to allocate %" PRId64 " byte silence buffer.", fs);
                          else {
                            // generate frames of silence with dither if necessary
                            pthread_cleanup_push(malloc_cleanup, &silence);

                            conn->previous_random_number = generate_zero_frames(
                                silence, fs, conn->enable_dither, conn->previous_random_number,
                                config.current_output_configuration);

                            debug(3, "Send %" PRId64 " frames of silence.", fs);
                            config.output->play(silence, fs, play_samples_are_untimed, 0, 0);
                            debug(3, "Sent %" PRId64 " frames of silence.", fs);
                            pthread_cleanup_pop(1); // deallocate silence
                            output_device_has_been_primed = 1;
                          }
                        }
                      } else {
                        if ((resp == -EAGAIN) || (resp == -EIO) || (resp == -ENODEV)) {
                          debug(2, "delay() information not (yet, hopefully!) available.");
                        } else {
                          debug(1, "delay() error %d: \"%s\".", -resp, strerror(-resp));
                        }
                        if (resp == sps_extra_code_output_stalled) {
                          if (config.unfixable_error_reported == 0) {
                            config.unfixable_error_reported = 1;
                            if (config.cmd_unfixable) {
                              command_execute(config.cmd_unfixable, "output_device_stalled", 1);
                            } else {
                              die("an unrecoverable error, \"output_device_stalled\", has been "
                                  "detected.");
                            }
                          }
                        } else {
                          debug(3, "Unexpected response to getting dac delay: %d.", resp);
                        }
                      }
                    }
                  }
                } else {
                  // if the output device doesn't have a delay, we simply send the lead-in
                  int64_t lead_time = conn->first_packet_time_to_play -
                                      get_absolute_time_in_ns(); // negative if we are late
                  void *silence;
                  int64_t frame_gap =
                      (lead_time * RATE_FROM_ENCODED_FORMAT(config.current_output_configuration)) /
                      1000000000;
                  // debug(1,"%d frames needed.",frame_gap);
                  while (frame_gap > 0) {
                    int64_t fs = RATE_FROM_ENCODED_FORMAT(config.current_output_configuration) / 10;

                    if (fs > frame_gap)
                      fs = frame_gap;

                    silence = malloc(
                        sps_format_sample_size(
                            FORMAT_FROM_ENCODED_FORMAT(config.current_output_configuration)) *
                        CHANNELS_FROM_ENCODED_FORMAT(config.current_output_configuration) * fs);
                    if (silence == NULL)
                      debug(1, "Failed to allocate %" PRId64 " frame silence buffer.", fs);
                    else {
                      // debug(1, "No delay function -- outputting %d frames of silence.", fs);
                      pthread_cleanup_push(malloc_cleanup, &silence);
                      conn->previous_random_number = generate_zero_frames(
                          silence, fs, conn->enable_dither, conn->previous_random_number,
                          config.current_output_configuration);
                      config.output->play(silence, fs, play_samples_are_untimed, 0, 0);
                      pthread_cleanup_pop(1); // deallocate silence
                    }
                    frame_gap -= fs;
                  }
                  conn->ab_buffering = 0;
                }
              }
#ifdef CONFIG_METADATA
              if (conn->ab_buffering == 0) {
                if ((curframe) && (curframe->ready) && (curframe->timestamp))
                  debug(3, "Current frame timestamp at \"resume\" is %u.", curframe->timestamp);
                else
                  debug(1, "Current frame at \"resume\" is not known.");

                send_ssnc_metadata('prsm', NULL, 0,
                                   0); // "resume", but don't wait if the queue is locked
              }
#endif
            }
          }
        }
      } else {
        // if (conn->ab_synced)
        // debug(1, "no buffers available at seqno %u.", conn->ab_read);
      }

      // Here, we work out whether to release a packet or wait
      // We release a packet when the time is right.

      // To work out when the time is right, we need to take account of (1) the actual time the
      // packet should be released, (2) the latency requested, (3) the audio backend latency offset
      // and (4) the desired length of the audio backend's buffer

      // The time is right if the current time is later or the same as
      // The packet time + (latency + latency offset - backend_buffer_length).
      // Note: the last three items are expressed in frames and must be converted to time.

      int do_wait = 0; // don't wait unless we can really prove we must
      if ((conn->ab_synced) && (curframe) && (curframe->ready) && (curframe->timestamp)) {
        do_wait = 1; // if the current frame exists and is ready, then wait unless it's time to let
                     // it go...

        // here, get the time to play the current frame.

        if (have_timestamp_timing_information(conn)) { // if we have a reference time

          uint64_t time_to_play;

          // we must enable packets to be released early enough for the
          // audio buffer to be filled to the desired length

          uint32_t desired_buffer_latency =
              (uint32_t)(config.audio_backend_buffer_desired_length * conn->input_rate);
          frame_to_local_time(curframe->timestamp - desired_buffer_latency, &time_to_play, conn);
          uint64_t current_buffer_delay = 0;
          int resp = -1;
          if (config.output->delay) {
            long l_delay;
            resp = config.output->delay(&l_delay);
            if (resp == 0) { // no error
              if (l_delay >= 0)
                current_buffer_delay = l_delay;
              else {
                debug(2, "Underrun of %ld frames reported, but ignored.", l_delay);
                current_buffer_delay =
                    0; // could get a negative value if there was underrun, but ignore it.
              }
            }
          }
          // If it's the first packet, or we don't have a working delay() function in the backend,
          // then wait until it's time to play it
          if ((((conn->first_packet_timestamp == curframe->timestamp) || (resp != 0)) &&
               (get_absolute_time_in_ns() >= time_to_play)) ||
              // Otherwise, if it isn't the first packet and we have a valid delay from the backend,
              // ensure the buffer stays nearly full
              ((conn->first_packet_timestamp != curframe->timestamp) && (resp == 0) &&
               (current_buffer_delay < desired_buffer_latency))) {
            do_wait = 0;
          }
          // here, do a sanity check. if the time_to_play is not within a few seconds of the
          // time now, the frame is probably not meant to be there, so let it go.
          if (do_wait != 0) {
            // this is a hack.
            // we subtract two 2^n unsigned numbers and get a signed 2^n result.
            // If we think of the calculation as occurring in modulo 2^n arithmetic
            // then the signed result's magnitude represents the shorter distance around
            // the modulo wheel of values from one number to the other.
            // The sign indicates the direction: positive means clockwise (upwards) from the
            // second number to the first (i.e. the first number comes "after" the second).

            int64_t time_difference = get_absolute_time_in_ns() - time_to_play;
            if ((time_difference > 10000000000) || (time_difference < -10000000000)) {
              debug(2,
                    "crazy time interval of %f seconds between time now: 0x%" PRIx64
                    " and time of packet: %" PRIx64 ".",
                    0.000000001 * time_difference, get_absolute_time_in_ns(), time_to_play);
              debug(2, "packet rtptime: %u, reference_timestamp: %u", curframe->timestamp,
                    conn->anchor_rtptime);

              do_wait = 0; // let it go
            }
          }
        }
      }
      if (do_wait == 0)
        // wait if the buffer is empty
        if ((conn->ab_synced != 0) && (conn->ab_read == conn->ab_write)) { // the buffer is empty!
          if (notified_buffer_empty == 0) {
            debug(4, "Connection %d: Buffer Empty", conn->connection_number);
            notified_buffer_empty = 1;
            // reset_input_flow_metrics(conn); // don't do a full flush parameters reset
            // conn->initial_reference_time = 0;
            // conn->initial_reference_timestamp = 0;
            // conn->first_packet_timestamp = 0; // make sure the first packet isn't late
          }
          do_wait = 1;
        }
      wait = (conn->ab_buffering || (do_wait != 0) || (!conn->ab_synced));
    } else {
      wait = 1; // keep waiting until the timing information becomes available
    }
    if (wait) {
      if (conn->frames_per_packet == 0)
        debug(1, "frames_per_packet is zero!");

      uint64_t time_to_wait_for_wakeup_ns = 20000000; // default if no input rate is set
      if (conn->input_rate != 0) {
        time_to_wait_for_wakeup_ns =
            1000000000 / conn->input_rate; // this is time period of one frame
        time_to_wait_for_wakeup_ns *= 4 * conn->frames_per_packet;
      }

#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
      uint64_t time_of_wakeup_ns = get_realtime_in_ns() + time_to_wait_for_wakeup_ns;
      uint64_t sec = time_of_wakeup_ns / 1000000000;
      uint64_t nsec = time_of_wakeup_ns % 1000000000;

      struct timespec time_of_wakeup;
      time_of_wakeup.tv_sec = sec;
      time_of_wakeup.tv_nsec = nsec;

      int rc = pthread_cond_timedwait(&conn->flowcontrol, &conn->ab_mutex,
                                      &time_of_wakeup); // this is a pthread cancellation point
      if ((rc != 0) && (rc != ETIMEDOUT))
        // if (rc)
        debug(3, "pthread_cond_timedwait returned error code %d.", rc);
#endif
#ifdef COMPILE_FOR_OSX
      uint64_t sec = time_to_wait_for_wakeup_ns / 1000000000;
      uint64_t nsec = time_to_wait_for_wakeup_ns % 1000000000;
      struct timespec time_to_wait;
      time_to_wait.tv_sec = sec;
      time_to_wait.tv_nsec = nsec;
      pthread_cond_timedwait_relative_np(&conn->flowcontrol, &conn->ab_mutex, &time_to_wait);
#endif
    }
  } while (wait);

  // seq_t read = conn->ab_read;
  if (curframe) {
    if (!curframe->ready) {
      // debug(1, "Supplying a silent frame for frame %u", read);
      conn->missing_packets++;
      curframe->timestamp = 0; // indicate a silent frame should be substituted
    }
    curframe->ready = 0;
  }
  conn->ab_read++;

  pthread_cleanup_pop(1); // unlock the ab_mutex
  pthread_cleanup_pop(1); // buffer_get_frame_cleanup_handler
  // debug(1, "Release frame %u.", curframe->timestamp);
#ifdef CONFIG_FFMPEG

#ifndef CONFIG_AIRPLAY_2
  if (config.decoder_in_use == 1 << decoder_ffmpeg_alac) {
#endif
    // clang-format off
    // If we're using the Hammerton or ALAC decoder, then curframe->data will 
    // point to a malloced buffer of the stereo interleaved LPCM/44100/S16/2 audio
    // But here, we must be using the FFMPEG decoder.
    // With the FFmpeg decoder we have an AVFrame in curframe->avframe.
    // The format could be anything -- it'll be transcoded here and placed in
    // malloc memory pointed to by curframe->data and the AVFrame will be freed.
    // If the avframe is NULL, then the length will be the number of frames of silence
    // to be inserted into the audio stream to replace an AVFrame of the same length
    // that is to be muted. Phew.
    // clang-format on

    if (curframe) {
      if (conn->resampler_ssrc != curframe->ssrc) {
        if (conn->resampler_ssrc == SSRC_NONE) {
          debug(2, "setting up software resampler for %s for the first time.",
                get_ssrc_name(curframe->ssrc));
        } else {
          debug(1, "Connection %d: queued audio buffers switching to \"%s\".", conn->connection_number,
                get_ssrc_name(curframe->ssrc));
          clear_software_resampler(conn);
          // ask the backend if it can give us its best choice for an ffmpeg configuration:
        }
        debug(3, "setup software resampler for %s", get_ssrc_name(curframe->ssrc));
        if (curframe->ssrc != SSRC_NONE) {
          setup_software_resampler(conn, curframe->ssrc);
        } else {
          debug(1, "attempt to setup_software_resampler for SSRC_NONE");
        }
      }
      size_t number_of_output_frames;
      uint8_t *pp;
      size_t pl;
      if (curframe->avframe) {
        conn->frames_retained_in_the_resampler =
            avframe_to_audio(conn, curframe->avframe, &pp, &pl, &number_of_output_frames);
        curframe->data = (short *)pp;
        curframe->length = number_of_output_frames;
        av_frame_free(&curframe->avframe);
        curframe->avframe = NULL;
      } else if (curframe->length != 0) {
        // if there's no data and no avframe, then the length is
        // the number of frames of silence requested.
        int ret = swr_inject_silence(conn->swr, curframe->length); // hardwired, ugh!
        if (ret)
          debug(1, "error %d", ret);
        // We need to get those frames of silence out of the resampler
        // so we'll pass in an empty AVFrame to flush them through
        AVFrame *avf = av_frame_alloc(); // empty frame
        conn->frames_retained_in_the_resampler =
            avframe_to_audio(conn, avf, &pp, &pl, &number_of_output_frames);
        curframe->data = (short *)pp;
        curframe->length = number_of_output_frames;
        av_frame_free(&avf);
      }
    }
#ifndef CONFIG_AIRPLAY_2
  }
#endif

#endif

#ifdef CONFIG_METADATA
  if ((curframe != NULL) && (conn->first_packet_timestamp) &&
      (conn->first_packet_timestamp == curframe->timestamp)) {
    char buffer[32];
    memset(buffer, 0, sizeof(buffer));
    // if this is not a resumption after a discontinuity,
    // say we have started receiving frames here
    if (curframe->timestamp_gap == 0) {
      snprintf(buffer, sizeof(buffer), "%" PRIu32 "/%" PRIu64 "", curframe->timestamp,
               conn->first_packet_time_to_play);
      send_ssnc_metadata('pffr', buffer, strlen(buffer),
                         0); // "first frame received", but don't wait if the queue is locked
      debug(3, "pffr: \"%s\"", buffer);
    } else {
      // otherwise, say a discontinuity occurred
      snprintf(buffer, sizeof(buffer), "%" PRIu32 "/%" PRId32 "", curframe->timestamp,
               curframe->timestamp_gap);
      send_ssnc_metadata(
          'pdis', buffer, strlen(buffer),
          0); // "a discontinuity of this many frames", but don't wait if the queue is locked
      debug(3, "pdis: \"%s\"", buffer);
    }
  }
#endif

  if (curframe) {
    // check sequencing
    if (conn->last_seqno_valid == 0) {
      conn->last_seqno_valid = 1;
      conn->last_seqno_read = curframe->sequence_number;
    } else {
      conn->last_seqno_read++;
      if (curframe->sequence_number != conn->last_seqno_read) {
        debug(1,
              "Player: packets out of sequence: expected: %u, got: %u, with ab_read: %u "
              "and ab_write: %u.",
              conn->last_seqno_read, curframe->sequence_number, conn->ab_read, conn->ab_write);
        conn->last_seqno_read = curframe->sequence_number; // reset warning...
      }
    }
  }

  return curframe;
}

static inline int32_t mean_32(int32_t a, int32_t b) {
  int64_t al = a;
  int64_t bl = b;
  int64_t mean = (al + bl) / 2;
  int32_t r = (int32_t)mean;
  if (r != mean)
    debug(1, "Error calculating average of two int32_t values: %d, %d.", a, b);
  return r;
}

// this takes an array of channels of signed 32-bit integers and
// (a) removes or inserts a frame as specified in "stuff",
// (b) multiplies each sample by the fixedvolume (a 16-bit quantity)
// (c) dithers the result to the output size 32/24/16/8 bits
// (d) outputs the result in the approprate format
// formats accepted include U8, S8, S16, S24, S24_3LE, S24_3BE and S32

// can only accept a plus or minus 1
// stuff: 1 means add 1; 0 means do nothing; -1 means remove 1
static int stuff_buffer_basic_32(int32_t *inptr, int length, sps_format_t l_output_format,
                                 char *outptr, int stuff, int dither, rtsp_conn_info *conn) {
  int tstuff = 0;
  if (length >= 3) {
    tstuff = stuff;
    if (tstuff)
      debug(3, "stuff_buffer_basic_32 %+d.", tstuff);
    char *l_outptr = outptr;
    if (stuff > 1)
      stuff = 1;
    if (stuff < -1)
      stuff = -1;
    if ((stuff > 1) || (stuff < -1) || (length < 100)) {
      // debug(1, "Stuff argument to stuff_buffer must be from -1 to +1 and length >100.");
      tstuff = 0; // if any of these conditions hold, don't stuff anything/
    }

    int i;
    int stuffsamp = length;
    if (tstuff)
      //      stuffsamp = rand() % (length - 1);
      stuffsamp =
          (rand() % (length - 2)) + 1; // ensure there's always a sample before and after the item

    for (i = 0; i < stuffsamp; i++) { // the whole frame, if no stuffing
      unsigned int channel;
      for (channel = 0; channel < conn->input_num_channels; channel++)
        process_sample(*inptr++, &l_outptr, l_output_format, conn->fix_volume, dither, conn);
    };
    if (tstuff) {
      if (tstuff == 1) {
        // debug(3, "+++++++++");
        // interpolate one sample
        unsigned int channel;
        for (channel = 0; channel < conn->input_num_channels; channel++)
          process_sample(mean_32(inptr[-2], inptr[0]), &l_outptr, l_output_format, conn->fix_volume,
                         dither, conn);
      } else if (stuff == -1) {
        // debug(3, "---------");
        unsigned int channel;
        for (channel = 0; channel < conn->input_num_channels; channel++)
          inptr++;
      }

      // if you're removing, i.e. stuff < 0, copy that much less over. If you're adding, do all the
      // rest.
      int remainder = length;
      if (tstuff < 0)
        remainder = remainder + tstuff; // don't run over the correct end of the output buffer

      for (i = stuffsamp; i < remainder; i++) {
        unsigned int channel;
        for (channel = 0; channel < conn->input_num_channels; channel++)
          process_sample(*inptr++, &l_outptr, l_output_format, conn->fix_volume, dither, conn);
      }
    }
  }
  return length + tstuff;
}

// this takes an array of channels of n signed 32-bit integers and
// (a) replaces all of them with channels of n+stuff (+/-1) signed 32-bit integers,
// by first order interpolation.
// (b) multiplies each sample by the fixedvolume (a 16-bit quantity)
// (c) dithers the result to the output size 32/24/16/8 bits
// (d) outputs the result in the approprate format
// formats accepted include U8, S8, S16, S24, S24_3LE, S24_3BE and S32

// stuff: 1 means add 1; 0 means do nothing; -1 means remove 1

static int stuff_buffer_vernier(int32_t *inptr, int length, sps_format_t l_output_format,
                                char *outptr, int stuff, int dither, rtsp_conn_info *conn) {
  int tstuff = 0;
  if (length >= 3) {
    tstuff = stuff;
    if ((stuff > INTERPOLATION_LIMIT) || (stuff < -INTERPOLATION_LIMIT) || (length < 100)) {
      debug(2,
            "Stuff argument %d to stuff_buffer_vernier of length %d must be from -%d to +%d and "
            "length > 100.",
            stuff, length, INTERPOLATION_LIMIT, INTERPOLATION_LIMIT);
      tstuff = 0; // if any of these conditions hold, don't stuff anything/
    }

    char *l_outptr = outptr;
    int i;

    if (tstuff == 0) {
      for (i = 0; i < length; i++) { // the whole frame, if no stuffing
        unsigned int channel;
        for (channel = 0; channel < conn->input_num_channels; channel++)
          process_sample(*inptr++, &l_outptr, l_output_format, conn->fix_volume, dither, conn);
      }
    } else {
      // we are using 64 bit integers to represent fixed point numbers
      // the high 32 bits are the integer value and the low 32 bits are the fraction.
      int64_t one_fp = 0x100000000L;

      // this result will always be less than or equal to the exact true value.
      int64_t step_size_fp = one_fp * (length - 1);
      step_size_fp = step_size_fp / (length + tstuff - 1);

      // the interpolation is done between the previous sample, starting
      // with the zeroth sample, and the next one.
      // the very first and very last sample of the stuffed frame should
      // correspond 100% to the first and last samples of the original frame.

      // the first sample will be calculated as 100% of the first sample and 0% of the next sample
      // however, the last sample can not be calculated as 100% of the last sample and
      // 0% of the next one, because there isn't a "next" sample after the last one, duh.

      // however, rather than add extra code to deal with the last sample,
      // simply use a copy of the last sample as the "next" one, and the maths will work out.

      int64_t current_input_sample_index_fp = 0;
      for (i = 0; i < length + tstuff; i++) {
        int64_t current_input_sample_floor_index =
            current_input_sample_index_fp >> 32; // this is the (integer) index of the sample before
                                                 // where the new sample will be interpolated
        if (current_input_sample_floor_index == length) {
          // generate the whole and fractional parts of current_input_sample_index_fp for printing
          // without converting to floating point, which may do rounding.
          int64_t current_input_sample_index_int = current_input_sample_index_fp >> 32;
          int64_t current_input_sample_index_low = current_input_sample_index_fp & 0xFFFFFFFF;
          current_input_sample_index_low =
              current_input_sample_index_low * 100000; // 100000 for 5 decimal places
          current_input_sample_index_low = current_input_sample_index_low >> 32;
          debug(1,
                "Can't see how this could ever happen, but "
                "current_input_sample_floor_index %" PRId64
                " has just stepped outside the frame of %d samples, with stuff %d and current_input_sample_index_fp at %" PRId64 ".%05" PRId64
                ".",
                current_input_sample_floor_index, length, stuff, current_input_sample_index_int,
                current_input_sample_index_low);
          current_input_sample_floor_index = length - 1; // hack
        }

        // increment the ceiling index, but ensure it stays within the frame
        int64_t current_input_sample_ceil_index = current_input_sample_floor_index + 1;
        if (current_input_sample_ceil_index == length) {
          if (current_input_sample_floor_index == length - 1) {
            current_input_sample_ceil_index = length - 1;
          } else {
            // generate the whole and fractional parts of current_input_sample_index_fp for printing
            // without converting to floating point, which may do rounding.
            int64_t current_input_sample_index_int = current_input_sample_index_fp >> 32;
            int64_t current_input_sample_index_low = current_input_sample_index_fp & 0xFFFFFFFF;
            current_input_sample_index_low =
                current_input_sample_index_low * 100000; // 100000 for 5 decimal places
            current_input_sample_index_low = current_input_sample_index_low >> 32;
            debug(1,
                  "Can't see how this could ever happen, but "
                  "current_input_sample_ceil_index %" PRId64
                  " has just stepped outside the frame of %d samples, with stuff %d and current_input_sample_index_fp at %" PRId64
                  ".%05" PRId64 ".",
                  current_input_sample_floor_index, length, stuff, current_input_sample_index_int,
                  current_input_sample_index_low);
          }
        }

        /*
        {
          // generate the whole and fractional parts of current_input_sample_index_fp for printing
          // without converting to floating point, which may do rounding.
          int64_t current_input_sample_index_int = current_input_sample_index_fp >> 32;
          int64_t current_input_sample_index_low = current_input_sample_index_fp & 0xFFFFFFFF;
          current_input_sample_index_low =
              current_input_sample_index_low * 100000; // 100000 for 5 decimal places
          current_input_sample_index_low = current_input_sample_index_low >> 32;
          debug(1,
                "samples: %u, stuff: %d, output_sample: %d, current_input_sample_index_fp: %" PRId64
                ".%05" PRId64 ", floor: %" PRId64 ", ceil: %" PRId64 ".",
                length, stuff, i, current_input_sample_index_int, current_input_sample_index_low,
                current_input_sample_floor_index, current_input_sample_ceil_index);
        }
        */
        unsigned int channel;
        for (channel = 0; channel < conn->input_num_channels; channel++) {
          int32_t current_sample =
              inptr[current_input_sample_floor_index * conn->input_num_channels + channel];
          int32_t next_sample =
              inptr[current_input_sample_ceil_index * conn->input_num_channels + channel];
          int64_t current_sample_fp = current_sample;
          // current_sample_fp = current_sample_fp << 32;
          int64_t next_sample_fp = next_sample;
          // next_sample_fp = next_sample_fp << 32;
          int64_t offset_from_floor_fp = current_input_sample_index_fp & 0xffffffff;
          int64_t offset_to_ceil_fp = one_fp - offset_from_floor_fp;
          int64_t interpolated_sample_value_fp =
              current_sample_fp * offset_to_ceil_fp + next_sample_fp * offset_from_floor_fp;
          interpolated_sample_value_fp =
              interpolated_sample_value_fp / one_fp; // back to a 32-bit samplle
          int32_t interpolated_sample_value = interpolated_sample_value_fp;
          process_sample(interpolated_sample_value, &l_outptr, l_output_format, conn->fix_volume,
                         dither, conn);
        }
        current_input_sample_index_fp = current_input_sample_index_fp + step_size_fp;
      }
    }
  }
  return length + tstuff;
}

#ifdef CONFIG_SOXR
// this takes an array of signed 32-bit integers and
// (a) uses libsoxr to
// resample the array to have one more or one less frame, as specified in
// stuff,
// (b) multiplies each sample by the fixedvolume (a 16-bit quantity)
// (c) dithers the result to the output size 32/24/16/8 bits
// (d) outputs the result in the approprate format
// formats accepted include U8, S8, S16, S24, S24_3LE, S24_3BE and S32

int32_t stat_n = 0;
double stat_mean = 0.0;
double stat_M2 = 0.0;
double longest_soxr_execution_time = 0.0;
int64_t packets_processed = 0;

int stuff_buffer_soxr_32(int32_t *inptr, int length, sps_format_t l_output_format, char *outptr,
                         int stuff, int dither, rtsp_conn_info *conn) {
  // if (scratchBuffer == NULL) {
  //  die("soxr scratchBuffer not initialised.");
  //}
  packets_processed++;
  int tstuff = stuff;
  if ((stuff > INTERPOLATION_LIMIT) || (stuff < -INTERPOLATION_LIMIT) || (length < 100)) {
    debug(2,
          "Stuff argument %d to stuff_buffer_soxr_32 of length %d must be from -%d to +%d and "
          "length > 100.",
          stuff, length, INTERPOLATION_LIMIT, INTERPOLATION_LIMIT);
    tstuff = 0; // if any of these conditions hold, don't stuff anything/
  }

  if (tstuff) {
    // debug(1, "stuff_buffer_soxr_32 %+d.",stuff);
    int32_t *scratchBuffer =
        malloc(sizeof(int32_t) * CHANNELS_FROM_ENCODED_FORMAT(config.current_output_configuration) *
               (length + tstuff));
    if (scratchBuffer != NULL) {
      soxr_io_spec_t io_spec;
      io_spec.itype = SOXR_INT32_I;
      io_spec.otype = SOXR_INT32_I;
      io_spec.scale = 1.0; // this seems to crash if not = 1.0
      io_spec.e = NULL;
      io_spec.flags = 0;

      size_t odone;

      uint64_t soxr_start_time = get_absolute_time_in_ns();

      soxr_error_t error =
          soxr_oneshot(length, length + tstuff, conn->input_num_channels, // Rates and # of chans.
                       inptr, length, NULL,                               // Input.
                       scratchBuffer, length + tstuff, &odone,            // Output.
                       &io_spec,    // Input, output and transfer spec.
                       NULL, NULL); // Default configuration.

      if (error)
        die("soxr error: %s\n", soxr_strerror(error));

      if (odone > (size_t)(length + INTERPOLATION_LIMIT))
        die("odone = %zu!\n", odone);

      // mean and variance calculations from "online_variance" algorithm at
      // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Online_algorithm

      double soxr_execution_time = (get_absolute_time_in_ns() - soxr_start_time) * 0.000000001;
      // debug(1,"soxr_execution_time_us: %10.1f",soxr_execution_time_us);
      if (soxr_execution_time > longest_soxr_execution_time)
        longest_soxr_execution_time = soxr_execution_time;
      stat_n += 1;
      double stat_delta = soxr_execution_time - stat_mean;
      if (stat_n != 0)
        stat_mean += stat_delta / stat_n;
      else
        warn("calculation error for stat_n");
      stat_M2 += stat_delta * (soxr_execution_time - stat_mean);

      int i;
      int32_t *ip, *op;
      ip = inptr;
      op = scratchBuffer;

      const int gpm = 5;
      // keep the first (dpm) samples, to mitigate the Gibbs phenomenon
      for (i = 0; i < gpm; i++) {
        unsigned int channel;
        for (channel = 0; channel < conn->input_num_channels; channel++)
          *op++ = *ip++;
      }

      // keep the last (dpm) samples, to mitigate the Gibbs phenomenon

      // pointer arithmetic, baby -- it's da bomb.
      op = scratchBuffer + (length + tstuff - gpm) * conn->input_num_channels;
      ip = inptr + (length - gpm) * conn->input_num_channels;
      for (i = 0; i < gpm; i++) {
        unsigned int channel;
        for (channel = 0; channel < conn->input_num_channels; channel++)
          *op++ = *ip++;
      }

      // now, do the volume, dither and formatting processing
      ip = scratchBuffer;
      char *l_outptr = outptr;
      for (i = 0; i < length + tstuff; i++) {
        unsigned int channel;
        for (channel = 0; channel < conn->input_num_channels; channel++)
          process_sample(*ip++, &l_outptr, l_output_format, conn->fix_volume, dither, conn);
      };
      free(scratchBuffer);
    } else {
      debug(1, "Cannot allocate scratchbuffer");
    }
  } else { // the whole frame, if no stuffing

    // now, do the volume, dither and formatting processing
    int32_t *ip = inptr;
    char *l_outptr = outptr;
    int i;

    for (i = 0; i < length; i++) {
      unsigned int channel;
      for (channel = 0; channel < conn->input_num_channels; channel++)
        process_sample(*ip++, &l_outptr, l_output_format, conn->fix_volume, dither, conn);
    };
  }

  if ((packets_processed % 1250 == 0) && (stat_n > 0)) {
    debug(4,
          "soxr_oneshot execution time in nanoseconds: mean, standard deviation and max "
          "for %" PRId32 " interpolations in the last "
          "1250 packets. %10.6f, %10.6f, %10.6f.",
          stat_n, stat_mean, stat_n <= 1 ? 0.0 : sqrtf(stat_M2 / (stat_n - 1)),
          longest_soxr_execution_time);
    stat_n = 0;
    stat_mean = 0.0;
    stat_M2 = 0.0;
    longest_soxr_execution_time = 0.0;
  }

  return length + tstuff;
}
#endif

char line_of_stats[1024];
int statistics_row; // statistics_line 0 means print the headings; anything else 1 means print the
                    // values. Set to 0 the first time out.
int statistics_column; // used to index through the statistics_print_profile array to check if it
                       // should be printed
int was_a_previous_column;
int *statistics_print_profile;

// these arrays specify which of the statistics specified by the statistics_item calls will actually
// be printed -- 2 means print, 1 means print only in a debug mode, 0 means skip

// clang-format off
int ap1_synced_statistics_print_profile[] =                  {2, 1, 2, 2, 0, 2, 1, 1, 2, 1, 1, 1, 0, 1, 1, 2, 2};
int ap1_nosync_statistics_print_profile[] =                  {2, 0, 0, 0, 0, 2, 1, 1, 2, 1, 1, 1, 0, 1, 1, 0, 0};
int ap1_nodelay_statistics_print_profile[] =                 {0, 0, 0, 0, 0, 2, 1, 1, 2, 0, 1, 1, 0, 1, 1, 0, 0};

int ap2_realtime_synced_stream_statistics_print_profile[] =  {2, 1, 2, 2, 0, 2, 1, 1, 2, 1, 1, 1, 0, 0, 1, 2, 2};
int ap2_realtime_nosync_stream_statistics_print_profile[] =  {2, 0, 0, 0, 0, 2, 1, 1, 2, 1, 1, 1, 0, 0, 1, 0, 0};
int ap2_realtime_nodelay_stream_statistics_print_profile[] = {0, 0, 0, 0, 0, 2, 1, 1, 2, 0, 1, 1, 0, 0, 1, 0, 0};

int ap2_buffered_synced_stream_statistics_print_profile[] =  {2, 2, 2, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 2, 2};
int ap2_buffered_nosync_stream_statistics_print_profile[] =  {2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0};
int ap2_buffered_nodelay_stream_statistics_print_profile[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0};
// clang-format on

void statistics_item(const char *heading, const char *format, ...) {
  if (((statistics_print_profile[statistics_column] == 1) && (debug_level() != 0)) ||
      (statistics_print_profile[statistics_column] == 2)) { // include this column?
    if (was_a_previous_column != 0) {
      if (statistics_row == 0)
        strcat(line_of_stats, " | ");
      else
        strcat(line_of_stats, "   ");
    }
    if (statistics_row == 0) {
      strcat(line_of_stats, heading);
    } else {
      char b[1024];
      b[0] = 0;
      va_list args;
      va_start(args, format);
      vsnprintf(b, sizeof(b), format, args);
      va_end(args);
      strcat(line_of_stats, b);
    }
    was_a_previous_column = 1;
  }
  statistics_column++;
}

double suggested_volume(rtsp_conn_info *conn) {
  double response = config.airplay_volume;
  if ((conn != NULL) && (conn->own_airplay_volume_set != 0)) {
    response = conn->own_airplay_volume;
  }
  return response;
}

#ifdef CONFIG_METADATA
void send_ssnc_stream_description(const char *type, const char *description) {
  send_ssnc_metadata('styp', type, strlen(type), 1);
  send_ssnc_metadata('sdsc', description, strlen(description), 1);
}
#endif

void player_thread_cleanup_handler(void *arg) {
  rtsp_conn_info *conn = (rtsp_conn_info *)arg;
  // debug(1, "Connection %d: player_thread_cleanup_handler start.", conn->connection_number);

#ifdef CONFIG_FFMPEG
  // debug(1, "FFmpeg clearup");
  clear_software_resampler(conn);
  clear_decoding_chain(conn);
  // debug(1, "FFmpeg clearup done");
#endif

  if (config.output->stop) {
#ifdef CONFIG_FFMPEG
    if (avflush(conn) > 1)
      debug(3, "ffmpeg flush at stop!");
#endif
    debug(2, "Connection %d: player: stop the output backend.", conn->connection_number);
    config.output->stop();
  }

  int oldState;
  pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
  debug(3, "Connection %d: player thread main loop exit via player_thread_cleanup_handler.",
        conn->connection_number);

  if ((conn->at_least_one_frame_seen_this_session != 0) && (config.statistics_requested)) {
    int64_t time_playing = get_absolute_time_in_ns() - conn->playstart;
    time_playing = time_playing / 1000000000;
    int64_t elapsedHours = time_playing / 3600;
    int64_t elapsedMin = (time_playing / 60) % 60;
    int64_t elapsedSec = time_playing % 60;
    if (conn->frame_rate_valid)
      inform("Connection %d: Playback stopped. Total playing time %02" PRId64 ":%02" PRId64
             ":%02" PRId64 ". "
             "Output: %0.2f (raw), %0.2f (corrected) "
             "frames per second.",
             conn->connection_number, elapsedHours, elapsedMin, elapsedSec, conn->raw_frame_rate,
             conn->corrected_frame_rate);
    else
      inform("Connection %d: Playback stopped. Total playing time %02" PRId64 ":%02" PRId64
             ":%02" PRId64 ".",
             conn->connection_number, elapsedHours, elapsedMin, elapsedSec);
  }

#ifdef CONFIG_DACP_CLIENT
  relinquish_dacp_server_information(
      conn); // say it doesn't belong to this conversation thread any more...
#else
  mdns_dacp_monitor_set_id(NULL); // say we're not interested in following that DACP id any more
#endif

  // four possibilities
  // 1 -- Classic Airplay -- "AirPlay 1"
  // 2 -- AirPlay 2 in Classic Airplay mode
  // 3 -- AirPlay 2 in Buffered Audio Mode
  // 4 -- AirPlay 3 in Realtime Audio Mode.

#ifdef CONFIG_AIRPLAY_2
  if (conn->airplay_type == ap_2) {
    debug(2, "Cancelling AP2 timing, control and audio threads...");
    if (conn->airplay_stream_type == realtime_stream) {
      debug(2, "Connection %d: Delete Realtime Audio Stream thread", conn->connection_number);
      pthread_cancel(conn->rtp_realtime_audio_thread);
      pthread_join(conn->rtp_realtime_audio_thread, NULL);

    } else if (conn->airplay_stream_type == buffered_stream) {

      debug(3,
            "Connection %d: Delete Buffered Audio Stream thread by player_thread_cleanup_handler",
            conn->connection_number);
      pthread_cancel(conn->rtp_buffered_audio_thread);
      pthread_join(conn->rtp_buffered_audio_thread, NULL);
      debug(3,
            "Connection %d: Deleted Buffered Audio Stream thread by player_thread_cleanup_handler",
            conn->connection_number);
    } else {
      die("Unrecognised Stream Type");
    }

    debug(2, "Connection %d: Delete AirPlay 2 Control thread", conn->connection_number);
    pthread_cancel(conn->rtp_ap2_control_thread);
    pthread_join(conn->rtp_ap2_control_thread, NULL);
  } else {
    debug(2, "Cancelling AP1-compatible timing, control and audio threads...");
#else
  debug(2, "Cancelling AP1 timing, control and audio threads...");
#endif
    debug(3, "Cancel timing thread.");
    pthread_cancel(conn->rtp_timing_thread);
    debug(3, "Join timing thread.");
    pthread_join(conn->rtp_timing_thread, NULL);
    debug(3, "Timing thread terminated.");
    debug(3, "Cancel control thread.");
    pthread_cancel(conn->rtp_control_thread);
    debug(3, "Join control thread.");
    pthread_join(conn->rtp_control_thread, NULL);
    debug(3, "Control thread terminated.");
    debug(3, "Cancel audio thread.");
    pthread_cancel(conn->rtp_audio_thread);
    debug(3, "Join audio thread.");
    pthread_join(conn->rtp_audio_thread, NULL);
    debug(3, "Audio thread terminated.");

#ifdef CONFIG_AIRPLAY_2
  }
  ptp_send_control_message_string("E");
#endif

  if (conn->outbuf) {
    free(conn->outbuf);
    conn->outbuf = NULL;
  }
  if (conn->tbuf) {
    free(conn->tbuf);
    conn->tbuf = NULL;
  }

  free_audio_buffers(conn);
  if (conn->stream.type == ast_apple_lossless) {
#ifdef CONFIG_APPLE_ALAC
    if (config.decoder_in_use == 1 << decoder_apple_alac) {
      apple_alac_terminate();
    }
#endif

#ifdef CONFIG_HAMMERTON
    if (config.decoder_in_use == 1 << decoder_hammerton) {
      alac_free(conn->decoder_info);
    }
#endif
  }

  conn->rtp_running = 0;

  pthread_setcancelstate(oldState, NULL);
  debug(2, "Connection %d: player terminated.", conn->connection_number);
}

void *player_thread_func(void *arg) {
  rtsp_conn_info *conn = (rtsp_conn_info *)arg;
  // if (config.output->prepare)
  // config.output->prepare(); // give the backend its first chance to prepare itself, knowing it
  // has access to the output device (i.e. knowing that it should not be in use by another program
  // at this time).
#ifdef CONFIG_METADATA
  uint64_t time_of_last_metadata_progress_update =
      0; // the assignment is to stop a compiler warning...
#endif

#ifdef CONFIG_CONVOLUTION
  double highest_convolver_output_db = 0.0;
#endif

  uint64_t previous_frames_played = 0; // initialised to avoid a "possibly uninitialised" warning
  uint64_t previous_raw_measurement_time =
      0; // initialised to avoid a "possibly uninitialised" warning
  uint64_t previous_corrected_measurement_time =
      0; // initialised to avoid a "possibly uninitialised" warning
  int previous_frames_played_valid = 0;

  conn->latency_warning_issued =
      0; // be permitted to generate a warning each time a play is attempted
  conn->packet_count = 0;
  conn->packet_count_since_flush = 0;
  conn->previous_random_number = 0;
  conn->decoder_in_use = 0;
  conn->ab_buffering = 1;
  conn->ab_synced = 0;
  conn->first_packet_timestamp = 0;
  conn->flush_requested = 0;
  conn->flush_output_flushed = 0; // only send a flush command to the output device once
  conn->flush_rtp_timestamp = 0;  // it seems this number has a special significance -- it seems to
                                  // be used as a null operand, so we'll use it like that too
  conn->fix_volume = 0x10000;
  conn->frames_per_packet = 352; // for ALAC -- will be changed if necessary

#ifdef CONFIG_AIRPLAY_2
  conn->ap2_rate = 0;
  conn->ap2_play_enabled = 0;

  unsigned int f = 0;
  for (f = 0; f < MAX_DEFERRED_FLUSH_REQUESTS; f++) {
    conn->ap2_deferred_flush_requests[f].inUse = 0;
    conn->ap2_deferred_flush_requests[f].active = 0;
  }
#endif

  const unsigned int sync_history_length = 40;
  int64_t sync_samples[sync_history_length];
  int64_t sync_samples_highest_error = 0;
  int64_t sync_samples_lowest_error = 0;
  int64_t sync_samples_second_highest_error;
  int64_t sync_samples_second_lowest_error;
  conn->sync_samples_index = 0;
  conn->sync_samples_count = 0;

  if (conn->stream.type == ast_apple_lossless) {
#ifdef CONFIG_HAMMERTON
    if (config.decoder_in_use == 1 << decoder_hammerton) {
      init_alac_decoder((int32_t *)&conn->stream.fmtp,
                        conn); // this sets up incoming rate, bit depth, channels.
                               // No pthread cancellation point in here
    }
#endif
#ifdef CONFIG_APPLE_ALAC
    if (config.decoder_in_use == 1 << decoder_apple_alac) {
      apple_alac_init(conn->stream.fmtp); // no pthread cancellation point in here
    }
#endif
  }
  // This must be after init_alac_decoder
  init_buffer(conn); // will need a corresponding deallocation. No cancellation points in here
  ab_resync(conn);

  if (conn->stream.encrypted) {
#ifdef CONFIG_MBEDTLS
    memset(&conn->dctx, 0, sizeof(mbedtls_aes_context));
    mbedtls_aes_setkey_dec(&conn->dctx, conn->stream.aeskey, 128);
#endif

#ifdef CONFIG_POLARSSL
    memset(&conn->dctx, 0, sizeof(aes_context));
    aes_setkey_dec(&conn->dctx, conn->stream.aeskey, 128);
#endif
  }

  conn->session_corrections = 0;
  conn->connection_state_to_output = get_requested_connection_state_to_output();

  int number_of_statistics, oldest_statistic, newest_statistic;
  uint32_t frames_since_last_stats_logged = 0;
  int at_least_one_frame_seen = 0;
  int64_t tsum_of_sync_errors, tsum_of_corrections, tsum_of_insertions_and_deletions;
  size_t tsum_of_frames;
  minimum_dac_queue_size = UINT64_MAX;
  int64_t tsum_of_gaps;
  int32_t minimum_buffer_occupancy = INT32_MAX;
  int32_t maximum_buffer_occupancy = INT32_MIN;

#ifdef CONFIG_AIRPLAY_2
  conn->ap2_audio_buffer_minimum_size = -1;
#endif

  conn->at_least_one_frame_seen_this_session = 0;
  conn->raw_frame_rate = 0.0;
  conn->corrected_frame_rate = 0.0;
  conn->frame_rate_valid = 0;

  conn->input_frame_rate = 0.0;
  conn->input_frame_rate_starting_point_is_valid = 0;

  conn->buffer_occupancy = 0;

  int play_samples = 0;
  uint64_t current_delay;
  int play_number = 0;
  conn->play_number_after_flush = 0;
  conn->time_of_last_audio_packet = 0;
  // conn->shutdown_requested = 0;
  number_of_statistics = oldest_statistic = newest_statistic = 0;
  tsum_of_sync_errors = tsum_of_corrections = tsum_of_insertions_and_deletions = 0;
  tsum_of_frames = 0;
  tsum_of_gaps = 0;

  // I think it's useful to keep this prime to prevent it from falling into a pattern with some
  // other process.

  static char rnstate[256];
  initstate(time(NULL), rnstate, 256);

  // signed short *inbuf;
  int inbuflength;

  // remember, the output device may never have been initialised prior to this call
#ifdef CONFIG_FFMPEG
  if (avflush(conn) > 1)
    debug(1, "ffmpeg flush at start!");
#endif

  // leave this relic -- jack and soundio still use it
  if (config.output->start != NULL)
    config.output->start(44100, SPS_FORMAT_S16_LE);

  conn->first_packet_timestamp = 0;
  conn->missing_packets = conn->late_packets = conn->too_late_packets = conn->resend_requests = 0;
  int sync_error_out_of_bounds =
      0; // number of times in a row that there's been a serious sync error

  // conn->statistics = malloc(sizeof(stats_t) * trend_samples);
  // if (conn->statistics == NULL)
  //   die("Failed to allocate a statistics buffer");

  conn->framesProcessedInThisEpoch = 0;
  conn->framesGeneratedInThisEpoch = 0;
  conn->correctionsRequestedInThisEpoch = 0;
  statistics_row = 0; // statistics_line 0 means print the headings; anything else 1 means print the
                      // values. Set to 0 the first time out.

  // decide on what statistics profile to use, if requested
#ifdef CONFIG_AIRPLAY_2
  if (conn->airplay_type == ap_2) {
    if (conn->airplay_stream_type == realtime_stream) {
      if (config.output->delay) {
        // if (config.no_sync == 0)
        statistics_print_profile = ap2_realtime_synced_stream_statistics_print_profile;
        // else
        // statistics_print_profile = ap2_realtime_nosync_stream_statistics_print_profile;
      } else {
        statistics_print_profile = ap2_realtime_nodelay_stream_statistics_print_profile;
      }
    } else {
      if (config.output->delay) {
        // if (config.no_sync == 0)
        statistics_print_profile = ap2_buffered_synced_stream_statistics_print_profile;
        // else
        //   statistics_print_profile = ap2_buffered_nosync_stream_statistics_print_profile;
      } else {
        statistics_print_profile = ap2_buffered_nodelay_stream_statistics_print_profile;
      }
    }
  } else {
#endif
    if (config.output->delay) {
      // if (config.no_sync == 0)
      statistics_print_profile = ap1_synced_statistics_print_profile;
      // else
      //   statistics_print_profile = ap1_nosync_statistics_print_profile;
    } else {
      statistics_print_profile = ap1_nodelay_statistics_print_profile;
    }
// airplay 1 stuff here
#ifdef CONFIG_AIRPLAY_2
  }
#endif

#ifdef CONFIG_AIRPLAY_2
  if (conn->timing_type == ts_ntp) {
#endif

    // create and start the timing, control and audio receiver threads
    named_pthread_create(&conn->rtp_audio_thread, NULL, &rtp_audio_receiver, (void *)conn,
                         "ap1_audio_%d", conn->connection_number);
    named_pthread_create(&conn->rtp_control_thread, NULL, &rtp_control_receiver, (void *)conn,
                         "ap1_control_%d", conn->connection_number);
    named_pthread_create(&conn->rtp_timing_thread, NULL, &rtp_timing_receiver, (void *)conn,
                         "ap1_tim_rcv_%d", conn->connection_number);

#ifdef CONFIG_AIRPLAY_2
  }
#endif

  pthread_cleanup_push(player_thread_cleanup_handler, arg); // undo what's been done so far

  // stop looking elsewhere for DACP stuff
  int oldState;
  pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);

#ifdef CONFIG_DACP_CLIENT
  set_dacp_server_information(conn);
#else
  mdns_dacp_monitor_set_id(conn->dacp_id);
#endif

  pthread_setcancelstate(oldState, NULL);

  // if not already set, set the volume to the pending_airplay_volume, if any, or otherwise to the
  // suggested volume.

  double initial_volume = suggested_volume(conn);
  debug(2, "Set initial volume to %.6f.", initial_volume);
  player_volume(initial_volume, conn); // will contain a cancellation point if asked to wait

  debug(2, "Play begin");

#ifdef CONFIG_FFMPEG
  int64_t frames_previously_retained_in_the_resampler = 0;
#endif

  // uint32_t flush_to_frame;
  // int enable_flush_to_frame = 0;
  int request_resync = 0;      // will be set if a big discontinuity is detected
  uint32_t frames_to_skip = 0; // when a discontinuity is registered
  int skipping_frames_at_start_of_play = 0;
  // debug(1, "player begin processing packets");
  while (1) {

#ifdef CONFIG_METADATA
    int this_is_the_first_frame = 0; // will be set if it is
#endif

    pthread_testcancel(); // allow a pthread_cancel request to take effect.

    // if we are using the software attenuator or downsampling or mixing to mono, enable dithering

    if ((conn->fix_volume != 0x10000) || // if not 0x10000, it is attenuating...
        ((conn->output_bit_depth > 0) &&
         (conn->input_effective_bit_depth > conn->output_bit_depth)) ||
        (config.playback_mode == ST_mono)) {
      if (conn->enable_dither == 0)
        debug(2, "enabling dither");
      conn->enable_dither = 1;
    } else {
      if (conn->enable_dither != 0)
        debug(2, "disabling dither");
      conn->enable_dither = 0;
    }

    abuf_t *inframe = buffer_get_frame(
        conn, request_resync); // this has a guaranteed [and needed!] cancellation point
    request_resync = 0;
    if (inframe) {
      if (inframe->data != NULL) {
        /*
        {
          uint64_t the_time_this_frame_should_be_played;
                    frame_to_local_time(inframe->timestamp,
                                        &the_time_this_frame_should_be_played, conn);
          int64_t lead_time = the_time_this_frame_should_be_played - get_absolute_time_in_ns();
          debug(1, "get_packet %u, lead time is %3.f ms.", inframe->timestamp, lead_time *
        0.000001);
        }
        */
        unsigned int last_sample_index;
        int frames_played = 0;
        int64_t sync_error = 0;
        int amount_to_stuff = 0;
        if (inframe->data) {
          if (play_number == 0)
            conn->playstart = get_absolute_time_in_ns();
          play_number++;
          //        if (play_number % 100 == 0)
          //          debug(3, "Play frame %d.", play_number);
          conn->play_number_after_flush++;

          if (inframe->timestamp == 0) {
            debug(2,
                  "Player has supplied a silent frame, (possibly frame %u) for play number %d, "
                  "status 0x%X after %u resend requests.",
                  conn->last_seqno_read + 1, play_number, inframe->status,
                  inframe->resend_request_number);
            conn->last_seqno_read++; // manage the packet out of sequence minder

            void *silence =
                malloc(sps_format_sample_size(
                           FORMAT_FROM_ENCODED_FORMAT(config.current_output_configuration)) *
                       CHANNELS_FROM_ENCODED_FORMAT(config.current_output_configuration) *
                       conn->frames_per_packet);
            if (silence == NULL) {
              debug(1, "Failed to allocate memory for a silent frame silence buffer.");
            } else {
              // the player may change the contents of the buffer, so it has to be zeroed each
              // time; might as well malloc and free it locally
              conn->previous_random_number = generate_zero_frames(
                  silence, conn->frames_per_packet, conn->enable_dither,
                  conn->previous_random_number, config.current_output_configuration);
              config.output->play(silence, conn->frames_per_packet, play_samples_are_untimed, 0, 0);
              free(silence);
              frames_played += conn->frames_per_packet;
            }
          } else {
            // process the frame
            // here, let's transform the frame of data, if necessary
            // we need an intermediate "transition" buffer

            if (conn->tbuf != NULL) {
              debug(1, "conn->tbuf not free'd");
              free(conn->tbuf);
            }
            conn->tbuf =
                malloc(sizeof(int32_t) *
                       CHANNELS_FROM_ENCODED_FORMAT(config.current_output_configuration) *
                       ((inframe->length) * conn->output_sample_ratio + INTERPOLATION_LIMIT));
            if (conn->tbuf == NULL)
              die("Failed to allocate memory for the transition buffer.");
            // size change
            conn->outbuf =
                malloc(sps_format_sample_size(
                           FORMAT_FROM_ENCODED_FORMAT(config.current_output_configuration)) *
                       CHANNELS_FROM_ENCODED_FORMAT(config.current_output_configuration) *
                       ((inframe->length) * conn->output_sample_ratio + INTERPOLATION_LIMIT));
            if (conn->outbuf == NULL)
              die("Failed to allocate memory for an output buffer.");

            if (conn->input_num_channels == 2) {
              // if (0) {

              switch (conn->input_bit_depth) {
              case 16: {
                unsigned int i, j;
                int16_t ls, rs;
                int32_t ll = 0, rl = 0;
                int16_t *inps = inframe->data;
                // int16_t *outps = tbuf;
                int32_t *outpl = (int32_t *)conn->tbuf;
                for (i = 0; i < (inframe->length); i++) {
                  ls = *inps++;
                  rs = *inps++;

                  // here, do the mode stuff -- mono / reverse stereo / leftonly / rightonly
                  // also, raise the 16-bit samples to 32 bits.

                  switch (config.playback_mode) {
                  case ST_mono: {
                    int32_t lsl = ls;
                    int32_t rsl = rs;
                    int32_t both = lsl + rsl;
                    both =
                        both
                        << (16 -
                            1); // keep all 17 bits of the sum of the 16 bit left and right channels
                                // -- the 17th bit will influence dithering later
                    ll = both;
                    rl = both;
                  } break;
                  case ST_reverse_stereo: {
                    ll = rs;
                    rl = ls;
                    ll = ll << 16;
                    rl = rl << 16;
                  } break;
                  case ST_left_only:
                    rl = ls;
                    ll = ls;
                    ll = ll << 16;
                    rl = rl << 16;
                    break;
                  case ST_right_only:
                    ll = rs;
                    rl = rs;
                    ll = ll << 16;
                    rl = rl << 16;
                    break;
                  case ST_stereo:
                    ll = ls;
                    rl = rs;
                    ll = ll << 16;
                    rl = rl << 16;
                    break; // nothing extra to do
                  }

                  // here, replicate the samples if you're upsampling

                  for (j = 0; j < conn->output_sample_ratio; j++) {
                    *outpl++ = ll;
                    *outpl++ = rl;
                  }
                }
              } break;
              case 32: {
                unsigned int i, j;
                int32_t ls, rs;
                int32_t ll = 0, rl = 0;
                int32_t *inps = (int32_t *)inframe->data;
                int32_t *outpl = (int32_t *)conn->tbuf;
                for (i = 0; i < (inframe->length); i++) {
                  ls = *inps++;
                  rs = *inps++;

                  // here, do the mode stuff -- mono / reverse stereo / leftonly / rightonly

                  switch (config.playback_mode) {
                  case ST_mono: {
                    int64_t lsl = ls;
                    int64_t rsl = rs;
                    int64_t both = lsl + rsl;
                    both = both >> 1;
                    uint32_t both32 = both;
                    ll = both32;
                    rl = both32;
                  } break;
                  case ST_reverse_stereo: {
                    ll = rs;
                    rl = ls;
                  } break;
                  case ST_left_only:
                    rl = ls;
                    ll = ls;
                    break;
                  case ST_right_only:
                    ll = rs;
                    rl = rs;
                    break;
                  case ST_stereo:
                    ll = ls;
                    rl = rs;
                    break; // nothing extra to do
                  }

                  // here, replicate the samples if you're upsampling

                  for (j = 0; j < conn->output_sample_ratio; j++) {
                    *outpl++ = ll;
                    *outpl++ = rl;
                  }
                }
              } break;

              default:
                die("Shairport Sync only supports 16 or 32 bit input (stereo)");
              }
            } else {
              // multichannel -- don't do anything odd here
              if (conn->input_bit_depth == 16) {
                unsigned int i;
                int16_t ss;
                int32_t sl;
                int16_t *inps = inframe->data;
                int32_t *outpl = (int32_t *)conn->tbuf;
                for (i = 0; i < (inframe->length) * conn->input_num_channels; i++) {
                  ss = *inps++;
                  sl = ss;
                  sl = sl << 16;
                  unsigned int j;
                  for (j = 0; j < conn->output_sample_ratio; j++) {
                    *outpl++ = sl;
                  }
                }
              } else if (conn->input_bit_depth == 32) {
                unsigned int i;
                int32_t *inpl = (int32_t *)inframe->data;
                int32_t *outpl = (int32_t *)conn->tbuf;
                for (i = 0; i < (inframe->length) * conn->input_num_channels; i++) {
                  int32_t sl = *inpl++;
                  unsigned int j;
                  for (j = 0; j < conn->output_sample_ratio; j++) {
                    *outpl++ = sl;
                  }
                }
              } else {
                die("Shairport Sync only supports 16 or 32 bit input (multichannel)");
              }
            }

            inbuflength = (inframe->length) * conn->output_sample_ratio;

            // We have a frame of data. We need to see if we want to add or remove a frame from
            // it to keep in sync. So we calculate the timing error for the first frame in the
            // DAC. If it's ahead of time, we add one audio frame to this frame to delay a
            // subsequent frame If it's late, we remove an audio frame from this frame to bring
            // a subsequent frame forward in time

            // now, go back as far as the total latency less, say, 100 ms, and check the
            // presence of frames from then onwards

            at_least_one_frame_seen = 1;

            int16_t bo = conn->ab_write - conn->ab_read; // do this in 16 bits
            conn->buffer_occupancy = bo;                 // 32 bits

            if (conn->buffer_occupancy < minimum_buffer_occupancy)
              minimum_buffer_occupancy = conn->buffer_occupancy;

            if (conn->buffer_occupancy > maximum_buffer_occupancy)
              maximum_buffer_occupancy = conn->buffer_occupancy;

            // now, before outputting anything to the output device, check the stats

            uint32_t stats_logging_interval_in_frames =
                8 * RATE_FROM_ENCODED_FORMAT(config.current_output_configuration);
            if ((stats_logging_interval_in_frames != 0) &&
                (frames_since_last_stats_logged > stats_logging_interval_in_frames)) {

              // here, calculate the input and output frame rates, where possible, even if
              // statistics have not been requested this is to calculate them in case they are
              // needed by the D-Bus interface or elsewhere.

              if (conn->input_frame_rate_starting_point_is_valid) {
                uint64_t elapsed_reception_time, frames_received;
                elapsed_reception_time = conn->frames_inward_measurement_time -
                                         conn->frames_inward_measurement_start_time;
                frames_received = conn->frames_inward_frames_received_at_measurement_time -
                                  conn->frames_inward_frames_received_at_measurement_start_time;
                conn->input_frame_rate = (1.0E9 * frames_received) /
                                         elapsed_reception_time; // an IEEE double calculation
                                                                 // with two 64-bit integers
              } else {
                conn->input_frame_rate = 0.0;
              }

              int stats_status = 0;
              if ((config.output->delay) && (config.output->stats)) {
                uint64_t frames_sent_for_play;
                uint64_t raw_measurement_time;
                uint64_t corrected_measurement_time;
                uint64_t actual_delay;
                stats_status =
                    config.output->stats(&raw_measurement_time, &corrected_measurement_time,
                                         &actual_delay, &frames_sent_for_play);
                // debug(1,"status: %d, actual_delay: %" PRIu64 ", frames_sent_for_play: %"
                // PRIu64
                // ", frames_played: %" PRIu64 ".", stats_status, actual_delay,
                // frames_sent_for_play, frames_sent_for_play - actual_delay);
                uint64_t frames_played_by_output_device = frames_sent_for_play - actual_delay;
                // If the status is zero, it means that there were no output problems since the
                // last time the stats call was made. Thus, the frame rate should be valid.
                if ((stats_status == 0) && (previous_frames_played_valid != 0)) {
                  uint64_t frames_played_in_this_interval =
                      frames_played_by_output_device - previous_frames_played;
                  int64_t raw_interval = raw_measurement_time - previous_raw_measurement_time;
                  int64_t corrected_interval =
                      corrected_measurement_time - previous_corrected_measurement_time;
                  if (raw_interval != 0) {
                    conn->raw_frame_rate = (1e9 * frames_played_in_this_interval) / raw_interval;
                    conn->corrected_frame_rate =
                        (1e9 * frames_played_in_this_interval) / corrected_interval;
                    conn->frame_rate_valid = 1;
                    // debug(1,"frames_played_in_this_interval: %" PRIu64 ", interval: %" PRId64
                    // ", rate: %f.",
                    //  frames_played_in_this_interval, interval, conn->frame_rate);
                  }
                }

                // uncomment the if statement if your want to get as long a period for
                // calculating the frame rate as possible without an output break or error
                if ((stats_status != 0) || (previous_frames_played_valid == 0)) {
                  // if we have just detected an outputting error, or if we have no
                  // starting information
                  if (stats_status != 0)
                    conn->frame_rate_valid = 0;
                  previous_frames_played = frames_played_by_output_device;
                  previous_raw_measurement_time = raw_measurement_time;
                  previous_corrected_measurement_time = corrected_measurement_time;
                  previous_frames_played_valid = 1;
                }
              }

              // we can now calculate running averages for sync error (frames), corrections
              // (ppm), insertions plus deletions (ppm)
              double average_sync_error = 0.0;
              double average_gap_ms = 0.0;
              double corrections_ppm = 0.0;
              double insertions_plus_deletions_ppm = 0.0;
              if (number_of_statistics == 0) {
                debug(1, "number_of_statistics is zero!");
              } else {
                average_sync_error =
                    (1000.0 * tsum_of_sync_errors) /
                    (number_of_statistics *
                     RATE_FROM_ENCODED_FORMAT(config.current_output_configuration));
                average_gap_ms = ((1.0 * tsum_of_gaps) / number_of_statistics) * 0.000001;
                if (tsum_of_frames != 0) {
                  corrections_ppm = (1000000.0 * tsum_of_corrections) / tsum_of_frames;
                  insertions_plus_deletions_ppm =
                      (1000000.0 * tsum_of_insertions_and_deletions) / tsum_of_frames;
                } else {
                  debug(3, "tsum_of_frames: %zu.", tsum_of_frames);
                }
              }
              if (config.statistics_requested) {
                if (at_least_one_frame_seen) {
                  do {
                    line_of_stats[0] = '\0';
                    statistics_column = 0;
                    was_a_previous_column = 0;
                    statistics_item("Av Sync Error (ms)", "%*.2f", 18, average_sync_error);
                    statistics_item("Net Sync PPM", "%*.1f", 12, corrections_ppm);
                    statistics_item("All Sync PPM", "%*.1f", 12, insertions_plus_deletions_ppm);
                    statistics_item("Av Sync Window (ms)", "%*.2f", 19, average_gap_ms);
                    statistics_item("    Packets", "%*d", 11, play_number);
                    statistics_item("Missing", "%*" PRIu64 "", 7, conn->missing_packets);
                    statistics_item("  Late", "%*" PRIu64 "", 6, conn->late_packets);
                    statistics_item("Too Late", "%*" PRIu64 "", 8, conn->too_late_packets);
                    statistics_item("Resend Reqs", "%*" PRIu64 "", 11, conn->resend_requests);
                    statistics_item("Min DAC Queue", "%*" PRIu64 "", 13, minimum_dac_queue_size);
                    statistics_item("Min Buffers", "%*" PRIu32 "", 11, minimum_buffer_occupancy);
                    statistics_item("Max Buffers", "%*" PRIu32 "", 11, maximum_buffer_occupancy);
#ifdef CONFIG_AIRPLAY_2
                    if (conn->ap2_audio_buffer_minimum_size > 10 * 1024)
                      statistics_item("Min Buffer Size", "%*" PRIu32 "k", 14,
                                      conn->ap2_audio_buffer_minimum_size / 1024);
                    else
                      statistics_item("Min Buffer Size", "%*" PRIu32 "", 15,
                                      conn->ap2_audio_buffer_minimum_size);
#else
                    statistics_item("N/A", "   "); // dummy -- should never be visible
#endif
                    statistics_item("Nominal FPS", "%*.2f", 11, conn->remote_frame_rate);
                    statistics_item("Received FPS", "%*.2f", 12, conn->input_frame_rate);
                    // only make the next two columns appear if we are getting stats information
                    // from the back end
                    if (config.output->stats) {
                      if (conn->frame_rate_valid) {
                        statistics_item("Output FPS (r)", "%*.2f", 14, conn->raw_frame_rate);
                        statistics_item("Output FPS (c)", "%*.2f", 14, conn->corrected_frame_rate);
                      } else {
                        statistics_item("Output FPS (r)", "           N/A");
                        statistics_item("Output FPS (c)", "           N/A");
                      }
                    } else {
                      statistics_column = statistics_column + 2;
                    }
                    statistics_row++;
                    inform("%s", line_of_stats);
                  } while (statistics_row < 2);
                } else {
                  inform("No frames received in the last sampling interval.");
                }
              }
              tsum_of_sync_errors = 0;
              tsum_of_corrections = 0;
              tsum_of_insertions_and_deletions = 0;
              number_of_statistics = 0;
              tsum_of_frames = 0;
              tsum_of_gaps = 0;
              minimum_dac_queue_size = UINT64_MAX;  // hack reset
              maximum_buffer_occupancy = INT32_MIN; // can't be less than this
              minimum_buffer_occupancy = INT32_MAX; // can't be more than this
#ifdef CONFIG_AIRPLAY_2
              conn->ap2_audio_buffer_minimum_size = -1;
#endif
              at_least_one_frame_seen = 0;
              frames_since_last_stats_logged = 0;
            }

            if (conn->at_least_one_frame_seen_this_session == 0) {
              conn->at_least_one_frame_seen_this_session = 1;

#ifdef CONFIG_METADATA
              this_is_the_first_frame = 1;
#endif

              char short_description[256];
              snprintf(short_description, sizeof(short_description), "%u/%s/%u",
                       RATE_FROM_ENCODED_FORMAT(config.current_output_configuration),
                       sps_format_description_string(
                           FORMAT_FROM_ENCODED_FORMAT(config.current_output_configuration)),
                       CHANNELS_FROM_ENCODED_FORMAT(config.current_output_configuration));
              // since this is the first frame of audio, inform the user if requested...
#ifdef CONFIG_AIRPLAY_2
              if (conn->airplay_stream_type == realtime_stream) {
                if (conn->airplay_type == ap_1) {
#ifdef CONFIG_METADATA
                  send_ssnc_stream_description("Classic", get_ssrc_name(conn->incoming_ssrc));
#endif
                  if (config.statistics_requested)
                    inform("Connection %d: Classic AirPlay (\"AirPlay 1\") Compatible playback. "
                           "Input format: %s. Output format: %s.",
                           conn->connection_number, get_ssrc_name(conn->incoming_ssrc),
                           short_description);
                } else {
#ifdef CONFIG_METADATA
                  send_ssnc_stream_description("Realtime", get_ssrc_name(conn->incoming_ssrc));
#endif
                  if (config.statistics_requested) {
                    if (conn->ap2_client_name == NULL)
                      inform("Connection %d: AirPlay 2 Realtime playback. "
                             "Input format: %s. Output format: %s.",
                             conn->connection_number, get_ssrc_name(conn->incoming_ssrc), "");
                    else
                      inform("Connection %d: AirPlay 2 Realtime playback. "
                             "Source: \"%s\". Input format: %s. Output format: %s.",
                             conn->connection_number, conn->ap2_client_name,
                             get_ssrc_name(conn->incoming_ssrc), short_description);
                  }
                }
              } else {
#ifdef CONFIG_METADATA
                send_ssnc_stream_description("Buffered", get_ssrc_name(conn->incoming_ssrc));
#endif

                if (config.statistics_requested) {

                  if (conn->ap2_client_name == NULL)
                    inform("Connection %d: AirPlay 2 Buffered playback. "
                           "Input format: %s. Output format: %s.",
                           conn->connection_number, get_ssrc_name(conn->incoming_ssrc),
                           short_description);
                  else
                    inform("Connection %d: AirPlay 2 Buffered playback. "
                           "Source: \"%s\". Input format: %s. Output format: %s.",
                           conn->connection_number, conn->ap2_client_name,
                           get_ssrc_name(conn->incoming_ssrc), short_description);
                }
              }
#else
#ifdef CONFIG_METADATA
              send_ssnc_stream_description("AirPlay", "ALAC/44100/S16/2");
#endif
              if (config.statistics_requested)
                inform("Connection %d: Classic AirPlay (\"AirPlay 1\") playback. "
                       "Input format: ALAC/44100/S16/2. Output format: %s.",
                       conn->connection_number, short_description);
#endif

#ifdef CONFIG_METADATA
              send_ssnc_metadata('odsc', short_description, strlen(short_description), 1);
#endif
            }

            // here, we want to check (a) if we are meant to do synchronisation,
            // (b) if we have a delay procedure, (c) if we can get the delay.

            // If any of these are false, we don't do any synchronisation stuff

            int resp = -1; // use this as a flag -- if negative, we can't rely on a real known delay
            current_delay = -1; // use this as a failure flag

            // if making the measurements takes too long (e.g. due to scheduling) , don't use
            // it.

            uint64_t mst = get_absolute_time_in_ns(); // measurement start time
            uint64_t delay_measurement_time = 0;
            if (config.output->delay) {
              long l_delay;
              resp = config.output->delay(&l_delay);
              delay_measurement_time =
                  get_absolute_time_in_ns(); // put this after delay() returns, as it may take an
                                             // appreciable amount of time to run
              if (resp == 0) {               // no error
                current_delay = l_delay;
                if (l_delay >= 0)
                  current_delay = l_delay;
                else {
                  debug(2, "Underrun of %ld frames reported, but ignored.", l_delay);
                  current_delay =
                      0; // could get a negative value if there was underrun, but ignore it.
                }
                if (current_delay < minimum_dac_queue_size) {
                  minimum_dac_queue_size = current_delay; // update for display later
                }
              } else {
                current_delay = 0;
                if ((resp == sps_extra_code_output_stalled) &&
                    (config.unfixable_error_reported == 0)) {
                  config.unfixable_error_reported = 1;
                  if (config.cmd_unfixable) {
                    warn("Connection %d: An unfixable error has been detected -- output device "
                         "is "
                         "stalled. Executing the "
                         "\"run_this_if_an_unfixable_error_is_detected\" command.",
                         conn->connection_number);
                    command_execute(config.cmd_unfixable, "output_device_stalled", 1);
                  } else {
                    warn("Connection %d: An unfixable error has been detected -- output device "
                         "is "
                         "stalled. \"No "
                         "run_this_if_an_unfixable_error_is_detected\" command provided -- "
                         "nothing "
                         "done.",
                         conn->connection_number);
                  }
                } else {
                  if ((resp != -EBUSY) &&
                      (resp != -ENODEV)) // delay and not-there errors can be reported if the
                                         // device is (hopefully temporarily) busy or unavailable
                                         // Note: ENODATA (a better fit) is not availabe in FreeBSD.
                    debug(1, "Delay error %d when checking running latency.", resp);
                }
              }
              // debug(1, "resp is %d, delay is %ld.", resp, l_delay);
            }
            if (resp == 0) {

              uint64_t the_time_this_frame_should_be_played;
              frame_to_local_time(inframe->timestamp, &the_time_this_frame_should_be_played, conn);

              uint64_t output_buffer_delay_time = current_delay;

#ifdef CONFIG_FFMPEG
              // the current delay should also include the frames that were kept in swr
              // before the current block was requested
              output_buffer_delay_time =
                  output_buffer_delay_time + frames_previously_retained_in_the_resampler;
              // debug(1,"Allowing for %" PRId64 " frames previously held in the resampler.",
              // frames_previously_retained_in_the_resampler);
              // now we'll update frames_previously_retained_in_the_resampler
              // to the figure after the current block
              frames_previously_retained_in_the_resampler = conn->frames_retained_in_the_resampler;
#endif

              output_buffer_delay_time =
                  output_buffer_delay_time *
                  1000000000; // there should be plenty of space in a uint64_t for any
                              // conceivable current_delay value
              output_buffer_delay_time =
                  output_buffer_delay_time /
                  RATE_FROM_ENCODED_FORMAT(config.current_output_configuration);
              debug(4,
                    "current_delay: %" PRId64 ", output_buffer_delay_time: %.3f, output rate: %u.",
                    current_delay, output_buffer_delay_time * 0.000000001,
                    RATE_FROM_ENCODED_FORMAT(config.current_output_configuration));

              uint64_t the_time_this_frame_will_be_played =
                  delay_measurement_time + output_buffer_delay_time;

              double centered_sync_error_time = 0.0;
              int64_t sync_error_ns = 0;
              int64_t measurement_time = get_absolute_time_in_ns() - mst;

              // debug(1, "measurement time: %" PRId64 " ns.", measurement_time);

              if (measurement_time < 2000000) {

                sync_error_ns =
                    the_time_this_frame_will_be_played - the_time_this_frame_should_be_played;
                sync_error = (sync_error_ns *
                              RATE_FROM_ENCODED_FORMAT(config.current_output_configuration)) /
                             1000000000;

                // debug(1, "measurement time: %" PRId64 " ns. Sync error: %" PRId64 " ns, %" PRId64
                // " frames, skipping_frames_at_start_of_play is %d.", measurement_time,
                // sync_error_ns, sync_error, skipping_frames_at_start_of_play);

                // A timestamp gap is when the timstamp of the next packet of frames is not equal to
                // the previous packet's timstamp + number o frames therein.

                // But wait! If there is a timestamp gap, this isn't really an error.

                // Also, if it's a sync error at the start of a play sequence, then
                // it can be dealt with by inserting a silence or skipping frames.

                // So, here we have enough information to decide what to do with the "frame" of
                // audio.

                // If we are already skipping frames because of a prior first frame,
                // we might need to adjust the skipping count due to a better time estimate

                if (skipping_frames_at_start_of_play != 0) {
                  if (sync_error <= 0) {
                    debug(3,
                          "cancel skipping at start of play -- skip estimate was: %" PRId32
                          ", but sync_error is now: %" PRId64 ".",
                          frames_to_skip, sync_error);
                    frames_to_skip = 0;
                    skipping_frames_at_start_of_play = 0;
                  } else if (frames_to_skip != sync_error) {
                    debug(3,
                          "updating skipping at start of play count from: %" PRId32 " to: %" PRId64
                          ".",
                          frames_to_skip, sync_error);
                    frames_to_skip = sync_error;
                  }
                }

                // If it's the first frame or if it's at a timestamp discontinuity, then we can
                // deal with it straight away.
                if ((inframe != NULL) && ((conn->first_packet_timestamp == inframe->timestamp) ||
                                          (inframe->timestamp_gap != 0))) {

                  // By default, when there is a sync error and some kind of discontinuity,
                  // e.g. a gap between timestamps of adjacent packets or a first packet,
                  // then we try to fix the sync error, either by skipping frames or by inserting a
                  // silence. However, if it's a negative timestamp gap between packets, only try to
                  // fix the timestamp_gap. The reason for this is that we don't know the purpose of
                  // the negative gaps. For all we know, it may be that the audio frames before and
                  // after the gap are meant to be contiguous.

                  int64_t gap_to_fix = sync_error; // this is what we look at normally

                  if (conn->first_packet_timestamp == inframe->timestamp) {
                    debug(3, "first frame: %u, sync_error %" PRId64 " frames.", inframe->timestamp,
                          sync_error);
                    skipping_frames_at_start_of_play = 1;
                  } else {
                    debug(3, "timestamp_gap: %d on frame %u, sync_error %" PRId64 " frames.",
                          inframe->timestamp_gap, inframe->timestamp, sync_error);
                    if (inframe->timestamp_gap < 0) {
                      gap_to_fix = -inframe->timestamp_gap; // this is frames at the input rate
                      int64_t gap_to_fix_ns = (gap_to_fix * 1000000000) / conn->input_rate;
                      gap_to_fix = (gap_to_fix_ns *
                                    RATE_FROM_ENCODED_FORMAT(config.current_output_configuration) + 1000000000/2) /
                                   1000000000; // this is frames at the output rate
                      debug(4, "gap_to_fix: %u frames at input rate, %" PRId64 " frames at output rate.", -inframe->timestamp_gap, gap_to_fix);
                      // debug(3, "due to timstamp gap of %d frames, skip %" PRId64 " output
                      // frames.", inframe->timestamp_gap, gap_to_fix);
                    }
                  }

                  if (gap_to_fix > 0) {
                    // debug(1, "drop %u frames, timestamp: %u, skipping_frames_at_start_of_play is
                    // %d.", gap_to_fix, inframe->timestamp, skipping_frames_at_start_of_play);
                    frames_to_skip += gap_to_fix;
                    sync_error_ns = 0;         // don't invoke any sync checking
                  } else if (gap_to_fix < 0) { // this packet is early, so insert the right number
                                               // of frames to zero the error
                    frames_to_skip = 0;
                    skipping_frames_at_start_of_play = 0;
                    int64_t gap = -gap_to_fix;
                    void *silence = malloc(
                        sps_format_sample_size(
                            FORMAT_FROM_ENCODED_FORMAT(config.current_output_configuration)) *
                        CHANNELS_FROM_ENCODED_FORMAT(config.current_output_configuration) * gap);
                    if (silence == NULL) {
                      debug(1, "Failed to allocate memory for a silent gap.");
                    } else {
                      // the player may change the contents of the buffer, so it has to be zeroed
                      // each time; might as well malloc and free it locally
                      conn->previous_random_number = generate_zero_frames(
                          silence, gap, conn->enable_dither, conn->previous_random_number,
                          config.current_output_configuration);
                      config.output->play(silence, gap, play_samples_are_untimed, 0, 0);
                      free(silence);
                      frames_played += gap;
                      // debug(1,"sent %d frames of silence.", gap);
                      sync_error_ns = 0; // don't invoke any sync checking
                      sync_error = 0;
                    }
                  }
                }
                // debug(1, "frames_to_skip: %u.", frames_to_skip);
                // don't do any sync error calculations if you're skipping frames
                if (frames_to_skip == 0) {
                  // first, make room in the array if it's full
                  if (conn->sync_samples_count == sync_history_length) {
                    conn->sync_samples_count--;
                  }
                  last_sample_index = conn->sync_samples_index;
                  sync_samples[conn->sync_samples_index] = sync_error_ns;
                  conn->sync_samples_count++;
                  conn->sync_samples_index = (conn->sync_samples_index + 1) % sync_history_length;

                  // now find the lowest and highest errors
                  sync_samples_highest_error = sync_samples[0];
                  sync_samples_lowest_error = sync_samples[0];
                  sync_samples_second_highest_error = sync_samples[0];
                  sync_samples_second_lowest_error = sync_samples[0];
                  unsigned int s;
                  int64_t mean = 0;
                  for (s = 0; s < conn->sync_samples_count; s++) {
                    mean += sync_samples[s];
                    if (sync_samples[s] > sync_samples_highest_error) {
                      sync_samples_second_highest_error = sync_samples_highest_error;
                      sync_samples_highest_error = sync_samples[s];
                    } else if (sync_samples[s] > sync_samples_second_highest_error) {
                      sync_samples_second_highest_error = sync_samples[s];
                    } else if (sync_samples[s] < sync_samples_lowest_error) {
                      sync_samples_second_lowest_error = sync_samples_lowest_error;
                      sync_samples_lowest_error = sync_samples[s];
                    } else if (sync_samples[s] < sync_samples_second_lowest_error) {
                      sync_samples_second_lowest_error = sync_samples[s];
                    }
                  }

                  if (conn->sync_samples_count != 0)
                    mean = mean / conn->sync_samples_count;

                  tsum_of_gaps = tsum_of_gaps + sync_samples_second_highest_error -
                                 sync_samples_second_lowest_error;

                  int64_t centered_sync_error_ns =
                      (sync_samples_second_highest_error + sync_samples_second_lowest_error) / 2;
                  centered_sync_error_time = centered_sync_error_ns * 0.000000001;

                  // debug(1, "centered_sync_error_ns: %" PRId64 ", %.3f sec.",
                  // centered_sync_error_ns, centered_sync_error_time);

                  // int64_t centered_sync_error =
                  //     (centered_sync_error_ns * config.current_output_configuration->rate) /
                  //     1000000000;

                  // decide whether to do a stuff

                  /*
                  // calculate the standard deviation

                  double sd = 0.0;
                  for (s = 0; s < conn->sync_samples_count; s++) {
                    sd += pow(sync_samples[s] - mean, 2);
                  }
                  if (conn->sync_samples_count != 0)
                    sd = sqrt(sd / conn->sync_samples_count);

                  // debug(1, "samples: %u, mean: %" PRId64 ", standard deviation: %f.",
                  // conn->sync_samples_count, mean, sd);
                  */

                  // it seems (?) that the standard deviation settles down markedly after 10 samples
                  // == 1024 * 10 frames in AAC
                  if (play_number * inbuflength >= 10 * 1024) {
                    // the tolerance is on either side of the correct, thus it contributes twice
                    // to the overall gap
                    int64_t tolerance_ns = (int64_t)(config.tolerance * 1000000000L);
                    // int64_t gap = 2 * tolerance_ns + sync_samples_second_highest_error -
                    //               sync_samples_second_lowest_error;
                    // int64_t gap = 2 * tolerance_ns;
                    // since the gap should be symmetrical about 0, stuff accordingly
                    if (centered_sync_error_ns > tolerance_ns) {
                      amount_to_stuff = -1 * (inbuflength / 350);
                      if (amount_to_stuff == 0)
                        amount_to_stuff = -1;
                      debug(4, "drop a frame, inbuflength is %d, amount_to_stuff is %d.",
                            inbuflength, amount_to_stuff);
                    } else if (centered_sync_error_ns < (-tolerance_ns)) {
                      amount_to_stuff = +1 * (inbuflength / 350);
                      if (amount_to_stuff == 0)
                        amount_to_stuff = 1;
                      debug(4, "add a frame, inbuflength is %d, amount_to_stuff is %d.",
                            inbuflength, amount_to_stuff);
                    } else {
                      debug(4,
                            "error is within tolerance: centered_sync_error_ns: %" PRId64
                            ", tolerance_ns: %" PRId64 " ns.",
                            centered_sync_error_ns, tolerance_ns);
                    }
                  }
                }
              }

              if (amount_to_stuff)
                debug(4,
                      //                          "stuff: %+d, sync_error: %+5.3f milliseconds.",
                      //                          amount_to_stuff, sync_error * 1000);
                      "stuff: %+d, sync_errors actual: %+5.3f milliseconds, bufferlength: %d, "
                      "sync window : %+5.3f "
                      "milliseconds, prior second highest: %+5.3f milliseconds, prior second "
                      "lowest: %+5.3f "
                      "milliseconds.",
                      amount_to_stuff, sync_error_ns * 0.000001, inbuflength,
                      (sync_samples_second_highest_error - sync_samples_second_lowest_error) *
                          0.000001,
                      sync_samples_second_highest_error * 0.000001,
                      sync_samples_second_lowest_error * 0.000001);

              // now, deal with sync errors and anomalies

              if ((config.no_sync == 0) && (inframe->timestamp != 0) &&
                  (config.resync_threshold > 0.0) &&
                  //                      (fabs(sync_error) > config.resync_threshold)) {
                  (fabs(centered_sync_error_time) > config.resync_threshold) &&
                  // don't count it if the error max and min values bracket (i.e. are on either
                  // size of) zero
                  !((sync_samples_highest_error >= 0) && ((sync_samples_lowest_error <= 0))) &&
                  (conn->sync_samples_count == sync_history_length)) {
                sync_error_out_of_bounds++;
              } else {
                sync_error_out_of_bounds = 0;
              }

              if (sync_error_out_of_bounds != 0) {
                debug(2,
                      "sync error for frame %" PRIu32
                      " out of bounds on %d successive occasions. Error is %.3f milliseconds -- "
                      "resync requested (%u, %" PRId64 ", %" PRId64 ").",
                      inframe->timestamp, sync_error_out_of_bounds, centered_sync_error_time * 1000,
                      conn->sync_samples_count, sync_samples_highest_error,
                      sync_samples_lowest_error);

                if (centered_sync_error_time < 0) {
                  request_resync = 1; // ask for a resync
                } else {
                  int16_t occ = conn->ab_write - conn->ab_read;
                  debug(2,
                        "drop late packet, timestamp: %u,  late by: %.3f ms, packets remaining in "
                        "the buffer: %u.",
                        inframe->timestamp, centered_sync_error_time * 1000, occ);
                  unsigned int s;
                  for (s = 0; s < conn->sync_samples_count; s++) {
                    debug(4, "sample: %u, value: %.3f ms", s, sync_samples[s] * 0.000001);
                  }
                  debug(4, "sync_history_length: %u, samples_count: %u, sample_index: %u",
                        sync_history_length, conn->sync_samples_count, last_sample_index);
                }
                sync_error_out_of_bounds = 0;
                // conn->sync_samples_index = 0;
                // conn->sync_samples_count = 0;
              } else {

                if (config.no_sync != 0)
                  amount_to_stuff = 0; // no stuffing if it's been disabled

                // Apply DSP here

                loudness_update(conn);

                if (conn->do_loudness
#ifdef CONFIG_CONVOLUTION
                    || config.convolution_enabled
#endif
                ) {

                  float(*fbufs)[1024] = malloc(conn->input_num_channels * sizeof(*fbufs));
                  // debug(1, "size of array allocated is %d bytes.", conn->input_num_channels *
                  // sizeof(*fbufs));
                  int32_t *tbuf32 = conn->tbuf;

                  // Deinterleave, and convert to float
                  unsigned int i, j;
                  for (i = 0; i < inframe->length; i++) {
                    for (j = 0; j < conn->input_num_channels; j++) {
                      fbufs[j][i] = tbuf32[conn->input_num_channels * i + j];
                    }
                  }

#ifdef CONFIG_CONVOLUTION
                  // Apply convolution
                  // First, have we got the right convolution setup?

                  static int convolver_is_valid = 0;
                  static size_t current_convolver_block_size = 0;
                  static unsigned int current_convolver_rate = 0;
                  static unsigned int current_convolver_channels = 0;
                  static double current_convolver_maximum_length_in_seconds = 0;

                  if (config.convolution_enabled) {
                    if (
                        // if any of these are true, we need to create a new convolver
                        // (conn->convolver_is_valid == 0) ||
                        (current_convolver_block_size != inframe->length) ||
                        (current_convolver_rate != conn->input_rate) ||
                        !((current_convolver_channels == 1) ||
                          (current_convolver_channels == conn->input_num_channels)) ||
                        (current_convolver_maximum_length_in_seconds !=
                         config.convolution_max_length_in_seconds) ||
                        (config.convolution_ir_files_updated == 1)) {

                      // look for a convolution ir file with a matching rate and channel count

                      convolver_is_valid = 0; // declare any current convolver as invalid
                      current_convolver_block_size = inframe->length;
                      current_convolver_rate = conn->input_rate;
                      current_convolver_channels = conn->input_num_channels;
                      current_convolver_maximum_length_in_seconds =
                          config.convolution_max_length_in_seconds;
                      config.convolution_ir_files_updated = 0;
                      debug(2, "try to initialise a %u/%u convolver.", current_convolver_rate,
                            current_convolver_channels);
                      char *convolver_file_found = NULL;
                      unsigned int ir = 0;
                      while ((ir < config.convolution_ir_file_count) &&
                             (convolver_file_found == NULL)) {
                        if ((config.convolution_ir_files[ir].samplerate ==
                             current_convolver_rate) &&
                            (config.convolution_ir_files[ir].channels ==
                             current_convolver_channels)) {
                          convolver_file_found = config.convolution_ir_files[ir].filename;
                        } else {
                          ir++;
                        }
                      }

                      // if no luck, try for a single-channel IR file
                      if (convolver_file_found == NULL) {
                        current_convolver_channels = 1;
                        ir = 0;
                        while ((ir < config.convolution_ir_file_count) &&
                               (convolver_file_found == NULL)) {
                          if ((config.convolution_ir_files[ir].samplerate ==
                               current_convolver_rate) &&
                              (config.convolution_ir_files[ir].channels ==
                               current_convolver_channels)) {
                            convolver_file_found = config.convolution_ir_files[ir].filename;
                          } else {
                            ir++;
                          }
                        }
                      }
                      if (convolver_file_found != NULL) {
                        // we have an apparently suitable convolution ir file, so lets initialise
                        // a convolver
                        convolver_is_valid = convolver_init(
                            convolver_file_found, conn->input_num_channels,
                            config.convolution_max_length_in_seconds, inframe->length);
                        convolver_wait_for_all();
                        // if (convolver_is_valid)
                        // debug(1, "convolver_init for %u channels was successful.",
                        // conn->input_num_channels); convolver_is_valid = convolver_init(
                        //     convolver_file_found, conn->input_num_channels,
                        //     config.convolution_max_length_in_seconds, inframe->length);
                      }

                      if (convolver_is_valid == 0)
                        debug(1, "can not initialise a %u/%u convolver.", current_convolver_rate,
                              conn->input_num_channels);
                      else
                        debug(1, "convolver: \"%s\".", convolver_file_found);
                    }
                    if (convolver_is_valid != 0) {
                      for (j = 0; j < conn->input_num_channels; j++) {
                        // convolver_process(j, fbufs[j], inframe->length);
                        convolver_process(j, fbufs[j], inframe->length);
                      }
                      convolver_wait_for_all();
                    }

                    // apply convolution gain even if no convolution is done...
                    float gain = pow(10.0, config.convolution_gain / 20.0);
                    for (i = 0; i < inframe->length; ++i) {
                      for (j = 0; j < conn->input_num_channels; j++) {
                        float output_level_db = 0.0;
                        if (fbufs[j][i] < 0.0)
                          output_level_db = 20 * log10(fbufs[j][i] / (float)INT32_MIN * 1.0);
                        else
                          output_level_db = 20 * log10(fbufs[j][i] / (float)INT32_MAX);
                        if (output_level_db > highest_convolver_output_db) {
                          highest_convolver_output_db = output_level_db;
                          if ((highest_convolver_output_db + config.convolution_gain) > 0.0)
                            warn("clipping %.1f dB with convolution gain set to %.1f dB!",
                                 highest_convolver_output_db + config.convolution_gain,
                                 config.convolution_gain);
                        }
                        fbufs[j][i] *= gain;
                      }
                    }
                  }

#endif
                  if (conn->do_loudness) {
                    loudness_process_blocks((float *)fbufs, inframe->length,
                                            conn->input_num_channels,
                                            (float)conn->fix_volume / 65536);
                  }

                  // Interleave and convert back to int32_t
                  for (i = 0; i < inframe->length; i++) {
                    for (j = 0; j < conn->input_num_channels; j++) {
                      tbuf32[conn->input_num_channels * i + j] = fbufs[j][i];
                    }
                  }

                  if (fbufs != NULL) {
                    free(fbufs);
                    fbufs = NULL;
                  }
                }
                // }
#ifdef CONFIG_SOXR
                double t = config.audio_backend_buffer_interpolation_threshold_in_seconds *
                           RATE_FROM_ENCODED_FORMAT(config.current_output_configuration);

                // figure out if we're going for soxr or something else

                if ((current_delay < t) || // delay is too small we definitely won't do soxr
                    (config.packet_stuffing == ST_basic) ||
                    (config.packet_stuffing == ST_vernier) ||
                    ((config.packet_stuffing == ST_auto) &&
                     ((config.soxr_delay_index == 0) || // soxr processing time unknown
                      (config.soxr_delay_index > config.soxr_delay_threshold) // too slow
                      ))) {
                  debug(3, "current_delay: %" PRIu64 ", dac buffer queue minimum length: %f.",
                        current_delay, t);
#endif
                  if (config.packet_stuffing == ST_basic)
                    play_samples = stuff_buffer_basic_32(
                        (int32_t *)conn->tbuf, inbuflength,
                        FORMAT_FROM_ENCODED_FORMAT(config.current_output_configuration),
                        conn->outbuf, amount_to_stuff, conn->enable_dither, conn);
                  else
                    play_samples = stuff_buffer_vernier(
                        (int32_t *)conn->tbuf, inbuflength,
                        FORMAT_FROM_ENCODED_FORMAT(config.current_output_configuration),
                        conn->outbuf, amount_to_stuff, conn->enable_dither, conn);

#ifdef CONFIG_SOXR
                } else { // soxr requested or auto requested with the index less or equal to the
                         // threshold
                  play_samples = stuff_buffer_soxr_32(
                      (int32_t *)conn->tbuf, inbuflength,
                      FORMAT_FROM_ENCODED_FORMAT(config.current_output_configuration), conn->outbuf,
                      amount_to_stuff, conn->enable_dither, conn);
                }
#endif

                if (conn->outbuf == NULL)
                  debug(1, "NULL outbuf to play -- skipping it.");
                else {
                  if (play_samples == 0)
                    debug(2, "nothing to play.");
                  else {
                    if (conn->software_mute_enabled) {
                      generate_zero_frames(conn->outbuf, play_samples, conn->enable_dither,
                                           conn->previous_random_number,
                                           config.current_output_configuration);
                    }
                    uint64_t should_be_time;
                    frame_to_local_time(inframe->timestamp, &should_be_time, conn);
                    // debug(1, "play frame %u.", inframe->timestamp);

                    // now, see if we are skipping some or all of these frames
                    if (frames_to_skip == 0) {
                      config.output->play(conn->outbuf, play_samples, play_samples_are_timed,
                                          inframe->timestamp, should_be_time);
                      frames_played += play_samples;
                    } else {
                      if (frames_to_skip > (unsigned int)play_samples) {
                        debug(3, "skipping a packet of %u frames.", play_samples);
                        debug_print_buffer(
                            4, conn->outbuf,
                            play_samples *
                                CHANNELS_FROM_ENCODED_FORMAT(config.current_output_configuration) *
                                sps_format_sample_size(FORMAT_FROM_ENCODED_FORMAT(
                                    config.current_output_configuration)));
                        frames_to_skip -= play_samples;
                      } else {

                        

                        size_t bytes_to_skip = frames_to_skip *
                            CHANNELS_FROM_ENCODED_FORMAT(config.current_output_configuration) *
                            sps_format_sample_size(
                                FORMAT_FROM_ENCODED_FORMAT(config.current_output_configuration));

                        char *play_starting_point = conn->outbuf + bytes_to_skip;
                          
                        config.output->play(play_starting_point, play_samples - frames_to_skip,
                                            play_samples_are_timed, inframe->timestamp,
                                            should_be_time);

                        debug(4, "skipping the first %u frames in a packet of %u frames.",
                              frames_to_skip, play_samples);

                        debug_print_buffer(4, conn->outbuf, bytes_to_skip);

                        frames_played += play_samples - frames_to_skip;
                        frames_to_skip = 0;
                        skipping_frames_at_start_of_play = 0;
                      }
                    }

#ifdef CONFIG_METADATA
                    // debug(1,"config.metadata_progress_interval is %f.",
                    // config.metadata_progress_interval);
                    if (config.metadata_progress_interval != 0.0) {
                      char hb[128];
                      if (this_is_the_first_frame != 0) {
                        memset(hb, 0, 128);
                        snprintf(hb, 127, "%" PRIu32 "/%" PRId64 "", inframe->timestamp,
                                 should_be_time);
                        send_ssnc_metadata('phb0', hb, strlen(hb), 1);
                        send_ssnc_metadata('phbt', hb, strlen(hb), 1);
                        time_of_last_metadata_progress_update = get_absolute_time_in_ns();
                      } else {
                        uint64_t mx = 1000000000;
                        uint64_t iv = config.metadata_progress_interval * mx;
                        iv = iv + time_of_last_metadata_progress_update;
                        int64_t delta = iv - get_absolute_time_in_ns();
                        if (delta <= 0) {
                          memset(hb, 0, 128);
                          snprintf(hb, 127, "%" PRIu32 "/%" PRId64 "", inframe->timestamp,
                                   should_be_time);
                          send_ssnc_metadata('phbt', hb, strlen(hb), 1);
                          time_of_last_metadata_progress_update = get_absolute_time_in_ns();
                        }
                      }
                    }
#endif
                  }
                }
              }
            } else {

              // if this is the first frame, see if it's close to when it's supposed to be
              // released, which will be its time plus latency and any offset_time

              if (config.packet_stuffing == ST_basic)
                play_samples = stuff_buffer_basic_32(
                    (int32_t *)conn->tbuf, inbuflength,
                    FORMAT_FROM_ENCODED_FORMAT(config.current_output_configuration), conn->outbuf,
                    0, conn->enable_dither, conn);
              else
                play_samples = stuff_buffer_vernier(
                    (int32_t *)conn->tbuf, inbuflength,
                    FORMAT_FROM_ENCODED_FORMAT(config.current_output_configuration), conn->outbuf,
                    0, conn->enable_dither, conn);
              if (conn->outbuf == NULL)
                debug(1, "NULL outbuf to play -- skipping it.");
              else {
                if (conn->software_mute_enabled) {
                  generate_zero_frames(conn->outbuf, play_samples, conn->enable_dither,
                                       conn->previous_random_number,
                                       config.current_output_configuration);
                }
                uint64_t should_be_time;
                frame_to_local_time(inframe->timestamp, &should_be_time, conn);
                debug(3, "play frame %u.", inframe->timestamp);
                config.output->play(conn->outbuf, play_samples, play_samples_are_timed,
                                    inframe->timestamp, should_be_time);
                frames_played += play_samples;
#ifdef CONFIG_METADATA
                // debug(1,"config.metadata_progress_interval is %f.",
                // config.metadata_progress_interval);
                if (config.metadata_progress_interval != 0.0) {
                  char hb[128];
                  if (this_is_the_first_frame != 0) {
                    memset(hb, 0, 128);
                    snprintf(hb, 127, "%" PRIu32 "/%" PRId64 "", inframe->timestamp,
                             should_be_time);
                    send_ssnc_metadata('phb0', hb, strlen(hb), 1);
                    send_ssnc_metadata('phbt', hb, strlen(hb), 1);
                    time_of_last_metadata_progress_update = get_absolute_time_in_ns();
                  } else {
                    uint64_t mx = 1000000000;
                    uint64_t iv = config.metadata_progress_interval * mx;
                    iv = iv + time_of_last_metadata_progress_update;
                    int64_t delta = iv - get_absolute_time_in_ns();
                    if (delta <= 0) {
                      memset(hb, 0, 128);
                      snprintf(hb, 127, "%" PRIu32 "/%" PRId64 "", inframe->timestamp,
                               should_be_time);
                      send_ssnc_metadata('phbt', hb, strlen(hb), 1);
                      time_of_last_metadata_progress_update = get_absolute_time_in_ns();
                    }
                  }
                }
#endif
              }
            }

            if (conn->tbuf) {
              free(conn->tbuf);
              conn->tbuf = NULL;
            }

            if (conn->outbuf) {
              free(conn->outbuf);
              conn->outbuf = NULL;
            }
          }
          tsum_of_frames = tsum_of_frames + frames_played;
          if (frames_played) {
            number_of_statistics++;
            frames_since_last_stats_logged += frames_played;
            // stats accumulation. We want an average of sync error, drift, adjustment,
            // number of additions+subtractions
            tsum_of_sync_errors = tsum_of_sync_errors + sync_error;
            tsum_of_corrections = tsum_of_corrections + amount_to_stuff;
            tsum_of_insertions_and_deletions =
                tsum_of_insertions_and_deletions + abs(amount_to_stuff);
          }
        }
        // free buffers and mark the frame as finished
#ifdef CONFIG_FFMPEG
        if (inframe->avframe != NULL) {
          av_frame_free(&inframe->avframe);
          inframe->avframe = NULL;
        }
#endif
        inframe->timestamp = 0;
        inframe->sequence_number = 0;
        inframe->resend_time = 0;
        inframe->initialisation_time = 0;
        inframe->timestamp_gap = 0;

      } else {
        debug(1, "audio block sequence number %u, ready status: %u with no data!",
              inframe->sequence_number, inframe->ready);
      }
      free_audio_buffer_payload(inframe);
    }
  }

  debug(1, "This should never be called.");
  pthread_cleanup_pop(1); // pop the cleanup handler
                          //  debug(1, "This should never be called either.");
                          //  pthread_cleanup_pop(1); // pop the initial cleanup handler
  pthread_exit(NULL);
}

static void player_send_volume_metadata(uint8_t vol_mode_both, double airplay_volume,
                                        double scaled_attenuation, int32_t max_db, int32_t min_db,
                                        int32_t hw_max_db) {
#ifdef CONFIG_METADATA
  // here, send the 'pvol' metadata message when the airplay volume information
  // is being used by shairport sync to control the output volume
  char dv[128];
  memset(dv, 0, 128);
  if (config.ignore_volume_control == 0) {
    if (vol_mode_both == 1) {
      // normalise the maximum output to the hardware device's max output
      snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume,
               (scaled_attenuation - max_db + hw_max_db) / 100.0,
               (min_db - max_db + hw_max_db) / 100.0, (max_db - max_db + hw_max_db) / 100.0);
    } else {
      snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, scaled_attenuation / 100.0,
               min_db / 100.0, max_db / 100.0);
    }
  } else {
    snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, 0.0, 0.0, 0.0);
  }
  send_ssnc_metadata('pvol', dv, strlen(dv), 1);
#else
  (void)vol_mode_both;
  (void)airplay_volume;
  (void)scaled_attenuation;
  (void)max_db;
  (void)min_db;
  (void)hw_max_db;
#endif
}

void player_volume_without_notification(double airplay_volume, rtsp_conn_info *conn) {
  debug_mutex_lock(&conn->volume_control_mutex, 5000, 4);
  // first, see if we are hw only, sw only, both with hw attenuation on the top or both with sw
  // attenuation on top

  enum volume_mode_type { vol_sw_only, vol_hw_only, vol_both } volume_mode;

  // take account of whether there is a hardware mixer, if a max volume has been specified and if a
  // range has been specified
  // the range might imply that both hw and software mixers are needed, so calculate this

  int32_t hw_max_db = 0, hw_min_db = 0; // zeroed to quieten an incorrect uninitialised warning
  int32_t sw_max_db = 0, sw_min_db = -9630;

  // if the device is giving us a decibel-denominated volume range
  if ((config.output->parameters != NULL) && (config.output->parameters()->volume_range != NULL)) {
    volume_mode = vol_hw_only;
    hw_max_db = config.output->parameters()->volume_range->maximum_volume_dB;
    hw_min_db = config.output->parameters()->volume_range->minimum_volume_dB;
    if (config.volume_max_db_set) {
      if (((config.volume_max_db * 100) <= hw_max_db) &&
          ((config.volume_max_db * 100) >= hw_min_db))
        hw_max_db = (int32_t)config.volume_max_db * 100;
      else if (config.volume_range_db) {
        hw_max_db = hw_min_db;
        sw_max_db = (config.volume_max_db * 100) - hw_min_db;
      } else {
        warn("The maximum output level is outside the range of the hardware mixer -- ignored");
      }
    }

    // here, we have set limits on the hw_max_db and the sw_max_db
    // but we haven't actually decided whether we need both hw and software attenuation
    // only if a range is specified could we need both
    if (config.volume_range_db) {
      // see if the range requested exceeds the hardware range available
      int32_t desired_range_db = (int32_t)trunc(config.volume_range_db * 100);
      if ((desired_range_db) > (hw_max_db - hw_min_db)) {
        volume_mode = vol_both;
        int32_t desired_sw_range = desired_range_db - (hw_max_db - hw_min_db);
        if ((sw_max_db - desired_sw_range) < sw_min_db)
          warn("The range requested is too large to accommodate -- ignored.");
        else
          sw_min_db = (sw_max_db - desired_sw_range);
      } else {
        hw_min_db = hw_max_db - desired_range_db;
      }
    }
  } else {
    // debug(1,"has no hardware mixer");
    volume_mode = vol_sw_only;
    if (config.volume_max_db_set) {
      if (((config.volume_max_db * 100) <= sw_max_db) &&
          ((config.volume_max_db * 100) >= sw_min_db))
        sw_max_db = (int32_t)config.volume_max_db * 100;
    }
    if (config.volume_range_db) {
      // see if the range requested exceeds the software range available
      int32_t desired_range_db = (int32_t)trunc(config.volume_range_db * 100);
      if ((desired_range_db) > (sw_max_db - sw_min_db))
        warn("The range requested is too large to accommodate -- ignored.");
      else
        sw_min_db = (sw_max_db - desired_range_db);
    }
  }

  // here, we know whether it's hw volume control only, sw only or both, and we have the hw and sw
  // limits.
  // if it's both, we haven't decided whether hw or sw should be on top
  // we have to consider the settings ignore_volume_control and mute.

  if (airplay_volume == -144.0) {
    // only mute if you're not ignoring the volume control
    if (config.ignore_volume_control == 0) {
      if ((config.output->mute) && (config.output->mute(1) == 0))
        debug(2,
              "player_volume_without_notification: volume mode is %d, airplay_volume is %f, "
              "hardware mute is enabled.",
              volume_mode, airplay_volume);
      else {
        conn->software_mute_enabled = 1;
        debug(2,
              "player_volume_without_notification: volume mode is %d, airplay_volume is %f, "
              "software mute is enabled.",
              volume_mode, airplay_volume);
      }
    }
    uint8_t vol_mode_both = (volume_mode == vol_both) ? 1 : 0;
    player_send_volume_metadata(vol_mode_both, airplay_volume, 0, 0, 0, 0);
  } else {
    int32_t max_db = 0, min_db = 0;
    switch (volume_mode) {
    case vol_hw_only:
      max_db = hw_max_db;
      min_db = hw_min_db;
      break;
    case vol_sw_only:
      max_db = sw_max_db;
      min_db = sw_min_db;
      break;
    case vol_both:
      // debug(1, "dB range passed is hw: %d, sw: %d, total: %d", hw_max_db - hw_min_db,
      //      sw_max_db - sw_min_db, (hw_max_db - hw_min_db) + (sw_max_db - sw_min_db));
      max_db =
          (hw_max_db - hw_min_db) + (sw_max_db - sw_min_db); // this should be the range requested
      min_db = 0;
      break;
    default:
      debug(1, "player_volume_without_notification: error: not in a volume mode");
      break;
    }
    double scaled_attenuation = max_db;
    if (config.ignore_volume_control == 0) {

      if (config.volume_control_profile == VCP_standard)
        scaled_attenuation = vol2attn(airplay_volume, max_db, min_db); // no cancellation points
      else if (config.volume_control_profile == VCP_flat)
        scaled_attenuation =
            flat_vol2attn(airplay_volume, max_db, min_db); // no cancellation points
      else if (config.volume_control_profile == VCP_dasl_tapered)
        scaled_attenuation =
            dasl_tapered_vol2attn(airplay_volume, max_db, min_db); // no cancellation points
      else
        debug(1, "player_volume_without_notification: unrecognised volume control profile");
    }
    // so here we have the scaled attenuation. If it's for hw or sw only, it's straightforward.
    double hardware_attenuation = 0.0;
    double software_attenuation = 0.0;

    switch (volume_mode) {
    case vol_hw_only:
      hardware_attenuation = scaled_attenuation;
      break;
    case vol_sw_only:
      software_attenuation = scaled_attenuation;
      break;
    case vol_both:
      // here, we now the attenuation required, so we have to apportion it to the sw and hw mixers
      // if we give the hw priority, that means when lowering the volume, set the hw volume to its
      // lowest
      // before using the sw attenuation.
      // similarly, if we give the sw priority, that means when lowering the volume, set the sw
      // volume to its lowest
      // before using the hw attenuation.
      // one imagines that hw priority is likely to be much better
      // if (config.volume_range_hw_priority) {
      if (config.volume_range_hw_priority != 0) {
        // hw priority
        if ((sw_max_db - sw_min_db) > scaled_attenuation) {
          software_attenuation = sw_min_db + scaled_attenuation;
          hardware_attenuation = hw_min_db;
        } else {
          software_attenuation = sw_max_db;
          hardware_attenuation = hw_min_db + scaled_attenuation - (sw_max_db - sw_min_db);
        }
      } else {
        // sw priority
        if ((hw_max_db - hw_min_db) > scaled_attenuation) {
          hardware_attenuation = hw_min_db + scaled_attenuation;
          software_attenuation = sw_min_db;
        } else {
          hardware_attenuation = hw_max_db;
          software_attenuation = sw_min_db + scaled_attenuation - (hw_max_db - hw_min_db);
        }
      }
      break;
    default:
      debug(1, "player_volume_without_notification: error: not in a volume mode");
      break;
    }

    if (((volume_mode == vol_hw_only) || (volume_mode == vol_both)) && (config.output->volume)) {
      config.output->volume(hardware_attenuation); // otherwise set the output to the lowest value
      // debug(1,"Hardware attenuation set to %f for airplay volume of
      // %f.",hardware_attenuation,airplay_volume);
      if (volume_mode == vol_hw_only)
        conn->fix_volume = 0x10000;
    }

    if ((volume_mode == vol_sw_only) || (volume_mode == vol_both)) {
      double temp_fix_volume = 65536.0 * pow(10, software_attenuation / 2000);

      if (config.ignore_volume_control == 0)
        debug(4, "Software attenuation set to %f, i.e %f out of 65,536, for airplay volume of %f",
              software_attenuation, temp_fix_volume, airplay_volume);
      else
        debug(3, "Software attenuation set to %f, i.e %f out of 65,536. Volume control is ignored.",
              software_attenuation, temp_fix_volume);

      conn->fix_volume = temp_fix_volume;
    }
    if (conn != NULL)
      debug(4, "Connection %d: AirPlay Volume set to %.3f, Output Level set to: %.2f dB.",
            conn->connection_number, airplay_volume, scaled_attenuation / 100.0);
    else
      debug(3, "AirPlay Volume set to %.3f, Output Level set to: %.2f dB. NULL conn.",
            airplay_volume, scaled_attenuation / 100.0);

    if (config.logOutputLevel) {
      inform("Output Level set to: %.2f dB.", scaled_attenuation / 100.0);
    }

    uint8_t vol_mode_both = (volume_mode == vol_both) ? 1 : 0;
    player_send_volume_metadata(vol_mode_both, airplay_volume, scaled_attenuation, max_db, min_db,
                                hw_max_db);

    if (config.output->mute)
      config.output->mute(0);
    conn->software_mute_enabled = 0;

    debug(4,
          "player_volume_without_notification: volume mode is %d, airplay volume is %.2f, "
          "software_attenuation dB: %.2f, hardware_attenuation dB: %.2f, muting "
          "is disabled.",
          volume_mode, airplay_volume, software_attenuation / 100.0, hardware_attenuation / 100.0);
  }
  // here, store the volume for possible use in the future
  config.airplay_volume = airplay_volume;
  conn->own_airplay_volume = airplay_volume;
  debug_mutex_unlock(&conn->volume_control_mutex, 4);
}

void player_volume(double airplay_volume, rtsp_conn_info *conn) {
  command_set_volume(airplay_volume);
  player_volume_without_notification(airplay_volume, conn);
}

void do_flush(uint32_t timestamp, rtsp_conn_info *conn) {

  debug(3, "do_flush: flush to %u.", timestamp);
  debug_mutex_lock(&conn->flush_mutex, 1000, 1);
  conn->flush_requested = 1;
  conn->flush_rtp_timestamp = timestamp; // flush all packets up to, but not including, this one.
  reset_input_flow_metrics(conn);
  debug_mutex_unlock(&conn->flush_mutex, 3);
}

void player_flush(uint32_t timestamp, rtsp_conn_info *conn) {
  debug(3, "player_flush");
  do_flush(timestamp, conn);
#ifdef CONFIG_CONVOLUTION
  convolver_clear_state();
#endif
#ifdef CONFIG_METADATA
  // only send a flush metadata message if the first packet has been seen -- it's a bogus message
  // otherwise
  if (conn->first_packet_timestamp) {
    char numbuf[32];
    snprintf(numbuf, sizeof(numbuf), "%u", timestamp);
    send_ssnc_metadata('pfls', numbuf, strlen(numbuf), 1); // contains cancellation points
  }
#endif
}

int player_play(rtsp_conn_info *conn) {
  debug(2, "Connection %d: player_play.", conn->connection_number);
  command_start(); // before startup, and before the prepare() method runs
  // give the output device as much advance warning as possible to get ready
  // and make sure it's done before launching the player thread
  if (config.output->prepare)
    config.output->prepare(); // give the backend its first chance to prepare itself, knowing it has
                              // access to the output device (i.e. knowing that it should not be in
                              // use by another program at this time).

  pthread_cleanup_debug_mutex_lock(&conn->player_create_delete_mutex, 5000, 1);
  if (conn->player_thread == NULL) {
    pthread_t *pt = malloc(sizeof(pthread_t));
    if (pt == NULL)
      die("Couldn't allocate space for pthread_t");

    int rc = named_pthread_create_with_priority(pt, 3, player_thread_func, (void *)conn,
                                                "player_%d", conn->connection_number);
    if (rc)
      debug(1, "Connection %d: error creating player_thread: %s", conn->connection_number,
            strerror(errno));
    conn->player_thread = pt; // set _after_ creation of thread
  } else {
    debug(1, "Connection %d: player thread already exists.", conn->connection_number);
  }
  pthread_cleanup_pop(1); // release the player_create_delete_mutex
#ifdef CONFIG_METADATA
  send_ssnc_metadata('pbeg', NULL, 0, 1); // contains cancellation points
#endif
  conn->is_playing = 1;
  return 0;
}

int player_stop(rtsp_conn_info *conn) {
  // note -- this may be called from another connection thread.
  debug(2, "Connection %d: player_stop.", conn->connection_number);
  int response = 0; // okay
  pthread_cleanup_debug_mutex_lock(&conn->player_create_delete_mutex, 5000, 1);
  pthread_t *pt = conn->player_thread;
  if (pt) {
    debug(3, "player_thread cancel...");
    conn->player_thread = NULL; // cleared _before_ cancelling of thread
    pthread_cancel(*pt);
    debug(3, "player_thread join...");
    if (pthread_join(*pt, NULL) == -1) {
      char errorstring[1024];
      strerror_r(errno, (char *)errorstring, sizeof(errorstring));
      debug(1, "Connection %d: error %d joining player thread: \"%s\".", conn->connection_number,
            errno, (char *)errorstring);
    } else {
      debug(2, "Connection %d: player_stop successful.", conn->connection_number);
    }
    free(pt);
    // reset_anchor_info(conn); // say the clock is no longer valid
#ifdef CONFIG_CONVOLUTION
    convolver_clear_state();
#endif
    response = 0; // deleted
  } else {
    debug(2, "Connection %d: no player thread.", conn->connection_number);
    response = -1; // already deleted or never created...
  }
  pthread_cleanup_pop(1); // release the player_create_delete_mutex
  if (response == 0) {    // if the thread was just stopped and deleted...
    conn->is_playing = 0;
/*
// this is done in the player cleanup handler
#ifdef CONFIG_AIRPLAY_2
    ptp_send_control_message_string("E"); // signify play is "E"nding
#endif
*/
#ifdef CONFIG_METADATA
    send_ssnc_metadata('pend', NULL, 0, 1); // contains cancellation points
#endif
    command_stop();
  }
  return response;
}
